国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android DownloadProvider 源碼詳解

2019-12-12 04:42:00
字體:
供稿:網(wǎng)友

Android DownloadProvider 源碼分析:

Download的源碼編譯分為兩個部分,一個是DownloadProvider.apk, 一個是DownloadProviderUi.apk.

這兩個apk的源碼分別位于

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

其中,DownloadProvider的部分是下載邏輯的實現(xiàn),而DownloadProviderUi是界面部分的實現(xiàn)。

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

所以還是有不少其它的類來分別進行操作。

DownloadProvider --  數(shù)據(jù)庫操作的封裝,繼承自ContentProvider;
DownloadManager -- 大部分邏輯是進一步封裝數(shù)據(jù)操作,供外部調(diào)用;
DownloadService -- 封裝文件download,delete等操作,并且操縱下載的norification;繼承自Service;
DownloadNotifier -- 狀態(tài)欄Notification邏輯;
DownloadReceiver -- 配合DownloadNotifier進行文件的操作及其Notification;
DownloadList -- Download app主界面,文件界面交互;

下載一般是從Browser里面點擊鏈接開始,我們先來看一下Browser中的代碼

在browser的src/com/Android/browser/DownloadHandler.Java函數(shù)中,我們可以看到一個很完整的Download的調(diào)用,我們在寫自己的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的各種參數(shù),然后最后調(diào)用了DownloadManager的enqueue進行下載,并且在開始后,彈出了開始下載的這個toast。manager是一個DownloadManager的實例,DownloadManager是存在與frameworks/base/core/java/android/app/DownloadManager.java。可以看到enqueue的實現(xiàn)為:

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函數(shù)主要是將Rquest實例分解組成一個ContentValues實例,并且添加到數(shù)據(jù)庫中,函數(shù)返回插入的這條數(shù)據(jù)返回的ID;ContentResolver.insert函數(shù)會調(diào)用到DownloadProvider實現(xiàn)的ContentProvider的insert函數(shù)中去,如果我們?nèi)ゲ榭磇nsert的code的話,我們可以看到操作是很多的。但是我們只需要關(guān)注幾個關(guān)鍵的部分:

...... //將相關(guān)的請求參數(shù),配置等插入到downloads數(shù)據(jù)庫; long rowID = db.insert(DB_TABLE, null, filteredValues); ...... //將相關(guān)的請求參數(shù),配置等插入到request_headers數(shù)據(jù)庫中; 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的調(diào)用了。因為是一個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) 

是去注冊監(jiān)聽Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI的Observer。
而oncreate之后,就會去調(diào)用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的函數(shù)中,我們會向mUpdateHandler發(fā)送一個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后發(fā)送MSG_FINAL_UPDATE Message,主要是為了“any finished operations that didn't trigger an update pass.”         enqueueFinalUpdate();       } else { //如果沒有Active的任務(wù)正在進行,就會停止Service以及其它         if (stopSelfResult(startId)) {           if (DEBUG_LIFECYCLE) Log.v(TAG, "Nothing left; stopped");           getContentResolver().unregisterContentObserver(mObserver);           mScanner.shutdown();           mUpdateThread.quit();         }       }       return true;     }   }; 

這邊的重點是updateLocked()函數(shù)

  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任務(wù)     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是沒有任何內(nèi)容的,info==null         if (info != null) { //從數(shù)據(jù)庫更新最新的Download info信息,來監(jiān)聽數(shù)據(jù)庫的改變并且反應(yīng)到界面上           updateDownload(reader, info, now);         } else { //添加新下載的Dwonload info到mDownloads,并且從數(shù)據(jù)庫讀取新的Dwonload info           info = insertDownloadLocked(reader, now);         } //這里的mDeleted參數(shù)表示的是當我刪除了正在或者已經(jīng)下載的內(nèi)容時,首先數(shù)據(jù)庫會update這個info.mDeleted為true,而不是直接刪除文件         if (info.mDeleted) { //不詳細解釋delete函數(shù),主要是刪除數(shù)據(jù)庫內(nèi)容和現(xiàn)在文件內(nèi)容           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函數(shù):

 public boolean startDownloadIfReady(ExecutorService executor) {     synchronized (this) {       final boolean isReady = isReadyToDownload();       final boolean isActive = mSubmittedTask != null && !mSubmittedTask.isDone();       if (isReady && !isActive) { //更新數(shù)據(jù)庫的任務(wù)狀態(tài)為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);         } //開始下載任務(wù)         mTask = new DownloadThread(             mContext, mSystemFacade, this, mStorageManager, mNotifier);         mSubmittedTask = executor.submit(mTask);       }       return isReady;     }   } 

在DownloadThread的處理中,如果HTTP的狀態(tài)是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中讀取內(nèi)容信息,“in.read(data)”,并且對數(shù)據(jù)庫中文件下載大小進行更新       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);     }   } 

至此,下載文件的流程就說完了,繼續(xù)回到DownloadService的updateLocked()函數(shù)中來;重點來分析DownloadNotifier的updateWith()函數(shù),這個方法用來更新Notification

//這段代碼是根據(jù)不同的狀態(tài)設(shè)置不同的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);       } 
//這段代碼是根據(jù)不同的狀態(tài)來設(shè)置不同的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);         } 

最后調(diào)用mNotifManager.notify(tag, 0, notif);根據(jù)不同的狀態(tài)來設(shè)置不同的Notification的title和description

 感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 乌鲁木齐市| 南江县| 蒙城县| 邯郸县| 怀化市| 佛山市| 包头市| 页游| 原阳县| 济宁市| 临湘市| 丹寨县| 胶南市| 阿拉善左旗| 贺州市| 怀宁县| 广灵县| 错那县| 延寿县| 望谟县| 绥中县| 宜州市| 高州市| 乌审旗| 滦南县| 漾濞| 徐州市| 太仓市| 班玛县| 绍兴县| 武安市| 桂阳县| 河北省| 泸州市| 金山区| 龙岩市| 余干县| 晋州市| 周至县| 本溪| 晋州市|