Android 下载模块分析(DownloadManager和DownloadProvider)
Android下载模块主要有2个部分组成:DownloadManager和DownloadProvider;其中DownloadManager提供接口供调用,具体的实现是 DownloadProvider,包括相关数据信息的保存及文件下载。
- DownloadManager.Request用来请求一个下载
- DownloadManager.Query 用来查询下载信息
- enqueue(Request request):执行下载,返回downloadId,downloadId可用于查询下载信息。
- remove(long ids):删除下载,若下载中取消下载。会同时删除下载文件和记录。
- query(Query query)查询下载信息
- getMaxBytesOverMobile(Context context)通过移动网络下载的最大字节数
- getMimeTypeForDownloadedFile(long id)得到下载的mineType
<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
DownloadManagerdownloadManager=(DownloadManager)getSystemService(DOWNLOAD_SERVICE);//文件下载地址String url="http://v.yingshibao.chuanke.com//001_zongshu.mp4";//创建一个Request对象DownloadManager.Request request=newDownloadManager.Request(Uri.parse(url));//设置下载文件路径request.setDestinationInExternalPublicDir("itbox","zongshu.mp4");//开始下载longdownloadId=downloadManager.enqueue(request);
- setDestinationInExternalFilesDir 设置文件下载路径
- allowScanningByMediaScanner() 表示允许MediaScanner扫描到这个文件夹,默认不允许。
- setTitle()设置下载中通知栏提示的标题
- setDescription()设置下载中通知栏提示的介绍。
- setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)表示下载进行中和下载完成的通知栏是否显示。默认只显示下载中通知。VISIBILITY_VISIBLE_NOTIFY_COMPLETED表示下载完成后显示通知栏提示。VISIBILITY_HIDDEN表示不显示任何通知栏提示,这个需要在AndroidMainfest中添加权限android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
- request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI) 表示下载允许的网络类型,默认在任何网络下都允许下载。有NETWORK_MOBILE、NETWORK_WIFI、NETWORK_BLUETOOTH三种及其组合可供选择。如果只允许wifi下载,而当前网络为3g,则下载会等待。
- request.setAllowedOverRoaming(boolean allow)移动网络情况下是否允许漫游。
- request.setMimeType() 设置下载文件的mineType。因为下载管理Ui中点击某个已下载完成文件及下载完成点击通知栏提示都会根据mimeType去打开文件。
- request.addRequestHeader(String header, String value)添加请求下载的网络链接的http头,比如User-Agent,gzip压缩等
class DownloadChangeObserver extends ContentObserver {private Handler handler;private long downloadId;public DownloadChangeObserver(Handler handler, long downloadId) {super(handler);this.handler = handler;this.downloadId = downloadId;}@Overridepublic void onChange(boolean selfChange) {updateView(handler, downloadId);}}
mContext.getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"),true,new DownloadChangeObserver(handler,downloadId));
public void updateView(Handler handler, long downloadId) {// 获取状态和字节int[] bytesAndStatus = getBytesAndStatus(downloadId);//handler.sendMessage(handler.obtainMessage(0, bytesAndStatus[0],bytesAndStatus[1], bytesAndStatus[2]));}public int[] getBytesAndStatus(long downloadId) {int[] bytesAndStatus = new int[] { -1, -1, 0 };DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);Cursor c = null;try {c = downloadManager.query(query);if (c != null && c.moveToFirst()) {// 当前下载的字节bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));// 总字节数bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));// 状态bytesAndStatus[2] = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));}} finally {if (c != null) {c.close();}}return bytesAndStatus;}
registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));BroadcastReceiver receiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {Log.v("chadm", "action = " + action);}}};

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;}
/*** Inserts a row in the database*/@Overridepublic Uri insert(final Uri uri, final ContentValues values) {//检查权限,如果没有相应权限,则remove相关数据,插入一条空记录checkInsertPermissions(values);SQLiteDatabase db = mOpenHelper.getWritableDatabase();// note we disallow inserting into ALL_DOWNLOADSint match = sURIMatcher.match(uri);//判断Uri,只支持对MY_DOWNLOADS的插入操作if (match != MY_DOWNLOADS) {Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: " + uri);throw new IllegalArgumentException("Unknown/Invalid URI " + uri);}// copy some of the input values as itContentValues filteredValues = new ContentValues();copyString(Downloads.Impl.COLUMN_URI, values, filteredValues);copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues);copyBoolean(Downloads.Impl.COLUMN_NO_INTEGRITY, values, filteredValues);copyString(Downloads.Impl.COLUMN_FILE_NAME_HINT, values, filteredValues);copyString(Downloads.Impl.COLUMN_MIME_TYPE, values, filteredValues);copyBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API, values, filteredValues);boolean isPublicApi =values.getAsBoolean(Downloads.Impl.COLUMN_IS_PUBLIC_API) == Boolean.TRUE;// validate the destination columnInteger dest = values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION);if (dest != null) {if (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)!= PackageManager.PERMISSION_GRANTED&& (dest == Downloads.Impl.DESTINATION_CACHE_PARTITION|| dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING|| dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION)) {throw new SecurityException("setting destination to : " + dest +" not allowed, unless PERMISSION_ACCESS_ADVANCED is granted");}// for public API behavior, if an app has CACHE_NON_PURGEABLE permission, automatically// switch to non-purgeable downloadboolean hasNonPurgeablePermission =getContext().checkCallingPermission(Downloads.Impl.PERMISSION_CACHE_NON_PURGEABLE)== PackageManager.PERMISSION_GRANTED;if (isPublicApi && dest == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE&& hasNonPurgeablePermission) {dest = Downloads.Impl.DESTINATION_CACHE_PARTITION;}if (dest == Downloads.Impl.DESTINATION_FILE_URI) {getContext().enforcePermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,Binder.getCallingPid(), Binder.getCallingUid(),"need WRITE_EXTERNAL_STORAGE permission to use DESTINATION_FILE_URI");checkFileUriDestination(values);} else if (dest == Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION) {getContext().enforcePermission(android.Manifest.permission.ACCESS_CACHE_FILESYSTEM,Binder.getCallingPid(), Binder.getCallingUid(),"need ACCESS_CACHE_FILESYSTEM permission to use system cache");}filteredValues.put(Downloads.Impl.COLUMN_DESTINATION, dest);}// validate the visibility columnInteger vis = values.getAsInteger(Downloads.Impl.COLUMN_VISIBILITY);if (vis == null) {if (dest == Downloads.Impl.DESTINATION_EXTERNAL) {filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);} else {filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY,Downloads.Impl.VISIBILITY_HIDDEN);}} else {filteredValues.put(Downloads.Impl.COLUMN_VISIBILITY, vis);}// copy the control column as iscopyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues);/** requests coming from* DownloadManager.addCompletedDownload(String, String, String,* boolean, String, String, long) need special treatment*/if (values.getAsInteger(Downloads.Impl.COLUMN_DESTINATION) ==Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {// these requests always are marked as 'completed'filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_SUCCESS);filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES,values.getAsLong(Downloads.Impl.COLUMN_TOTAL_BYTES));filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);copyInteger(Downloads.Impl.COLUMN_MEDIA_SCANNED, values, filteredValues);copyString(Downloads.Impl._DATA, values, filteredValues);copyBoolean(Downloads.Impl.COLUMN_ALLOW_WRITE, values, filteredValues);} else {filteredValues.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);filteredValues.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);filteredValues.put(Downloads.Impl.COLUMN_CURRENT_BYTES, 0);}// set lastupdate to current timelong lastMod = mSystemFacade.currentTimeMillis();filteredValues.put(Downloads.Impl.COLUMN_LAST_MODIFICATION, lastMod);// use packagename of the caller to set the notification columnsString pckg = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE);String clazz = values.getAsString(Downloads.Impl.COLUMN_NOTIFICATION_CLASS);if (pckg != null && (clazz != null || isPublicApi)) {int uid = Binder.getCallingUid();try {if (uid == 0 || mSystemFacade.userOwnsPackage(uid, pckg)) {filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, pckg);if (clazz != null) {filteredValues.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS, clazz);}}} catch (PackageManager.NameNotFoundException ex) {/* ignored for now */}}// copy some more columns as iscopyString(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, values, filteredValues);copyString(Downloads.Impl.COLUMN_COOKIE_DATA, values, filteredValues);copyString(Downloads.Impl.COLUMN_USER_AGENT, values, filteredValues);copyString(Downloads.Impl.COLUMN_REFERER, values, filteredValues);// UID, PID columnsif (getContext().checkCallingPermission(Downloads.Impl.PERMISSION_ACCESS_ADVANCED)== PackageManager.PERMISSION_GRANTED) {copyInteger(Downloads.Impl.COLUMN_OTHER_UID, values, filteredValues);}filteredValues.put(Constants.UID, Binder.getCallingUid());if (Binder.getCallingUid() == 0) {copyInteger(Constants.UID, values, filteredValues);}// copy some more columns as iscopyStringWithDefault(Downloads.Impl.COLUMN_TITLE, values, filteredValues, "");copyStringWithDefault(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues, "");// is_visible_in_downloads_ui columnif (values.containsKey(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI)) {copyBoolean(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, values, filteredValues);} else {// by default, make external downloads visible in the UIboolean isExternal = (dest == null || dest == Downloads.Impl.DESTINATION_EXTERNAL);filteredValues.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, isExternal);}// public api requests and networktypes/roaming columnsif (isPublicApi) {copyInteger(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, values, filteredValues);copyBoolean(Downloads.Impl.COLUMN_ALLOW_ROAMING, values, filteredValues);copyBoolean(Downloads.Impl.COLUMN_ALLOW_METERED, values, filteredValues);}if (Constants.LOGVV) {Log.v(Constants.TAG, "initiating download with UID "+ filteredValues.getAsInteger(Constants.UID));if (filteredValues.containsKey(Downloads.Impl.COLUMN_OTHER_UID)) {Log.v(Constants.TAG, "other UID " +filteredValues.getAsInteger(Downloads.Impl.COLUMN_OTHER_UID));}}//将数据插入到DB里面long rowID = db.insert(DB_TABLE, null, filteredValues);if (rowID == -1) {Log.d(Constants.TAG, "couldn't insert into downloads database");return null;}//将请求头数据插入到DB里面insertRequestHeaders(db, rowID, values);//通知有内容改变notifyContentChanged(uri, match);// Always start service to handle notifications and/or scanningfinal Context context = getContext();//启动DownloadService开始下载context.startService(new Intent(context, DownloadService.class));return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);}
@Overridepublic 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;}
private void enqueueUpdate() {mUpdateHandler.removeMessages(MSG_UPDATE);mUpdateHandler.obtainMessage(MSG_UPDATE, mLastStartId, -1).sendToTarget();}
private Handler.Callback mUpdateCallback = new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);final int startId = msg.arg1;if (DEBUG_LIFECYCLE) Log.v(TAG, "Updating for startId " + startId);// Since database is current source of truth, our "active" status// depends on database state. We always get one final update pass// once the real actions have finished and persisted their state.// TODO: switch to asking real tasks to derive active state// TODO: handle media scanner timeoutsfinal boolean isActive;synchronized (mDownloads) {isActive = updateLocked();}if (msg.what == MSG_FINAL_UPDATE) {// Dump thread stacks belonging to poolfor (Map.Entry<Thread, StackTraceElement[]> entry :Thread.getAllStackTraces().entrySet()) {if (entry.getKey().getName().startsWith("pool")) {Log.d(TAG, entry.getKey() + ": " + Arrays.toString(entry.getValue()));}}// Dump speed and update detailsmNotifier.dumpSpeeds();Log.wtf(TAG, "Final update pass triggered, isActive=" + isActive+ "; someone didn't update correctly.");}if (isActive) {// Still doing useful work, keep service alive. These active// tasks will trigger another update pass when they're finished.// Enqueue delayed update pass to catch finished operations that// didn't trigger an update pass; these are bugs.enqueueFinalUpdate();} else {// No active tasks, and any pending update messages can be// ignored, since any updates important enough to initiate tasks// will always be delivered with a new startId.if (stopSelfResult(startId)) {if (DEBUG_LIFECYCLE) Log.v(TAG, "Nothing left; stopped");getContentResolver().unregisterContentObserver(mObserver);mScanner.shutdown();mUpdateThread.quit();}}return true;}};
private boolean updateLocked() {final long now = mSystemFacade.currentTimeMillis();boolean isActive = false;long nextActionMillis = Long.MAX_VALUE;final Set<Long> staleIds = Sets.newHashSet(mDownloads.keySet());final ContentResolver resolver = getContentResolver();final Cursor cursor = resolver.query(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,null, null, null, null);try {//更新DB里面所有下载记录final DownloadInfo.Reader reader = new DownloadInfo.Reader(resolver, cursor);final int idColumn = cursor.getColumnIndexOrThrow(Downloads.Impl._ID);while (cursor.moveToNext()) {final long id = cursor.getLong(idColumn);staleIds.remove(id);DownloadInfo info = mDownloads.get(id);//如果下载信息保存在mDownloads里面,则直接更新,由于我们是新添加的一个任务,info为空,走insertDownloadLocked这一步if (info != null) {updateDownload(reader, info, now);} else {//创建一个新的DownloadInfo,然后添加到mDownloads里面去info = insertDownloadLocked(reader, now);}if (info.mDeleted) {// Delete download if requested, but only after cleaning upif (!TextUtils.isEmpty(info.mMediaProviderUri)) {resolver.delete(Uri.parse(info.mMediaProviderUri), null, null);}deleteFileIfExists(info.mFileName);resolver.delete(info.getAllDownloadsUri(), null, null);} else {// Kick off download task if ready 准备开始下载final boolean activeDownload = info.startDownloadIfReady(mExecutor);// Kick off media scan if completedfinal boolean activeScan = info.startScanIfReady(mScanner);if (DEBUG_LIFECYCLE && (activeDownload || activeScan)) {Log.v(TAG, "Download " + info.mId + ": activeDownload=" + activeDownload+ ", activeScan=" + activeScan);}isActive |= activeDownload;isActive |= activeScan;}// Keep track of nearest next actionnextActionMillis = Math.min(info.nextActionMillis(now), nextActionMillis);}} finally {cursor.close();}// Clean up stale downloads that disappearedfor (Long id : staleIds) {deleteDownloadLocked(id);}// Update notifications visible to usermNotifier.updateWith(mDownloads.values());// Set alarm when next action is in future. It's okay if the service// continues to run in meantime, since it will kick off an update pass.if (nextActionMillis > 0 && nextActionMillis < Long.MAX_VALUE) {if (Constants.LOGV) {Log.v(TAG, "scheduling start in " + nextActionMillis + "ms");}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;}
public boolean startDownloadIfReady(ExecutorService executor) {synchronized (this) {//判断是否可以下载,由于mControl为0,返回truefinal boolean isReady = isReadyToDownload();//判断是否有任务正在进行,对象是新创建的,mSubmittedTask 为空final boolean isActive = mSubmittedTask != null && !mSubmittedTask.isDone();if (isReady && !isActive) {//如果当前状态不是正在下载,将当前状态更新为正在下载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;}}
@Overridepublic void run() {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);try {runInternal();} finally {mNotifier.notifyDownloadSpeed(mInfo.mId, 0);}}private void runInternal() {// Skip when download already marked as finished; this download was// probably started again while racing with UpdateThread.if (DownloadInfo.queryDownloadStatus(mContext.getContentResolver(), mInfo.mId)== Downloads.Impl.STATUS_SUCCESS) {Log.d(TAG, "Download " + mInfo.mId + " already finished; skipping");return;}State state = new State(mInfo);PowerManager.WakeLock wakeLock = null;int finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR;int numFailed = mInfo.mNumFailed;String errorMsg = null;final NetworkPolicyManager netPolicy = NetworkPolicyManager.from(mContext);final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);try {wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);wakeLock.setWorkSource(new WorkSource(mInfo.mUid));wakeLock.acquire();// while performing download, register for rules updatesnetPolicy.registerListener(mPolicyListener);Log.i(Constants.TAG, "Download " + mInfo.mId + " starting");// Remember which network this download started on; used to// determine if errors were due to network changes.final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mInfo.mUid);if (info != null) {state.mNetworkType = info.getType();}// Network traffic on this thread should be counted against the// requesting UID, and is tagged with well-known value.TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_DOWNLOAD);TrafficStats.setThreadStatsUid(mInfo.mUid);try {// TODO: migrate URL sanity checking into client side of APIstate.mUrl = new URL(state.mRequestUri);} catch (MalformedURLException e) {throw new StopRequestException(STATUS_BAD_REQUEST, e);}//执行下载executeDownload(state);finalizeDestinationFile(state);finalStatus = Downloads.Impl.STATUS_SUCCESS;} catch (StopRequestException error) {// remove the cause before printing, in case it contains PIIerrorMsg = error.getMessage();String msg = "Aborting request for download " + mInfo.mId + ": " + errorMsg;Log.w(Constants.TAG, msg);if (Constants.LOGV) {Log.w(Constants.TAG, msg, error);}finalStatus = error.getFinalStatus();// Nobody below our level should request retries, since we handle// failure counts at this level.if (finalStatus == STATUS_WAITING_TO_RETRY) {throw new IllegalStateException("Execution should always throw final error codes");}// Some errors should be retryable, unless we fail too many times.if (isStatusRetryable(finalStatus)) {if (state.mGotData) {numFailed = 1;} else {numFailed += 1;}if (numFailed < Constants.MAX_RETRIES) {final NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mInfo.mUid);if (info != null && info.getType() == state.mNetworkType&& info.isConnected()) {// Underlying network is still intact, use normal backofffinalStatus = STATUS_WAITING_TO_RETRY;} else {// Network changed, retry on any next availablefinalStatus = STATUS_WAITING_FOR_NETWORK;}}}// fall through to finally block} catch (Throwable ex) {errorMsg = ex.getMessage();String msg = "Exception for id " + mInfo.mId + ": " + errorMsg;Log.w(Constants.TAG, msg, ex);finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR;// falls through to the code that reports an error} finally {if (finalStatus == STATUS_SUCCESS) {TrafficStats.incrementOperationCount(1);}TrafficStats.clearThreadStatsTag();TrafficStats.clearThreadStatsUid();cleanupDestination(state, finalStatus);notifyDownloadCompleted(state, finalStatus, errorMsg, numFailed);Log.i(Constants.TAG, "Download " + mInfo.mId + " finished with status "+ Downloads.Impl.statusToString(finalStatus));netPolicy.unregisterListener(mPolicyListener);if (wakeLock != null) {wakeLock.release();wakeLock = null;}}mStorageManager.incrementNumDownloadsSoFar();}
/*** Fully execute a single download request. Setup and send the request,* handle the response, and transfer the data to the destination file.*/private void executeDownload(State state) throws StopRequestException {state.resetBeforeExecute();//设置下载文件相关信息,文件是否存在、是否从0开始下载还是接着下载setupDestinationFile(state);// skip when already finished; remove after fixing race in 5217390if (state.mCurrentBytes == state.mTotalBytes) {Log.i(Constants.TAG, "Skipping initiating request for download " +mInfo.mId + "; already completed");return;}while (state.mRedirectionCount++ < Constants.MAX_REDIRECTS) {// Open connection and follow any redirects until we have a useful// response with body.HttpURLConnection conn = null;try {checkConnectivity();conn = (HttpURLConnection) state.mUrl.openConnection();conn.setInstanceFollowRedirects(false);conn.setConnectTimeout(DEFAULT_TIMEOUT);conn.setReadTimeout(DEFAULT_TIMEOUT);addRequestHeaders(state, conn);final int responseCode = conn.getResponseCode();switch (responseCode) {case HTTP_OK:if (state.mContinuingDownload) {throw new StopRequestException(STATUS_CANNOT_RESUME, "Expected partial, but received OK");}processResponseHeaders(state, conn);transferData(state, conn);return;case HTTP_PARTIAL:if (!state.mContinuingDownload) {throw new StopRequestException(STATUS_CANNOT_RESUME, "Expected OK, but received partial");}transferData(state, conn);return;case HTTP_MOVED_PERM:case HTTP_MOVED_TEMP:case HTTP_SEE_OTHER:case HTTP_TEMP_REDIRECT:final String location = conn.getHeaderField("Location");state.mUrl = new URL(state.mUrl, location);if (responseCode == HTTP_MOVED_PERM) {// Push updated URL back to databasestate.mRequestUri = state.mUrl.toString();}continue;case HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:throw new StopRequestException(STATUS_CANNOT_RESUME, "Requested range not satisfiable");case HTTP_UNAVAILABLE:parseRetryAfterHeaders(state, conn);throw new StopRequestException(HTTP_UNAVAILABLE, conn.getResponseMessage());case HTTP_INTERNAL_ERROR:throw new StopRequestException(HTTP_INTERNAL_ERROR, conn.getResponseMessage());default:StopRequestException.throwUnhandledHttpError(responseCode, conn.getResponseMessage());}} catch (IOException e) {// Trouble with low-level socketsthrow new StopRequestException(STATUS_HTTP_DATA_ERROR, e);} finally {if (conn != null) conn.disconnect();}}throw new StopRequestException(STATUS_TOO_MANY_REDIRECTS, "Too many redirects");}
private void addRequestHeaders(State state, HttpURLConnection conn) {for (Pair<String, String> header : mInfo.getHeaders()) {conn.addRequestProperty(header.first, header.second);}// Only splice in user agent when not already definedif (conn.getRequestProperty("User-Agent") == null) {conn.addRequestProperty("User-Agent", userAgent());}// Defeat transparent gzip compression, since it doesn't allow us to// easily resume partial downloads.conn.setRequestProperty("Accept-Encoding", "identity");if (state.mContinuingDownload) {if (state.mHeaderETag != null) {conn.addRequestProperty("If-Match", state.mHeaderETag);}conn.addRequestProperty("Range", "bytes=" + state.mCurrentBytes + "-");}}
public void onReceive(final Context context, final Intent intent) {if (mSystemFacade == null) {mSystemFacade = new RealSystemFacade(context);}String action = intent.getAction();if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {if (Constants.LOGVV) {Log.v(Constants.TAG, "Received broadcast intent for " +Intent.ACTION_BOOT_COMPLETED);}startService(context);} else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {if (Constants.LOGVV) {Log.v(Constants.TAG, "Received broadcast intent for " +Intent.ACTION_MEDIA_MOUNTED);}startService(context);} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {final ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);final NetworkInfo info = connManager.getActiveNetworkInfo();if (info != null && info.isConnected()) {startService(context);}} else if (action.equals(Constants.ACTION_RETRY)) {startService(context);} else if (action.equals(Constants.ACTION_OPEN)|| action.equals(Constants.ACTION_LIST)|| action.equals(Constants.ACTION_HIDE)) {final PendingResult result = goAsync();if (result == null) {// TODO: remove this once test is refactoredhandleNotificationBroadcast(context, intent);} else {sAsyncHandler.post(new Runnable() {@Overridepublic void run() {handleNotificationBroadcast(context, intent);result.finish();}});}}}
Android 下载模块分析(DownloadManager和DownloadProvider)的更多相关文章
- android Camera模块分析
Android Camera Module Architecture and Bottom layer communication mechanism ----------- ...
- Android WIFI模块分析
一:什么是WIFI WIFI是一种无线连接技术.可用于手机.电脑.PDA等终端. WIFI技术产生的目的是改善基于IEEE802.11标准的无线网络产品之间的互通性,也就是说WIFI是基于802.11 ...
- android 新闻应用、Xposed模块、酷炫的加载动画、下载模块、九宫格控件等源码
Android精选源码 灵活的ShadowView,可替代CardView使用 基于Tesseract-OCR实现自动扫描识别手机号 Android播放界面仿QQ音乐开源音乐播放器 新闻应用项目采用了 ...
- Android平台APK分析工具包androguard的部署使用和原理分析
原创文章,转载请注明出处,谢谢. Android应用程序分析主要有静态分析和动态分析两种,常见的静态分析工具是Apktool.dex2jar以及jdgui.今天突然主要到Google code上有个叫 ...
- Qualcomm Android display架构分析
Android display架构分析(一) http://blog.csdn.net/BonderWu/archive/2010/08/12/5805961.aspx http://hi.baidu ...
- android usb挂载分析---MountService启动
android usb挂载分析---MountService启动 分类: android框架 u盘挂载2012-03-27 23:00 11799人阅读 评论(4) 收藏 举报 androidsock ...
- 高通Android display架构分析
目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...
- React Native Android原生模块开发实战|教程|心得|怎样创建React Native Android原生模块
尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://blog.csdn.net/fengyuzhengfan/article/details/54691503) 告诉大家一个好消息. ...
- ZT 4.3 android bluetooth hfp分析
4.3 android bluetooth hfp分析 2013-08-20 20:16 592人阅读 评论(3) 收藏 举报 所有程序执行的代码都是有入口的,在这里我们暂时分析一种情景,蓝牙打开着, ...
随机推荐
- JS开发调试
开发调试工具 页面制作之开发调试工具(1) 开发工具介绍 开发工具一般分为两种类型:文本编辑器和集成开发环境(IDE) 常用的文本编辑器:Sublime Text.Notepad++.EditPl ...
- 关于webbrowser控件自动登陆的问题
原文:关于webbrowser控件自动登陆的问题 楼主dtb(陈少)2004-03-04 22:16:55 在 VB / 网络编程 提问 请问怎么用webbrowser自动登陆www.jp168.co ...
- code forces 148D Bag of mice (概率DP)
time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standa ...
- SQL表连接
背景 在上次的自考科目<数据库系统原理>中.已经接触到了关于数据库表连接的一些知识,近期的学习过程中又用到了关于数据库表的连接问题,趁此再跟大家一起回想一下. 导图总结 首先用一张思维导图 ...
- NET 2015
.NET 2015 英文原文:Understanding .NET 2015 Understanding 翻译为了解或理解,对于 .NET 来说,2015 年注定会更加精彩,所以标题就用了“精彩”这个 ...
- Facebook HHVM 和 Hack 手册----1.什么是Hack?
什么是Hack? Hack 是一种基于HHVM(HipHop VM 是Facebook推出的用来执行PHP代码的虚拟机,它是一个PHP的JIT编译器,同时具有产生快速代码和即时编译的优点)的新型编程语 ...
- linux 在系统启动过程
从学习<鸟哥linux私人厨房> 用于在计算机系统启动,计算机硬件和软件由(它包含的操作系统软件)包括.对于操作系统在同一台计算机硬件方面的表现,该系统配备有硬件是公用,不同的系统是 的操 ...
- ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则
原文:ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则 对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象, ...
- Linux常用命令汇总-速查
对Linux新手有用的20个命令 对中级Linux用户有用的20个命令 对Linux专家非常有用的20个命令 20个最受欢迎的Linux命令 20个有趣的Linux命令
- 从一道数学题弹程序员的思维:数学题,求证:(a+b%c)%c=(a+b)%c
在学校论坛看到这道题目,全忘了的感觉. 如果你是高中的,那我觉得你完全没问题.但是,在这个博客园的圈子,觉得全部人都是程(ban)序(zhuan)员(gong)相关的人员,解决这个问题有点难度,毕竟, ...