【问题标题】:utility class's static member (null) lifecycle实用程序类的静态成员(空)生命周期
【发布时间】:2018-01-04 10:03:05
【问题描述】:

我有一个具有常量和静态方法的实用程序类。 此外,它有一个静态字段。

public class MyUtil implements IMyUtil {
   public static String IS_DEBUG = false;
   ...
   private static MyEnumType mMyEnum;

   ...
   // static getter setter methods for mMyEnum
}

为了简单起见,我有两个片段(FragmentAFragmentB)。 FragmentA 设置mMyEnum 值,然后将其与getter 方法一起使用。当用户按下按钮时,我会显示 FragmentB。 在 FragmentB 的 onActiviyCreated() 方法中,我得到了 mMyEnum 值。

在我的测试设备上一切正常。但是在 Google Play 控制台中,我在这一行看到了一堆 NullPointerException 错误:

String testString = MyUtil.getMyEnum().getSomeStringValue();

我不明白为什么那行可以是myEnum null 以及为什么我不能重现它。 MyUtil 显然不能为null,getSomeStringValue() 可以为null,但它不会抛出NullPointerException,所以唯一的myEnum 可以为null。但为什么?如果它可以为空,我该如何重现它?

谢谢。

更新 #1: Play 展示了广泛的设备(三星 A3、S6、S7、S8、LG X、G3、华为 P9 等)和 API 版本(5.0 -> 7.1)。完整的堆栈跟踪是:

java.lang.RuntimeException: 
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2984)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3045)
  at android.app.ActivityThread.-wrap14 (ActivityThread.java)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1642)
  at android.os.Handler.dispatchMessage (Handler.java:102)
  at android.os.Looper.loop (Looper.java:154)
  at android.app.ActivityThread.main (ActivityThread.java:6776)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1496)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1386)
Caused by: java.lang.NullPointerException: 
  at com.mycompany.fragment.FragmentB.onActivityCreated (FragmentB.java:35)
  at android.support.v4.app.Fragment.performActivityCreated (Fragment.java:2089)
  at android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1133)
  at android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1290)
  at android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1272)
  at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated (FragmentManagerImpl.java:2149)
  at android.support.v4.app.FragmentController.dispatchActivityCreated (FragmentController.java:201)
  at android.support.v4.app.FragmentActivity.onStart (FragmentActivity.java:600)
  at android.support.v7.app.AppCompatActivity.onStart (AppCompatActivity.java:178)
  at android.app.Instrumentation.callActivityOnStart (Instrumentation.java:1256)
  at android.app.Activity.performStart (Activity.java:6972)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2937)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3045)
  at android.app.ActivityThread.-wrap14 (ActivityThread.java)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1642)
  at android.os.Handler.dispatchMessage (Handler.java:102)
  at android.os.Looper.loop (Looper.java:154)
  at android.app.ActivityThread.main (ActivityThread.java:6776)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1496)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1386)

【问题讨论】:

  • 您是否看到导致崩溃的设备和这些设备的信息?通常 Google Play、Google 或我现在不记得的任何人都会对随机崩溃的随机应用程序进行猴子测试(通常是因为它们不维护活动)。让我尝试从 Crashlytics 获得类似的崩溃,我将在此处发布。同时,你能回答这两个问题吗? 1.- 您是否在外部记录您的崩溃(Crashlytics、Firebase)。 2.- 您能否提供来自 Google Play 控制台的这些崩溃的完整信息?
  • 谢谢。更新了我的问题。
  • 我一直假设您的意思是 NullException 的 NullPointerException。如果是其他问题,请告诉我们
  • 是的对不起,我的意思是 NullPointerException。我正在尝试“减慢”系统速度。我会发布结果,谢谢。

标签: java android nullpointerexception static


【解决方案1】:

长期存在的静态成员存在问题,因为 Android 可能已释放内存并在某个时候删除了此值(当没有片段处于活动状态并引用它时)。您还没有明确说明是否可以在不先从 FragmentA 设置值的情况下到达 FragmentB,所以这也可能是您的问题。

附加说明 - 常量应该是常量 - 所以应该有 final 关键字。

我建议的解决方案是摆脱这种糟糕的架构,其中两个组件依赖外部的上帝对象来维持它们的状态。在意图中将您的枚举值作为 Extra 传递。

您可以使用

写入捆绑包
public static final String KEY_ENUM = "MyEnumKey";

bundle.putString(KEY_ENUM, myEnum.name()):

然后用

回读
Bundle bundle = intent.getExtras();
MyEnumTypemyEnum = MyEnumType.valueOf(bundle.getString(FragmentA.KEY_ENUM));

另一个建议是确保 myEnum 永远不会为空(默认情况下为空)。声明如下:

private static MyEnumType mMyEnum = MyEnumType.MY_DEFAULT_VALUE;

【讨论】:

  • 感谢您的建议。如果没有 FragmentA(A 是登录屏幕),您将无法获得 FragmentB。我无法设置最终关键字,因为 myEnum 在用户按下登录按钮后会获得一个值。通过意图额外发送适当的值将是一个不错的选择,但我真的想重现错误(从中学习,我想知道,修复是修复原始问题)。也许我不能故意复制它?
  • 这是对您的实际常量的引用 - IS_DEBUG。如果您在 FragmentB 上切换应用程序并执行高内存操作(运行 youtube 和防病毒扫描然后 tinder 等)然后切换回您的应用程序会发生什么?
  • 到目前为止我还没有收到 NullException,我的设备刚刚变慢了。
  • 我通过额外的意图添加参数解决了这个问题,到目前为止它似乎没问题(没有崩溃)。感谢您的帮助!
猜你喜欢
  • 2014-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-06
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多