web-dev-qa-db-ja.com

nullオブジェクト参照で仮想メソッド 'Java.lang.String Android.content.Context.getPackageName()'を呼び出そうとしました

Activityがあり、それ自体に3つのFragmentがあります。

これらのフラグメントの1つには、カスタムアダプタを備えたRecyclerViewがあり、そのアイテムの1つをクリックすると、同じActivityの新しいインスタンスである別のページに移動します。ただし、特定の動作により、アプリでエラーが発生します。

私のアクティビティから、アイテムの1つをクリックすると、同じアクティビティの新しいインスタンスが表示されます。これは問題ありません。次に、戻るボタンを押すと、最初のアクティビティに戻ります。ただし、これらの項目の1つをもう一度クリックすると(同じアクティビティの新しいインスタンスを起動するため)、次のエラーが発生します。

Java.lang.NullPointerException:nullオブジェクト参照で仮想メソッド 'Java.lang.String Android.content.Context.getPackageName()'を呼び出そうとしました

また、アクティビティにあるフラグメントの1つで、アクティビティの新しいインスタンス(つまり、3つのアイテムがある場所)を呼び出していることを考慮することも重要です。だから、私がそれを呼んでいるとき、私は次のようなものを持っています:

_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が返されることがわかりました。

だから、問題はこの行です

private static OnEntryClickListener mOnEntryClickListener;

静的であるため、実行時にクラスのインスタンスが1つあります。アイテムをクリックすると、同じアクティビティの2番目のインスタンスが作成され、mOnEntryClickListenerの別のインスタンスも作成され、前のインスタンスが上書きされます。したがって、押し戻してアクティビティの最初のインスタンスに戻ると、破棄された2番目のアクティビティのmOnEntryClickListenerのインスタンスを使用していることになります。

11
Mimmo Grottoli

この問題は、匿名のOnClickListenerが元のフラグメントからgetActivity()メソッドをキャプチャすることに関連している可能性があります。 OnClickListenerMyFragmentを実装してみて、動作がまったく変わるかどうかを確認できます(それほど単純ではないかもしれません)。

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

アプリケーションがMyApplicationを実行するように、AndroidManifest.xmlを変更します。

_<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
Cory Charlton

私のソリューションを使用してみてください: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
Phan Dinh Thai

フラグメントの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);
                }
            }
        });
    }

    ...

}
0
Suhaib Roomy

フラグメントで、タイプYourActivityの変数を作成します。

public MainActivity activity;

次に、アタッチのメソッドを使用して、値を割り当てます。

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

次に、アクティビティをコンテキストとして意図を使用します。

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