Android 10 - 没有找到处理意图的活动

bur*_*add 14 android activitynotfoundexception

我的第 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);
Run Code Online (Sandbox Code Playgroud)

这是每次返回的堆栈跟踪,仅在 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)
Run Code Online (Sandbox Code Playgroud)

Com*_*are 15

ACTION_VIEW(适用于 APK)并且ACTION_INSTALL_PACKAGE在 Android 10 中已弃用。您需要切换到PackageInstallerAPI。

此示例应用程序演示了安装简单 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(getApplication(), 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(getApplication(), InstallReceiver::class.java)
        val pi = PendingIntent.getBroadcast(
          getApplication(),
          PI_INSTALL,
          intent,
          PendingIntent.FLAG_UPDATE_CURRENT
        )

        session.commit(pi.intentSender)
        session.close()
      }
    }
}
Run Code Online (Sandbox Code Playgroud)

当 Activity 或 Fragment 调用install(),向UriAPK提供 a 时,我PackageInstaller用来安装它:

  • 获得PackageInstallerPackageManager
  • 创建一个SessionParams并从中打开一个会话
  • 写APK(从读取的字节InputStreamUri)一个OutputStream由该会话提供
  • 调用commit()以实际开始安装过程,结果通过PendingIntent
  • 呼叫close()结束会话

API 很笨重,但它旨在处理广泛的场景,包括“App Bundle”式的多 APK 安装。

  • 我自己回来回答这个问题,因为我在交叉引用 Google 的 APIDemos 时找到了答案并实现了修复,答案与你的完全相同,所以我接受了答案,但 Kotlin 的例子让我感到畏缩:(我只对 Java 感兴趣,很高兴找到 Google 用 Ja​​va 编写的示例:https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/ apis/content/InstallApkSessionApi.java (4认同)
  • 如果启动了“Intent.ACTION_INSTALL_PACKAGE”操作的意图,是否有任何方法可以像同样的方式显示旧的更新/安装流程?我的观点是,使用 PackageInstaller 不会向用户显示安装更新进度,而是会默默地更新并关闭应用程序而不通知他。 (3认同)
  • 只要我在 Android 10 上拥有 `<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />`,使用 `FileProvider.getUriForFile(` 和 ACTION_VIEW 与 `addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)` 就对我有用 (3认同)
  • @mathew11:据我所知,你将负责该用户界面。请参阅 https://developer.android.com/reference/kotlin/android/content/pm/PackageInstaller.SessionCallback#onProgressChanged(kotlin.Int,%20kotlin.Float) (2认同)
  • 我在哪里可以找到官方文档,其中说 ACTION_VIEW 对于 apk 已弃用? (2认同)