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

 

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

Click To Expand Code

 

之后我们直接调用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)

Android下载管理DownloadManager功能扩展和bug修改的更多相关文章

  1. android下载管理、理财、浏览器、商品筛选、录音源码等

    Android精选源码 android仿美拍直播的点赞动画   android视频播放器完美切换全屏.小窗口源码   android类似随手记理财类源码   android简单浏览器源码   andr ...

  2. Android公共库(缓存 下拉ListView 下载管理Pro 静默安装 root运行 Java公共类)

    介绍总结的一些android公共库,包含缓存(图片缓存.预取缓存).公共View(下拉及底部加载更多ListView.底部加载更多ScrollView.滑动一页Gallery).及Android常用工 ...

  3. Android使用AsyncTask实现可以断点续传的DownloadManager功能

    http://www.it165.net/pro/html/201211/4210.html 最近做项目卡壳了,要做个Android的应用市场,其他方面都还好说,唯独这个下载管理算是给我难住了,究其原 ...

  4. Chrome 应用推荐 - 下载管理扩展: Chrono

    地址:http://goo.gl/JVdxvg Chrono下载管理器让你轻松高效地管理Chrome浏览器中的下载任务.Chrono与Chrome浏览器紧密地整合在一起,如菜单.工具栏支持等等.Chr ...

  5. DownloadManager 下载管理类

    演示 简介 从Android 2.3开始新增了一个下载管理类,在SDK的文档中我们查找android.app.DownloadManager可以看到.下载管理类可以长期处理多个HTTP下载任务,客户端 ...

  6. Android 下载模块分析(DownloadManager和DownloadProvider)

    Android下载模块主要有2个部分组成:DownloadManager和DownloadProvider:其中DownloadManager提供接口供调用,具体的实现是 DownloadProvid ...

  7. Android 进价5_自定义广播 通过广播更新ListView的适配器 下载管理

    1.在处理下载管理时,服务在后台运行,下载完成后要更新listview列表的按钮,将“下载”改成“打开”这样一个功能. 在Activity里面写一个静态内部类,继承广播.其中属性text_button ...

  8. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  9. Android线程管理之AsyncTask异步任务

    前言: 前面几篇文章主要学习了线程以及线程池的创建与使用,今天来学习一下AsyncTask异步任务,学习下AsyncTask到底解决了什么问题?然而它有什么弊端?正所谓知己知彼百战百胜嘛! 线程管理相 ...

随机推荐

  1. Verilog对数据进行四舍五入(round)与饱和(saturation)截位

    转自https://www.cnblogs.com/liujinggang/p/10549095.html 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件 ...

  2. 编写自己的dapper lambda扩展-使用篇

    前言 这是针对dapper的一个扩展,支持lambda表达式的写法,链式风格让开发者使用起来更加优雅.直观.现在暂时只有MsSql的扩展,也没有实现事务的写法,将会在后续的版本补充. 这是个人业余的开 ...

  3. Android恶意样本数据集汇总

    硕士论文的研究方向为Android恶意应用分类,因此花了一点时间去搜集Android恶意样本.其中一部分来自过去论文的公开数据集,一部分来自社区或平台的样本.现做一个汇总,标明了样本或数据集的采集时间 ...

  4. slurmdbd.conf系统初始配置

    # Archive info ArchiveJobs=yes ArchiveDir=/usr/local/globle/softs/slurm/16.05.3/archive/ ArchiveStep ...

  5. Centos6.9下RabbitMQ集群部署记录

    之前简单介绍了CentOS下单机部署RabbltMQ环境的操作记录,下面详细说下RabbitMQ集群知识,RabbitMQ是用erlang开发的,集群非常方便,因为erlang天生就是一门分布式语言, ...

  6. Centos7下部署两套python版本并存环境的操作记录

    需求说明:centos7.2系统的开发机器上已经自带了python2.7版本,但是开发的项目中用的是python3.5版本,为了保证Centos系统的正常运行,以及节省机器资源(不想因此再申请另外一台 ...

  7. 《linux内核设计与实现》第五章

    第五章 系统调用 一.与内核通信 系统调用在用户空间进程和硬件设备之间添加了一个中间层.作用: 为用户空间提供了一种硬件的抽象接口. 系统调用保证了系统的稳定和安全. 每个进程都运行在虚拟系统中,而在 ...

  8. GuiHelloWorld

    package com.home.test; import java.awt.Color; import java.awt.Cursor; import java.awt.Font; import j ...

  9. 用软件工程分析开源项目octave的移植

    在本科的时候学习了软件工程,报考了信息系统项目管理师的考试,,虽然没有过,但是其实还是学了一些相关项目管理方面的知识,,9大管理,,当年应该能背出来,,, 1 项目整体管理 2 项目范围管理 3 项目 ...

  10. synchronized关键字的学习与总结