java .lang.NullPointerException: попытка вызвать виртуальный метод 'android .content.pm.PackageManager - PullRequest
3 голосов
/ 10 июля 2020

Введение

Я использую flutter v1.17.5 DeviceApps v1.0.10 и SystemAlertWindow v0.2.2 + 3 (намеренно не последняя версия). И я хочу открыть свое приложение из окна системного предупреждения, которое работает на переднем плане, даже когда приложение закрыто.

Я использую плагин SystemOverlayWindow , и этот плагин является действием SystemAlertWindowPlugin. java

В моем Application.kt я регистрирую плагины и передаю реестр

public class Application: FlutterApplication(), PluginRegistrantCallback {

   override fun onCreate() {
     super.onCreate();
     FlutterFirebaseMessagingService.setPluginRegistrant(this);
     SystemAlertWindowPlugin.setPluginRegistrant(this);
     createNotificationChannels();
     FlutterMain.startInitialization(this);
   }

   override fun registerWith(registry: PluginRegistry?) {
    if (!registry!!.hasPlugin("io.flutter.plugins.firebasemessaging")) {
      FirebaseMessagingPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
    }
    if (!registry!!.hasPlugin("in.jvapps.system_alert_window")) {
      SystemAlertWindowPlugin.registerWith(registry!!.registrarFor("in.jvapps.system_alert_window"));
    }
    if (!registry!!.hasPlugin("fr.g123k.deviceapps")) {
      DeviceAppsPlugin.registerWith(registry!!.registrarFor("fr.g123k.deviceapps"));
    }
   }

Я также регистрирую еще один плагин называется DeviceApps . Это DeviceAppsPlugin DeviceAppsPlugin. java

Проблема

Короче

Итак, системный оверлей (который выполняется на переднем плане) вызывает> обратный вызов dart> вызывает метод плагина DeviceApps> возникает ошибка

Длинная версия

У меня есть статус c обратный вызов, который зарегистрирован здесь , и он вызывается, когда у меня есть щелчок при взаимодействии с окном системного предупреждения. Но теперь я не хочу вызывать плагин DeviceApps в моем коде дротика из этого stati c callback

Таким образом, канал метода вызовет this , и это запустит stati c обратный вызов, определенный в dart.

Это stati c обратный вызов dart, который регистрируется и вызывается с фоновым каналом

  static Future<void> systemOverlayOnClickListner(String tag) async {
    switch (tag) {
      case 'button_app_to_foreground':
        DeviceApps.openApp('com.companyname.appname'); // this is where I try to run the plugin
        await SystemAlertWindow.closeSystemWindow();
        break;
    }

Обратный вызов вызывает метод подключаемого модуля DeviceApps. И это вызывает проблемы, потому что этот метод будет пытаться получить диспетчер пакетов из действия, переданного в его конструкторе. Но в соответствии с этой ошибкой активность равна нулю.

E/MethodChannel#g123k/device_apps(24210): Failed to handle method call
E/MethodChannel#g123k/device_apps(24210): java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()' on a null object reference
E/MethodChannel#g123k/device_apps(24210):   at fr.g123k.deviceapps.DeviceAppsPlugin.openApp(DeviceAppsPlugin.java:141)

Таким образом, она вызовет getPackageManager () для нулевого объекта.

Действие равно нулю только тогда, когда оно вызывается из этого stati c обратный вызов, который вызывается фоновым каналом. Но не тогда, когда я обычно вызываю это из области приложения. Почему это так?

Conslusion

Итак, в заключение вызов плагина работает нормально, когда я вызываю его из области своего приложения. Но как только обратный вызов вызывается средствами фонового канала, активность внезапно становится нулевой.

Я не могу просто запустить изоляцию в своем приложении и отправить ему сообщение из моего обратного вызова, например, как это сделано здесь . Потому что мне нужен этот код для работы, когда приложение закрыто, а изолятор в области приложения не работает в фоновом режиме.

Итак, как я могу открыть свое приложение из обратного вызова?

Это - это полная трассировка стека

E/flutter (26735): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(error, Attempt to invoke virtual method 'android.content.pm.PackageManager android.app.Activity.getPackageManager()' on a null object reference, null)
E/flutter (26735): #0      StandardMethodCodec.decodeEnvelope 
package:flutter/…/services/message_codecs.dart:569
E/flutter (26735): #1      MethodChannel._invokeMethod 
package:flutter/…/services/platform_channel.dart:156
E/flutter (26735): <asynchronous suspension>
E/flutter (26735): #2      MethodChannel.invokeMethod 
package:flutter/…/services/platform_channel.dart:329
E/flutter (26735): #3      DeviceApps.openApp 
package:device_apps/device_apps.dart:81
E/flutter (26735): #4      SystemOverlayController.systemOverlayOnClickListner 
package:appname/…/singletons/system_overlay_controller.dart:51
E/flutter (26735): #5      callbackDispatcher.<anonymous closure> 
package:system_alert_window/system_alert_window.dart:136
E/flutter (26735): #6      MethodChannel._handleAsMethodCall 
package:flutter/…/services/platform_channel.dart:409
E/flutter (26735): #7      MethodChannel.setMethodCallHandler.<anonymous closure> 
package:flutter/…/services/platform_channel.dart:377
E/flutter (26735): #8      _DefaultBinaryMessenger.handlePlatformMessage 
package:flutter/…/services/binding.dart:199
E/flutter (26735): #9      _invoke3.<anonymous closure>  (dart:ui/hooks.dart:290:15)
E/flutter (26735): #10     _rootRun  (dart:async/zone.dart:1184:13)
E/flutter (26735): #11     _CustomZone.run  (dart:async/zone.dart:1077:19)
E/flutter (26735): #12     _CustomZone.runGuarded  (dart:async/zone.dart:979:7)
E/flutter (26735): #13     _invoke3  (dart:ui/hooks.dart:289:10)
E/flutter (26735): #14     _dispatchPlatformMessage  (dart:ui/hooks.dart:164:5

Пример репозитория

Я даже пытался добавить метод открытого приложения непосредственно в разветвленную версию окно системного предупреждения. И реализовал это в примере репозитория, который вы можете найти здесь, возьмите ветку с именем my-branch.

https://github.com/michael-ottink/system_overlay_callback_null_activity

Но это выдает точно такую ​​же ошибку. Даже если я использую точно такое же действие. Думаю, это как-то связано с фоновым каналом.

Запустите приложение и нажмите кнопку, чтобы получить оверлей. Переведите приложение в фоновый режим и нажмите «Открыть» в оверлее. Возникает ошибка.

Дополнительная информация

Я думаю, что это - аналогичная проблема, только здесь они решили не регистрировать плагин, потому что он находится на переднем плане только. Если в моем случае я хочу форкнуть любой из этих плагинов и изменить их, чтобы он также работал в фоновом режиме. Как мне это сделать?

1 Ответ

0 голосов
/ 17 июля 2020

У меня есть хак, который устраняет симптомы cra sh по адресу https://github.com/fmatosqg/system_alert_window/tree/hack_fix

Короче говоря, плагин обращается к активности, когда она больше не доступна. Мое быстрое исправление сохраняет activity.applicationContext (гарантированно существует, пока приложение живо - даже если это всего лишь оверлей) и сохраняет его в переменной stati c, которая может пережить объект из класса SystemAlertWindowPlugin.

. . .
    private static Context staticContext;

. . .
    private boolean openApp(String packageName) {
        Intent launchIntent = staticContext.getPackageManager().getLaunchIntentForPackage(packageName);

        if (launchIntent != null) {
            // null pointer check in case package name was not found
            staticContext.startActivity(launchIntent);

            return true;
        }
        return false;
    }

Я хотел бы объяснить, почему это неправильное решение, но сейчас у меня мало времени, и я постараюсь вернуться к объяснению причин позже. Но я не эксперт по плагинам флаттера, и я не был бы уверен в правильности кода, не потратив намного больше времени на понимание самого плагина, чего я, вероятно, не смогу.

I сделал очень похожее исправление с очень похожим cra sh в другом плагине, я надеюсь, что это поможет вам или автору плагина: https://github.com/hnvn/flutter_image_cropper/pull/167

Наконец, несколько советов . Android сильно меняется от версии к версии, и я настоятельно рекомендую вам попробовать это и другие исправления на запущенном эмуляторе android 10 И 11, поскольку они становятся все более и более ограничительными при запуске действий. Я знаю об изменениях из фона, но я не в курсе всех деталей, или если запуск из оверлея представляет собой передний план или фон для целей ANDROID. Вкус: https://www.reddit.com/r/androiddev/comments/dcleem/android_10_restricts_how_to_start_activity_from/

...