Android app更新适配安卓10、11版本

Android app内部更新适配安卓10、11版本

前言

​ App内部更新现在基本每个app中都有,由于安卓各大应用市场不统一,不像Ios那样只有一个应用商城。并且现在安卓已经更新到11版本了,中间有几个版本还需对App内部更新做适配,所以说这里记录一下。本笔记目前适配Android11版本。本文只讲下载和安装的核心,不对更新版本的校验和更新弹出框做优化,有需要的可以自行添加。这里参考了android两位大神的文章https://blog.csdn.net/lmj623565791/article/details/72859156?spm=1001.2014.3001.5502、https://guolin.blog.csdn.net/article/details/105419420。

在这里插入图片描述

6.0版本之前

其实在6.0版本之前是什么都不要做的,直接将apk文件下载到sd卡目录中再调用如下代码就可以了

File file = new File(Environment.getExternalStorageDirectory(), "testandroid7-debug.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
6.0-7.0之间

到了6.0之后如果将文件存储到sd的目录和读取sd卡下载好的文件中需要申请READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE这两个读写权限。就是Environment.开头调用的这些目录。除了sdcard/Android/data/你的包名下/…这是你app的私有目录无需申请权限。

7.0之后

在官方7.0的以上的系统中,尝试传递 file://URI可能会触发FileUriExposedException。而你用之前的sd获得的路径恰好就是这种,接下来说说解决方案:

  1. 在清单文件AndroidManifest.xml配置如下

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepaths"/>
    </provider>
    

    name是固定的

    authorities是应用的唯一标识一般用应用的包名再拼上一些随便的字符,

    exported=false固定的,

    grantUriPermissions=true固定的

    meta中的name固定的

    resource这边需要再res下创建一个xml文件夹,在xml文件夹下创建一个文件,文件内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
       <external-files-path
           name="app"
           path="/updateApp"
           />
    </resources>
    

    可配置标签说明

    <root-path/> 代表设备的根目录new File("/");
    <files-path/> 代表context.getFilesDir()
    <cache-path/> 代表context.getCacheDir()
    <external-path/> 代表Environment.getExternalStorageDirectory()
    <external-files-path>代表context.getExternalFilesDirs()
    <external-cache-path>代表getExternalCacheDirs()
    

    因为我这里用的的sdcard/Android/data/你的包名下/updateApp/updateApp.apk所以无需请求权限,但是这个目录下的文件是会计算到你应用的大小中的并且应用一卸载这里面的内容也会跟着卸载。

    安装apk稍作修改

      private fun getUriForFile(file:File) = if(Build.VERSION.SDK_INT >= 24) getUriForFile24(file) else Uri.fromFile(file)
    
        private fun getUriForFile24(file:File) = FileProvider.getUriForFile(this,"$packageName.fileprovider",file)
    
        private fun setIntentDataAndType(intent: Intent,type:String,file: File,writeAble:Boolean) {
            if(Build.VERSION.SDK_INT >= 24){
                intent.setDataAndType(getUriForFile(file),type)
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                if(writeAble) {
                    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                }
            }else {
                intent.setDataAndType(Uri.fromFile(file),type)
            }
        }
    

    调用安装

    File file = new File(Environment.getExternalStorageDirectory(), "testandroid7-debug.apk");
    Intent intent = new Intent(Intent.ACTION_VIEW);
    // 调用上面的方法
    setIntentDataAndType(intent, "application/vnd.android.package-archive",file,true);
    startActivity(intent);
    
    10.0之后

    其实安卓10之后增加了作用域访问权限,已经不能使用之前的那种File(路径)的方式去访问sd卡下的路径了,专门有需要使用contentResolver结合MediaStore去访问,这里不展开讲解。可以去看郭神的文章https://guolin.blog.csdn.net/article/details/105419420而我们为了规避掉这个事情,还是上面的将apk的包下载到自己应用下的目录就可以无需做任何改动。

    我也尝试将apk下载到download下,下载可以,但是我不知道怎么取出来,所以放弃了。有知道的大佬可以告知我一声。

    代码示例
    package com.qmc.update
    
    import android.content.Intent
    import android.net.Uri
    import android.os.Build
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    import androidx.core.content.FileProvider
    import pub.devrel.easypermissions.EasyPermissions
    import java.io.File
    import java.io.FileOutputStream
    import java.net.HttpURLConnection
    import java.net.URL
    
    
    class MainActivity : AppCompatActivity(),EasyPermissions.PermissionCallbacks {
        val TAG = this.javaClass.simpleName
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            downLoadApp()
        }
    
        private fun downLoadApp() {
            Thread {
                val url = URL("http://192.168.1.120:3001/yanyue/updateApp.apk")
                val httpURLConnection = url.openConnection() as HttpURLConnection
                httpURLConnection.requestMethod = "GET"
                httpURLConnection.connect()
                try {
                    val responseCode = httpURLConnection.responseCode
                    if (responseCode == HttpURLConnection.HTTP_OK) {
                        val inputStream = httpURLConnection.inputStream
                        val downLoadPath =
                            getExternalFilesDir(null)?.absolutePath + File.separator + "/updateApp"
                        val file = File(downLoadPath)
                        if(!file.exists()) {
                            file.mkdir()
                        }
                        val outputStream = FileOutputStream("$downLoadPath/update.apk")
                        val byteArray = ByteArray(1024)
                        var count: Int
                        do {
                            count = inputStream.read(byteArray)
                            if (count != -1) {
                                outputStream.write(byteArray, 0, count)
                            } else {
                                inputStream.close()
                                outputStream.close()
                                break
                            }
                        } while (true)
    					inputStream.close()
                        outputStream.close()
                        val updateFile = File("$downLoadPath/update.apk")
                        if(file.exists()){
                            runOnUiThread {
                                val installIntent = Intent(Intent.ACTION_VIEW)
                                setIntentDataAndType(installIntent,"application/vnd.android.package-archive",updateFile,true)
                                startActivity(installIntent)
                            }
    
                        } else {
                            Log.e(TAG, "downLoadApp: 文件不存在")
                        }
                    }
                } catch (e: Exception) {
                    Log.e(TAG, "downLoadApp: ${e.message}")
                }
            }.start()
        }
    
        private fun getUriForFile(file:File) = if(Build.VERSION.SDK_INT >= 24) getUriForFile24(file) else Uri.fromFile(file)
    
        private fun getUriForFile24(file:File) = FileProvider.getUriForFile(this,"$packageName.fileprovider",file)
    
        private fun setIntentDataAndType(intent: Intent,type:String,file: File,writeAble:Boolean) {
            if(Build.VERSION.SDK_INT >= 24){
                intent.setDataAndType(getUriForFile(file),type)
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                if(writeAble) {
                    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                }
            }else {
                intent.setDataAndType(Uri.fromFile(file),type)
            }
        }
        override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
           Toast.makeText(this,"权限拒绝",Toast.LENGTH_SHORT).show()
        }
    
        override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
            downLoadApp()
        }
    
        override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<out String>,
            grantResults: IntArray
        ) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            EasyPermissions.onRequestPermissionsResult(requestCode,permissions,grantResults,this)
        }
    }
    
    

在这里插入图片描述

注意在清单文件中添加网络权限!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值