okhttp实现app更新
使用 OkHttp 的 Interceptor 来监听下载进度并更新进度条
1.创建一个自定义的 OkHttp Interceptor 来监听下载进度
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.ResponseBody
class ProgressInterceptor(private val listener: ProgressListener) : Interceptor {
interface ProgressListener {
fun onProgress(bytesRead: Long, contentLength: Long, done: Boolean)
}
override fun intercept(chain: Interceptor.Chain): Response {
val originalResponse = chain.proceed(chain.request())
return originalResponse.newBuilder()
.body(ProgressResponseBody(originalResponse.body!!, listener))
.build()
}
}
2.创建一个自定义的 ResponseBody,其中会回调进度监听器以更新进度条
import okhttp3.MediaType
import okhttp3.ResponseBody
import okio.*
import java.io.IOException
class ProgressResponseBody(
private val responseBody: ResponseBody,
private val listener: ProgressInterceptor.ProgressListener
) : ResponseBody() {
private var bufferedSource: BufferedSource? = null
override fun contentLength(): Long {
return responseBody.contentLength()
}
override fun contentType(): MediaType? {
return responseBody.contentType()
}
override fun source(): BufferedSource {
if (bufferedSource == null) {
bufferedSource = source(responseBody.source()).buffer()
}
return bufferedSource!!
}
private fun source(source: Source): Source {
return object : ForwardingSource(source) {
private var totalBytesRead = 0L
@Throws(IOException::class)
override fun read(sink: Buffer, byteCount: Long): Long {
val bytesRead = super.read(sink, byteCount)
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
listener.onProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
return bytesRead
}
}
}
}
3.创建一个实现进度监听器的回调函数
class DownloadCallback(private val progressBar: ProgressBar) : ProgressInterceptor.ProgressListener {
override fun onProgress(bytesRead: Long, contentLength: Long, done: Boolean) {
val percentage = (bytesRead * 100 / contentLength).toInt()
// 更新进度条
progressBar.progress = percentage
}
}
4.使用这些组件来下载文件并更新进度条
fun downloadFileWithProgress(
url: String,
destPath: String,
progressBar: ProgressBar,
onComplete: (success: Boolean) -> Unit
) {
val client = OkHttpClient.Builder()
.addNetworkInterceptor(ProgressInterceptor(DownloadCallback(progressBar)))
.build()
val request = Request.Builder().url(url).build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 处理下载失败的情况
e.printStackTrace()
onComplete(false) // 标记下载失败
}
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful) {
// 处理响应不成功的情况
onComplete(false) // 标记下载失败
return
}
val responseBody = response.body
if (responseBody != null) {
// 下载和保存文件的逻辑与之前相同
val inputStream = responseBody.byteStream()
val outputStream = FileOutputStream(destPath)
val buffer = ByteArray(4096)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
outputStream.write(buffer, 0, bytesRead)
}
outputStream.flush()
outputStream.close()
inputStream.close()
onComplete(true) // 标记下载成功
}
}
})
}
安装app
1.安装
fun startApkInstallation(apkFilePath: String) {
val apkFile = File(apkFilePath)
if (apkFile.exists()) {
val intent = Intent(Intent.ACTION_VIEW)
val apkUri = FileProvider.getUriForFile(
applicationContext,
BuildConfig.APPLICATION_ID + ".provider",
apkFile
)
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}
2.在AndroidManifest.xml 文件中配置FileProvider
<application>
...
<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/provider_paths" />
</provider>
...
</application>
3.在 res/xml 目录下创建一个 provider_paths.xml 文件,用于配置 FileProvider
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>
4.AndroidManifest.xml 文件中配置权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />