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
Disabling Android's Chrome Pull-Down-To-Refresh Feature
Android: HTML in Textview with Link Clickable
What Is The Easiest Way to Use Svg Images in Android
Activity.Finish() Called But Activity Stays Loaded in Memory
In HTML5, How to Keep an Android Device's Screen On
Android Back Button on a Progressive Web Application Closes De App
Android: Support Multiple Screens
Broadcast Receiver for Missed Call in Android
When Does Adt Set Buildconfig.Debug to False
How to Add Firebase-Admin to My Android Project
Android Ndk Std::To_String Support
Add Support Library to Android Studio Project
Android 4.0.1 Breaks Webview HTML 5 Local Storage