Android DownloadManager 更新应用

DownloadManagerAndroid 处理长期运行的 HTTP 下载的系统服务

客户端可以请求的 URI 被下载到一个特定的目标文件

客户端可以在后台与 http 交互进行下载,或者在下载失败,或者连接改变,重新启动系统后重新下载,还可以进入系统的下载管理界面查看进度

DownloadManager

DownloadManger 有两个内部类 RequestQuery

  1. Request 类可设置下载的一些属性
  2. Query 类可查询当前下载的进度,下载地址,文件存放目录等数据

所需权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

获得对象,开始下载

DownloadManager dm = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Request req = new DownloadManager.Request(Uri.parse(url));
long id = dm.enqueue(req);

每下载的一个文件对应一个 id ,通过此 id 可以查询数据

取消下载

dm.remove(id1, id2, id3);  

返回成功取消的下载的个数,如果一个下载被取消了,所有相关联的文件,部分下载的文件和完全下载的文件都会被删除.

Request 类

Request 类用于设置下载的一些属性

指定下载位置,及文件名称

/**
 * 目录: Android -> data -> 包名 -> files -> Download -> xxx.mp3
 * 这个文件是你的应用所专用的,软件卸载后,下载的文件将随着卸载全部被删除
 */
req.setDestinationInExternalFilesDir( this , Environment.DIRECTORY_DOWNLOADS ,  "xxx.mp3" );  

/**
 * 下载的文件存放地址  SD 卡 download 文件夹,xxx.mp3
 * 软件卸载后,下载的文件会保留
 */
//在 SD 卡上创建一个文件夹
req.setDestinationInExternalPublicDir("/epmyg/"  , "xxx.mp3" ) ;  

/**
 * 如果下载的文件希望被其他的应用共享
 * 特别是那些你下载下来希望被 Media Scanner 扫描到的文件(比如音乐文件)
 */
req.setDestinationInExternalPublicDir( Environment.DIRECTORY_MUSIC,  "xxx.mp3" );  

/**
 * 文件将存放在外部存储的确实 download 文件内,如果无此文件夹,创建之,如果有,下面将返回 false。
 * 系统有个下载文件夹,比如小米手机系统下载文件夹  SD卡 --> Download 文件夹
 */
//创建目录
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdir() ; 

//设置文件存放路径
req.setDestinationInExternalPublicDir(  Environment.DIRECTORY_DOWNLOADS  , "xxx.mp3" ) ;
}

指定下载的网络类型

  1. 指定在 WIFI 状态下,执行下载操作

    req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
    
  2. 指定在 MOBILE 状态下,执行下载操作

    req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE);
    
  3. 是否允许漫游状态下,执行下载操作

    req.setAllowedOverRoaming(boolean);
    
  4. 是否允许 "计量式的网络连接" 执行下载操作,默认是允许的

    req.setAllowedOverMetered(boolean);
    

定制 Notification 样式

  1. 设置 Notification 的标题和描述

    req.setTitle("标题");  
    req.setDescription("描述");
    
  2. 设置 Notification 的显示,和隐藏

    request.setNotificationVisibility(visibility);
    

    参数可选的值有

    说明
    VISIBILTY_HIDDEN Notification 将不会显示,如果设置该属性的话,必须要添加权限
    VISIBILITY_VISIBLE Notification 显示,但是只是在下载任务执行的过程中显示,下载完成自动消失(默认值)
    VISIBILITY_VISIBLE_NOTIFY_COMPLETED Notification 显示,下载进行时,和完成之后都会显示
    VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION 只有当任务完成时,Notification 才会显示

    如果参数为 VISIBILTY_HIDDEN 则需要添加权限

    android.permission.DOWNLOAD_WITHOUT_NOTIFICATION
    

设置下载文件类型

req.setMimeType("application/vnd.android.package-archive");

这是安卓 .apk 文件的类型

有些机型必须设置此方法,才能在下载完成后,点击通知栏的 Notification 时,才能正确的打开安装界面

不然会弹出一个 Toast(can not open file)

添加请求下载的网络链接的 http 头,比如 User-Agent,gzip 压缩等

req.addRequestHeader(String header, String value);

Query 类

可能我们不仅想要在 Notification 中显示进度就好了,还想要在 APP 中也需要获取实时下载进度

这就会用到 Query

Query 只有两个方法,因为它把数据保存在数据库中去了

我们需要获得一个 Cursor 结果集,通过结果集获得我们想要的数据

DownloadManager.Query query = new DownloadManager.Query();
Cursor cursor = downloadManager.query(query.setFilterById(id));

if (cursor != null && cursor.moveToFirst()) {

    //  下载的文件到本地的目录
    String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));

    //已经下载的字节数
    int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));

    //  总需下载的字节数
    int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

    // Notification 标题
    String title =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));

    // 描述
    String description =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION));

    // 下载对应 id
    long id =cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));

    //下载文件名称
    String filename =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));

    // 下载文件的URL链接
    String url =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI));
}

这只能获取一次,数据库中的信息

如果要时时获取,可以使用 Timer 类,每隔一段时间去查询数据库即可

使用 DownloadManager 更新应用并覆盖安装


  1. 创建一个 空的 Android 项目 cn.twle.android.DownloadManager

  2. 我们要更新的版本地址为 /static/i/android/downloadmanager_v2.0.apk

    如果你改了包名等则需要你自己制作一个更高的版本

  3. 修改 activity_main.xml 创建下载界面

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:gravity="center"
        android:padding="16dp"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="downloadmanager_v2.0.apk"/>
        <ProgressBar
            android:id="@+id/pb_update"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="10dp"
            android:layout_gravity="center_horizontal"
            android:max="100"
            android:progress="0"
            />
    
        <TextView
            android:id="@+id/progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="0%"/>
    
        <Button
            android:id="@+id/down"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="立即更新"/>
    </LinearLayout>
    
  4. 修改 AndroidManifest.xml 添加权限

    <uses-permission android:name="android.permission.INTERNET" />;
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>;
    
  5. 修改 AndroidManifest.xml<application>...</application> 之间注册一个 Provider

    <provider
         android:name="android.support.v4.content.FileProvider"
         android:authorities="${applicationId}.provider"
         android:exported="false"
         android:grantUriPermissions="true">
         <meta-data
             android:name="android.support.FILE_PROVIDER_PATHS"
             android:resource="@xml/provider_paths" />
    </provider>
    
  6. 然后在 res 下新建资源目录 xml,在 res/xml 下新建文件 provider_paths.xml

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="external_files" path="."/>
    </paths>
    
  7. 修改 MainActivity.java

    package cn.twle.android.downloadmanager;
    
    import android.app.Activity;
    import android.app.DownloadManager;
    import android.content.Intent;
    import android.database.Cursor;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.support.v4.content.FileProvider;
    import android.util.Log;
    import android.view.View;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    import java.io.File;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class MainActivity extends Activity implements View.OnClickListener {
    
        private TextView down;
        private TextView progress;
        private ProgressBar pb_update;
        private DownloadManager downloadManager;
        private DownloadManager.Request request;
    
        public static String filename  = "downloadmanager_v2.0.apk";
        public static String downloadUrl = "http://192.168.0.105:4000/static/i/android/";
        Timer timer;
        long id;
        TimerTask task;
    
        Handler handler =new Handler(){
            @Override
            public void handleMessage(Message msg) {
    
                super.handleMessage(msg);
    
                Bundle bundle = msg.getData();
                int pro = bundle.getInt("pro");
                pb_update.setProgress(pro);
                progress.setText(String.valueOf(pro)+"%");
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            down = (TextView) findViewById(R.id.down);
            down.setOnClickListener(this);
    
            progress = (TextView) findViewById(R.id.progress);
    
            pb_update = (ProgressBar) findViewById(R.id.pb_update);
            pb_update.setMax(100);
    
            downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
            request = new DownloadManager.Request(Uri.parse(downloadUrl + filename));
    
            request.setTitle("简单教程");
            request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
            request.setAllowedOverRoaming(false);
            request.setMimeType("application/vnd.android.package-archive");
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    
            //创建目录
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdir() ;
    
            //设置文件存放路径
            request.setDestinationInExternalPublicDir(  Environment.DIRECTORY_DOWNLOADS  , filename) ;
    
            final  DownloadManager.Query query = new DownloadManager.Query();
    
            timer = new Timer();
            task = new TimerTask() {
                @Override
                public void run() {
                    Cursor cursor = downloadManager.query(query.setFilterById(id));
                    if (cursor != null && cursor.moveToFirst()) {
                        if (cursor.getInt(
                                cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
                            pb_update.setProgress(100);
                            install(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename );
                            task.cancel();
                        }
                        String title = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));
                        String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
    
                        int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                        int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                        int pro =  (bytes_downloaded * 100) / bytes_total;
    
                        Message msg =Message.obtain();
                        Bundle bundle = new Bundle();
                        bundle.putInt("pro",pro);
                        bundle.putString("name",title);
                        msg.setData(bundle);
                        handler.sendMessage(msg);
                    }
                    cursor.close();
                }
            };
            timer.schedule(task, 0,1000);
        }
    
        @Override
        public void onClick(View v)
        {
            id = downloadManager.enqueue(request);
            task.run();
            down.setClickable(false);
    
        }
    
        private void install(String path) {
    
            try {
    
                Log.d("download:",path);
                Intent intent = new Intent(Intent.ACTION_VIEW);
    
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 4.0以上系统弹出安装成功打开界面
    
                //设置intent的data和Type属性android 7.0以上crash,改用provider
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    
                    Uri fileUri = FileProvider.getUriForFile(this, getPackageName()+".provider", new File(path));
                    intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }else {
                    intent.setDataAndType(Uri.parse("file://" + path), "application/vnd.android.package-archive");
                }
                //跳转
                startActivity(intent);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

参考文档

  1. Android DownloadManager

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.