01.使用DownLoadManager进行下载
02.使用OkHttpClient()进行下载
1.创建下载回调:
public interface DownloadCallBack {
void onProgress(int i);
}
2.创建FileProvider,安装apk文件需要使用FileProvider,同时注意添加相关的权限:
<?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.imooc.updatedemo">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- <uses-permission android:name="android.permission.INSTALL_PACKAGES"-->
<!-- tools:ignore="ProtectedPermissions" />-->
<uses-permission android:name='android.permission.INTERNET'/> <!-- 联网权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 写入SD卡权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" /> <!-- 在SD卡中创建和删除文件的权限 -->
<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/Theme.UpdateDemo">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".InstallReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.imooc.updatedemo.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
4.创建FileProvider的xml(定义共享文件路径)文件:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external"
path="Android/data/com.imooc.updatedemo/files" />
<external-path
name="root"
path="Download" />
<external-path
name="external.Download"
path="Android/data/com.imooc.updatedemo/files/Download" />
</paths>
5.使用DownLoadManager或者OkHttpClient()进行下载:
//参考文章:https://www.jianshu.com/p/b57628947aec
class MainActivity : AppCompatActivity() {
private var downloadId: Long? = null
private lateinit var downLoadManager: DownloadManager
private val handler: Handler = object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what) {
0 -> {
val status = msg.obj
Log.i("hy55", "status=$status")
if (status == DownloadManager.STATUS_RUNNING || status == DownloadManager.STATUS_PAUSED || status == DownloadManager.STATUS_PENDING) {
val num1 = msg.arg1
Log.i("hy55", "num1=$num1")
val num2 = msg.arg2
Log.i("hy55", "num2=$num2")
val progress = (num1 * 1.0f / num2 * 100).toInt()
Log.i("hy55", "progress=$progress")
runOnUiThread {
findViewById<TextView>(R.id.downloadtext).text = "$progress%"
findViewById<ProgressBar>(R.id.downloadBar).progress = progress
findViewById<TextView>(R.id.progressText).text = "$progress%"
}
} else {
showMessage("下载完毕")
runOnUiThread {
findViewById<TextView>(R.id.downloadtext).text = "100%"
findViewById<ProgressBar>(R.id.downloadBar).progress = 100
findViewById<TextView>(R.id.progressText).text = "100%"
}
}
}
}
}
}
private val runnable = object : Runnable {
override fun run() {
val query = DownloadManager.Query()
query.setFilterById(downloadId!!)
val cursor = downLoadManager.query(query)
if (cursor == null) {
showMessage("下载失败")
} else {
val bytesAndStatus = getDownloadProgress(cursor)
val msg = handler.obtainMessage(0, bytesAndStatus[0], bytesAndStatus[1], bytesAndStatus[2])
handler.sendMessage(msg)
}
handler.postDelayed(this, 100)
}
}
override fun onStop() {
super.onStop()
//清除Handler,取消广播
handler.removeCallbacksAndMessages(null)
}
override fun onDestroy() {
super.onDestroy()
//清除Handler,取消广播
handler.removeCallbacksAndMessages(null)
this.unregisterReceiver(downloadStatusReceiver)
}
private val downloadStatusReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
checkStatus()
}
}
private fun showMessage(str: String) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show()
}
private fun checkStatus() {
val query = DownloadManager.Query()
query.setFilterById(downloadId!!)
val cursor = downLoadManager.query(query)
if(cursor.moveToFirst()) {
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
when (status) {
DownloadManager.STATUS_PAUSED -> {
showMessage("下载暂停")
}
DownloadManager.STATUS_PENDING -> {
showMessage("下载延迟")
}
DownloadManager.STATUS_RUNNING -> {
showMessage("正在下载")
// getDownloadProgress(cursor)
}
DownloadManager.STATUS_SUCCESSFUL -> {
showMessage("下载成功")
// val loadRoot = Environment.getExternalStorageDirectory()?.absolutePath + File.separator + "Download"
val loadRoot = getExternalFilesDir(null)?.absolutePath + File.separator + "Download"
val loadFile = File(loadRoot, "updateDemo.apk")
installApk(applicationContext, loadFile)
}
DownloadManager.STATUS_FAILED -> {
showMessage("下载失败")
}
}
}
}
private fun getDownloadProgress(cursor: Cursor): IntArray {
val bytesAndStatus = IntArray(3)
try{
if (cursor.moveToFirst()) {
//已下载字节数
val downloadBytesIdx = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
//总的字节数
val totalBytesIdx = cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
val status = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
bytesAndStatus[0] = cursor.getLong(downloadBytesIdx).toInt()
bytesAndStatus[1] = cursor.getLong(totalBytesIdx).toInt()
bytesAndStatus[2] = cursor.getLong(status).toInt()
Log.i("hy55", "bytesAndStatus=$bytesAndStatus")
}
} finally {
if (cursor != null) {
cursor.close()
}
}
return bytesAndStatus
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
downLoadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
//点击下载apk
findViewById<Button>(R.id.btn).setOnClickListener {
val uri = Uri.parse("https://rel.huya.com/apk/live.apk")
val request = DownloadManager.Request(uri)
request.setAllowedOverRoaming(true)
request.setNotificationVisibility(1)
//设置文件类型
val mimeTypeMap = MimeTypeMap.getSingleton()
val mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl("https://rel.huya.com/apk/live.apk"))
request.setMimeType(mimeString)
request.setTitle("正在下载")
request.setVisibleInDownloadsUi(true)
//setDestinationInExternalPublicDir()->Environment.DIRECTORY_DOWNLOADS:文件路径/storage/emulated/0/Download/updateDemo.apk
//setDestinationInExternalFilesDir->Environment.DIRECTORY_DOWNLOADS:文件路径:/storage/emulated/0/Android/data/com.imooc.updatedemo/files/Download/updateDemo.apk
request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, "updateDemo.apk")
// 将下载请求放入队列
downloadId = downLoadManager.enqueue(request)
Log.i("hy55", "downloadId=$downloadId")
handler.post(runnable)
//检查初始下载状态
checkStatus()
//注册下载完成的广播,进行apk安装
this.registerReceiver(downloadStatusReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
//使用Http请求下载
findViewById<Button>(R.id.btn2).setOnClickListener {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://rel.huya.com/apk/live.apk")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
Log.i("hy55", "onResponse response=$response")
updateApk(response, object : DownloadCallBack {
override fun onProgress(i: Int) {
runOnUiThread {
findViewById<TextView>(R.id.progressText).text = "$i%"
}
}
})
}
})
}
}
private fun updateApk(response: okhttp3.Response, downloadCallBack: DownloadCallBack) {
Log.i("hy55", "updateApk response=$response")
downloadApk(response, downloadCallBack)
// DownLoadUtil.downloadApk(response)
}
@SuppressLint("SetTextI18n")
fun downloadApk(response: Response, downloadCallBack: DownloadCallBack) {
Log.i("hy55", "downloadApk response=$response")
var `is`: InputStream? = null
var fos: FileOutputStream? = null
val buff = ByteArray(2048)
var len: Int
try {
`is` = response.body()!!.byteStream()
Log.i("hy55", "is=$`is`")
val file = createFile()
Log.i("hy55", "downloadApk file=$file")
val total = response.body()!!.contentLength()
Log.i("hy55", "total=$total")
val contentLength = total
var sum: Long = 0
fos = FileOutputStream(file)
Log.i("hy55", "fos=$fos")
while (`is`.read(buff).also { len = it } != -1) {
fos.write(buff, 0, len)
sum += len.toLong()
val progress = (sum * 1.0f / total * 100).toInt()
downloadCallBack.onProgress(progress)
runOnUiThread {
findViewById<TextView>(R.id.downloadtext).text = "$progress%"
findViewById<ProgressBar>(R.id.downloadBar).progress = progress
}
Log.i("hy55", "progress=$progress")
}
fos.flush()
Log.i("hy55", "installApk file=$file")
//4.下载完成,安装apk
// UpdateUtil.installUseRoot(file.absolutePath)
installApk(applicationContext, file)
} catch (e: Exception) {
e.printStackTrace()
// breakpoint(downloadUrl,emitter);
} finally {
try {
`is`?.close()
fos?.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun installApk(context: Context?, file: File?) {
// val loadRoot = Environment.getExternalStorageDirectory()?.absolutePath + File.separator + "Download"
// val loadFile = File(loadRoot, "updateDemo.apk")
// Log.i("hy55", "loadFile=$loadFile")
if (context == null) {
return
}
val authority = context.packageName + ".fileProvider"
Log.i("hy55", "authority=$authority")
val apkUri = FileProvider.getUriForFile(context, authority, file!!)
Log.i("hy55", "apkUri=$apkUri")
val intent = Intent(Intent.ACTION_VIEW)
intent.action = Intent.ACTION_INSTALL_PACKAGE
intent.addCategory(Intent.CATEGORY_DEFAULT)
Log.i("hy55", "----->>>>>1")
try {
//判读版本是否在7.0以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Log.i("hy55", "----->>>>>2")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
Log.i("hy55", "----->>>>>3")
} else {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
Log.i("hy55", "----->>>>>4")
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
}
context.startActivity(intent)
} catch (e : IllegalArgumentException) {
e.printStackTrace()
} catch (e : ActivityNotFoundException) {
e.printStackTrace()
} catch (e : java.lang.Exception) {
e.printStackTrace()
}
Log.i("hy55", "intent=$intent")
Log.i("hy55", "----->>>>>5")
//弹出安装窗口把原程序关闭,强制杀掉进程可能会导致安装包解析失败
// android.os.Process.killProcess(android.os.Process.myPid())
}
private fun createFile(): File {
// val root = Environment.DIRECTORY_DOWNLOADS
val root = getExternalFilesDir(null)?.absolutePath
val file = File(root, "updateDemo.apk")
if (file.exists()) {
file.delete()
}
try {
file.createNewFile()
return file
} catch (e: IOException) {
e.printStackTrace()
}
Log.i("hy55", "createFile file=$file")
return file
}
}
注意:
//注意:/storage/emulated/0相当于sdcard/
//文件路径:/storage/emulated/0/Android/data/com.imooc.updatedemo/files/Download/updateDemo.apk
request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, "updateDemo.apk")
//安装Apk的时候需要设置路径:/storage/emulated/0/Android/data/com.imooc.updatedemo/files/Download/updateDemo.apk
val loadRoot = getExternalFilesDir(null)?.absolutePath + File.separator + "Download"
val loadFile = File(loadRoot, "updateDemo.apk")
installApk(applicationContext, loadFile)
//注意:/storage/emulated/0相当于sdcard/
//文件路径/storage/emulated/0/Download/updateDemo.apk
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "updateDemo.apk")
//安装Apk需要设置路径:/storage/emulated/0/Download/updateDemo.apk
val loadRoot = Environment.getExternalStorageDirectory()?.absolutePath + File.separator + "Download"
val loadFile = File(loadRoot, "updateDemo.apk")
installApk(applicationContext, loadFile)
//注意:/storage/emulated/0相当于sdcard/
//getExternalFilesDir() -> 文件路径:/storage/emulated/0/Android/data/com.imooc.updatedemo/files/updateDemo.apk
val root = getExternalFilesDir(null)?.absolutePath
val file = File(root, "updateDemo.apk")
//注意:/storage/emulated/0相当于sdcard/
//Environment.getExternalStorageDirectory() -> 文件路径:/storage/emulated/0/Download
val loadRoot = Environment.getExternalStorageDirectory()?.absolutePath + File.separator + "Download"
val loadFile = File(loadRoot, "updateDemo.apk")
/**
* 静默安装
*
* @param filePath
* @return
*/
@Throws(java.lang.Exception::class)
fun installUseRoot(filePath: String): Boolean {
require(!TextUtils.isEmpty(filePath)) { "Please check apk file path!" }
var result = false
var outputStream: OutputStream? = null
var process: Process? = null
var errorStream: BufferedReader? = null
try {
process = Runtime.getRuntime().exec("su")
outputStream = process.outputStream
val command = "pm install -r $filePath\n"
outputStream.write(command.toByteArray())
outputStream.flush()
outputStream.write("exit\n".toByteArray())
outputStream.flush()
process.waitFor()
errorStream = BufferedReader(InputStreamReader(process.errorStream))
val msg = StringBuilder()
var line: String?
while (errorStream.readLine().also { line = it } != null) {
msg.append(line)
}
// Logger.d("install msg is " + msg);
if (!msg.toString().contains("Failure")) {
result = true
}
} finally {
try {
outputStream?.close()
errorStream?.close()
} catch (e: IOException) {
result = false
process!!.destroy()
}
}
return result
}
private fun breakpoint(downloadUrl: String, emitter: ObservableEmitter<Int>) {
val client = OkHttpClient()
val request = Request.Builder()
.url(downloadUrl)
// .addHeader("RANGE", "bytes=" + downloadLength.toString() + "-" + contentLength)
.build()
client.newCall(request).enqueue(object : Callback() {
override fun onFailure(call: Call?, e: IOException?) {
//下载失败
breakpoint(downloadUrl, emitter)
}
@Throws(IOException::class)
override fun onResponse(call: Call?, response: Response) {
if (response.body() == null) {
//下载失败
breakpoint(downloadUrl, emitter)
return
}
var `is`: InputStream? = null
var randomFile: RandomAccessFile? = null
val buff = ByteArray(2048)
var len: Int
try {
`is` = response.body()!!.byteStream()
val root = Environment.getExternalStorageDirectory().path
val file = File(root, "updateDemo.apk")
randomFile = RandomAccessFile(file, "rwd")
randomFile.seek(downloadLength)
val total: Long = contentLength
var sum: Long = downloadLength
while (`is`.read(buff).also { len = it } != -1) {
randomFile.write(buff, 0, len)
sum += len.toLong()
val progress = (sum * 1.0f / total * 100).toInt()
//下载中,更新下载进度
emitter.onNext(progress)
downloadLength = sum
}
//4.下载完成,安装apk
installApk(this@MainActivity, file)
} catch (e: java.lang.Exception) {
e.printStackTrace()
breakpoint(downloadUrl, emitter)
} finally {
try {
`is`?.close()
randomFile?.close()
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
}
})
}