3

У меня есть Activity, который сам по себе имеет три Fragment s.Попытка вызвать виртуальный метод 'java.lang.String android.content.Context.getPackageName()' на ссылке нулевого объекта

В одном из этих фрагментов есть RecyclerView с пользовательским адаптером, и нажатие на один из его элементов перейдет на другую страницу, которая является новым экземпляром того же Activity. Однако определенное поведение вызывает ошибку в моем приложении.

В моей деятельности щелчок по одному из элементов вызывает новый экземпляр той же Деятельности, что и отлично. Затем я нажимаю кнопку «Назад», и я возвращаюсь к первой операции. Но нажав на один из этих пунктов снова (для запуска нового экземпляра той же деятельности) вызывает следующее сообщение об ошибке:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference

Также важно учитывать, что я называю новый экземпляр деятельности (т.е. там, где три элемента), в одном из фрагментов, которые у меня есть в моей деятельности. Так что, когда я называю его, у меня есть что-то вроде:

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()); из метода overriden onClick в пользовательской адаптете р.

Я также читал похожие ответы, но они не решили мою проблему.

Edit:

С помощью:

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.

+2

Вероятно, 'getActivity()' возвращает значение null. Где вы устанавливаете свой 'OnClickListener'? –

+0

, пожалуйста, добавьте код java и - не обязательно - проблема в коде – piotrek1543

+0

Похоже, вы используете тот же фрагмент, но с новым действием, поэтому, когда он идет, чтобы получить ссылку на Activity, его больше нет. Вы используете случайно сохраненный фрагмент или передаете ссылку фрагмента на новый экземпляр Activity? Вероятно, нам нужно увидеть какой-то код, чтобы понять это. – NoChinDeluxe

ответ

8

Таким образом, проблема эта линия

private static OnEntryClickListener mOnEntryClickListener; 

Поскольку это статический у вас есть один экземпляр класса во время выполнения. Когда вы нажимаете на элемент, создается второй экземпляр одного и того же действия, и создается еще один экземпляр mOnEntryClickListener, перезаписывающий предыдущий. Итак, когда вы нажимаете назад, чтобы вернуться к первому экземпляру Activity, вы используете экземпляр mOnEntryClickListener второго действия, которое было уничтожено.

+0

Большое вам спасибо! Это решило мою проблему, но мне также пришлось удалить 'static' из моего' ViewHolder'. Я также прочитал этот вопрос о статических внутренних классах и нашел [отличный ответ] (http://stackoverflow.com/a/10968689/4230345) об утечках памяти и статических/внутренних классах. –

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

    } 

    ... 

} 

Edit: Это хак, а не то, что я обычно рекомендую но может получить вас вокруг проблемы, если вы в крайнем случае ...

Продлить 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); 
     } 
    }); 
} 
+0

Спасибо за ответ. Честно говоря, для меня сейчас почти полночь, поэтому я посплю и завтра проведу. ;) –

+0

Ваше решение не сработало для меня. Тем не менее, я упомянул в своем ответе, что у меня есть кнопки, которые при нажатии открывают действие. Но я фактически использую «RecyclerView» с настраиваемым адаптером, который я создал, и активность открывается, когда я нажимаю на элемент из этого. Проблема может быть в адаптере, потому что, когда я тестировал то, что у меня было раньше, с тремя кнопками вместо RecyclerView и адаптером, он работал нормально. Причина, по которой я не упоминал об этом раньше, заключается в том, что я предположил, что с адаптером не будет проблем, поэтому, не считая этого, вопрос был бы более простым. –

+0

Я обновил свой ответ, я не уверен, помогает ли он, но я подумал, что я могу включить в него пользовательский адаптер, если проблема там. –

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

+0

Я не могу ссылаться на 'openAnotherActivity' (нестатический контекст) из статического контекста. Кроме того, 'MyApplication.getInstance()' должен быть из другого ответа. –

+0

Так что я думаю, что проблема заключается в статическом контексте. Почему вам нужно сделать Holder как статический класс, пока он может быть в нормальном контексте? –

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

    ... 

} 
Смежные вопросы

 Смежные вопросы