java.lang.NullPointerException: 尝试调用虚方法'android.content.pm.PackageManager

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.PackageManager

简介

我是 运行ning flutter v1.17.5 DeviceApps v1.0.10 和 SystemAlertWindow v0.2.2+3(不是最新版本)。而且我想从系统警报 window 打开我的应用程序,即在前台 运行ning,即使应用程序已关闭。

我正在使用 SystemOverlayWindow 插件,该插件是 activity 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

问题

简而言之

所以系统覆盖(运行s 在前台)调用 > dart 回调 > 调用 DeviceApps 插件的方法 > 错误发生

长版

我有一个已注册的静态回调 here,当我与系统警报 window 进行点击交互时,它会被调用。但是现在我不想从那个静态回调

在我的飞镖代码中调用 DeviceApps 插件

因此方法通道将调用 this,这将 运行 dart 中定义的静态回调。

这是在后台通道注册和调用的静态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 插件的方法。这会导致问题,因为 this 方法将尝试从传入它的构造函数的 activity 中获取包管理器。但是根据这个错误 activity 是空的。

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

activity 只有在从后台通道调用的静态回调中调用时才为 null。但是当我从应用程序范围正常调用它时不是。为什么会这样?

结论

所以总而言之,当我从我的应用程序范围调用插件时,调用插件工作正常。但是一旦通过后台通道的方式调用回调,突然 activity 为空。

我不能只在我的应用程序中启动一个隔离,然后从我的回调中向它发送一条消息,就像它的完成方式一样 here。因为我需要此代码在应用程序关闭时工作,并且应用程序范围的隔离不会在后台 运行。

那么如何从回调中打开我的应用程序?

这是完整的堆栈跟踪

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

示例回购

我什至尝试将打开应用程序方法直接添加到 forked 版本的系统警报 window。并将其实现到一个示例回购中,您可以在此处找到,采用名为 my-branch 的分支。

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

但它抛出完全相同的错误。即使我使用完全相同的activity。所以我觉得跟背景频道有关系。

运行 应用程序,然后单击按钮以获取叠加层。将应用程序带到后台,然后单击叠加层中的打开。发生错误。

额外信息

我认为 是一个类似的问题,只是他们选择不注册插件,因为它只是前台。在我的例子中,我想分叉这些插件中的任何一个并修改它们,以便它也可以在后台运行。我该怎么做?

我在 https://github.com/fmatosqg/system_alert_window/tree/hack_fix

找到了修复崩溃症状的 hack

简而言之,插件在不再可用时访问 activity。我的快速修复存储 activity.applicationContext(保证在应用程序运行时存在 - 即使它只是一个覆盖层)并将其存储在一个静态变量中,该变量可能比 class 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;
    }

我想解释一下为什么这不是一个正确的解决方法,但我现在时间不够,稍后会尝试回来说明原因。但我不是 flutter 插件方面的专家,如果不花更多时间了解插件本身,我将无法确定正确的代码,我可能做不到。

我在另一个插件上做了一个非常相似的修复和非常相似的崩溃,我希望它能帮助指导你或插件作者: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/