欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android下载安装Apk

程序员文章站 2022-06-25 09:39:43
1.自定义监听类,以返回下载结果interface DownLoadListener { /** * 下载成功之后的文件 */ fun onDownloadSuccess(file: File) /** * 下载进度 */ fun onDownloading(progress: Int) /** * 下载异常信息 */ fun onDownloadFailed(e:Exception)}进行...

1.自定义监听类,用来返回下载结果

interface DownLoadListener {
    /**
     * 下载成功之后的文件
     */
    fun onDownloadSuccess(file: File)
    /**
     * 下载进度
     */
    fun onDownloading(progress: Int)
    /**
     * 下载异常信息
     */
    fun onDownloadFailed(e:Exception)
}
  1. 进行文件下载
/**
     * @param destFileDir 文件下载目录
     * @param response okHttp的返回值
     * @param downLoadListener 监听事件,用于返回当前下载进度等
     */
    private fun downLoad(
        destFileDir: String,
        response: Response,
        downLoadListener: DownLoadListener
    ) {
        val byte = ByteArray(2048)
        var len: Int
        val fileOutputStream: FileOutputStream

        val file = File(destFileDir)
        if (!file.exists()) {
            file.mkdirs()
        }
        val apkFile = File(file, GeneralUtil.apkName)

        val input = response.body?.byteStream()
        val apkSize: Long = response.body?.contentLength() ?: 0L
        println("获取到的apk大小:$apkSize")
        fileOutputStream = FileOutputStream(apkFile)

        var sum = 0.0
        if (apkSize != 0L) {
            while ((input?.read(byte).also { len = it!! }) != -1) {
                fileOutputStream.write(byte, 0, len)
                sum += len
                //返回当前的下载进度
                downLoadListener.onDownloading((sum / apkSize * 100).toInt())
            }
        }

        //刷新
        fileOutputStream.flush()
        //返回结果:当前已经下载成功
        downLoadListener.onDownloadSuccess(file)

        //关闭流
        input?.close()
        fileOutputStream.close()
    }

3.在Activity中监听下载成功或者失败的返回结果

override fun onDownloadSuccess(file: File) {
        //下载新版本apk完成
        manager.cancel(1)
        //跳转到新的activity,这个activity用来做安装apk的操作
        OpenApkFile.startOpenApkFile(this)
    }

    //上一次更新通知栏的时间
    private var lastTime: Long = 0L

    override fun onDownloading(progress: Int) {
        //动态更新进度
        if (lastTime == 0L) {
            lastTime = System.currentTimeMillis()
        }
		//与上一次更新通知栏相隔大于1s再进行更新,否则压力过大
        if (System.currentTimeMillis() - lastTime > 1000) { 
            lastTime = System.currentTimeMillis()
            //notificationView为自定义的通知栏的布局
            notificationView.setProgressBar(R.id.progress, 100, progress, false)
            notificationView.setTextViewText(R.id.content, "$progress%")
            //manager为NotificationManager
            manager.notify(1, notification)
        }
    }

    override fun onDownloadFailed(e: Exception) {
        //下载失败,需要删除下载失败后的文件
        val file = File(GeneralUtil.getDownLoadApkPathWithApkName(this))
        file.deleteOnExit()
        sendMessage(handler, HANDLERTYPE.FAIL.type, "新版本下载失败")
        manager.cancel(1)
    }

4.下载完成之后跳转到安装apk的界面

class OpenApkFile : AppCompatActivity() {

    private var apkFileIntent: Intent? = null

    companion object {
        fun startOpenApkFile(context: Context) {
            context.startActivity(Intent(context, OpenApkFile::class.java))
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        apkFileIntent = getApkFileIntent(GeneralUtil.getDownLoadApkPathWithApkName(this), this)
        if (apkFileIntent != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                //版本高于26需要申请权限来安装apk
                if (!this.packageManager.canRequestPackageInstalls()) {
                    AlertDialog.Builder(this)
                        .setTitle("提示")
                        .setMessage("暂未开启权限,需要您开启权限安装最新版本,以获取更好的体验!")
                        .setNeutralButton("确定") { dialogInterface, _ ->
                            run {
                                val parse = Uri.parse("package:$packageName")
                                val intent =
                                    Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, parse)
                                startActivityForResult(intent, 1)
                                dialogInterface.dismiss()
                            }
                        }
                        .setNegativeButton("取消") { dialogInterface, _ ->
                            run {
                                Toast.makeText(this, "已拒绝安装", Toast.LENGTH_SHORT).show()
                                dialogInterface.dismiss()
                                finish()
                            }
                        }
                        .create().show()
                } else {
                    //已获得权限直接安装
                    startActivity(apkFileIntent)
                    finish()
                }
            } else {
                //版本低于26则直接安装
                startActivity(apkFileIntent)
                finish()
            }
        } else {
            //无法获取到uri,抛出了异常
            AlertDialog.Builder(this)
                .setTitle("提示")
                .setMessage("无法获取安装包,请联系管理员获取帮助")
                .setNeutralButton("确定") { dialogInterface, _ ->
                    run {
                        dialogInterface.dismiss()
                        finish()
                    }
                }
                .create().show()
        }
    }

    private fun getApkFileIntent(param: String?, context: Context): Intent? {
        try {
            println("文件所在地址:$param")
            val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                FileProvider.getUriForFile(
                    context,
                    context.applicationContext.packageName.toString() + ".provider",
                    File(param!!)
                )
            } else {
                Uri.fromFile(File(param!!))
            }
            println("编码之后:$uri")
            return Intent(Intent.ACTION_VIEW).run {
                addCategory(Intent.CATEGORY_DEFAULT)
                flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
//                grantUriPermission(packageName,uri,Intent.FLAG_GRANT_READ_URI_PERMISSION)
                setDataAndType(uri, "application/vnd.android.package-archive")
                this
            }
        } catch (e: Exception) {
            e.printStackTrace()
            return null
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            1 -> {
                startActivity(apkFileIntent)
                finish()
            }
            else -> {

            }
        }
    }
}

5.FileProvider需要设置路径的xml
文件存放于res目录下的xml中:
Android下载安装Apk
file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
    <files-path
        name="aaa"
        path="Apk/"/>
</paths>
</resources>

此段代码代表的路径为:data/data/com.包名/files/Apk/,name字段可以随便取名,不影响,path为data/data/com.包名/files下的子目录;
6.在AndroidManifest.xml注册FileProvider;

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

本文地址:https://blog.csdn.net/weixin_45379305/article/details/107297630