Android在线应用内更新(站内更新) 适配6.0、7.0、8.0

概要
在线应用内更新 在APP开发中是最基础的一项功能。主要可以分为以下几步:

  1. 获取当前版本信息;
  2. 获取后台线上版本信息;
  3. 版本对比,提示更新
  4. 点击取消,跳过更新,进入app
  5. 点击确定,开始下载
  6. 下载结束,提示安装

正文
这里略过1—4步骤,主要讲下载及安装部分。

文件的下载有很多中方式与第三方框架,这里由于仅仅是下载应用,所有综合考虑,强烈建议使用安卓 SDK 自带下载框架 DownloadManager 。

1.最基础也是重要的 申请文件读写权限、网络权限,8.0系统需要获取“未知应用安装权限”


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

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

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

2.检测版本与安装权限

由于 8.0 “未知应用安装权限”被除掉,替换为“未知来源应用安装权限”,默认是关闭状态,需要用户手动开启才能安装最新版本安装包,所以这里需要判断是否为 8.0 系统,然后获取是否有该权限。
为了用户体验比较好,这里我在 onResume 中进行了版本判断。其中 haveInstallPermission 为 boolean 类型,默认为true。

//版本判断
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    //获取是否有安装未知来源应用的权限
     haveInstallPermission = MainActivity.this.getPackageManager().canRequestPackageInstalls();
}

3.封装下载请求

这里由于注释很详细,就不多做解释,直接上代码。关于 DownloadManager.Request 的属性详解可以查看这里

public void downloadAPK() {
   //已存在 -- 删除
   File apkFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxxx.apk");
   if (apkFile != null && apkFile.exists()) {
       apkFile.delete();
   }
   //下载 (AppCont.Update_URL 是你的app下载地址)
   DownloadManager.Request request = new DownloadManager.Request(Uri.parse(AppCont.Update_URL));
   //设置title
 request.setTitle(GetResourcesUtil.getString(R.string.app_name));
   // 完成后显示通知栏
 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
   //设置存储路径 --这个是应用专用的,软件卸载后,下载的文件将随着卸载全部被删除
   request.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, "xxx.apk");
   //设置 文件类型
   request.setMimeType("application/vnd.android.package-archive");
   downloadManager.enqueue(request);
}

4.开始下载

在第二步中拿到了 是否有安装未知来源应用的权限 ,可以在用户点击确定下载最新版本时去判断,如果没有权限,跳转到权限授权界面;如果有权限,则直接下载更新。

if (!haveInstallPermission) {
    ToastUtils.showShort("请打开安装未知来源应用的权限");
    //没有权限 在 yourAppPackageName 设置你的app包名
    Uri packageURI = Uri.parse("package:" + yourAppPackageName);
    Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
    MainActivity.this.startActivityForResult(intent, 010);
}else {
    diaglog.dismiss();
    ToastUtils.showShort("正在下载....");
    //下载更新
    updateUtil = new UpdateUtil(MainActivity.this);
    updateUtil.downloadAPK();
}

5.设置广播接收器,监听下载完成

downloadmanager 在下载完成时,会发送一条广播,我们可以设置接收器,接受这条广播,并做下载完成后的操作。
这里先做广播接收器

public class DownloadManagerReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        //下载完成
        if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
            //获取 downloadmanager 下载任务id
           long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
           //安装应用
            UpdateUtil updateUtil = new UpdateUtil(context);
            updateUtil.installApk(id);
        }
    }
}

// AndroidManifest.xml
"com.xxxx.xxxx.util.DownloadManagerReceiver">
    
        "android.intent.action.DOWNLOAD_COMPLETE"/>
    

在更新提示的界面注册广播接收器。建议在 onResume() 中注册,在onPause() 中解除注册。

@Override
protected void onResume() {
    super.onResume();
    //注册广播
    downloadManagerReceiver = new DownloadManagerReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
    registerReceiver(downloadManagerReceiver, intentFilter);
    }
}
@Override
protected void onPause() {
    super.onPause();
    //解除注册
    unregisterReceiver(downloadManagerReceiver);
}

6.安装app

在安装 app 的时候需要获取 apk 存储地址,这里需要注意一下,需要做6.0以下(不包含6.0)/6.0/7.0以上(包含7.0)三种版本的适配。

6.0以下与6.0系统的区别 与 适配:
6.0以下(不包含6.0)系统的可以直接获取存储路径;getUriForDownloadedFile得到 值为: content://downloads/my_downloads/10
Android6.0以下,getUriForDownloadedFile得到的值为:file:///storage/emulated/0/Android/data/packgeName/files/Download/xxx.apk

7.0适配 :
由于7.0对文件访问权限做出了限制,必须通过 FileProvider 封装后访问。具体详解可以查看这里

public Uri getDownloadPath(long downloadId) {
   Uri path = null;
   if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
       //6.0 以下
       path = downloadManager.getUriForDownloadedFile(downloadId);
   } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
       //6.0 — 7.0
       path = Uri.fromFile(queryDownloadedApk(downloadId));
   } else {
       //7.0 以上
       path = FileProvider.getUriForFile(context, "com.xxx.xxxx", new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxxx.apk"));
   }
   return path;
}
private File queryDownloadedApk(long downloadId) {
    File downloadFilePath = null;
    DownloadManager.Query query = new DownloadManager.Query();
    Cursor cursor = null;
    try {
        cursor = downloadManager.query(query.setFilterById(downloadId));
        if (cursor != null || cursor.moveToFirst()) {
            String uriString = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
            if (!TextUtils.isEmpty(uriString)) {
                downloadFilePath = new File(Uri.parse(uriString).getPath());
            }
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    return downloadFilePath;
}

获取到apk存储路径后,可以进行最后的步骤安装了,这里很简单,直接上代码。

public void installApk(long id) {
    //在上面已经获取到apk存储路径
    Uri uri = getDownloadPath(id);
    //跳转安装
    Intent intentInstall = new Intent();
    intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intentInstall.setAction(Intent.ACTION_VIEW);
    intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
    context.startActivity(intentInstall);
}

感谢以下博客提供的思路与学习机会:
系统自带DownloadManager详解
Android app 在线更新那点事儿(适配Android6.0、7.0、8.0)
Android系统内置下载器服务DownloadManager的使用

你可能感兴趣的:(android)