首页 > 解决方案 > java.lang.NullPointerException:尝试调用虚拟方法'android.content.pm.PackageManager

问题描述

介绍

我正在运行颤振 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

问题

简而言之

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

长版

我有一个在此处注册的静态回调,当我与系统警报窗口进行点击交互时,它会被调用。但是现在我不想从那个静态回调中调用我的飞镖代码中的 DeviceApps 插件

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

这是使用后台通道注册和调用的静态 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()。

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

结论

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

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

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

这是完整的堆栈跟踪

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

示例回购

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

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

但它会引发完全相同的错误。即使我使用完全相同的活动。所以我认为这与背景频道有关。

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

额外信息

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

标签: javaandroidflutterdartandroid-activity

解决方案


I got a hack that fixes the symptoms of the crash, at https://github.com/fmatosqg/system_alert_window/tree/hack_fix

In short, the plugin accesses activity when it's no longer available. My quick fix stores the activity.applicationContext (guaranteed to exist while the application is alive - even if it's only an overlay) and stores it in a static variable that may outlive the object from 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;
    }

I'd like to explain why this is not a proper fix but I'm short on time now and will try to get back later with the reasons. But I'm not an expert on flutter plugins and I wouldn't be sure on how the proper code is without spending lots more time understanding the plugin itself, which I probably won't be able to.

I did a very similar fix with a very similar crash on another plugin, I hope it can help guide you or the plugin author: https://github.com/hnvn/flutter_image_cropper/pull/167

Last, some piece of advice. Android changes a lot from version to version and I'd urge you to try this and other fixes on emulator running android 10 AND 11, since they're getting more and more restrictive on launching activities. I know about changes from background, but I'm not up to date in all the details, or if launching from overlay constitutes foreground or background for ANDROID purposes. A taste of it: https://www.reddit.com/r/androiddev/comments/dcleem/android_10_restricts_how_to_start_activity_from/


推荐阅读