“尝试在空对象引用上从字段'int android.content.pm.ApplicationInfo.targetSdkVersion'读取”例外

时间:2018-11-06 14:11:18

标签: android mockito android-espresso

在使用Mockito运行Espresso测试时,我目前遇到这样的问题:"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.'

此外,崩溃的日志猫:

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监视活动的情况下启动活动,则不会出现此问题。

更新:

通过在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;
}

是否可以在不修改系统类的情况下达到类似的结果?

2 个答案:

答案 0 :(得分:3)

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

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

解决方案是:

  

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

答案 1 :(得分: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是错误的; new ActivityTestRule<>在没有<T>的情况下看起来很奇怪,应该看起来像一样:

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);
    }
}