Android Fragment 生命周期问题(onActivityResult 上的 NullPointerException)
Posted
技术标签:
【中文标题】Android Fragment 生命周期问题(onActivityResult 上的 NullPointerException)【英文标题】:Android Fragment lifecycle issue (NullPointerException on onActivityResult) 【发布时间】:2014-06-08 09:40:56 【问题描述】:我遇到了一个问题,我找不到任何解释。 我有一个使用 TabManager 显示片段的 FragmentActivity,如下所示:
public class WorkOrderFormTabFragmentActivity extends FragmentActivity
TabHost mTabHost;
TabManager mTabManager;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.work_order_form_tab_new);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup();
mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent);
mTabManager.addTab(mTabHost.newTabSpec("form").setIndicator("Form"),
WorkOrderFormFragment.class, null);
mTabManager.addTab(mTabHost.newTabSpec("pictures").setIndicator("Pictures"),
PictureListFragment.class, null);
if (savedInstanceState != null)
mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
@Override
protected void onSaveInstanceState(Bundle outState)
super.onSaveInstanceState(outState);
outState.putString("tab", mTabHost.getCurrentTabTag());
public static class TabManager implements TabHost.OnTabChangeListener
private final FragmentActivity mActivity;
private final TabHost mTabHost;
private final int mContainerId;
private final HashMap<String, TabInfo> mTabs = new HashMap<String, TabInfo>();
TabInfo mLastTab;
static final class TabInfo
private final String tag;
private final Class<?> clss;
private final Bundle args;
private Fragment fragment;
TabInfo(String _tag, Class<?> _class, Bundle _args)
tag = _tag;
clss = _class;
args = _args;
static class DummyTabFactory implements TabHost.TabContentFactory
private final Context mContext;
public DummyTabFactory(Context context)
mContext = context;
@Override
public View createTabContent(String tag)
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
public TabManager(FragmentActivity activity, TabHost tabHost, int containerId)
mActivity = activity;
mTabHost = tabHost;
mContainerId = containerId;
mTabHost.setOnTabChangedListener(this);
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args)
tabSpec.setContent(new DummyTabFactory(mActivity));
String tag = tabSpec.getTag();
TabInfo info = new TabInfo(tag, clss, args);
info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag);
if (info.fragment != null && !info.fragment.isDetached())
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.detach(info.fragment);
ft.commit();
mTabs.put(tag, info);
mTabHost.addTab(tabSpec);
@Override
public void onTabChanged(String tabId)
TabInfo newTab = mTabs.get(tabId);
if (mLastTab != newTab)
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
if (mLastTab != null)
if (mLastTab.fragment != null)
//ft.detach(mLastTab.fragment);
ft.hide(mLastTab.fragment);
if (newTab != null)
if (newTab.fragment == null)
newTab.fragment = Fragment.instantiate(mActivity,
newTab.clss.getName(), newTab.args);
ft.add(mContainerId, newTab.fragment, newTab.tag);
else
//ft.attach(newTab.fragment);
ft.show(newTab.fragment);
mLastTab = newTab;
ft.commit();
mActivity.getSupportFragmentManager().executePendingTransactions();
在此 FragmentActivity 的第二个选项卡中,用户可以管理图片列表并使用相机添加更多图片。
片段代码:
public class PictureListFragment extends Fragment
static final int TAKE_PICTURE_ACTIVITY = 1;
static final int EDIT_PICTURE_ACTIVITY = 2;
FormPictureListAdapter lvAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewgrp,
Bundle savedInstanceState)
// Inflate the layout for this fragment
View cont = inflater.inflate(R.layout.form_picture_list, viewgrp, false);
LinearLayout container = (LinearLayout)cont.findViewById(R.id.formPictureListLayout);
try
final Context context = getActivity();
ListView ls2 = new ListView(context);
// clear previous results in the LV
ls2.setAdapter(null);
// populate
ArrayList<MFPicture> pictures = new ArrayList<MFPicture>();
//pictures.add(0, new MFPicture());
pictures.addAll(((MFApplication)getActivity().getApplication()).getCurrentForm().getPictures());
lvAdapter = new FormPictureListAdapter(context, pictures);
ls2.setAdapter(lvAdapter);
LinearLayout.LayoutParams Params = new LinearLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT, 0, 1f);
ls2.setLayoutParams(Params);
ls2.setOnItemClickListener(new OnItemClickListener()
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
final MFPicture picture = ((MFPictureView)view).getPicture();
final int idx = position;
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
switch (which)
case DialogInterface.BUTTON_POSITIVE:
//Edit picture
EditPictureActivity.setPicture(picture);
Intent configurationOpen = new Intent(getActivity(), EditPictureActivity.class);
startActivityForResult(configurationOpen, EDIT_PICTURE_ACTIVITY);
break;
case DialogInterface.BUTTON_NEGATIVE:
//Delete picture
((MFApplication)getActivity().getApplication()).getCurrentForm().getPictures().remove(idx);
MFUtils.deleteFile(picture.getPath());
lvAdapter.updatePictureList(((MFApplication)getActivity().getApplication()).getCurrentForm().getPictures());
lvAdapter.notifyDataSetChanged();
break;
;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(getResources().getString(R.string.wo_bem_regie_list_el_action)).setPositiveButton(getResources().getString(R.string.wo_bem_regie_list_el_edit), dialogClickListener)
.setNegativeButton(getResources().getString(R.string.wo_bem_regie_list_el_delete), dialogClickListener).show();
);
container.addView(ls2);
LayoutInflater layoutInflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view=layoutInflater.inflate(R.layout.add_btn_bottom,container);
view.setBackgroundResource(R.drawable.list_selector_even);
TextView text = (TextView)view.findViewById(R.id.title);
text.setText(getResources().getString(R.string.wo_picturelist_add));
view.setOnClickListener(new OnClickListener()
@Override
public void onClick(View v)
v.setBackgroundResource(R.drawable.list_selector_even);
String pictureFile = ((MFApplication)getActivity().getApplication()).getNextPictureFile();
String picPath = MFUtils.MF_STORAGE_PATH+"/"+pictureFile;
Log.e("FormPictureListActivity", "PicturePath : "+picPath);
//setBackgroundResource(android.R.drawable.list_selector_background);
try
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(picPath)));
startActivityForResult(intent, TAKE_PICTURE_ACTIVITY);
catch (ActivityNotFoundException e)
Log.e("FormPictureListActivity", "Call failed", e);
);
catch(Exception e)
e.printStackTrace();
Log.e("FormPictureListActivity", "Error:", e);
return cont;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
//super.onActivityResult(requestCode, resultCode, data);
String pictureFile = ((MFApplication)getActivity().getApplication()).getPictureFile();
Log.d("FormPictureListActivity", "ActivityResult:"+resultCode);
Log.d("FormPictureListActivity", "ActivityResult-picFile:"+pictureFile);
if (requestCode == TAKE_PICTURE_ACTIVITY)
if(resultCode == getActivity().RESULT_OK)
Log.d("FormPictureListActivity", "ActivityResult:OK");
MFPicture picture = new MFPicture(MFPicture.TYPE_PICTURE, MFUtils.MF_STORAGE_PATH+"/"+pictureFile);
((MFApplication)getActivity().getApplication()).getCurrentForm().getPictures().add(picture);
lvAdapter.updatePictureList(((MFApplication)getActivity().getApplication()).getCurrentForm().getPictures());
lvAdapter.notifyDataSetChanged();
EditPictureActivity.setPicture(picture);
Intent configurationOpen = new Intent(getActivity(), EditPictureActivity.class);
startActivityForResult(configurationOpen, EDIT_PICTURE_ACTIVITY);
else if (requestCode == EDIT_PICTURE_ACTIVITY)
EditPictureActivity.getPicture().setComment(EditPictureActivity.getPicture().getCommentUIValue());
lvAdapter.updatePictureList(((MFApplication)getActivity().getApplication()).getCurrentForm().getPictures());
lvAdapter.notifyDataSetChanged();
在我的测试设备(Nexus 5、Galaxy Nexus、Galaxy Mini 2)上一切正常,但我不时收到来自我无法访问的其他设备(主要是运行 Android 4.0.4 的设备)的错误:
java.lang.RuntimeException: Unable to resume activity
com.mf.mobile.android/com.mf.mobile.android.WorkOrderFormTabFragmentActivity: java.lang.RuntimeException: Failure delivering result ResultInfowho=null, request=131073, result=-1, data=null to activity com.mf.mobile.android/com.mf.mobile.android.WorkOrderFormTabFragmentActivity: java.lang.NullPointerException
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2616)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2644)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2130)
at android.app.ActivityThread.access$600(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1248)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4645)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:809)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:576)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.RuntimeException: Failure delivering result ResultInfowho=null, request=131073, result=-1, data=null to activity com.mf.mobile.android/com.mf.mobile.android.WorkOrderFormTabFragmentActivity: java.lang.NullPointerException
at android.app.ActivityThread.deliverResults(ActivityThread.java:3156)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2599)
... 12 more
Caused by: java.lang.NullPointerException
at com.timewise.mobile.android.fragments.PictureListFragment.onActivityResult(PictureListFragment.java:138)
at android.support.v4.app.FragmentActivity.onActivityResult(FragmentActivity.java:166)
at android.app.Activity.dispatchActivityResult(Activity.java:4662)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3152)
... 13 more
NPE 出现在这行代码中: lvAdapter.updatePictureList(((MframeApplication)getActivity().getApplication()).getCurrentForm().getPictures());
这意味着此时 lvAdapter 变量为空。但是这个变量应该已经在 Fragment 的 onCreateView 中设置了......这让我觉得,在某些时候 Fragment 可能已经在没有调用 onCreateView 的情况下被重新创建。
我找不到有关此问题的任何解释。你能帮我解决这个问题吗?
非常感谢
【问题讨论】:
【参考方案1】:好的,我认为如果用户打开您的应用然后点击主页按钮,然后点击显示正在运行的应用程序按钮(主页按钮右侧的那个)或按住主页按钮,就会发生这种情况某些手机上的按钮,然后选择您的应用程序以返回到它。然后,如果您查看生命周期,它将调用 onResume(即上面的堆栈跟踪中的内容),而无需再次调用 onCreateView。
Fragment Lifecycle
现在,您应该能够通过进入 Nexus 5 上的开发者选项并选择不保留活动来模仿这种情况以查看它的发生。然后打开您的应用程序,转到该片段,点击主页按钮,然后点击正在运行的应用程序按钮并选择您的应用程序,我认为它会向您显示该异常。
【讨论】:
你好 Kaediil,首先'坦克你!让我重现这个NPE!所以在 onResume 函数中处理 ListView 适配器才是正确的处理方式,对吧?但是为什么 'lvAdapter' 变量在恢复时变为空? 它变为 null 因为 onDestroy 被调用,否则底层活动已被操作系统销毁以回收内存。作为 Activity 生命周期的参考资料,但是当有人在低内存设备上离开您的应用程序时,它会成为被回收/杀死的候选对象,从而从您的身下消失。 如果对您有帮助,请给答案投票并将其标记为正确吗?以上是关于Android Fragment 生命周期问题(onActivityResult 上的 NullPointerException)的主要内容,如果未能解决你的问题,请参考以下文章
Android Fragment 生命周期问题(onActivityResult 上的 NullPointerException)