android - Android 10 - 找不到处理 Intent 的 Activity
问题描述
我的第 3 方应用程序可以让最终用户从我们的服务器下载更新的 APK,然后应用程序将在下载完成后调用该 APK 上的安装包管理器。同样的方法适用于所有版本的 Android 操作系统,但现在它会在 Android 10 (api 29) 上崩溃。我还没有看到任何人有类似的问题,任何帮助将不胜感激!
这是我用来从我的应用程序中调用 APK 文件的方法:
Intent intent = new Intent(Intent.ACTION_VIEW);
final File apkFile = new File(Files.getApkFileName());
Log.v("dt.update", "Start update from " + apkFile.getAbsolutePath());
intent.setDataAndType(Uri.fromFile(apkFile), application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
这是每次返回的堆栈跟踪,仅在 Android 10 / API29 上:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.MyAppHere, PID: 11107
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=file:///storage/emulated/0/Download/updatedapp.apk typ=application/vnd.android.package-archive flg=0x10000000 }
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2051)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1709)
at android.app.Activity.startActivityForResult(Activity.java:5192)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:676)
at android.app.Activity.startActivityForResult(Activity.java:5150)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:663)
at android.app.Activity.startActivity(Activity.java:5521)
at android.app.Activity.startActivity(Activity.java:5489)
at android.view.View.performClick(View.java:7140)
at android.view.View.performClickInternal(View.java:7117)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27351)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
解决方案
ACTION_VIEW
(适用于 APK)并且ACTION_INSTALL_PACKAGE
在 Android 10 中已弃用。您需要切换到PackageInstaller
API。
此示例应用程序演示了安装简单 APK 的基础知识。胆量在MainMotor
:
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Q
https://commonsware.com/AndroidQ
*/
package com.commonsware.q.appinstaller
import android.app.Application
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.PackageInstaller
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
private const val NAME = "mostly-unused"
private const val PI_INSTALL = 3439
class MainMotor(app: Application) : AndroidViewModel(app) {
private val installer = app.packageManager.packageInstaller
private val resolver = app.contentResolver
fun install(apkUri: Uri) {
viewModelScope.launch(Dispatchers.Main) {
installCoroutine(apkUri)
}
}
private suspend fun installCoroutine(apkUri: Uri) =
withContext(Dispatchers.IO) {
resolver.openInputStream(apkUri)?.use { apkStream ->
val length =
DocumentFile.fromSingleUri(application, apkUri)?.length() ?: -1
val params =
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = installer.createSession(params)
val session = installer.openSession(sessionId)
session.openWrite(NAME, 0, length).use { sessionStream ->
apkStream.copyTo(sessionStream)
session.fsync(sessionStream)
}
val intent = Intent(application, InstallReceiver::class.java)
val pi = PendingIntent.getBroadcast(
application,
PI_INSTALL,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
session.commit(pi.intentSender)
session.close()
}
}
}
当一个活动或片段调用install()
时,向Uri
APK 提供一个,我用PackageInstaller
它来安装它:
PackageInstaller
从_PackageManager
- 创建一个
SessionParams
并从中打开一个会话 InputStream
将 APK 的字节(从 中读取Uri
)写入OutputStream
该会话提供的- 调用
commit()
以实际开始安装过程,结果通过PendingIntent
- 调用
close()
以关闭会话
API 很笨重,但它旨在处理广泛的场景,包括“App Bundle”风格的多 APK 安装。
推荐阅读
- reactjs - 点击提交按钮后获取表单中输入的数据并使用react显示数据
- android - 嵌套的回收器适配器项目进入错误的回收器视图
- delphi - OAuth 1.0 生成签名与邮递员生成签名
- php - 如何选择仅在刀片上获取的值?
- android-studio - EduTools 输出显示不正确的解决方案
- json - 如何优化 React 中的组件循环
- heroku - 电子邮件仍然可以发送到 herokumanager.com 域吗?
- angular - 从 Angular 10 升级到 11 时出错
- amazon-web-services - amazon linux 2 yum 缺少更新(AWS 安全中心)
- c - 提示编译器 void * 指向零内存