简体   繁体   中英

Fragment Crashes on ViewPager's OnPageChangeListener's onPageSelected method

I met a strange thing with ViewPager. I googled but do not find anything useful.
So I post what I have met here, maybe later someone get the same problem, or maybe luckily some genius guys can solve it.
The situation shows as following:
There are two page in the demo application.

  1. HomeActivity is the main page, and there is a viewpager in it. The viewpager have five fragments(Class PlusOneFragment) in it. In the fagment, there are two textviews. In the onCreateView method we assign one TextView to mValueTv field.
  2. AboutActivity is the other page used to trigger a application crash. Beacause I have used UncaughtExceptionHandler to catch exception in a custom Application class, which inherit from android.app.Application . So then application will goto the HomeActivity.

But when crashing, Then something sucks. The applcation crashes again.
That really bugged me.
In the HomeActivity, the viewpager has a ViewPager.OnPageChangeListener.
When its page changed, a methond of the specific position's fragment will be invoked in the onPageSelected method.
The method will set fragment mValueTv's value. In normal situation, it works fine, but when it comes to crash, the method throws a java.lang.NullPointerException exception.

05-06 21:37:24.392  17120-17120/com.rxread.viewpagerissue E/MessageQueue-JNI﹕ java.lang.NullPointerException
        at com.rxread.viewpagerissue.PlusOneFragment.onPageSeleted(PlusOneFragment.java:49)
        at com.rxread.viewpagerissue.HomeActivity$1.onPageSelected(HomeActivity.java:33)
        at android.support.v4.view.ViewPager.scrollToItem(ViewPager.java:571)
        at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:555)
        at android.support.v4.view.ViewPager.onTouchEvent(ViewPager.java:2022)
        at android.view.View.dispatchTouchEvent(View.java:7725)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2212)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1945)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2218)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2218)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2218)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2218)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2218)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2218)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2242)
        at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1536)
        at android.app.Activity.dispatchTouchEvent(Activity.java:2458)
        at android.support.v7.internal.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:59)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2190)
        at android.view.View.dispatchPointerEvent(View.java:7905)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4009)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3888)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3499)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3468)
        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3575)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3476)
        at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3632)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3499)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3468)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3476)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3449)
        at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5657)
        at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5637)
        at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5608)
        at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5815)
        at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:138)
        at android.os.Looper.loop(Looper.java:123)
        at android.app.ActivityThread.main(ActivityThread.java:5081)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
        at dalvik.system.NativeStart.main(Native Method)

Because there were too much code, so I packed them. The demo link is .
I really hope someone can help me. Thanks very much.


HomeActivity

public class HomeActivity extends ActionBarActivity {
ViewPager mViewpager;
BaseTabHostPagerAdapter mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home);

    mViewpager= (ViewPager) this.findViewById(R.id.home_ivp);
    mViewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener(){

        @Override
        public void onPageScrolled(int i, float v, int i1) {

        }

        @Override
        public void onPageSelected(int i) {
            Object object=mAdapter.getItem(i);
            if(object instanceof PlusOneFragment){
                ((PlusOneFragment)object).onPageSeleted(i);
            }
        }

        @Override
        public void onPageScrollStateChanged(int i) {

        }
    });

    mAdapter=new BaseTabHostPagerAdapter(getSupportFragmentManager());
    mViewpager.setAdapter(mAdapter);
    mAdapter.addPager("new 1", PlusOneFragment.newInstance("new 1"));
    mAdapter.addPager("new 2",PlusOneFragment.newInstance("new 2"));
    mAdapter.addPager("new 3",PlusOneFragment.newInstance("new 3"));
    mAdapter.addPager("new 4", PlusOneFragment.newInstance("new 4"));
    mAdapter.addPager("new 5", PlusOneFragment.newInstance("new 5"));
    mAdapter.notifyDataSetChanged();

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_home, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        AboutActivity.actionTo(this);
        return true;
    }

    return super.onOptionsItemSelected(item);
}

}


AboutActivity

public class AboutActivity extends ActionBarActivity {

public static void actionTo(Context context){
    Intent intent=new Intent(context,AboutActivity.class);
    context.startActivity(intent);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_about);

    TextView mClickToCrash= (TextView) findViewById(R.id.about_to_crash_tv);
    mClickToCrash.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int exception=2-2;
            int a=3/exception;
            Toast.makeText(AboutActivity.this, "GO EXCEPTION" + a, Toast.LENGTH_SHORT).show();
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_about, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

PlusOneFragment

public class PlusOneFragment extends Fragment {
private static final String ARG_PARAM1 = "param1";

private String mParam1;
private TextView mValueTv;

/**
 * Use this factory method to create a new instance of
 * this fragment using the provided parameters.
 *
 * @return A new instance of fragment PlusOneFragment.
 */
// TODO: Rename and change types and number of parameters
public static PlusOneFragment newInstance(String name) {
    PlusOneFragment fragment = new PlusOneFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, name);
    fragment.setArguments(args);
    return fragment;
}

public PlusOneFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
    }
}

public void onPageSeleted(int position){
    mValueTv.setText("onPageSeleted-Value:" +position+"--"+ SystemClock.currentThreadTimeMillis());
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_plus_one, container, false);
    TextView textView= (TextView) view.findViewById(R.id.fragment_name);
    textView.setText(mParam1);
    mValueTv= (TextView) view.findViewById(R.id.fragment_value);
    textView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mValueTv.setText("Value:"+ SystemClock.currentThreadTimeMillis());
            Toast.makeText(v.getContext(),"Toast",Toast.LENGTH_SHORT).show();
        }
    });
    return view;
}

@Override
public void onResume() {
    super.onResume();
}

ViewPagerIssueApplication

public class ViewPagerIssueApplication extends Application{
@Override
public void onCreate() {
    super.onCreate();
    CrashHandler handler = CrashHandler.getInstance();
    handler.init(getApplicationContext());
    Thread.setDefaultUncaughtExceptionHandler(handler);
}

}


CrashHandler

public class CrashHandler implements UncaughtExceptionHandler {
private static CrashHandler myCrashHandler;
private Context mContext;

private CrashHandler() {

}

public static synchronized CrashHandler getInstance() {
    if (myCrashHandler != null) {
        return myCrashHandler;
    } else {
        myCrashHandler = new CrashHandler();
        return myCrashHandler;
    }
}

public void init(Context context) {
    mContext = context;
}

@Override
public void uncaughtException(Thread arg0, Throwable exception) {
    StringWriter stackTrace = new StringWriter();
    exception.printStackTrace(new PrintWriter(stackTrace));
    System.err.println(stackTrace);
    System.exit(1);
}

}


BaseTabHostPagerAdapter

public class BaseTabHostPagerAdapter extends FragmentPagerAdapter {

private List<String> names = new ArrayList<String>();
private List<Fragment> fragments = new ArrayList<Fragment>();

public BaseTabHostPagerAdapter(FragmentManager fm) {
    super(fm);
}

@Override
public Fragment getItem(int position) {

    if(position > fragments.size()){
        return null;
    }
    return fragments.get(position);
}

public List<Fragment> getFragmentsList(){
    return fragments;
}

@Override
public int getCount() {
    return fragments.size();
}

@Override
public CharSequence getPageTitle(int position) {

    if(position > names.size()){
        return null;
    }
    return names.get(position);
}

public void addPager(String tabStr,Fragment fragment) {

    if (fragment != null) {
        fragments.add(fragment);
        names.add(tabStr);
    }
}

public void setTab(int location, String tab){

    if(location<0||location>names.size()){
        return;
    }
    if(null!=tab){
        names.remove(location);
        names.add(location, tab);
    }
}

}


Finally, with my team leader's help, I found that some fragments were not attached to the HomeActivity When crashing back to HomeActivity.
The onPageSelected method is invoked ahead of onCreateView method.
The solution is doing getActivity null check in PlusOneFragment's onPageSeleted method.

    public void onPageSeleted(int position) {
    if (getActivity() == null) {
        return;
    }
    mValueTv.setText("onPageSeleted-Value:" + position + "--" + SystemClock.currentThreadTimeMillis());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM