I am trying to figure out why my android app crashes sometimes, with the above exception. I can't find the source of the problem from what the logcat says.
It happens rarely.
Logcat output:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1360) at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1378) at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595) at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574) at android.support.v4.app.FragmentTabHost.onAttachedToWindow(FragmentTabHost.java:282) at android.view.View.dispatchAttachedToWindow(View.java:12752) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2577) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2584) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2584) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2584) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1427) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1192) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6231) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:788) at android.view.Choreographer.doCallbacks(Choreographer.java:591) at android.view.Choreographer.doFrame(Choreographer.java:560) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:774) at android.os.Handler.handleCallback(Handler.java:808) at android.os.Handler.dispatchMessage(Handler.java:103) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5292) 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:824) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640) at dalvik.system.NativeStart.main(Native Method)
Main Activity:
public class MainActivity extends FragmentActivity {
private FragmentTabHost mTabHost;
private UiLifecycleHelper uiHelper;
LocationClient mClient;
LocationRequest mRequest;
private String user;
private String userId;
private ImageButton addPlaceBtn;
private SQLiteDataSource datasource;
private TextView notifTextView;
private boolean appIsOn = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getActionBar();
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
setContentView(R.layout.activity_main);
datasource = new SQLiteDataSource(this);
datasource.open();
uiHelper = new UiLifecycleHelper(this, null);
uiHelper.onCreate(savedInstanceState);
if (isFacebookLoggedIn()) {
System.out.println("We are logged in!");
} else {
showLogin();
}
if (datasource.getAllImageItems().isEmpty()) {
Intent splash = new Intent(this, SplashActivity.class);
startActivity(splash);
}
addPlaceBtn = (ImageButton) findViewById(R.id.addPlace1);
uiHelper = new UiLifecycleHelper(this, null);
uiHelper.onCreate(savedInstanceState);
mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(),
android.R.id.tabcontent);
mTabHost.addTab(
mTabHost.newTabSpec("tab1").setIndicator("PREPARATE", null),
OffersFragmentActivity.class, null);
mTabHost.addTab(
mTabHost.newTabSpec("tab2").setIndicator("CATEGORII", null),
CategoriesActivity.class, null);
mTabHost.addTab(mTabHost.newTabSpec("tab3")
.setIndicator("CAIETE", null), BooksFragmentActivity.class,
null);
for (int i = 0; i < mTabHost.getTabWidget().getChildCount(); i++) {
mTabHost.getTabWidget().getChildAt(i)
.setBackgroundResource(R.drawable.tab_text_selector);
mTabHost.getTabWidget().setDividerDrawable(R.drawable.divider);
final TextView tv = (TextView) mTabHost.getTabWidget()
.getChildAt(i).findViewById(android.R.id.title);
if (tv == null)
continue;
else
tv.setTextColor(0xFFFFFFFF);
}
mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
public void onTabChanged(String tabId) {
switch (mTabHost.getCurrentTab()) {
case 0:
addPlaceBtn.setVisibility(View.VISIBLE);
break;
case 1:
addPlaceBtn.setVisibility(View.GONE);
break;
case 2:
addPlaceBtn.setVisibility(View.GONE);
break;
default:
break;
}
}
});
addPlaceBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (user != null && isOnline()) {
Intent i = new Intent(getApplication(),
CameraActivity.class);
startActivity(i);
} else if (user == null) {
// If user is not logged in, start login activity
Intent i = new Intent(getApplication(), LoginActivity.class);
startActivity(i);
} else if (!isOnline()) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
MainActivity.this);
alertDialogBuilder.setTitle("Internetul este oprit");
alertDialogBuilder
.setMessage("Pentru actualizarea continutului aveti nevoie de internet. Porniti acum?");
alertDialogBuilder.setPositiveButton("Da",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int id) {
// go to a new activity of the app
Intent settingsIntent = new Intent(
Settings.ACTION_DATA_ROAMING_SETTINGS);
settingsIntent
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(settingsIntent);
finish();
}
});
// set negative button: No message
alertDialogBuilder.setNegativeButton("Nu",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int id) {
// cancel the alert box and put a Toast to
// the user
finish();
}
});
AlertDialog alertDialog = alertDialogBuilder.create();
alertDialog.show();
}
}
});
if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(this));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
// Associate searchable configuration with the SearchView
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search)
.getActionView();
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
final Menu m = menu;
final MenuItem item = menu.findItem(R.id.action_notifications);
View actionView = item.getActionView();
notifTextView = (TextView) actionView.findViewById(R.id.notifTextView);
String unreadNotifs = datasource.countUnreadNotif();
if (!unreadNotifs.equals("0")) {
notifTextView.setText(unreadNotifs);
} else {
notifTextView.setVisibility(View.GONE);
}
item.getActionView().setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
m.performIdentifierAction(item.getItemId(), 0);
}
});
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_user) {
Intent i = new Intent(this, UserProfile.class);
i.putExtra("user", user);
i.putExtra("userId", userId);
startActivity(i);
return true;
}
if (id == R.id.action_notifications) {
Intent i = new Intent(this, Notifications.class);
startActivity(i);
return true;
}
return super.onOptionsItemSelected(item);
}
private boolean isFacebookLoggedIn() {
Session session = Session.getActiveSession();
if (session != null) {
// Session can be open, check for valid token
if (!session.isClosed()) {
if (!session.getAccessToken().equalsIgnoreCase("")) {
return true;
}
}
}
return false;
}
// FB Login Session and user info
public void showLogin() {
Intent i = new Intent(this, LoginActivity.class);
startActivity(i);
}
@Override
protected void onResume() {
super.onResume();
uiHelper.onResume();
appIsOn = true;
invalidateOptionsMenu();
if (!isOnline()) {
showGpsButton();
}
SharedPreferences preferences = getSharedPreferences("USERINFO",
Context.MODE_PRIVATE);
user = preferences.getString("name", null);
userId = preferences.getString("userId", null);
userId = preferences.getString("userId", null);
GCMActivity gcm = new GCMActivity(this);
gcm.registerIfNeeded();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
uiHelper.onSaveInstanceState(outState);
}
@Override
public void onPause() {
super.onPause();
uiHelper.onPause();
appIsOn = false;
}
@Override
public void onDestroy() {
super.onDestroy();
uiHelper.onDestroy();
datasource.deleteAllRows();
}
public void showGpsButton() {
LinearLayout buttonLayout = (LinearLayout) findViewById(R.id.gpsWarning);
buttonLayout.setVisibility(View.VISIBLE);
buttonLayout.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent settingsIntent = new Intent(
Settings.ACTION_DATA_ROAMING_SETTINGS);
settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(settingsIntent);
finish();
}
});
}
public boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnected()) {
return true;
}
return false;
}
}
I was experiencing a similar issue on KitKat. But on Lollipop it seems to work fine. Regardless, we have to support many OS versions, right? :)
The primary cause seems to be that the starting of a new activity is forcing the MainActivity
to pause before it's even completely finished creating. When it pauses, it saves the instance state right away. But since Fragment
instances get committed in a weird end-of-main-thread cycle, that start-pause-save cycle becomes out of order throwing this crash. And in some cases, it could be a race condition and happen sporadically.
Here's some code that seems to fix it after repeated testing:
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
Handler hanlder = new Handler();
hanlder.post(new Runnable() {
@Override
public void run() {
if (isFacebookLoggedIn()) {
System.out.println("We are logged in!");
} else {
showLogin();
}
if (datasource.getAllImageItems().isEmpty()) {
Intent splash = new Intent(MainActivity.this, SplashActivity.class);
startActivity(splash);
}
}
});
}
The trick is to spawn a Handler
and post to the end of the main thread to guarantee it will happen after all Fragment
instances have been committed. Doing it in onPostCreate
instead of immediately in onCreate
should help the situation as well.
User contributions licensed under CC BY-SA 3.0