http://www.trinea.cn/android/android-downloadmanager-pro/
本文主要介绍如何修改Android系统下载管理,以支持更多的功能及部分bug修改和如何编译生效。目前内容包括暂停下载、继续下载、通知设置NotiExtra和NotiClass、wifi切换到3g自动暂停、Bug修改。
PS: 很多童鞋不是自己做rom,所以就算修改了系统源码编译出来的包在其他系统上也不通用
这里推荐DownloadProvider@Github(并不是我的开源项目,我的项目为TrineaAndroidCommon@Github,包含图片缓存,下拉刷新,静默安装等,欢迎关注),系统下载管理的独立版,可整合进自己的应用,感谢@DONG童鞋提供地址。
下面需要修改的DownloadManager.java所在目录为frameworks/base/core/java/android/app
DownloadInfo.java, DownloadProvider.java,DownloadThread.java文件所在目录为packages/providers/DownloadProvider/src/com/android/providers/downloads
1、暂停、继续下载功能
(1) DownloadProvider类修改
| 
 1 
 | 
 public int update(final Uri uri, final ContentValues values, final String where, final String[] whereArgs) 
 | 
函数,修改后代码如下(只增加了一行有效代码):
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
 | 
 if (Binder.getCallingPid() != Process.myPid()) { 
 filteredValues = new ContentValues(); 
 copyString(Downloads.Impl.COLUMN_APP_DATA, values, filteredValues); 
 copyInteger(Downloads.Impl.COLUMN_VISIBILITY, values, filteredValues); 
 Integer i = values.getAsInteger(Downloads.Impl.COLUMN_CONTROL); 
 if (i != null) { 
 filteredValues.put(Downloads.Impl.COLUMN_CONTROL, i); 
 startService = true; 
 } 
 // trinea BEGIN, added by trinea@trinea.cn 2013/03/01 
 copyInteger(Downloads.Impl.COLUMN_STATUS, values, filteredValues); 
 // trinea END 
 copyInteger(Downloads.Impl.COLUMN_CONTROL, values, filteredValues); 
 copyString(Downloads.Impl.COLUMN_TITLE, values, filteredValues); 
 copyString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI, values, filteredValues); 
 copyString(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues); 
 copyInteger(Downloads.Impl.COLUMN_DELETED, values, filteredValues); 
} else { 
 | 
其中以// trinea BEGIN开头,// trinea END结尾为修改部分,下面代码示例同样如此。因为DownloadProvider安全策略对非该进程id的修改会过滤掉COLUMN_STATUS状态,所以我们需要添加该行。
(2) DownloadThread类修改
| 
 1 
 | 
 private void setupDestinationFile(State state, InnerState innerState) 
 | 
函数中这个注释掉一个else if,如下:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
 | 
 // trinea BEGIN, noted by trinea@trinea.cn 2013/03/01 
//} else if (mInfo.mETag == null && !mInfo.mNoIntegrity) { 
//    // This should‘ve been caught upon failure 
//    if (Constants.LOGVV) { 
//        Log.d(TAG, "setupDestinationFile() unable to resume download, deleting " 
//                + state.mFilename); 
//    } 
//    f.delete(); 
//    throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME, 
//            "Trying to resume a download that can‘t be resumed"); 
// trinea END 
 | 
上面一段代码表示一个验证过程,可以去掉。
mETag为数据库中的etag字段值,代码中没有解释,感觉是一个验证值,类似hashcode。
mNoIntegrity为数据中no_integrity字段值,表示启动下载的应用程序能否验证下载的文件的完整性。不过坑爹的是对于etag和no_integrity都没有提供设置的接口
(3) DownloadManager类中添加对外接口
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
 | 
 /** 
 * pause download, added by trinea@trinea.cn 2013/03/01 
 *  
 * @param ids the IDs of the downloads to be paused 
 * @return the number of downloads actually paused 
 */ 
public int pauseDownload(long... ids) { 
 if (ids == null || ids.length == 0) { 
 // called with nothing to remove! 
 throw new IllegalArgumentException("input param ‘ids‘ can‘t be null"); 
 } 
 ContentValues values = new ContentValues(); 
 values.put(Downloads.Impl.COLUMN_CONTROL, Downloads.Impl.CONTROL_PAUSED); 
 values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PAUSED_BY_APP); 
 if (ids.length == 1) { 
 return mResolver.update(ContentUris.withAppendedId(mBaseUri, ids[0]), values, 
 null, null); 
 } 
 return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), 
 getWhereArgsForIds(ids)); 
} 
/** 
 * resume download, added by trinea@trinea.cn 2013/03/01 
 *  
 * @param ids the IDs of the downloads to be resumed 
 * @return the number of downloads actually resumed 
 */ 
public int resumeDownload(long... ids) { 
 if (ids == null || ids.length == 0) { 
 // called with nothing to remove! 
 throw new IllegalArgumentException("input param ‘ids‘ can‘t be null"); 
 } 
 ContentValues values = new ContentValues(); 
 values.put(Downloads.Impl.COLUMN_CONTROL, Downloads.Impl.CONTROL_RUN); 
 values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_RUNNING); 
 if (ids.length == 1) { 
 return mResolver.update(ContentUris.withAppendedId(mBaseUri, ids[0]), values, 
 null, null); 
 } 
 return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), 
 getWhereArgsForIds(ids)); 
} 
 | 
无论是暂停还是继续我们都是同时把Downloads.Impl.COLUMN_CONTROL和Downloads.Impl.COLUMN_STATUS字段进行修改,因为在DownloadInfo的private boolean isReadyToStart(long now)函数中,会对COLUMN_CONTROL字段进行判断,如果是用户手动暂停的话,是不会自动继续的,部分代码如下:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
 | 
 private boolean isReadyToStart(long now) { 
 if (DownloadHandler.getInstance().hasDownloadInQueue(mId)) { 
 // already running 
 return false; 
 } 
 if (mControl == Downloads.Impl.CONTROL_PAUSED) { 
 // the download is paused, so it‘s not going to start 
 Xlog.i(Constants.DL_ENHANCE, "Download is paused " + 
 "then no need to start"); 
 return false; 
 } 
 …… 
} 
 | 
之后我们直接调用DownloadManager的pauseDownload和resumeDownload接口即可
PS:也可以试试不做第二步的修改,而将第一步DownloadProvider的update函数修改变为
| 
 1 
2 
3 
4 
 | 
 // trinea BEGIN, added by trinea@trinea.cn 2013/03/01 
copyInteger(Downloads.Impl.COLUMN_STATUS, values, filteredValues); 
copyInteger(Downloads.Impl.COLUMN_NO_INTEGRITY, values, filteredValues); 
// trinea END 
 | 
第二步修改变为在public int resumeDownload(long… ids)加入
| 
 1 
2 
3 
 | 
 values.put(Downloads.Impl.COLUMN_CONTROL, Downloads.Impl.CONTROL_RUN); 
values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_RUNNING); 
values.put(Downloads.Impl.COLUMN_NO_INTEGRITY, true); 
 | 
没有亲自试,不过按照逻辑应该也可以。
2、通知栏可以设置NotiExtra和NotiClass
(1) DownloadProvider类中修改private void checkInsertPermissions(ContentValues values)函数
| 
 1 
2 
3 
4 
5 
6 
 | 
 values.remove(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI); 
values.remove(Downloads.Impl.COLUMN_MEDIA_SCANNED); 
// BEGIN, added by trinea@trinea.cn 2013/03/01 
values.remove(Downloads.Impl.COLUMN_NOTIFICATION_CLASS); 
values.remove(Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS); 
// trinea END 
 | 
在DownloadProvider insert之前会调用checkInsertPermissions检查不能被插入的字段插入,这里我们需要允许这两个字段存在。
(2) DownloadManager.Request添加对外接口
在Request中添加接口以及允许字段修改。通过允许设置NotiExtra和NotiClass,我们可以给系统传递更丰富的参数,在通知栏点击相应或是DownloadUi中通过broadcast将这些参数传递出来方便应用调用。
3、wifi切换到3g自动暂停
(1) 修改DownloadInfo.java
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
 | 
 private int checkIsNetworkTypeAllowed(int networkType) { 
 if (mIsPublicApi) { 
 final int flag = translateNetworkTypeToApiFlag(networkType); 
 final boolean allowAllNetworkTypes = mAllowedNetworkTypes == ~0; 
 if (!allowAllNetworkTypes && (flag & mAllowedNetworkTypes) == 0) { 
 return NETWORK_TYPE_DISALLOWED_BY_REQUESTOR; 
 } 
 // trinea BEGIN 
 if (mStatus == Downloads.Impl.STATUS_WAITING_FOR_NETWORK 
 && flag != DownloadManager.Request.NETWORK_WIFI) { 
 return NETWORK_TYPE_DISALLOWED_BY_REQUESTOR; 
 } 
 // trinea END 
 } 
 return checkSizeAllowedForNetwork(networkType); 
} 
 | 
表示等待网络时始终只等待wifi
(2) 修改DownloadReceiver.java
| 
 1 
2 
3 
4 
5 
6 
7 
 | 
 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { 
 NetworkInfo info = (NetworkInfo) 
 intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); 
 if (info != null && info.isConnected()) { 
 startService(context); 
 } 
} else if (action.equals(Constants.ACTION_RETRY)) { 
 | 
修改为:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
 | 
 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { 
 NetworkInfo info = (NetworkInfo) 
 intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); 
 // trinea BEGIN 
 /**  
 * modified by trinea@trinea.cn @2013/04/01, resume download only when network type is wifi 
 */ 
 if (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI) { 
 // trinea END 
 startService(context); 
 } 
} else if (action.equals(Constants.ACTION_RETRY)) { 
 | 
表示只有连接wifi时才唤醒service去检查是否下载
(3) 修改DownloadThread.java
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
 | 
 /** 
 * Check if the download has been paused or canceled, stopping the request appropriately if it 
 * has been. 
 */ 
private void checkPausedOrCanceled(State state) throws StopRequestException { 
 synchronized (mInfo) { 
 if (mInfo.mControl == Downloads.Impl.CONTROL_PAUSED) { 
 Xlog.i(Constants.DL_ENHANCE, "DownloadThread: checkPausedOrCanceled: user pause download"); 
 throw new StopRequestException( 
 Downloads.Impl.STATUS_PAUSED_BY_APP, "download paused by owner"); 
 } 
 if (mInfo.mStatus == Downloads.Impl.STATUS_CANCELED) { 
 throw new StopRequestException(Downloads.Impl.STATUS_CANCELED, "download canceled"); 
 } 
 } 
 // if policy has been changed, trigger connectivity check 
 if (mPolicyDirty) { 
 // trinea BEGIN 
 /**  
 * add by trinea@trinea.cn @2013/04/01, when switched from wifi to 3g, pause all download 
 */ 
 NetworkInfo info = mSystemFacade.getActiveNetworkInfo(mInfo.mUid); 
 if (info != null && !info.isConnected()) { 
 mInfo.mStatus = Downloads.Impl.STATUS_WAITING_FOR_NETWORK; 
 } 
 // trinea END 
 checkConnectivity(); 
 } 
} 
 | 
表示如果网络变化并且表示网络断开时,下载状态变为等待网络。
4、Bug修改
(1) 当存储空间不足时,利用DownloadManager下载,只显示通知栏提示,在下载管理UI中不显示
DownloadManager的Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri)函数修改如下:
| 
 1 
2 
3 
4 
5 
6 
 | 
 if ((mStatusFlags & STATUS_RUNNING) != 0) { 
 parts.add(statusClause("=", Downloads.Impl.STATUS_RUNNING)); 
 // trinea BEGIN 
 parts.add(statusClause("=", Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR)); 
 // trinea END 
} 
 | 
DownloadManager的CursorTranslator类的private int translateStatus(int status) 函数修改如下:
| 
 1 
2 
3 
4 
5 
 | 
 // trinea BEGIN 
case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR: 
// trinea END 
case Downloads.Impl.STATUS_RUNNING: 
 return STATUS_RUNNING; 
 | 
5、编译安装
修改后是需要重新编译的,需同时编译framweork和DownloadProvider。
framework编译命令为:./makeMtk model mm frameworks/base/core/
编译后apk所在路径为out\target\product\model\system\framework\secondary_framework.jar,之后push到system/framework重启即可。编译命令中model为机型,非mtk平台命令有所不同
DownloadProvider编译命令为./makeMtk model mm packages/providers/DownloadProvider/
编译后apk所在路径为out\target\product\model\system\app\DownloadProvider.apk,之后push到system/app即可(可能需要先删除/system/app/目录下的DownloadProvider.odex)
Android下载管理DownloadManager功能扩展和bug修改
原文:http://www.cnblogs.com/tonny-li/p/4531539.html