在Fragment中使用AsyncHttpClient框架异步访问接口,在接口的回调中,刷新界面,调用getActivity()有时会出现nullPointer的情况,这是什么原因呢?
(一) getActivity() 什么时候返回空,什么时候不返回空?
查看Fragment的getActivity()的代码:
/**
* Return the Activity this fragment is currently associated with.
*/
final public Activity getActivity() {
return mActivity;
}
返回的是Fragment的mActivity变量。
// Activity this fragment is attached to.
Activity mActivity;
mActivity的初始化是在FragmentManager的moveToState()方法中。
case Fragment.INITIALIZING:
if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
if (f.mSavedFragmentState != null) {
f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
f.mTarget = getFragment(f.mSavedFragmentState,
FragmentManagerImpl.TARGET_STATE_TAG);
if (f.mTarget != null) {
f.mTargetRequestCode = f.mSavedFragmentState.getInt(
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
}
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
if (!f.mUserVisibleHint) {
f.mDeferStart = true;
if (newState > Fragment.STOPPED) {
newState = Fragment.STOPPED;
}
}
}
f.mActivity = mActivity;
f.mParentFragment = mParent;
f.mFragmentManager = mParent != null
? mParent.mChildFragmentManager : mActivity.mFragments;
f.mCalled = false;
f.onAttach(mActivity);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
mActivity.onAttachFragment(f);
}
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState);
}
f.mRetaining = false;
if (f.mFromLayout) {
// For fragments that are part of the content view
// layout, we need to instantiate the view immediately
// and the inflater will take care of adding it.
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), null, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
}
}
可以看到,f.mActivity在Fragment的初始化阶段被初始化为FragmentManager的mActivity变量,之后依次调用了f.onAttach()、f.perfomCreate()(里面调用了onCreate())、f.performCreateView() (里面调用了onCreateView())、f.onViewCreated()等回调方法。
至于f.mActivity被置空也是在FragmentManager的moveToState()方法里。
case Fragment.CREATED:
if (newState < Fragment.CREATED) {
if (mDestroyed) {
if (f.mAnimatingAway != null) {
// The fragment's containing activity is
// being destroyed, but this fragment is
// currently animating away. Stop the
// animation right now -- it is not needed,
// and we can't wait any more on destroying
// the fragment.
Animator anim = f.mAnimatingAway;
f.mAnimatingAway = null;
anim.cancel();
}
}
if (f.mAnimatingAway != null) {
// We are waiting for the fragment's view to finish
// animating away. Just make a note of the state
// the fragment now should move to once the animation
// is done.
f.mStateAfterAnimating = newState;
newState = Fragment.CREATED;
} else {
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
if (!f.mRetaining) {
f.performDestroy();
}
f.mCalled = false;
f.onDetach();
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onDetach()");
}
if (!keepActive) {
if (!f.mRetaining) {
makeInactive(f);
} else {
f.mActivity = null;
f.mParentFragment = null;
f.mFragmentManager = null;
}
}
}
}
可以看到,f.mActivity的置空操作是在Fragment销毁阶段,在onDetach()回调之后。
因此,在Fragment的回调生命周期中,getActivity()都不会返回空。getActivity()为空表明,AsyncHttpClient的回调是在Fragment销毁后,才开始执行的。
(二)AsyncHttpClient异步请求的回调
AsyncHttpClient异步请求是在内部的线程池中运行的,回调是在通过Looper线程的Handler传回发起调用的线程执行。此时,有可能出现页面、Fragment已经被销毁的情况,从而getActivity()返回null。
解决方案是:在页面退出时,取消正在发起的请求;在回调中,做与UI相关的操作,需要对Activity进行判断(getActivity() != null && !getActivity.isFinish())。