尝试在空对象引用上调用虚拟方法 'java.lang.String android.content.Context.getPackageName()'
Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
我有一个 Activity
,它本身有三个 Fragment
。
在其中一个片段中,有一个带有自定义适配器的 RecyclerView
,点击它的其中一项会转到另一个页面,这是相同 Activity
的新实例.但是,某些行为会导致我的应用程序出错。
在我的 Activity 中,单击其中一项会弹出相同 Activity 的新实例,这很好。然后我按下后退按钮,我回到了第一个 Activity。但是再次单击这些项目之一(以启动相同 Activity 的新实例)会导致以下错误:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
同样重要的是要考虑到我正在 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.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);
}
在片段的内部class(onEntryClick
)中,我发现调用getActivity()
returns null
.
问题可能与您的匿名 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);
}
...
}
编辑: 这是一个 hack,不是我通常推荐的东西,但如果你处于紧要关头,可能会帮助你解决这个问题...
扩展 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);
}
});
}
尝试使用我的解决方案:
在您的 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
问题
在您的片段的 oncreate 中,保存您的 activity 的引用并在启动其他 activity 之前检查您的 activity 是否处于恢复状态。
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);
}
}
});
}
...
}
所以,问题是这一行
private static OnEntryClickListener mOnEntryClickListener;
因为它是静态的,所以您在运行时只有一个 class 实例。当您单击一个项目时,会创建相同 Activity 的第二个实例,并且还会创建 mOnEntryClickListener
的另一个实例,覆盖前一个实例。因此,当您按回 return 到 Activity 的第一个实例时,您正在使用第二个 Activity 的 mOnEntryClickListener
实例,它已被销毁。
在您的片段中创建一个类型为 Your Activity:
的变量
public MainActivity activity;
然后使用附加方法并分配值:
@Override
public void onAttach(Activity activity){
this.activity = activity;
}
然后将您的意图与 activity 一起用作上下文:
Intent mIntent = new Intent(activity, MyActivity.class);
就算问题已经解决了,我也想说说我的问题是什么,我的解决办法
问题
在我的代码中,我添加了一个带有 firebaseAuth.addAuthStateListener(listener)
的侦听器,因此当用户登录并启动新的 activity 时,将执行 listener
并导致错误 (因为里面还有一个 startActivity(getContext(), ...)
).
解决方案
使用方法firebaseAuth.removeAuthStateListener(listener)
传递监听器的引用,问题就解决了。在启动导致问题的 activity 之前,您必须删除引用。
我有一个 Activity
,它本身有三个 Fragment
。
在其中一个片段中,有一个带有自定义适配器的 RecyclerView
,点击它的其中一项会转到另一个页面,这是相同 Activity
的新实例.但是,某些行为会导致我的应用程序出错。
在我的 Activity 中,单击其中一项会弹出相同 Activity 的新实例,这很好。然后我按下后退按钮,我回到了第一个 Activity。但是再次单击这些项目之一(以启动相同 Activity 的新实例)会导致以下错误:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
同样重要的是要考虑到我正在 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.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);
}
在片段的内部class(onEntryClick
)中,我发现调用getActivity()
returns null
.
问题可能与您的匿名 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);
}
...
}
编辑: 这是一个 hack,不是我通常推荐的东西,但如果你处于紧要关头,可能会帮助你解决这个问题...
扩展 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);
}
});
}
尝试使用我的解决方案: 在您的 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
问题
在您的片段的 oncreate 中,保存您的 activity 的引用并在启动其他 activity 之前检查您的 activity 是否处于恢复状态。
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);
}
}
});
}
...
}
所以,问题是这一行
private static OnEntryClickListener mOnEntryClickListener;
因为它是静态的,所以您在运行时只有一个 class 实例。当您单击一个项目时,会创建相同 Activity 的第二个实例,并且还会创建 mOnEntryClickListener
的另一个实例,覆盖前一个实例。因此,当您按回 return 到 Activity 的第一个实例时,您正在使用第二个 Activity 的 mOnEntryClickListener
实例,它已被销毁。
在您的片段中创建一个类型为 Your Activity:
的变量public MainActivity activity;
然后使用附加方法并分配值:
@Override
public void onAttach(Activity activity){
this.activity = activity;
}
然后将您的意图与 activity 一起用作上下文:
Intent mIntent = new Intent(activity, MyActivity.class);
就算问题已经解决了,我也想说说我的问题是什么,我的解决办法
问题
在我的代码中,我添加了一个带有 firebaseAuth.addAuthStateListener(listener)
的侦听器,因此当用户登录并启动新的 activity 时,将执行 listener
并导致错误 (因为里面还有一个 startActivity(getContext(), ...)
).
解决方案
使用方法firebaseAuth.removeAuthStateListener(listener)
传递监听器的引用,问题就解决了。在启动导致问题的 activity 之前,您必须删除引用。