Android 利用DownLoadManager下载与安装apk(适配7.0 和8.0)

相关权限配置

//在AndroidManifest.xml 添加以下权限

<!--网络通信权限-->
<uses-permission android:name="android.permission.INTERNET"/>
<!--SD卡写入数据权限,注意:如果编译版本大于API 23 (6.0以上),需要注意该动态权限的申请 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>   
<!--Android8.0的诸多新特性中有一个非常重要的特性:未知来源应用权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!--VISIBILITY_HIDDEN表示不显示任何通知栏提示的权限-->
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>

大致流程如下:

  • 使用DownLoadManager下载升级Apk
  • 注册一个BroadcastReceiver来监听DownLoadManager下载完成后发送的广播
  • 调用系统去安装Apk

下载安装工具类DownLoadUtils

public class DownloadUtils {

    private static final String TAG = "DownloadUtils";


    //下载器
    private DownloadManager downloadManager;
    //上下文
    private Context mContext;
    //下载的ID
    private long downloadId;

    public DownloadUtils(Context context) {
        this.mContext = context;
    }


    public void downloadApk(String downLoadUrl, String description) {

        String apkName = downLoadUrl.substring(downLoadUrl.lastIndexOf("/") + 1);

        MyLogger.d(TAG, "DownLoadUrl: %s \nDownLoadDescription: \n%s", downLoadUrl, description);
        DownloadManager.Request request;
        try {
            request = new DownloadManager.Request(Uri.parse(downLoadUrl));
        } catch (Exception e) {
            MyLogger.d(TAG, "downLoad failed :%s", e.getLocalizedMessage());
            return;
        }

        request.setTitle("下载Notification的标题内容");
        request.setDescription(description);

        //在通知栏显示下载进度
        request.allowScanningByMediaScanner();
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

        //设置保存下载apk保存路径
        request.setDestinationInExternalPublicDir("/Download/", apkName);

        //获取DownloadManager
        downloadManager = (DownloadManager) mContext.getApplicationContext().getSystemService(Context.DOWNLOAD_SERVICE);

        //将下载请求加入下载队列,加入下载队列后会给该任务返回一个long型的id,通过该id可以取消任务,重启任务、获取下载的文件等等
        downloadId = downloadManager.enqueue(request);

        //注册广播接收者,监听下载状态
        mContext.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

    //广播监听下载的各个状态
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
                Uri downloadFileUri = downloadManager.getUriForDownloadedFile(downloadId);
                installApk(downloadFileUri, context);
            }
        }
    };


    private String getRealFilePath(Context context, Uri uri) {
        if (null == uri) return null;
        final String scheme = uri.getScheme();
        String data = null;
        if (scheme == null)
            data = uri.getPath();
        else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
            data = uri.getPath();
        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
            Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
            if (null != cursor) {
                if (cursor.moveToFirst()) {
                    int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                    if (index > -1) {
                        data = cursor.getString(index);
                    }
                }
                cursor.close();
            }
        }
        return data;
    }


    private void installApk(Uri downLoadApkUri, Context context) {

        if (downLoadApkUri == null) {
            MyLogger.e("Download apk failed,empty apk uri");
            return;
        } else {
            MyLogger.d(TAG, "Download apk finish ,apkUri:%s", downLoadApkUri.toString());
        }

        //获取待安装的apk文件
        File apkFile = new File(getRealFilePath(context, downLoadApkUri));
        if (!apkFile.exists()) {
            MyLogger.d(TAG, "Apk file is not exist.");
            return;
        }

        //调用系统安装apk
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {   //7.0版本以上
            Uri uriForFile = FileProvider.getUriForFile(context, "com.example.app.fileProvider", apkFile);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(uriForFile, "application/vnd.android.package-archive");
            MyLogger.d(TAG, "Install apk,\ndata: %s", uriForFile);
        } else {
            Uri uri = Uri.fromFile(apkFile);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
            MyLogger.d(TAG, "Install apk,\ndata: %s", uri);
        }

        try {
            context.startActivity(intent);
        } catch (Exception e) {
            MyLogger.e(TAG, "Start system install activity exception: %s", e.getLocalizedMessage());
        }

    }
}


AndroidManifest.xml文件新增


 

res目录下新增xml文件夹,添加一个file_paths.xml文件,内容如下


    

path="."其实是对应外置SD卡的根目录
官方文档:https://developer.android.com/reference/android/support/v4/content/FileProvider.html

注意事项

  • API 23版本以上编译的应用,如果指定的Apk下载目录为外部目录,要留意读写外部目录动态权限的申请

  • 为了提高私有文件的安全性,面向Android 7.0 或更高版本的应用私有目录被限制访问

  • 如果Apk下载成功后能跳到系统安装apk页面但是安装失败,请确保两个Apk签名版本一样。

  • 传递给DownLoadManager的Apk下载地址Url不能为需要重定向的地址,否则会失败

  • DownloadManager#getUriForDownloadedFile(long)该方法获取到的Uri路径会因版本而异

    //获取apk下载成功后的Uri路径:             
    //7.0以上   content://downloads/all_downloads/{downloadId}
    //7.0以下   file:///storage/sdcard/Download/{apkName}
    DownloadManager.getUriForDownloadedFile(downloadId)
    

你可能感兴趣的:(android)