kotlin版 OKHttp + Okio 下载更新apk

508 阅读1分钟

1、去除https限制,因为服务器没有配置证书,所以要去掉限制,不需要的可以去除逻辑

2、做了MD5文件验证

3、适配绝大多数Android版本

4、注意filepath生成和provider地址就好了

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import androidx.core.content.FileProvider
import com.factory.machine.MyApp
import okhttp3.*
import okio.*
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.math.BigInteger
import java.security.MessageDigest
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.*


object DownloadUtil {
    private val manager = object : X509TrustManager {
        @Throws(CertificateException::class)
        override fun checkClientTrusted(
            x509Certificates: Array<X509Certificate?>?,
            s: String?
        ) {
        }

        @Throws(CertificateException::class)
        override fun checkServerTrusted(
            x509Certificates: Array<X509Certificate?>?,
            s: String?
        ) {
        }

        override fun getAcceptedIssuers(): Array<X509Certificate?> = arrayOfNulls(0)

    }

    private fun createSSLSocketFactory(): SSLSocketFactory? {
        var sSLSocketFactory: SSLSocketFactory? = null
        try {
            val sc: SSLContext = SSLContext.getInstance("TLS")
            sc.init(null, arrayOf<TrustManager>(manager), SecureRandom())
            sSLSocketFactory = sc.socketFactory
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return sSLSocketFactory
    }

    fun startDown(context: Context, url: String, fileMD5:String, calllback: (Int) -> Unit) {
        val factory = createSSLSocketFactory()
        factory?.let {
            val client = OkHttpClient.Builder().run {
                sslSocketFactory(it, manager)
                    .hostnameVerifier { _, _ -> true }
            }.build()
            val request = Request.Builder()
                .url(url)
                .addHeader("Connection", "close")    //这里不设置可能产生EOF错误
                .build()
            client.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    e.printStackTrace()
                }

                override fun onResponse(call: Call, response: Response) {
                    response.body?.let { body ->

                        val digest = MessageDigest.getInstance("MD5")
                        digest?.let {
                            var bs: BufferedSource? = null
                            var out: BufferedSink? = null
                            try {
                                var sum: Long = 0
                                val byteLength = 4096
                                val buf = ByteArray(byteLength)
                                val total = body.contentLength()
                                var len: Int
                                bs = body.byteStream().source().buffer()
                                val filePath =
                                    "${MyApp.context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)}/${System.currentTimeMillis()}.apk"
                                val file =
                                    File(filePath)
                                out = file.sink().buffer()
                                var oldProcess = 0
                                var process = 0


                                while (bs.read(buf).also { len = it } != -1) {
                                    out.write(buf, 0, len)
                                    digest.update(buf, 0, len)
                                    sum += len.toLong()
                                    process = (sum * 100 / total.toFloat()).toInt()
                                    if (process - oldProcess > 0) {
                                        calllback(process)
                                    }
                                    oldProcess = process

                                }
                                out.flush()
                                val bigInt = BigInteger(1, digest.digest())
                                var md5 = bigInt.toString(16)
                                if(md5.length == 31){
                                    md5 = "0$md5"
                                }
                                if(md5 == fileMD5){
                                    installApk(context, filePath)
                                }


                            } catch (e: Exception) {
                                e.printStackTrace()
                            } finally {
                                try {
                                    bs?.close()
                                } catch (e: Exception) {
                                    e.printStackTrace()
                                }
                                try {
                                    out?.close()
                                } catch (e: Exception) {
                                    e.printStackTrace()
                                }
                            }
                        }


                    }

                }
            })
        }

    }

    fun installApk(context: Context, downloadApk: String) {
        val intent = Intent(Intent.ACTION_VIEW)
        val file = File(downloadApk)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            val apkUri: Uri = FileProvider.getUriForFile(
                context,
                "com.factory.machine.provider",
                file
            )
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
        } else {
            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
            val uri: Uri = Uri.fromFile(file)
            intent.setDataAndType(uri, "application/vnd.android.package-archive")
        }
        context.startActivity(intent)
    }

}