Android版本更新完成自动安装解决方案(兼容版本7.0,8.0)

1.简介

  • 发布新版本时,用户已经安装的旧版本提示安装更新
  • 版本更新分为选择更新和强制更新,区别就是弹窗提示是否可以取消,页面是否可以跳转问题

2.特点

1.Android7.0版本以下

这里不做解释,后面看代码。

2.Android7.0版本的FileProvider

主要就是在代码中和AndroidManifest.xml中使用FileProvider类

2.Android8.0及以上版本

版本更新后不能够自动打开安装页面,无法自动安装问题。主要是REQUEST_INSTALL_PACKAGES权限相关的问题

3.代码

  • 主要类DownloadService.java
public class DownloadService extends Service {

    public String DOWNLOAD_PATH = Environment.getExternalStorageDirectory() + "/download/AppUpdate.apk";
    private String url;//下载链接
    private long refernece;

    private BroadcastReceiver receiver;
    private IntentFilter filter;

    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            url = intent.getStringExtra("url");
            if (url != null && !TextUtils.isEmpty(url)) {
                download(url);
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    public void download(String url) {
        if (new File(DOWNLOAD_PATH).exists()) {
            new File(DOWNLOAD_PATH).delete();
        }
        receiver();
        DownloadManager dManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        Uri uri = Uri.parse(url);
        DownloadManager.Request request = new DownloadManager.Request(uri);
        //这里的"AppUpdate.apk"要对应DOWNLOAD_PATH的"AppUpdate.apk"
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "AppUpdate.apk");
        request.setDescription("新版本下载中...");
        request.setTitle("版本更新");
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setMimeType("application/vnd.android.package-archive");
        // 设置为可被媒体扫描器找到
        request.allowScanningByMediaScanner();
        // 设置为可见和可管理
        request.setVisibleInDownloadsUi(true);
        try {
            refernece = dManager.enqueue(request);
        } catch (Exception e) {
        }
    }

    
    public void receiver() {
        filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        receiver = new BroadcastReceiver() {
            public void onReceive(Context context, Intent intent) {
                long myDwonloadID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                if (refernece == myDwonloadID) {
                    DownloadManager dManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
                    setPermission(DOWNLOAD_PATH);
                    Intent install = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:" + getPackageName()));
                    install.setAction(Intent.ACTION_VIEW);
                    install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    install.addCategory(Intent.CATEGORY_DEFAULT);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//大于Android版本7.0
                        //这里的"com.android.app.appupdate.provider"需要和AndroidManifest.xml的provider节点下的authorities属性保持一致
                        Uri contentUri = FileProvider.getUriForFile(context, "com.android.app.appupdate.provider", new File(DOWNLOAD_PATH));
                        install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//这里不能是setFlags(),set会覆盖掉之前的flags
                        install.setDataAndType(contentUri, "application/vnd.android.package-archive");
                        context.startActivity(install);
                    } else {
                        installApk(context, refernece);
                    }
                }
            }
        };
        registerReceiver(receiver, filter);
    }

    private void installApk(Context context, long downloadApkId) {

        DownloadManager dManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(downloadApkId);
        Cursor c = dManager.query(query);
        if (c != null) {
            if (c.moveToFirst()) {
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
                if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                    String downloadFileUrl = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                    startInstall(context, Uri.parse(downloadFileUrl));
                }
            }
            c.close();
        }
    }

    private boolean startInstall(Context context, Uri uri) {
        if (!new File(uri.getPath()).exists()) {
            System.out.println(" local file has been deleted! ");
            return false;
        }
        Intent intent = new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction(Intent.ACTION_VIEW);
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        context.startActivity(intent);
        return true;
    }

    /**
     * 提升读写权限
     *
     * @param filePath 文件路径
     */
    public static void setPermission(String filePath) {
        String command = "chmod " + "777" + " " + filePath;
        Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec(command);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void onDestroy() {
        unregisterReceiver(receiver);
    }
}
  • AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.android.app.appupdate">

    <!-- 安装首先需要用户手动给予存储权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 安装应用的权限(8.0及以上会需要用户手动打开允许安装未知应用的权限,但在provider_paths的配置路径里,
    可以配置不需要用户手动打开权限,也可跳转自动安装) -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".DownloadService" />

        <!--注意FileProvider.getUriForFile()的第二个参数要和authorities一致-->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.android.app.appupdate.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>

    </application>

</manifest>
  • 在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" path="."/>-->
    <!--<external-path path="." name="external_storage_root"/>-->
    <!--<external-path name="beta_external_path" path="Download/"/>-->
    <!--<external-path name="beta_external_files_path" path="Android/data/"/>-->

    <!-- 这段代码很重要,这里的路径+android.permission.REQUEST_INSTALL_PACKAGES权限就可以解决,8.0及以上版本,
    不能自动安装下载包的问题 -->
    <root-path name="root" path="" />
    
    <external-path name="external_storage_root" path="." />
    <external-path name="external_storage_download" path="Download" />

<!--<files-path name="name" path="path" /> 对应getFilesDir()。
    <cache-path name="name" path="path" /> 对应getCacheDir()。
    <external-path name="name" path="path" /> 对应Environment.getExternalStorageDirectory()。
    <external-files-path name="name" path="path" /> 对应getExternalFilesDir()。
    <external-cache-path name="name" path="path" /> 对应getExternalCacheDir()。-->

    <!--子节点	对应路径
    root-path	.
    files-path	Context.getFilesDir()
    cache-path	Context.getCacheDir()
    external-path	Environment.getExternalStorageDirectory()
    external-files-path	Context.getExternalFilesDir(null)
    external-cache-path	Context.getExternalCacheDir()-->

</paths >

activity做跳转

 Intent intent = new Intent(mActivity, DownloadService.class);
 intent.putExtra("url", "https://github.com/fanhenghao/AppUpdate/raw/master/AppUpdate_release1.0.1.apk");
                                mActivity.startService(intent);

4.参考

  • 我个人GitHub的完整代码示例:AppUpdate

5.总结

  • Android7.0需要FileProvider
  • Android8.0需要一个允许安装未知应用的权限和修改provider_paths.xml的配置路径
  • 可能会遇到下载安装时签名不一致的问题,可以在app下的build.gradle的android节点下添加
signingConfigs {
//这里是你自己的签名相关配置
        release {
            keyAlias 'paymentbox'
            keyPassword 'android'
            storeFile file('/Users/fanhenghao/Desktop/work/paymentbox/paymentbox.jks')
            storePassword 'android'
        }
        debug {
            keyAlias 'paymentbox'
            keyPassword 'android'
            storeFile file('/Users/fanhenghao/Desktop/work/paymentbox/paymentbox.jks')
            storePassword 'android'
        }
 }
  • 我遇到的问题:

1.强制或是选择更新都差不多,就是一个弹窗是否消失和页面是否跳转问题;另外,版本更新一定需要存储权限,如果app涉及到强制更新的话,我做的是不给存储权限直接退出app。
2.先前以为8.0系统的手机需要用户手动给予安装未知应用的权限,结果小米平台的审核不通过,所以做了不需要强制用户打开的安装未知应用的权限。

6.最后

开通了个公众号,扫码关注一下,可以获得超过1个G的免费PDF书籍学习资料,并且可以及时收到我分享的内容哦!
在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值