[置顶] DownloadProvider 源码详细分析

DownloadProvider 简介

      DownloadProvider 是Android提供的DownloadManager的增强版,亮点是支持断点下载,提供了“开始下载”,“暂停下载”,“重新下载”,“删除下载”接口。源码下载地址

DownloadProvider 详细分析

     DownloadProvider开始下载的是由DownloadManager 的 enqueue方法启动的,启动一个新的下载任务的时序图

[置顶] DownloadProvider 源码详细分析_第1张图片

       开始新的下载时候会调用DownloadManager的enqueue方法,然后再执行DownloadProvider的insert方法,将下载信息写入数据库,包括下载链接地址等,然后再调用DownloadService的onCreate或者onStartCommand方法。
        DownloadProvider类是非常重要的类,所有操作都跟此类有关,因为要保存下载状态。
    在分析DownloadProvider的insert方法前,先看看insert方法的源码
@Override
public Uri insert(final Uri uri, final ContentValues values) {
	checkInsertPermissions(values);
	SQLiteDatabase db = mOpenHelper.getWritableDatabase();

	// note we disallow inserting into ALL_DOWNLOADS
	int match = sURIMatcher.match(uri);
	if (match != MY_DOWNLOADS) {
		Log.d(Constants.TAG, "calling insert on an unknown/invalid URI: "
				+ uri);
		throw new IllegalArgumentException("Unknown/Invalid URI " + uri);
	}

	ContentValues filteredValues = new ContentValues();

	......

	Integer dest = values.getAsInteger(Downloads.COLUMN_DESTINATION);
	if (dest != null) {
	
		......
		
	}
	Integer vis = values.getAsInteger(Downloads.COLUMN_VISIBILITY);
	if (vis == null) {
		if (dest == Downloads.DESTINATION_EXTERNAL) {
			filteredValues.put(Downloads.COLUMN_VISIBILITY,
					Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
		} else {
			filteredValues.put(Downloads.COLUMN_VISIBILITY,
					Downloads.VISIBILITY_HIDDEN);
		}
	} else {
		filteredValues.put(Downloads.COLUMN_VISIBILITY, vis);
	}
	......

	String pckg = values.getAsString(Downloads.COLUMN_NOTIFICATION_PACKAGE);
	String clazz = values.getAsString(Downloads.COLUMN_NOTIFICATION_CLASS);
	if (pckg != null && (clazz != null || isPublicApi)) {
		......
	}
	......

	//启动下载服务
	Context context = getContext();
	context.startService(new Intent(context, DownloadService.class));

	//插入数据库
	long rowID = db.insert(DB_TABLE, null, filteredValues);
	if (rowID == -1) {
		Log.d(Constants.TAG, "couldn't insert into downloads database");
		return null;
	}

	insertRequestHeaders(db, rowID, values);
	//启动下载服务
	context.startService(new Intent(context, DownloadService.class));
	notifyContentChanged(uri, match);
	return ContentUris.withAppendedId(Downloads.CONTENT_URI, rowID);
}
       每次开始一个新的下载任务,都会插入数据库,然后启动启动下载服务类DownloadService,所以真正处理下载的是后台服务DownloadService,DownloadService中有个下载线程类DownloadThread,DownloadService时序图

[置顶] DownloadProvider 源码详细分析_第2张图片

    如果DownloadService没有启动将会执行onCreate()------>onStartCommand()方法,否则执行onStartCommand()方法。然后执行updateFromProvider()方法启动UpdateThread线程,准备启动DownloadThread线程。
    分析UpdateThread的run方法前先看看run方法的源码:
public void run() {
	Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

	//如果数据里的存储的达到了1000以上时候,将会删除status>200即失败的记录
	trimDatabase();
	removeSpuriousFiles();

	boolean keepService = false;
	// for each update from the database, remember which download is
	// supposed to get restarted soonest in the future
	long wakeUp = Long.MAX_VALUE;
        //会一直在此循环,直到启动完所有下载任务
	for (;;) {
		synchronized (DownloadService.this) {
			if (mUpdateThread != this) {
				throw new IllegalStateException(
						"multiple UpdateThreads in DownloadService");
			}
			if (!mPendingUpdate) {
				mUpdateThread = null;
				if (!keepService) {
					stopSelf();
				}
				if (wakeUp != Long.MAX_VALUE) {
					scheduleAlarm(wakeUp);
				}
				return;
			}
			mPendingUpdate = false;
		}

		long now = mSystemFacade.currentTimeMillis();
		keepService = false;
		wakeUp = Long.MAX_VALUE;
		Set<Long> idsNoLongerInDatabase = new HashSet<Long>(
				mDownloads.keySet());

		Cursor cursor = getContentResolver().query(
				Downloads.ALL_DOWNLOADS_CONTENT_URI, null, null, null,
				null);
		if (cursor == null) {
			continue;
		}
		try {
			DownloadInfo.Reader reader = new DownloadInfo.Reader(
					getContentResolver(), cursor);
			int idColumn = cursor.getColumnIndexOrThrow(Downloads._ID);

			for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor
					.moveToNext()) {
				long id = cursor.getLong(idColumn);
				idsNoLongerInDatabase.remove(id);
				DownloadInfo info = mDownloads.get(id);
				if (info != null) {
					updateDownload(reader, info, now);
				} else {
					info = insertDownload(reader, now);
				}
				if (info.hasCompletionNotification()) {
					keepService = true;
				}
				long next = info.nextAction(now);
				if (next == 0) {
					keepService = true;
				} else if (next > 0 && next < wakeUp) {
					wakeUp = next;
				}
			}
		} finally {
			cursor.close();
		}

		for (Long id : idsNoLongerInDatabase) {
			deleteDownload(id);
		}

		// is there a need to start the DownloadService? yes, if there
		// are rows to be deleted.

		for (DownloadInfo info : mDownloads.values()) {
			if (info.mDeleted) {
				keepService = true;
				break;
			}
		}

		mNotifier.updateNotification(mDownloads.values());

		// look for all rows with deleted flag set and delete the rows
		// from the database
		// permanently
		for (DownloadInfo info : mDownloads.values()) {
			if (info.mDeleted) {
				Helpers.deleteFile(getContentResolver(), info.mId,
						info.mFileName, info.mMimeType);
			}
		}
	}
}
      UpdateThread线程负责从数据库中获取下载任务,该线程不会每次都新建新的对象,只有UpdateThread为空时候才会新建,在此线程的run()方法中有两个for循环,外循环是从数据获取下载任务,确保插入数据库的任务都能被启动,直到启动完所有的下载任务才会退出。内循环是遍历从数据库获取的没完成或者刚开始的下载任务,启动下载。
   DownloadThrea线程是真正负责下载的线程,每次启动一个任务都会创建一个新的下载线程对象(对手机来说会耗很大的CPU,所有要加上同步锁或者线程池来控制同时下载的数量),看看DownloadThread run方法的时序图:
[置顶] DownloadProvider 源码详细分析_第3张图片
    从这个时序图可以看出,这里涉及的源码比较多,在这没有写,看的时候一定要对照的源码来看。其实在下载的时候会发生很多的异常,如网络异常,内存卡容量不足等,所以捕获异常很重要的,捕获后进行相关的处理,这也是体现出考虑全面的地方。

   



你可能感兴趣的:(android,开源代码研究)