使用 DownloadManager 下载完 apk 自动提示安装

4,273
原文链接: www.jianshu.com

每个Android App都会有版本更新的功能,而下载功能Google官方推荐使用 DownloadManager
服务, 一起来看看如何使用DownloadManager实现版本更新的功能。


recoder.gif

    /**
     * 下载Apk, 并设置Apk地址,
     * 默认位置: /storage/sdcard0/Download
     *
     * @param context    上下文
     * @param downLoadUrl 下载地址
     * @param infoName   通知名称
     * @param description  通知描述
     */
    @SuppressWarnings("unused")
    public static void downloadApk(
            Context context,
            String downLoadUrl,
            String description,
            String infoName) {

        DownloadManager.Request request;
        try {
            request = new DownloadManager.Request(Uri.parse(appUrl));
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        request.setTitle(infoName);
        request.setDescription(description);

        //在通知栏显示下载进度
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        }

        //设置保存下载apk保存路径
        request.setDestinationInExternalPublicDir(CommonCons.SAVE_APP_LOCATION, CommonCons.SAVE_APP_NAME);

        Context appContext = context.getApplicationContext();
        DownloadManager manager = (DownloadManager)
                appContext.getSystemService(Context.DOWNLOAD_SERVICE);
      //进入下载队列
        manager.enqueue(request);

    }

当DownloadManager下载完成后会发出一个广播 android.intent.action.DOWNLOAD_COMPLETE,创建一个广播接收者,处理自动提示安装:

/**
 * 安装下载接收器
 * Created by maimingliang on 2016/8/11.
 */

public class InstallReceiver extends BroadcastReceiver {

    private static final String TAG = "InstallReceiver";


    // 安装下载接收器
    @Override public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
            installApk(context);
        }
    }

    // 安装Apk
    private void installApk(Context context) {

        try {
            Intent i = new Intent(Intent.ACTION_VIEW);
            String filePath = CommonCons.APP_FILE_NAME;
            i.setDataAndType(Uri.parse("file://" + filePath), "application/vnd.android.package-archive");
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        }catch (Exception e){
            Log.e(TAG,"安装失败");
            e.printStackTrace();
        }

}

可以看出,实现起来其实是很容易的。不过要注意下面的几个坑:

坑一


QQ截图20161008112555.png

图上报错的原因是 targetSdkVersion >=23 导致的,在API23以后引入了动态权限的概念。

如何解决?有两种方式:

方法一:

把build.gradle 文件中的targetSdkVersion < 23。这种方式也是最简单的。

方法二:

动态的获取权限:

 @TargetApi(23)
    private void getPersimmions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            ArrayList permissions = new ArrayList();
            /*
             * 读写权限和电话状态权限非必要权限(建议授予)只会申请一次,用户同意或者禁止,只会弹一次
             */
            // 读写权限
            if (addPermission(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                permissionInfo += "Manifest.permission.WRITE_EXTERNAL_STORAGE Deny \n";
            }

            if (permissions.size() > 0) {
                requestPermissions(permissions.toArray(new String[permissions.size()]), SDK_PERMISSION_REQUEST);
            }
        }
    }

    @TargetApi(23)
    private boolean addPermission(ArrayList permissionsList, String permission) {
        if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { // 如果应用没有获得对应权限,则添加到列表中,准备批量申请
            if (shouldShowRequestPermissionRationale(permission)){
                return true;
            }else{
                permissionsList.add(permission);
                return false;
            }

        }else{
            return true;
        }
    }

    @TargetApi(23)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // TODO Auto-generated method stub
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    }

坑二


QQ截图20161008113535.png

有两种原因:

原因一:
没有权限读取这个文件或者文件路径错误。

解决方案:
把apk的路径固定死

String filePath = CommonCons.APP_FILE_NAME;

原因二:
单独设置data和type,如下:

i.setData(Uri.parse("file://" + filePath));
i.setType( "application/vnd.android.package-archive");

坑三


QQ截图20161008113834.png


没有设置FLAG_ACTIVITY_NEW_TASK

i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

坑四


QQ截图20161008112839.png

APK签名不一致, 比如之前是debug版 现在你更新安装release版 .

DownloadManager 下载完 apk 自动提示安装Demo

END.