App Crash After Activity Has Been Killed in Background

App crash after activity has been killed in background

After doing some research, it seems that the problem stems from the misnaming of FragmentPagerAdapter's method - being named getItem(), but not clearly specifying that the abstract method getItem(int position) is supposed to return a new instance of a fragment rather than just "get an instance of one".

Of course, there is not much we can do about an incorrect name after it's been out in the wild for 7 years, but at least we can fix the bug that stems from this issue in your code ;)


Without further ado, the cause of your NPE is that onCreateView (where your Presenter is instantiated) is never called.

This happens because you are creating the fragment here:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)
...
homeFragment = HomeScreenFragment.newInstance()
incidentFragment = IncidentScreenFragment.newInstance()
}

You return this fragment from inside getItem(int position) in your FragmentPagerAdapter:

override fun getItem(position: Int): Fragment = when(position) {
...
1 -> activity.incidentFragment
...
}

So what we know about activity.incidentFragment is that in it, onCreateView() is never called.

This is caused by the fact that it's never actually added to a FragmentManager and never displayed on the screen.

That's because super.onCreate(savedInstanceState) in Activity recreates all Fragments, using their no-args constructor, via reflection, while keeping their tag (see findFragmentByTag).

So as you can see in this answer, or as I can quote here:

    // Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));

The getItem(position) method is only called if the Fragment is not found by the fragment tag that the FragmentPagerAdapter sets for the fragment, which IS automatically recreated after low memory condition kills your app.

Therefore, YOUR new fragment (that you create by hand in the Activity) is NEVER used, and therefore it has no view, never initialized, never added to FragmentManager, it's not the same instance as what's actually inside your ViewPager, and it crashes when you call it. Boom!



Solution is to instantiate the Fragment inside FragmentPagerAdapter's getItem(position) method. To get an instance of the fragment, use this answer.

Android: Unexplained crashes when the app is reopened after a while

Activities not on screen are frequently killed by Android for memory. When coming back to an Activity via recents, the most recent activity will be launched but if it was killed it will have no context- it will get the intent it was launched with but have no static variables or singletons assigned to previous values. This causes problems for a lot of apps. The solution is to either allow any activity to fill out these values somehow, or to detect this case and launch an appropriate activity with CLEAR_TOP set to kill the old state.

Oh, and to reproduce it all you need is force stop from the activites manager settings. Go to activity D, then force stop. Then return via recents.

As an example- I have an app with a LoginActivity, and a MainActivity. MainActivity assumes LoginActivity has set up a user singleton. My code in MainActivity is:

public void onCreate(Bundle bundle){
super.onCreate(bundle);
if(User.getCurrentUser() == null){
Intent intent = new Intent(this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
return;
}
//Continue on with onCreate for this activity
}

This will relaunch the login activity if the User object wasn't set up.

App crashes when restoring from background after a long time

For more accurate answer you put your code here. On, Android memory is limited so VM can remove any piece of code it think un-necessary.

Look into Activity life-cycle method, specially into onResume and make sure that you understand that perfectly. So many time application crashes just for not using Activity life-cycle method properly.

Another important design consideration for Activity is, no matter what happened with persistence data you Activity should display its UI with some default value. So assumption is like this, if i have data i will display if i don't, i really do not care. Your UI should never ever crash with or without data. You can use String.xml for storing some default value or even in layouts.

if you still want go with singleton class, which is perfectly fine but make sure you do the following checking every time you try to access your singleton.

if (instance==null)
instance=Singleton.getInstance()

your getInstance() method not only return you current instance it will also make sure that

  • it initializes all object and variable
  • other singleton methods as instance method

Do not statically access data from one Activity to another. It is not good for android specially for the type problem you are facing now and also it is not very good OOP programming practice.

SharedPreference is good way to persist data, if that meet your requirement go for it.

if you want to pass data from different Android component like Activity, Service or BroadcastReciever you can put it inside a bundle and and send as intent. And, as always their are SQLLite data storage, file IO etc etc.

Navigation causes crash after opening killed activity

Apparently the problem was having the same id for navigation graphs and destinations. https://issuetracker.google.com/issues/161825212

Background services causing crash

Android can (and will) stop your Service whenever it wants to. Because you have returned START_STICKY from onStartCommand(), Android should restart your Service after it has been killed. In this case you will get a null Intent in onStartCommand() after the restart. There is no way to prevent Android from killing your Service if it wants to.

You need to save any useful data in a persistent storage (SharedPreferences, file, database, etc.) on a regular basis so that you can recover after being restarted.



Related Topics



Leave a reply



Submit