Android下载管理DownloadManager功能扩展和bug修改

简介:

本文主要介绍如何修改Android系统下载管理,以支持更多的功能及部分bug修改和如何编译生效。目前内容包括暂停下载、继续下载、通知设置NotiExtra和NotiClass、wifi切换到3g自动暂停、Bug修改。

更多下载相关开源项目可见 Android 下载

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类修改

Java

1

public int update(final Uri uri, final ContentValues values, final String where, final String[] whereArgs)

函数,修改后代码如下(只增加了一行有效代码):

Java

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类修改

Java

1

private void setupDestinationFile(State state, InnerState innerState)

函数中这个注释掉一个else if,如下:

Java

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类中添加对外接口

DownloadManager中添加暂停、继续接口

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

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字段进行判断,如果是用户手动暂停的话,是不会自动继续的,部分代码如下:

点击展开代码

之后我们直接调用DownloadManager的pauseDownload和resumeDownload接口即可

PS:也可以试试不做第二步的修改,而将第一步DownloadProvider的update函数修改变为

Java

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)加入

Java

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)函数

Java

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添加对外接口

DownloadManager.Request支持setNotiClass、setNotiExtras

在Request中添加接口以及允许字段修改。通过允许设置NotiExtra和NotiClass,我们可以给系统传递更丰富的参数,在通知栏点击相应或是DownloadUi中通过broadcast将这些参数传递出来方便应用调用。

3、wifi切换到3g自动暂停
(1) 修改DownloadInfo.java

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

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)) {

修改为:

Java

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

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)函数修改如下:

Java

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) 函数修改如下:

Java

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)

目录
相关文章
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
223 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
|
1月前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
68 4
Android开发表情emoji功能开发
|
23天前
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
28 2
|
24天前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
46 3
|
2月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
3月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
433 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
3月前
|
API 开发工具 Android开发
Android源码下载
Android源码下载
456 0
|
3月前
|
存储 监控 数据库
Android经典实战之OkDownload的文件分段下载及合成原理
本文介绍了 OkDownload,一个高效的 Android 下载引擎,支持多线程下载、断点续传等功能。文章详细描述了文件分段下载及合成原理,包括任务创建、断点续传、并行下载等步骤,并展示了如何通过多种机制保证下载的稳定性和完整性。
107 0
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载、编译AOSP源码:基于Ubuntu 21.04,android-12.1.0_r27
文章记录了作者在Ubuntu 21.04服务器上配置环境、下载并编译基于Android 12.1.0_r27版本的AOSP源码的过程,包括解决编译过程中遇到的问题和错误处理方法。
188 0