尝试在空对象引用上调用虚拟方法“java.lang.String android.content.Context.getPackageName()”

Posted

技术标签:

【中文标题】尝试在空对象引用上调用虚拟方法“java.lang.String android.content.Context.getPackageName()”【英文标题】:Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference 【发布时间】:2016-04-25 13:09:56 【问题描述】:

我有一个Activity,它本身有三个Fragments。

在其中一个片段中,有一个带有自定义适配器的RecyclerView,单击其中一个项目将转到另一个页面,这是相同Activity 的新实例。但是,某种行为会导致我的应用出现错误。

在我的 Activity 中,单击其中一项会显示同一 Activity 的新实例,这很好。然后我按下后退按钮,我被带回到第一个活动。但是再次单击这些项目之一(以启动同一 Activity 的新实例)会导致以下错误:

java.lang.NullPointerException:

考虑到我在我的 Activity 中的一个片段中调用 Activity 的新实例(即三个项目所在的位置)也很重要。所以,当我调用它时,我有类似的东西:

public class MyActivity extends AppCompatActivity 

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        ...
        ViewPager viewPager = (ViewPager) findViewById(R.id.detail_viewpager);
        viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
        TabLayout tabLayout = (TabLayout) findViewById(R.id.detail_tabs);
        tabLayout.setTabTextColors(
                ContextCompat.getColor(this, R.color.text_white_secondary),
                ContextCompat.getColor(this, R.color.text_white));
        tabLayout.setSelectedTabIndicatorColor(ContextCompat.getColor(this, R.color.white));
        tabLayout.setupWithViewPager(viewPager);
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
    

    ...

    public class ViewPagerAdapter extends FragmentPagerAdapter 

        public ViewPagerAdapter(FragmentManager fm) 
            super(fm);
        

        @Override
        public int getCount() 
            return 3;
        

        @Override
        public Fragment getItem(int position) 
            switch (position) 
                case 0: return new MainFragment();
                case 1: return new MyFragment();
                case 2: return new MyOtherFragment();
            
            return null;
        

        @Override
        public CharSequence getPageTitle(int position) 
            Locale l = Locale.getDefault();
            switch (position) 
                case 0:
                    return getString(R.string.tab_main_frag).toUpperCase(l);
                case 1:
                    return getString(R.string.tab_my_frag).toUpperCase(l);
                case 2:
                    return getString(R.string.tab_my_other_frag).toUpperCase(l);
            
            return null;
        
    

    ...

    public static class MyFragment extends Fragment implements MyRVAdapter.OnEntryClickListener 

        ...

        private ArrayList<ItemObj> mArrayList;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
            ...
            doStuff();
            ...
        

        private void doStuff() 
            ...
            mArrayList = ...;
            MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
            adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() 
                @Override
                public void onEntryClick(View view, int position) 
                    Intent intent = new Intent(getActivity(), MyActivity.class);
                    intent.putExtra("INFORMATION", mArrayList.get(position));
                    startActivity(intent);
                
            );
        

        ...

    

    ...

这是我的自定义适配器的一部分:

public class MyRVAdapter extends RecyclerView.Adapter<MyRVAdapter.MyViewHolder> 

    public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener 

        ...

        MyViewHolder(View itemView) 
            super(itemView);
            itemView.setOnClickListener(this);
            ...
        

        @Override
        public void onClick(View v) 
            // The user may not set a click listener for list items, in which case our listener
            // will be null, so we need to check for this
            if (mOnEntryClickListener != null) 
                mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
            
        
    

    private Context mContext;
    private ArrayList<ItemObj> mArray;

    public MyRVAdapter(Context context, ArrayList<ItemObj> array) 
        mContext = context;
        mArray = array;
    

    @Override
    public int getItemCount() 
        return mArray.size();
    

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tile_simple, parent, false);
        return new MyViewHolder(view);
    

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) 
        ItemObj anItem = mArray.get(position);

        ...
    

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) 
        super.onAttachedToRecyclerView(recyclerView);
    


    private static OnEntryClickListener mOnEntryClickListener;

    public interface OnEntryClickListener 
        void onEntryClick(View view, int position);
    

    public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) 
        mOnEntryClickListener = onEntryClickListener;
    


这是完整的错误:

01-23 14:07:59.083 388-388/com.mycompany.myapp E/androidRuntime: FATAL EXCEPTION: main
Process: com.mycompany.myapp, PID: 388
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
    at android.content.ComponentName.<init>(ComponentName.java:77)
    at android.content.Intent.<init>(Intent.java:4570)
    at com.mycompany.myapp.MyActivity$MyFragment$1.onEntryClick(MyActivity.java:783)
    at com.mycompany.myapp.adapter.MyRVAdapter$MyViewHolder.onClick(MyRVAdapter.java:42)
    at android.view.View.performClick(View.java:5197)
    at android.view.View$PerformClick.run(View.java:20926)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:5951)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)

错误指向第一行:Intent intent = new Intent(getActivity(), MyActivity.class);(来自片段)首先,后面的行(错误中)指向自定义适配器中覆盖的onClick 方法中的mOnEntryClickListener.onEntryClick(v, getLayoutPosition());

我也读过类似的答案,但他们没有解决我的问题。

编辑:

通过使用:

if (getActivity() == null) 
    Log.d(LOG_TAG, "Activity context is null");
 else 
    Intent intent = new Intent(getActivity(), MyActivity.class);
    intent.putExtra("INFORMATION", mArrayList.get(position));
    startActivity(intent);

在片段的内部类(onEntryClick)中,我发现调用getActivity()会返回null

【问题讨论】:

getActivity() 可能返回 null。你在哪里设置你的OnClickListener 请添加 java 代码 - 没必要 - 问题出在代码中 这听起来就像你在使用同一个 Fragment,但是使用了一个新的 Activity,所以当它去获取 Activity 引用时,它不再存在了。您是在使用保留的 Fragment,还是将 Fragment 引用传递给 Activity 的新实例?我们可能需要查看一些代码才能理解这一点。 我认为它发生的是OnClickListener 正在封装原始片段。没有看到OnClickListener 的设置位置,虽然我不能确定。 @CoryCharlton OnClickListener 在我的Activity 中的Fragment 中被调用。 【参考方案1】:

即使问题已经解决了,我也想说我的问题是什么,我的解决方案。

问题

在我的代码中,我添加了一个带有firebaseAuth.addAuthStateListener(listener) 的侦听器,因此当用户登录并启动新活动时,listener 被执行并导致错误(因为在里面我有另一个startActivity(getContext(), ...))。

解决方案

使用方法firebaseAuth.removeAuthStateListener(listener) 传递侦听器的引用并解决问题。在开始导致问题的活动之前,您必须删除引用。

【讨论】:

【参考方案2】:

在您的片段中创建您的活动类型的变量:

public MainActivity activity;

然后使用attach上的方法并赋值:

@Override
public void onAttach(Activity activity)
    this.activity = activity;

然后将您的意图与活动一起用作上下文:

Intent mIntent = new Intent(activity, MyActivity.class);

【讨论】:

【参考方案3】:

所以,问题是这一行

private static OnEntryClickListener mOnEntryClickListener;

由于它是静态的,因此您在运行时拥有该类的单个实例。当您单击一个项目时,会创建同一个 Activity 的第二个实例,并且还会创建另一个 mOnEntryClickListener 实例,覆盖前一个实例。因此,当您按下返回以返回 Activity 的第一个实例时,您正在使用第二个 Activity 的 mOnEntryClickListener 实例,该实例已被销毁。

【讨论】:

非常感谢!这解决了我的问题,但我也必须从我的ViewHolder 中删除static。我还阅读了这个关于静态内部类的特殊问题,并找到了 a great answer 关于内存泄漏和静态/内部类的信息。 你能帮我解决这个错误吗***.com/questions/51405397/…@MimmoGrottoli @FarbodSalamat-Zadeh 实际上没有理由需要从 Viewholder 声明中删除 staticViewholder 应该是 static class,即:只是一个没有行为(方法)的数据容器。如果您向Viewholder 添加方法,迫使您从class 定义中删除static 修饰符,这意味着每个Viewholder 对象现在都有对外部类的引用(在这种情况下,适配器)。这是对存储的浪费,意味着您的架构存在缺陷。只是说'【参考方案4】:

在您的片段的 oncreate 中,保存您的活动的引用,并在启动其他活动之前检查您的活动是否处于恢复状态。

public static class MyFragment extends Fragment implements MyRVAdapter.OnEntryClickListener 

    ...

    private ArrayList<ItemObj> mArrayList;
    private MyActivity mActivity;
    @Override
    public void onCreate(Bundle savedInstanceState) 
         mActivity = getActivity();
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        ...
        doStuff();
        ...
    

    private void doStuff() 
        ...
        mArrayList = ...;
        MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
        adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() 
            @Override
            public void onEntryClick(View view, int position) 
                if (!mActivity.isFinishing() 
                    Intent intent = new Intent(mActivity, MyActivity.class);
                    intent.putExtra("INFORMATION", mArrayList.get(position));
                    startActivity(intent);
                
            
        );
    

    ...


【讨论】:

【参考方案5】:

尝试使用我的解决方案: 在您的 MyActivity 中,创建一个新函数:

public void openAnotherActivity(ObjectItem item) 
      Intent intent = new Intent(MyApplication.getInstance(), MyActivity.class);
      intent.putExtra("INFORMATION", item);
      startActivity(intent);

然后修改doStuff()为:

    private void doStuff() 
    ...
    mArrayList = ...;
    MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
    adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() 
        @Override
        public void onEntryClick(View view, int position) 
            openAnotherActivity(mArrayList.get(position))
        
    );

我不确定它是否能帮助你,但我认为至少它可以解决context 问题

【讨论】:

我无法从静态上下文中引用 openAnotherActivity(非静态上下文)。另外,MyApplication.getInstance() 是否意味着来自另一个答案。 所以我认为问题在于静态上下文。为什么需要将Holder设置为静态类,而它可以在正常上下文中【参考方案6】:

问题可能与您的匿名OnClickListener 从原始片段中捕获getActivity() 方法有关。您可以尝试在您的MyFragment 上实现OnClickListener,看看行为是否发生了变化(可能没那么简单):

public static class MyFragment extends Fragment implements View.OnClickListener 

    ...

    private void doStuff() 
        button.setOnClickListener(this);
    

    @Override
    public void onClick(View v) 
        Intent intent = new Intent(getActivity(), MyActivity.class);
        intent.putExtra("INFORMATION", value);
        startActivity(intent);

    

    ...



编辑:这是一种技巧,我通常不会推荐,但如果您遇到困难,可能会解决问题...

扩展Application 以提供静态应用程序实例:

public class MyApplication extends Application 
    private static MyApplication _instance;

    @Override
    public void onCreate() 
        super.onCreate();

        _instance = this;
    

    public static MyApplication getInstance() 
        return _instance;
    

修改您的 AndroidManifest.xml 以便您的应用程序运行 MyApplication:

<application
    android:name="com.package.MyApplication"
<!-- Rest of your manifest -->

然后将getActivity() 替换为对MyApplication.getInstance() 的调用:

private void doStuff() 
    ...
    mArrayList = ...;
    MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
    adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() 
        @Override
        public void onEntryClick(View view, int position) 
            Intent intent = new Intent(MyApplication.getInstance(), MyActivity.class);
            intent.putExtra("INFORMATION", mArrayList.get(position));
            startActivity(intent);
        
    );

【讨论】:

感谢您的回答。老实说,对我来说现在已经快午夜了,所以我会睡一觉,明天再测试一下。 ;) 您的解决方案对我不起作用。但是,我在回答中提到我有按钮,单击时会打开活动。但我实际上是在使用RecyclerView 和我制作的自定义适配器,当我点击其中的一个项目时,活动就会打开。问题可能出在适配器中,因为当我使用三个按钮而不是 RecyclerView 和适配器测试我之前拥有的东西时,它运行良好。我之前没有提到它的原因是我认为适配器不会有问题,所以不包括它会提出一个更简单的问题。 我已经更新了我的答案,我不确定它是否有帮助,但我想如果问题存在,我还不如包含自定义适配器。 问题可能就在那里。你在哪里设置你的OnEntryClickListener 抱歉 - 请参阅我的更新答案(我之前忘记包含它)。

以上是关于尝试在空对象引用上调用虚拟方法“java.lang.String android.content.Context.getPackageName()”的主要内容,如果未能解决你的问题,请参考以下文章

java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“ActionBar.setNavigationMode(int)”

NullPointerException:尝试在空对象引用上调用虚拟方法 AlertDialog.setTitle(java.lang.CharSequence) [重复]

(Kotlin,viewBinding)java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“boolean java.lang.Thread.isAlive(

尝试在空对象引用上调用虚拟方法“java.lang.String android.content.Context.getPackageName()”

尝试在空对象引用上调用虚拟方法“java.lang.String android.content.Context.getPackageName()”

java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“int android.view.View.getImportantForAccessibility()”