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

Mik*_*ink 6 java android android-activity dart flutter

介绍

\n

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

\n

我正在使用SystemOverlayWindow插件,该插件是一个 Activity SystemAlertWindowPlugin.java

\n

在我的Application.kt中,我注册插件并传递注册表

\n
public class Application: FlutterApplication(), PluginRegistrantCallback {\n\n   override fun onCreate() {\n     super.onCreate();\n     FlutterFirebaseMessagingService.setPluginRegistrant(this);\n     SystemAlertWindowPlugin.setPluginRegistrant(this);\n     createNotificationChannels();\n     FlutterMain.startInitialization(this);\n   }\n\n   override fun registerWith(registry: PluginRegistry?) {\n    if (!registry!!.hasPlugin("io.flutter.plugins.firebasemessaging")) {\n      FirebaseMessagingPlugin.registerWith(registry!!.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));\n    }\n    if (!registry!!.hasPlugin("in.jvapps.system_alert_window")) {\n      SystemAlertWindowPlugin.registerWith(registry!!.registrarFor("in.jvapps.system_alert_window"));\n    }\n    if (!registry!!.hasPlugin("fr.g123k.deviceapps")) {\n      DeviceAppsPlugin.registerWith(registry!!.registrarFor("fr.g123k.deviceapps"));\n    }\n   }\n
Run Code Online (Sandbox Code Playgroud)\n

我还注册了另一个名为DeviceApps的插件。这是DeviceAppsPlugin DeviceAppsPlugin.java

\n

问题

\n

简而言之

\n

因此,系统覆盖层(在前台运行)调用 > dart 回调 > 调用 DeviceApps 插件的方法 > 发生错误

\n

长版

\n

我在这里注册了一个静态回调,当我与系统警报窗口进行单击交互时,它会被调用。但现在我不想从静态回调中调用我的 dart 代码中的 DeviceApps 插件

\n

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

\n

这是通过后台通道注册和调用的静态 dart 回调

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

回调将调用 DeviceApps 插件的方法。这会导致问题,因为方法将尝试从传入其构造函数的活动中获取包管理器。但根据此错误,活动为空。

\n
E/MethodChannel#g123k/device_apps(24210): Failed to handle method call\nE/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\nE/MethodChannel#g123k/device_apps(24210):   at fr.g123k.deviceapps.DeviceAppsPlugin.openApp(DeviceAppsPlugin.java:141)\n
Run Code Online (Sandbox Code Playgroud)\n

因此它将在空对象上调用 getPackageManager()。

\n

仅当从后台通道调用的静态回调调用该活动时,该活动才为空。但当我从应用程序范围正常调用它时就不会了。为什么会这样呢?

\n

结论

\n

因此,总而言之,当我从应用程序范围调用该插件时,调用该插件效果很好。但是,一旦通过后台通道调用回调,活动就会突然变为空。

\n

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

\n

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

\n

这是完整的堆栈跟踪

\n
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)\nE/flutter (26735): #0      StandardMethodCodec.decodeEnvelope \npackage:flutter/\xe2\x80\xa6/services/message_codecs.dart:569\nE/flutter (26735): #1      MethodChannel._invokeMethod \npackage:flutter/\xe2\x80\xa6/services/platform_channel.dart:156\nE/flutter (26735): <asynchronous suspension>\nE/flutter (26735): #2      MethodChannel.invokeMethod \npackage:flutter/\xe2\x80\xa6/services/platform_channel.dart:329\nE/flutter (26735): #3      DeviceApps.openApp \npackage:device_apps/device_apps.dart:81\nE/flutter (26735): #4      SystemOverlayController.systemOverlayOnClickListner \npackage:appname/\xe2\x80\xa6/singletons/system_overlay_controller.dart:51\nE/flutter (26735): #5      callbackDispatcher.<anonymous closure> \npackage:system_alert_window/system_alert_window.dart:136\nE/flutter (26735): #6      MethodChannel._handleAsMethodCall \npackage:flutter/\xe2\x80\xa6/services/platform_channel.dart:409\nE/flutter (26735): #7      MethodChannel.setMethodCallHandler.<anonymous closure> \npackage:flutter/\xe2\x80\xa6/services/platform_channel.dart:377\nE/flutter (26735): #8      _DefaultBinaryMessenger.handlePlatformMessage \npackage:flutter/\xe2\x80\xa6/services/binding.dart:199\nE/flutter (26735): #9      _invoke3.<anonymous closure>  (dart:ui/hooks.dart:290:15)\nE/flutter (26735): #10     _rootRun  (dart:async/zone.dart:1184:13)\nE/flutter (26735): #11     _CustomZone.run  (dart:async/zone.dart:1077:19)\nE/flutter (26735): #12     _CustomZone.runGuarded  (dart:async/zone.dart:979:7)\nE/flutter (26735): #13     _invoke3  (dart:ui/hooks.dart:289:10)\nE/flutter (26735): #14     _dispatchPlatformMessage  (dart:ui/hooks.dart:164:5\n
Run Code Online (Sandbox Code Playgroud)\n

示例回购协议

\n

我什至尝试将打开应用程序方法直接添加到系统警报窗口的分叉版本中。并将其实现到您可以在此处找到的示例存储库中,以名为 my-branch 的分支为例。

\n

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

\n

但它会抛出完全相同的错误。即使我使用完全相同的活动。所以我认为这与后台通道有关。

\n

运行应用程序,然后单击按钮以获取叠加层。将应用程序置于后台,然后单击叠加层中的“打开”。出现错误。

\n

额外信息

\n

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

\n

Fab*_*bio 0

我有一个修复崩溃症状的黑客,位于https://github.com/fmatosqg/system_alert_window/tree/hack_fix

简而言之,插件会在活动不再可用时访问活动。我的快速修复存储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;
    }
Run Code Online (Sandbox Code Playgroud)

我想解释为什么这不是一个正确的解决方案,但我现在时间不够,稍后会尝试回复原因。但我不是 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/