Android DownloadProvider 源码详解

Android DownloadProvider 源码分析:

Download的源码编译分为两个部分,一个是DownloadProvider.apk, 一个是DownloadProviderUi.apk.

这两个apk的源码分别位于

packages/providers/DownloadProvider/ui/src
packages/providers/DownloadProvider/src

其中,DownloadProvider的部分是下载逻辑的实现,而DownloadProviderUi是界面部分的实现。

然后DownloadProvider里面的下载虽然主要是通过DownloadService进行的操作,但是由于涉及到Notification的更新,下载进度的展示,下载的管理等。

所以还是有不少其它的类来分别进行操作。

DownloadProvider --  数据库操作的封装,继承自ContentProvider;
DownloadManager -- 大部分逻辑是进一步封装数据操作,供外部调用;
DownloadService -- 封装文件download,delete等操作,并且操纵下载的norification;继承自Service;
DownloadNotifier -- 状态栏Notification逻辑;
DownloadReceiver -- 配合DownloadNotifier进行文件的操作及其Notification;
DownloadList -- Download app主界面,文件界面交互;

下载一般是从Browser里面点击链接开始,我们先来看一下Browser中的代码

在browser的src/com/Android/browser/DownloadHandler.Java函数中,我们可以看到一个很完整的Download的调用,我们在写自己的app的时候,也可以对这一段进行参考:

public static void startingDownload(Activity activity,
    String url, String userAgent, String contentDisposition,
    String mimetype, String referer, boolean privateBrowsing, long contentLength,
    String filename, String downloadPath) {
  // java.net.URI is a lot stricter than KURL so we have to encode some
  // extra characters. Fix for b 2538060 and b 1634719
  WebAddress webAddress;
  try {
    webAddress = new WebAddress(url);
    webAddress.setPath(encodePath(webAddress.getPath()));
  } catch (Exception e) {
    // This only happens for very bad urls, we want to chatch the
    // exception here
    Log.e(LOGTAG, "Exception trying to parse url:" + url);
    return;
  } 

  String addressString = webAddress.toString();
  Uri uri = Uri.parse(addressString);
  final DownloadManager.Request request;
  try {
    request = new DownloadManager.Request(uri);
  } catch (IllegalArgumentException e) {
    Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show();
    return;
  }
  request.setMimeType(mimetype);
  // set downloaded file destination to /sdcard/Download.
  // or, should it be set to one of several Environment.DIRECTORY* dirs
  // depending on mimetype?
  try {
    setDestinationDir(downloadPath, filename, request);
  } catch (Exception e) {
    showNoEnoughMemoryDialog(activity);
    return;
  }
  // let this downloaded file be scanned by MediaScanner - so that it can
  // show up in Gallery app, for example.
  request.allowScanningByMediaScanner();
  request.setDescription(webAddress.getHost());
  // XXX: Have to use the old url since the cookies were stored using the
  // old percent-encoded url.
  String cookies = CookieManager.getInstance().getCookie(url, privateBrowsing);
  request.addRequestHeader("cookie", cookies);
  request.addRequestHeader("User-Agent", userAgent);
  request.addRequestHeader("Referer", referer);
  request.setNotificationVisibility(
      DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
  final DownloadManager manager = (DownloadManager) activity
      .getSystemService(Context.DOWNLOAD_SERVICE);
  new Thread("Browser download") {
    public void run() {
      manager.enqueue(request);
    }
  }.start();
  showStartDownloadToast(activity);
}

在这个操作中,我们看到添加了request的各种参数,然后最后调用了DownloadManager的enqueue进行下载,并且在开始后,弹出了开始下载的这个toast。manager是一个DownloadManager的实例,DownloadManager是存在与frameworks/base/core/java/android/app/DownloadManager.java。可以看到enqueue的实现为:

public long enqueue(Request request) {
  ContentValues values = request.toContentValues(mPackageName);
  Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
  long id = Long.parseLong(downloadUri.getLastPathSegment());
  return id;

enqueue函数主要是将Rquest实例分解组成一个ContentValues实例,并且添加到数据库中,函数返回插入的这条数据返回的ID;ContentResolver.insert函数会调用到DownloadProvider实现的ContentProvider的insert函数中去,如果我们去查看insert的code的话,我们可以看到操作是很多的。但是我们只需要关注几个关键的部分:

......
//将相关的请求参数,配置等插入到downloads数据库;
long rowID = db.insert(DB_TABLE, null, filteredValues);
......
//将相关的请求参数,配置等插入到request_headers数据库中;
insertRequestHeaders(db, rowID, values);
......
if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) ==
        Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
      // When notification is requested, kick off service to process all
      // relevant downloads.
//启动DownloadService进行下载及其它工作
      if (Downloads.Impl.isNotificationToBeDisplayed(vis)) {
        context.startService(new Intent(context, DownloadService.class));
      }
    } else {
      context.startService(new Intent(context, DownloadService.class));
    }
    notifyContentChanged(uri, match);
    return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);

在这边,我们就可以看到下载的DownloadService的调用了。因为是一个startService的方法,所以我们在DownloadService里面,是要去走oncreate的方法的。

@Override
public void onCreate() {
  super.onCreate();
  if (Constants.LOGVV) {
    Log.v(Constants.TAG, "Service onCreate");
  } 

  if (mSystemFacade == null) {
    mSystemFacade = new RealSystemFacade(this);
  } 

  mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
  mStorageManager = new StorageManager(this); 

  mUpdateThread = new HandlerThread(TAG + "-UpdateThread");
  mUpdateThread.start();
  mUpdateHandler = new Handler(mUpdateThread.getLooper(), mUpdateCallback);
  mScanner = new DownloadScanner(this);
  mNotifier = new DownloadNotifier(this);
  mNotifier.cancelAll(); 

  mObserver = new DownloadManagerContentObserver();
  getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
      true, mObserver);
}

这边的话,我们可以看到先去启动了一个handler去接收callback的处理

mUpdateThread = new HandlerThread(TAG + "-UpdateThread");
 mUpdateThread.start();
 mUpdateHandler = new Handler(mUpdateThread.getLooper(), mUpdateCallback);

然后去

getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
        true, mObserver)

是去注册监听Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI的Observer。
而oncreate之后,就会去调用onStartCommand方法.

@Override
ublic int onStartCommand(Intent intent, int flags, int startId) {
  int returnValue = super.onStartCommand(intent, flags, startId);
  if (Constants.LOGVV) {
    Log.v(Constants.TAG, "Service onStart");
  }
  mLastStartId = startId;
  enqueueUpdate();
  return returnValue;
}

在enqueueUpdate的函数中,我们会向mUpdateHandler发送一个MSG_UPDATE Message,

private void enqueueUpdate() {
  mUpdateHandler.removeMessages(MSG_UPDATE);
  mUpdateHandler.obtainMessage(MSG_UPDATE, mLastStartId, -1).sendToTarget();
}

mUpdateCallback中接收到并且处理:

private Handler.Callback mUpdateCallback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
      Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
      final int startId = msg.arg1;
      final boolean isActive;
      synchronized (mDownloads) {
        isActive = updateLocked();
      }
      ......
      if (isActive) {
//如果Active,则会在Delayed 5×60000ms后发送MSG_FINAL_UPDATE Message,主要是为了“any finished operations that didn't trigger an update pass.”
        enqueueFinalUpdate();
      } else {
//如果没有Active的任务正在进行,就会停止Service以及其它
        if (stopSelfResult(startId)) {
          if (DEBUG_LIFECYCLE) Log.v(TAG, "Nothing left; stopped");
          getContentResolver().unregisterContentObserver(mObserver);
          mScanner.shutdown();
          mUpdateThread.quit();
        }
      }
      return true;
    }
  };

这边的重点是updateLocked()函数


  private boolean updateLocked() {
    final long now = mSystemFacade.currentTimeMillis(); 

    boolean isActive = false;
    long nextActionMillis = Long.MAX_VALUE;
//mDownloads初始化是一个空的Map<Long, DownloadInfo>
    final Set<Long> staleIds = Sets.newHashSet(mDownloads.keySet()); 

    final ContentResolver resolver = getContentResolver();
//获取所有的DOWNLOADS任务
    final Cursor cursor = resolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
        null, null, null, null);
    try {
      final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);
      final int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID);
//迭代Download Cusor
      while (cursor.moveToNext()) {
        final long id = cursor.getLong(idColumn);
        staleIds.remove(id); 

        DownloadInfo info = mDownloads.get(id);
//开始时,mDownloads是没有任何内容的,info==null
        if (info != null) {
//从数据库更新最新的Download info信息,来监听数据库的改变并且反应到界面上
          updateDownload(reader, info, now);
        } else {
//添加新下载的Dwonload info到mDownloads,并且从数据库读取新的Dwonload info
          info = insertDownloadLocked(reader, now);
        }
//这里的mDeleted参数表示的是当我删除了正在或者已经下载的内容时,首先数据库会update这个info.mDeleted为true,而不是直接删除文件
        if (info.mDeleted) {
//不详细解释delete函数,主要是删除数据库内容和现在文件内容
          if (!TextUtils.isEmpty(info.mMediaProviderUri)) {
        resolver.delete(Uri.parse(info.mMediaProviderUri), null, null);
          }
          deleteFileIfExists(info.mFileName);
          resolver.delete(info.getAllDownloadsUri(), null, null); 

        } else {
          // 开始下载文件
          final boolean activeDownload = info.startDownloadIfReady(mExecutor); 

          // 开始media scanner
          final boolean activeScan = info.startScanIfReady(mScanner);
          isActive |= activeDownload;
          isActive |= activeScan;
        } 

        // Keep track of nearest next action
        nextActionMillis = Math.min(info.nextActionMillis(now), nextActionMillis);
      }
    } finally {
      cursor.close();
    }
    // Clean up stale downloads that disappeared
    for (Long id : staleIds) {
      deleteDownloadLocked(id);
    }
    // Update notifications visible to user
    mNotifier.updateWith(mDownloads.values());
    if (nextActionMillis > 0 && nextActionMillis < Long.MAX_VALUE) {
      final Intent intent = new Intent(Constants.ACTION_RETRY);
      intent.setClass(this, DownloadReceiver.class);
      mAlarmManager.set(AlarmManager.RTC_WAKEUP, now + nextActionMillis,
          PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_ONE_SHOT));
    }
    return isActive;
  }

重点来看看文件的下载,startDownloadIfReady函数:


 public boolean startDownloadIfReady(ExecutorService executor) {
    synchronized (this) {
      final boolean isReady = isReadyToDownload();
      final boolean isActive = mSubmittedTask != null && !mSubmittedTask.isDone();
      if (isReady && !isActive) {
//更新数据库的任务状态为STATUS_RUNNING
        if (mStatus != Impl.STATUS_RUNNING) {
          mStatus = Impl.STATUS_RUNNING;
          ContentValues values = new ContentValues();
          values.put(Impl.COLUMN_STATUS, mStatus);
          mContext.getContentResolver().update(getAllDownloadsUri(), values, null, null);
        }
//开始下载任务
        mTask = new DownloadThread(
            mContext, mSystemFacade, this, mStorageManager, mNotifier);
        mSubmittedTask = executor.submit(mTask);
      }
      return isReady;
    }
  }

在DownloadThread的处理中,如果HTTP的状态是ok的话,会去进行transferDate的处理。

private void transferData(State state, HttpURLConnection conn) throws StopRequestException {
......
in = conn.getInputStream();
......
//获取InputStream和OutPutStream
if (DownloadDrmHelper.isDrmConvertNeeded(state.mMimeType)) {
          drmClient = new DrmManagerClient(mContext);
          final RandomAccessFile file = new RandomAccessFile(
              new File(state.mFilename), "rw");
          out = new DrmOutputStream(drmClient, file, state.mMimeType);
          outFd = file.getFD();
        } else {
          out = new FileOutputStream(state.mFilename, true);
          outFd = ((FileOutputStream) out).getFD();
        }
......
// Start streaming data, periodically watch for pause/cancel
      // commands and checking disk space as needed.
      transferData(state, in, out);
......
}

------

private void transferData(State state, InputStream in, OutputStream out)
      throws StopRequestException {
    final byte data[] = new byte[Constants.BUFFER_SIZE];
    for (;;) {
//从InputStream中读取内容信息,“in.read(data)”,并且对数据库中文件下载大小进行更新
      int bytesRead = readFromResponse(state, data, in);
      if (bytesRead == -1) { // success, end of stream already reached
        handleEndOfStream(state);
        return;
      }
      state.mGotData = true;
//利用OutPutStream写入读取的InputStream,"out.write(data, 0, bytesRead)"
      writeDataToDestination(state, data, bytesRead, out);
      state.mCurrentBytes += bytesRead;
      reportProgress(state);
      }
      checkPausedOrCanceled(state);
    }
  }

至此,下载文件的流程就说完了,继续回到DownloadService的updateLocked()函数中来;重点来分析DownloadNotifier的updateWith()函数,这个方法用来更新Notification

//这段代码是根据不同的状态设置不同的Notification的icon
 if (type == TYPE_ACTIVE) {
        builder.setSmallIcon(android.R.drawable.stat_sys_download);
      } else if (type == TYPE_WAITING) {
        builder.setSmallIcon(android.R.drawable.stat_sys_warning);
      } else if (type == TYPE_COMPLETE) {
        builder.setSmallIcon(android.R.drawable.stat_sys_download_done);
      }
//这段代码是根据不同的状态来设置不同的notification Intent
// Build action intents
      if (type == TYPE_ACTIVE || type == TYPE_WAITING) {
        // build a synthetic uri for intent identification purposes
        final Uri uri = new Uri.Builder().scheme("active-dl").appendPath(tag).build();
        final Intent intent = new Intent(Constants.ACTION_LIST,
            uri, mContext, DownloadReceiver.class);
        intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
            getDownloadIds(cluster));
        builder.setContentIntent(PendingIntent.getBroadcast(mContext,
            0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
        builder.setOngoing(true); 

      } else if (type == TYPE_COMPLETE) {
        final DownloadInfo info = cluster.iterator().next();
        final Uri uri = ContentUris.withAppendedId(
            Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, info.mId);
        builder.setAutoCancel(true); 

        final String action;
        if (Downloads.Impl.isStatusError(info.mStatus)) {
          action = Constants.ACTION_LIST;
        } else {
          if (info.mDestination != Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {
            action = Constants.ACTION_OPEN;
          } else {
            action = Constants.ACTION_LIST;
          }
        } 

        final Intent intent = new Intent(action, uri, mContext, DownloadReceiver.class);
        intent.putExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS,
            getDownloadIds(cluster));
        builder.setContentIntent(PendingIntent.getBroadcast(mContext,
            0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); 

        final Intent hideIntent = new Intent(Constants.ACTION_HIDE,
            uri, mContext, DownloadReceiver.class);
        builder.setDeleteIntent(PendingIntent.getBroadcast(mContext, 0, hideIntent, 0));
      }

//这段代码是更新下载的Progress
if (total > 0) {
          final int percent = (int) ((current * 100) / total);
          percentText = res.getString(R.string.download_percent, percent); 

          if (speed > 0) {
            final long remainingMillis = ((total - current) * 1000) / speed;
            remainingText = res.getString(R.string.download_remaining,
                DateUtils.formatDuration(remainingMillis));
          } 

          builder.setProgress(100, percent, false);
        } else {
          builder.setProgress(100, 0, true);
        }

最后调用mNotifManager.notify(tag, 0, notif);根据不同的状态来设置不同的Notification的title和description

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Android 动画之TranslateAnimation应用详解

    android中提供了4中动画: AlphaAnimation 透明度动画效果 ScaleAnimation 缩放动画效果 TranslateAnimation 位移动画效果 RotateAnimation 旋转动画效果 本节讲解TranslateAnimation动画,TranslateAnimation比较常用,比如QQ,网易新闻菜单条的动画,就可以用TranslateAnimation实现, 通过TranslateAnimation(float fromXDelta, float toXD

  • 解决Android SDK下载和更新失败的方法详解

    最近刚换了电脑,开始搭建Android开发环境的时候,下载SDK总是会出现如下错误: 复制代码 代码如下: Failed to fetch URL http://dl-ssl.google.com/android/repository/addons_list-1.xml. 说dl-ssl.google.com在大陆被强了,解决方法就是修改C:\Windows\System32\drivers\etc\hosts文件.添加一行: 复制代码 代码如下: 74.125.237.1       dl-s

  • android PopupWindow 和 Activity弹出窗口实现方式

    本人小菜一个.目前只见过两种弹出框的实现方式,第一种是最常见的PopupWindow,第二种也就是Activity的方式是前几天才见识过.感觉很霸气哦.没想到,activity也可以做伪窗口. 先贴上最常见的方法,主要讲activity的方法. 一.弹出PopupWindow 复制代码 代码如下: /** * 弹出menu菜单 */ public void menu_press(){ if(!menu_display){ //获取LayoutInflater实例 inflater = (Layo

  • android调试工具DDMS的使用详解

    具体可见http://developer.android.com/tools/debugging/ddms.html. DDMS为IDE和emultor.真正的android设备架起来了一座桥梁.开发人员可以通过DDMS看到目标机器上运行的进程/现成状态,可以 android的屏幕到开发机上,可以看进程的heap信息,可以查看logcat信息,可以查看进程分配内存情况,可以像目标机发送短信以及打电话,可 以像android开发发送地理位置信息.可以像gdb一样attach某一个进程调试. SDK

  • android客户端从服务器端获取json数据并解析的实现代码

    首先客户端从服务器端获取json数据 1.利用HttpUrlConnection 复制代码 代码如下: /**      * 从指定的URL中获取数组      * @param urlPath      * @return      * @throws Exception      */     public static String readParse(String urlPath) throws Exception {                  ByteArrayOutputSt

  • android listview优化几种写法详细介绍

    这篇文章只是总结下getView里面优化视图的几种写法,就像孔乙己写茴香豆的茴字的几种写法一样,高手勿喷,勿笑,只是拿出来分享,有错误的地方欢迎大家指正,谢谢. listview Aviewthatshowsitemsinaverticallyscrollinglist. 一个显示一个垂直的滚动子项的列表视图在android开发中,使用listview的地方很多,用它来展现数据,成一个垂直的视图.使用listview是一个标准的适配器模式,用数据--,界面--xml以及适配器--adapter,

  • android压力测试命令monkey详解

    一.Monkey 是什么?Monkey 就是SDK中附带的一个工具. 二.Monkey 测试的目的?:该工具用于进行压力测试. 然后开发人员结合monkey 打印的日志 和系统打印的日志,结局测试中出现的问题. 三.Monkey 测试的特点?Monkey 测试,所有的事件都是随机产生的,不带任何人的主观性. 四.Monkey 命令详解 1).标准的monkey 命令[adb shell] monkey [options] <eventcount> , 例如:adb shell monkey -

  • android TextView设置中文字体加粗实现方法

    英文设置加粗可以在xml里面设置: 复制代码 代码如下: <SPAN style="FONT-SIZE: 18px">android:textStyle="bold"</SPAN> 英文还可以直接在String文件里面直接这样填写: 复制代码 代码如下: <string name="styled_text">Plain, <b>bold</b>, <i>italic</

  • Android应用开发SharedPreferences存储数据的使用方法

    SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对).SharedPreferences常用来存储一些轻量级的数据. 复制代码 代码如下: //实例化SharedPreferences对象(第一步) SharedPreferences mySharedPreferences= getSharedPreferences("test", Activity.MODE_PRIVATE);

  • Android DownloadProvider 源码详解

    Android DownloadProvider 源码分析: Download的源码编译分为两个部分,一个是DownloadProvider.apk, 一个是DownloadProviderUi.apk. 这两个apk的源码分别位于 packages/providers/DownloadProvider/ui/src packages/providers/DownloadProvider/src 其中,DownloadProvider的部分是下载逻辑的实现,而DownloadProviderUi

  • Android context源码详解及深入分析

    Android context详解 前言: Context都没弄明白,还怎么做Android开发? Activity mActivity =new Activity() 作为Android开发者,不知道你有没有思考过这个问题,Activity可以new吗?Android的应用程序开发采用Java语言,Activity本质上也是一个对象,那上面的写法有什么问题呢?估计很多人说不清道不明.Android程序不像Java程序一样,随便创建一个类,写个main()方法就能运行,Android应用模型是基

  • Android Matrix源码详解

    Matrix的数学原理 在Android中,如果你用Matrix进行过图像处理,那么一定知道Matrix这个类.Android中的Matrix是一个3 x 3的矩阵,其内容如下: Matrix的对图像的处理可分为四类基本变换: Translate           平移变换 Rotate                旋转变换 Scale                  缩放变换 Skew                  错切变换 从字面上理解,矩阵中的MSCALE用于处理缩放变换,MSK

  • Android实现屏幕锁定源码详解

    最近有朋友问屏幕锁定的问题,自己也在学习,网上找了下也没太详细的例子,看的资料书上也没有有关屏幕锁定程序的介绍,下个小决心,自己照着官方文档学习下,现在做好了,废话不多说,先发下截图,看下效果,需要注意的地方会加注释,有问题的朋友可以直接留言,我们共同学习交流,共同提高进步!直接看效果图: 一:未设置密码时进入系统设置的效果图如下: 二:设置密码方式预览: 三:密码解密效果图 四:九宫格解密时的效果图 下面来简单的看下源码吧,此处讲下,这个小DEMO也是临时学习下的,有讲的不明白的地方请朋友直接

  • Android线程间通信Handler源码详解

    目录 前言 01. 用法 02.源码 03.结语 前言 在[Android]线程间通信 - Handler之使用篇主要讲了 Handler 的创建,发送消息,处理消息 三个步骤.那么接下来,我们也按照这三个步骤,从源码中去探析一下它们具体是如何实现的.本篇是关于创建源码的分析. 01. 用法 先回顾一下,在主线程和非主线程是如何创建 Handler 的. //主线程 private val mHandler: Handler = object : Handler(Looper.getMainLo

  • Android开发数据结构算法ArrayList源码详解

    目录 简介 ArrayList源码讲解 初始化 扩容 增加元素 一个元素 一堆元素 删除元素 一个元素 一堆元素 修改元素 查询元素 总结 ArrayList优点 ArrayList的缺点 简介 ArrayList是List接口的一个实现类,它是一个集合容器,我们通常会通过指定泛型来存储同一类数据,ArrayList默认容器大小为10,自身可以自动扩容,当容量不足时,扩大为原来的1.5倍,和上篇文章的Vector的最大区别应该就是线程安全了,ArrayList不能保证线程安全,但我们也可以通过其

  • Spring AOP底层源码详解

    ProxyFactory的工作原理 ProxyFactory是一个代理对象生产工厂,在生成代理对象之前需要对代理工厂进行配置.ProxyFactory在生成代理对象之前需要决定到底是使用JDK动态代理还是CGLIB技术. // config就是ProxyFactory对象 // optimize为true,或proxyTargetClass为true,或用户没有给ProxyFactory对象添加interface if (config.isOptimize() || config.isProxy

  • Java并发编程之ConcurrentLinkedQueue源码详解

    一.ConcurrentLinkedQueue介绍 并编程中,一般需要用到安全的队列,如果要自己实现安全队列,可以使用2种方式: 方式1:加锁,这种实现方式就是我们常说的阻塞队列. 方式2:使用循环CAS算法实现,这种方式实现队列称之为非阻塞队列. 从点到面, 下面我们来看下非阻塞队列经典实现类:ConcurrentLinkedQueue (JDK1.8版) ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全的队列.当我们添加一个元素的时候,它会添加到队列的尾部,当我们

  • Java8中AbstractExecutorService与FutureTask源码详解

    目录 前言 一.AbstractExecutorService 1.定义 2.submit 3.invokeAll 4.invokeAny 二.FutureTask 1.定义 2.构造方法 3.get 4.run/ runAndReset 5. cancel 三.ExecutorCompletionService 1.定义 2.submit 3.take/ poll 总结 前言 本篇博客重点讲解ThreadPoolExecutor的三个基础设施类AbstractExecutorService.F

  • python目标检测SSD算法预测部分源码详解

    目录 学习前言 什么是SSD算法 ssd_vgg_300主体的源码 学习前言 ……学习了很多有关目标检测的概念呀,咕噜咕噜,可是要怎么才能进行预测呢,我看了好久的SSD源码,将其中的预测部分提取了出来,训练部分我还没看懂 什么是SSD算法 SSD是一种非常优秀的one-stage方法,one-stage算法就是目标检测和分类是同时完成的,其主要思路是均匀地在图片的不同位置进行密集抽样,抽样时可以采用不同尺度和长宽比,然后利用CNN提取特征后直接进行分类与回归,整个过程只需要一步,所以其优势是速度

随机推荐