3

我有一個Activity,它本身有三個Fragment s。嘗試在空對象引用上調用虛擬方法'java.lang.String android.content.Context.getPackageName()'

在其中一個片段中,有一個RecyclerView帶有自定義適配器,並且單擊其中一個項目將轉到另一個頁面,該頁面是相同Activity的新實例。但是,某些行爲會在我的應用程序中導致錯誤。

從我的活動中,點擊其中一個項目可以調出相同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());來自自定義適配器中的覆蓋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

+2

'getActivity()'可能返回null。你在哪裏設置你的'OnClickListener'? –

+0

請添加java代碼並且 - 沒必要 - 問題出現在代碼中 – piotrek1543

+0

它幾乎聽起來像您使用的是相同的Fragment,但帶有一個新的Activity,因此當它獲取Activity引用時,它不再存在。你是否在使用保留的片段,或者你是否將片段引用傳遞給活動的新實例?我們可能需要查看一些代碼才能瞭解這一點。 – NoChinDeluxe

回答

8

所以,問題是這條線

private static OnEntryClickListener mOnEntryClickListener; 

由於它是靜態的,你在運行時擁有類的一個實例。當您單擊某個項目時,會創建同一活動的第二個實例,並創建另一個實例mOnEntryClickListener,覆蓋前一個實例。因此,當您按回以返回到活動的第一個實例時,您正在使用第二個活動的mOnEntryClickListener實例,該實例已被銷燬。

+0

非常感謝!這解決了我的問題,但我也必須從'ViewHolder'中刪除'static'。我也讀了關於靜態內部類的這個特定問題,並且發現了關於內存泄漏和靜態/內部類的[很好的答案](http://stackoverflow.com/a/10968689/4230345)。 –

0

該問題很可能與您的匿名OnClickListener從原始片段捕獲getActivity()方法有關。你可以嘗試實現您MyFragmentOnClickListener,看看行爲在所有的變化(可能不是那麼簡單):

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

感謝您的答覆。說實話,現在對我來說已經快到午夜了,所以我明天會睡一覺並測試一下。 ;) –

+0

你的解決方案並不適合我。不過,我在我的回答中提到,我有按鈕,點擊後可打開活動。但是我實際上使用了一個帶有自定義適配器的'RecyclerView',並且當我點擊一個項目時打開Activity。這個問題可能出現在適配器中,因爲當我使用三個按鈕而不是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); 
       } 
      } 
     }); 
    } 

    ... 

} 
相關問題