Android 10 - не найдена активность для обработки намерений - PullRequest
1 голос
/ 24 сентября 2019

Мое стороннее приложение позволяет конечному пользователю загрузить обновленный 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)

1 Ответ

1 голос
/ 25 сентября 2019

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(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()
      }
    }
}

Когда действие или фрагмент вызывает install(), предоставляя Uri для APK, я использую PackageInstaller для его установки:

  • Получить PackageInstaller из PackageManager
  • Создать SessionParams и открыть из него сессию
  • Записать байты APK (считанные из InputStreamот Uri) до OutputStream, предоставленного этим сеансом
  • Позвоните commit(), чтобы фактически начать процесс установки, с результатами, доставленными обратно в приложение через PendingIntent
  • Позвоните close(), чтобы закрыть сеанс

API неуклюж, но он разработан для обработки широкого спектра сценариев, включая установку нескольких приложений APK в стиле App Bundle.

...