【问题标题】:"Attempt to read from field 'int android.content.pm.ApplicationInfo.targetSdkVersion' on a null object reference" exception“尝试从空对象引用上的字段'int android.content.pm.ApplicationInfo.targetSdkVersion'读取”异常
【发布时间】:2018-11-06 14:11:17
【问题描述】:

在使用 Mockito 运行 Espresso 测试时,我目前遇到这个问题,即在主 Activity 启动后,"Attempt to read from the field 'int android.content.pm.ApplicationInfo.targetSdkVersion' on a null object reference" 异常随机出现。

这是主Activity的初始化部分:

private MainActivity subject;
SingleActivityFactory<MainActivity> activityFactory =
    new SingleActivityFactory<MainActivity>(MainActivity.class) {
        @Override
        protected MainActivity create(Intent intent) {
            subject = spy(getActivityClassToIntercept());
            return subject;
        }
    };

@Rule public ActivityTestRule<MainActivity> mainActivityRule =
    new ActivityTestRule<>(activityFactory, true, false);

@Before
public void init() {
    mainActivityRule.launchActivity(null);
}

这是个例外:

java.lang.NullPointerException: Attempt to read from field 'int 
android.content.pm.ApplicationInfo.targetSdkVersion' on a null object 
reference
at android.view.View.getLayoutDirection(View.java:10277)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:446)
at android.view.View.measure(View.java:23169)
at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1059)
at android.view.View.measure(View.java:23169)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:141)
at android.view.View.measure(View.java:23169)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1535
Test running failed: Instrumentation run failed due to 'Process crashed.'

还有崩溃的logcat:

2018-11-06 15:34:50.391 3299-3299/com.application.android E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.application.android, PID: 3299
java.lang.NullPointerException: Attempt to read from field 'int android.content.pm.ApplicationInfo.targetSdkVersion' on a null object reference
    at android.view.View.getLayoutDirection(View.java:10277)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:446)
    at android.view.View.measure(View.java:23169)
    at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1059)
    at android.view.View.measure(View.java:23169)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)

这里的问题是这个问题并不一致 - 失败率约为 40%。有人对这个问题有任何见解吗? 另外,我应该提到,如果在没有使用 Mockito 监视 Activity 的情况下启动了 Activity,则不会出现此问题。

更新:

通过在 View.java 类中引入更改解决了该问题:

@ResolvedLayoutDir
public int getLayoutDirection() {
    //added check if ApplicationInfo is not null
    if(getContext().getApplicationInfo() == null){
            View v = this;
            Log.i("View id: ", getContext().getString(v.getId()));
            Log.e("Main error", "Bug catched");
    }
    else {
        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
        if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED;
            return LAYOUT_DIRECTION_RESOLVED_DEFAULT;
        }
    }
    return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ==
            PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}

不修改系统类是否可以达到类似的效果?

【问题讨论】:

  • 你在代码中使用getFragmentManager()方法吗?还有@Datenshi
  • 是的,我在几个地方这样做
  • 请添加解决方案as an answer,而不是将其添加到问题中。您也可以accept your own answer,但您必须等待 48 小时才能这样做。

标签: android mockito android-espresso


【解决方案1】:

我认为您的问题与previous issue有关,

在使用getFragmentManager() 方法时会产生类似的行为,

解决办法是:

改用 getSupportFragmentManager() 并从 v4.app.Fragment 类扩展

【讨论】:

  • 遗憾的是,建议的解决方案还不够,即使将所有 getFragmentManager() 方法更改为 getSupportFragmentManager 方法,问题仍然存在。
  • @TomasMarch 你是否替换了v4.app.Fragment 的所有用法?
  • 你是说android.app.Fragment吗?我已将android.app.Fragment 的所有用法更改为v4.app.Fragment,但并没有改变结果。
【解决方案2】:

很可能Context 没有正确设置...

android.view.View 中的方法实际上是这样的:

public int getLayoutDirection() {
    return mViewFlags & LAYOUT_DIRECTION_MASK;
}

没有什么可以使用targetSdkVersion,但只有一次:

case R.styleable.View_fadingEdge:
    if (context.getApplicationInfo().targetSdkVersion >= ICE_CREAM_SANDWICH) {
        break;
    }

太糟糕了,测试代码不完整——很可能ActivityTestRule 是错误的;没有&lt;T&gt;new ActivityTestRule&lt;&gt; 看起来很奇怪,应该看起来很相似:

new ActivityTestRule<MainActivity>(getActivityFactory(), true, false);

mainActivityRule.launchActivity(null); 也显得毫无用处。

有关功能示例,请参阅 InterceptingActivitySampleTest.java

刚刚又找到一个例子,和题中的例子差不多:

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

    MainActivity subject;

    SingleActivityFactory<MainActivity> activityFactory = new SingleActivityFactory<MainActivity>(MainActivity.class) {
        @Override
        protected MainActivity create(Intent intent) {
            subject = spy(getActivityClassToIntercept());
            return subject;
        }
    };

    @Rule
    public ActivityTestRule<MainActivity> testRule = new ActivityTestRule<>(activityFactory, true, true);

    @Test
    public void activity_isBeingSpied() {
        verify(subject).setContentView(R.layout.layout_main);
    }
}

【讨论】:

  • 感谢@Martin 的建议。我已经完全按照您的示例中的建议更新了我的测试,但是问题仍然存在。也许您还有其他想法,为什么没有正确启动上下文?
  • @TomasMarch 可能会在return subject; 或其他地方设置断点。我很确定没有提供Context(基于错误消息)。 android.support.testandroidx.test 也使用不同的测试运行器。堆栈跟踪似乎也有点短,因为它只是框架类,而不是 MainActivity 或类似的。
  • 当错误是零星的时,这提示存在竞争条件(不一定在测试代码中)。见stackoverflow.com/a/39983311/549372
猜你喜欢
  • 2015-11-24
  • 1970-01-01
  • 1970-01-01
  • 2022-07-03
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 2017-07-14
相关资源
最近更新 更多