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

时间:2016-01-19 21:32:48

标签: android android-fragments nullpointerexception android-context

我有一个Activity,它本身有三个Fragment

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

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

  

java.lang.NullPointerException:尝试调用虚方法' java.lang.String android.content.Context.getPackageName()'在空对象引用上

同样重要的是要考虑我在我的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);(来自片段),首先是(在错误中)从自定义中的覆盖mOnEntryClickListener.onEntryClick(v, getLayoutPosition());方法指向onClick的行适配器。

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

修改:

使用:

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

6 个答案:

答案 0 :(得分:8)

所以,问题是这一行

plugin.xml

由于它是静态的,因此在运行时只有一个类的实例。单击某个项目时,将创建同一活动的第二个实例,并创建另一个private static OnEntryClickListener mOnEntryClickListener; 实例,覆盖前一个实例。因此,当您按回以返回活动的第一个实例时,您正在使用第二个活动的mOnEntryClickListener实例,该实例已被销毁。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

尝试使用我的解决方案: 在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问题

答案 3 :(得分:0)

在片段的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);
                }
            }
        });
    }

    ...

}

答案 4 :(得分:0)

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

public MainActivity activity;

然后使用附加方法并指定值:

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

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

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

答案 5 :(得分:0)

即使问题已经解决,我也想说我的问题和解决方案。

问题

在我的代码中,我使用firebaseAuth.addAuthStateListener(listener)添加了一个侦听器,因此当用户登录并开始新活动时,将执行listener并导致错误(因为内部还有另一个{ {1}}。

解决方案

使用方法startActivity(getContext(), ...)传递侦听器的引用,即可解决问题。您必须先删除引用,然后再开始引起问题的活动。