I get an issue which for I cannot find any explanation. I have a FragmentActivity that displays fragments using a TabManager, as follows :
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();
}
}
}
In the second tab of this FragmentActivity user can manage a list of pictures and use the camera to add more pictures.
Fragment code :
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();
}
}
}
Everything works well on my test devices (Nexus 5, Galaxy Nexus, Galaxy Mini 2) but I receive from time to time errors from other devices I have no access to (mainly devices running 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 ResultInfo{who=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 ResultInfo{who=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
The NPE appears on this line of code : lvAdapter.updatePictureList(((MframeApplication)getActivity().getApplication()).getCurrentForm().getPictures());
This means that at this time lvAdapter variable is null. But this variable should have been set in onCreateView of Fragment... Which makes me think that, at some point Fragment may have been recreated without calling onCreateView.
I cannot find any explanation about this issue. Can you help me on this one?
Many thanks