I have the following stack trace for a crash caused by an NPE:
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.daybreak.my.app/com.daybreak.my.app.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2430)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.daybreak.my.app.TimesFragment.onLocationChange(TimesFragment.java:446)
at com.daybreak.my.app.MainActivity.onLocationChange(MainActivity.java:289)
at com.daybreak.my.app.MainActivity.onCreate(MainActivity.java:112)
at android.app.Activity.performCreate(Activity.java:6302)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2383)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
The way I have setup my app is as follows:
MainActivity
public class MainActivity extends AppCompatActivity implements LocationChangeListener {
#Override
public void onCreate(Bundle savedInstanceState) {
//...
onLocationChange(LocationManager.getSavedLocation(this)); // Manually calling onLocationChange() method
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) return;
showFragment(new TimesFragment(), TimesFragment.TAG);
}
}
#Override
public void onLocationChange(Locatin location) {
if (location == null) return;
//...
// Call attached onLocationChange() if it implements LocationChangeListener
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (f instanceof LocationChangeListener)
((LocationChangeListener) f).onLocationChange(location);
}
}
TimesFragment
public class TimesFragment extends Fragment implements LocationChangeListener {
private ViewSwitcher viewSwitcher;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//...
viewSwitcher = (ViewSwitcher) view.findViewById(R.id.view_switcher);
}
#Override
public void onLocationChange(Location location) {
this.location = location;
viewSwitcher.setOnClickListener(null); //<-- NPE Cause here
updateContent();
}
}
MY EXPECTATION
From what I understand, the Activity.onCreate() will only be called after a fresh start or after the user navigates back to the app after the app has been killed (explicitly by the user or by memory management when other apps need memory). If this happens the fragments will also be destroyed and will need to be created, i.e., fragment's onCreateView() will be called. Therefore calling onLocationChange() from the MainActivity.onCreate() before attaching the fragment is safe as findFragmentById() within the onLocationChange() would not find any fragment.
REALITY
From the stack trace we can see that the call initiated from MainActivity.onCreate(). But what's puzzling for me is that at the time onLocationChange() is called from within MainActivity.onCreate(), findFragmentById() within the onLocationChange() finds the fragment in the view container and calls the fragments onLocationChange(). When this happens the viewSwitcher is NULL and causes the app to crash.
Obviously, fragment has already been added to the view container and the fragments onCreateView() has not been called yet.
THE QUESTION
I am not able to recreate this crash, and not sure of the lifecycle process that is causing this.
So can anyone tell me
how to reproduce this error and
the lifecycle process that is responsible for the flow that is causing the NPE?
This is being caused by device rotation. Can recreate the stack trace by rotating the device.
NOTE: This can happen even when app's orientation is locked (as in my case); if the user is in another app in an orientation that is different than the orientation your app is locked to and they switch back to the app, the orientation lifecycle for your app will be fired.
SOLUTION
Add f != null && f.isResumed() before calling methods from the fragment. isResumed() will return false if fragment hasn't been resumed after recreation.
Related
I have Bottom Navigation with fragment and Retrofit for api call
I call loadFragment for switching the fragments as below
private boolean loadFragment(Fragment fragment) {
//switching fragment
if (fragment != null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
return true;
}
return false;
}
The problem is my app crashes if i start clicking randomly and change fragment quickly, when i check logcat it shows NPE while setting some data.
My Fragment consist of following methods
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_current, container, false);
unbinder = ButterKnife.bind(this, view);
context = getActivity();
callCategoryAPI();
return view;
}
callCategory() is api retrofit call in separate controller class and brings back response via interface and then set data.
So what i suspect is my API returns response (as its asynchronous) but views are not available as user has changes the fragment (quickly) so views are null.
I already tried setuserVisibleHint also tried block click for 1200ms and also checked is my fragment view created, How to stop this crash? and make retrofit call Lifecycle dependent?
Logcat
at com.example.CurrentFragment$1.onApiSuccess(CurrentFragment.java:82)
at com.example.services.current_statement.CurrentStatementController$2.onResponse(CurrentStatementController.java:91)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:171)
at android.app.ActivityThread.main(ActivityThread.java:6606)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
--
Fatal Exception: java.lang.NullPointerException: Attempt to invoke
virtual method 'void
android.widget.TextView.setText(java.lang.CharSequence)' on a null
object reference
at com.example.fragment.statements.CurrentFragment$1.onApiSuccess(CurrentFragment.java:82)
at com.example.services.current_statement.CurrentStatementController$2.onResponse(CurrentStatementController.java:91)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:171)
at android.app.ActivityThread.main(ActivityThread.java:6606)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
set in your fragment
if(activity != null && isAdded) {
// perform your task
}
Check in fragment
if(activity != null && isAdded){
Perform your operation
}
set in your fragment
if(getActivity() != null && isAdded) {
//do your operation
}
This is the problem:
Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
You would like to set text to a textview which is null. You should check at first that your textview is not null like this:
if (textview != null){
textview.setText("mytext");
}
Use this library below to block the activity until the network operation is finished.
Spot Dialog
This is what I used for my app in the image which I showed you earliar.
I may be late, but i hope i can help other developer as well , for i have encountered this problem too. just use isAdded() in your condition at onResponse callback inside the if-else
ex: inside your onResponse callback :
if (isAdded()){
// success response
} else {
if(isAdded()){
// error response
}
}
code is for kotlin in android:
TextView?.text
here ? mean not null
use it like shows as above
If you change navigation drawer menu item quickly, current fragment will replace by new one too. Android can remove any ongoing calculation that is running in main thread. But if any heavy task like background network response result or handler delayed task that has a reference of fragment can find null because fragment has already detach or replace by new one.
So you just have to check that, your desire fragment is still added in activity or not before process response data that has come lately.
if(frag.isAdded()){
//lets process your delayed response data
}
I have issue with passing data to fragments. It crashes 0.1% of all times on production. Let's say on 100k opening of activity it happens 100 times. It looks like not very often, but it very bothering me and I think that I am doing something wrong with fragments initialization with data. The thing is, that I create fragments only one time, and all other times I need to pass data to them I am doing it next way: myFragmentInstance.setData(Object someData); And crash happens because it tells that those view elements in fragment are not found and they are NULL, but everything should be fine if I have not recreated them. I am not rotating my phone, or have not enough of memory on it. It happens on network reconnect, because on network reconnect I am going to server for fresh data and then set that new data to my fragments. I have photos of fields of two fragments I use, maby some of you know what that data can tell about status of fragments at the moment of crash.
I am using library ButterKnife to initialize fields of fragments and activities, not initializing it with findById, maby it has some influence or no?
Here is link to simple project (only this issue on github): https://github.com/yozhik/Reviews/tree/master/app/src/main/java/com/ylet/sr/review
Description:
CollapsingActivity - activity with Collapsing AppBarLayout. Which loads one or two fragments into "fragment_content_holder" and it has TabLayout to switch between fragments in view pager.
In activity method onCreate() - I'm just simulating request to server (loadData), and when some fake data is loaded - I am showing fragments in view pager, on first call - I am creating new TabMenuAdapter extends FragmentPagerAdapter, populate it with fragments and save links to instances. On the next call - I don't create fragments from scratch and just populate them with fresh data.
MenuFragment1, MenuFragment1 - two fragments. MenuFragment1 - has method public void setupData(SomeCustomData data), to set new data, not recreating fragment on network reconnect.
NetworkStateReceiver - listens to network change and send notifications.
TabMenuAdapter - just simple class to hold fragments.
05-11 18:11:05.088 12279-12279/com.myProjectName E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myProjectName, PID: 12279
java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:111)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5268)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.yozhik.myProjectName.view.fragments.MyFinalTermsFragment.setupMyInformation(MyFinalTermsFragment.java:145)
at com.yozhik.myProjectName.view.fragments.MyFinalTermsFragment.setupWithData(MyFinalTermsFragment.java:133)
at com.yozhik.myProjectName.view.activity.MyFinalActivity.onDataLoaded(MyFinalActivity.java:742)
at com.yozhik.myProjectName.presenter.MyFinalPresenter$1.onNext(MyFinalPresenter.java:55)
at com.yozhik.myProjectName.presenter.MyFinalPresenter$1.onNext(MyFinalPresenter.java:47)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:200)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5268)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:902)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:697)
05-11 18:11:07.953 2155-3877/? E/WifiStateMachine: Did not find remoteAddress {192.168.200.1} in /proc/net/arp
05-11 18:11:07.966 2155-3877/? E/WifiStateMachine: WifiStateMachine CMD_START_SCAN source -2 txSuccessRate=3800.62 rxSuccessRate=4732.06 targetRoamBSSID=any RSSI=-68
05-11 18:11:07.967 2155-3877/? E/WifiStateMachine: WifiStateMachine L2Connected CMD_START_SCAN source -2 2324, 2325 -> obsolete
05-11 18:11:08.021 2155-3896/? E/ConnectivityService: Unexpected mtu value: 0, wlan0
05-11 18:11:08.579 13514-13366/? E/WakeLock: release without a matched acquire!
Fragment which is crashing in method setupData because data_1_txt is NULL sometimes.
public class MenuFragment1 extends Fragment {
public SomeCustomData transferedDataFromActivity;
private TextView data_1_txt;
public static MenuFragment1 newInstance(SomeCustomData data) {
Log.d("TEST", "MenuFragment1.newInstance");
MenuFragment1 fragment = new MenuFragment1();
Bundle args = new Bundle();
args.putSerializable("DATA_FROM_ACTIVITY", data);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("TEST", "MenuFragment1.onCreate");
if (getArguments() != null) {
this.transferedDataFromActivity = (SomeCustomData) getArguments().getSerializable("DATA_FROM_ACTIVITY");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d("TEST", "MenuFragment1.onCreateView");
View v = inflater.inflate(R.layout.menu_fragment_1, container, false);
data_1_txt = (TextView) v.findViewById(R.id.data_1_txt);
setupInOnCreateView();
return v;
}
protected void setupInOnCreateView() {
Log.d("TEST", "MenuFragment1.setupInOnCreateView");
//initialization of all view elements of layout with data is happens here.
setupData(transferedDataFromActivity);
}
public void setupData(SomeCustomData data) {
Log.d("TEST", "MenuFragment1.setupData");
this.transferedDataFromActivity = data;
if (transferedDataFromActivity != null) {
data_1_txt.setText(transferedDataFromActivity.Name);
}
}
}
Fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/green"
android:orientation="vertical">
<TextView
android:id="#+id/data_1_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/yellow"
android:text="Test"
android:textSize="20sp" />
<include layout="#layout/description_layout" />
</LinearLayout>
in my experience if there is an error in fragments it is usually because of pre-loading of fragments in viewpager and TabMenu so what I did and Suggest you do to is to check if the fragment is visible to user and if they were, get data and other things so here is my code:
public class Fragment1 extends Fragment {
boolean visible = false;
public static Fragment1 newInstance() {
return new Fragment1();
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
if (visible && isResumed()) {
onResume();
} else if (visible) {
getMyData();
}
return rootView;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
visible = isVisibleToUser;
if (isVisibleToUser) {
getMyData();
}
else {
}
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
this way if the view is not created yet and is not visible to user fragment won't do anything .
hope this helps.
I'm pretty sure your issues are due to keeping the references to fragments in an Array. Fragments have lifecycles and the references are not guaranteed to persist. As you said, it's hard to reproduce and track down exactly what's going wrong, but maybe you don't need to.
Some suggestions on how to fix this:
Do not store the references to fragments. Pretty much follow the example on Google's page (https://developer.android.com/training/animation/screen-slide) and instantiate a new fragment every time it's requested.
If you are worried about performance and caching is solving it, try using FragmentStatePagerAdapter - it caches pages and manages fragments' states.
If you need to access page fragments from the main fragment (or the activity), instead of storing references, use `findFragmentByTag' which will always return the currently active instance of the fragment.
I strongly believe that things aren't that complicated as others explained. If i understand it correctly, the delay caused by the network transaction is the culprit.
Consider this scenario.
You are making a network request which makes some changes in the view
at the end.
You switch the pager. (Fragment is detached, views are
destroyed)
Here comes the callback from the network request. Guess what! A crash.
So when dealing dealing with views of fragments, it's always a good idea to be more careful. I usually do this.
//in the base class
protected boolean isSafe()
{
return !(this.isRemoving() || this.getActivity() == null || this.isDetached()
|| !this.isAdded() || this.getView() == null);
}
//usage in derived classes
onNewtworkResult(Result result) {
if(!isSafe())
return;
//rest of the code
}
Alternatively, You can wrap the potential code in a try catch too. But that is more like a blind shot (At least in this case).
I'm working on the project initially created by another developer. There is a root Activity (let's call it CustomActivity) with code below.
private static SomeOtherClass instance = null;
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(null);
if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler());
}
// #see http://stackoverflow.com/questions/19545889/app-restarts-rather-than-resumes)
if (!isTaskRoot() && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER) && null != getIntent().getAction()
&& getIntent().getAction().equals(Intent.ACTION_MAIN)) {
finish();
return;
}
instance = new SomeOtherClass();
this.screen = new CustomFragment();
this.getFragmentManager.beginTransaction()
.replace(R.id.content_frame, screen).commit();
...
}
#Override
protected void onDestroy()
{
this.instance = null;
...
}
static public SomeOtherClass getInstance()
{
return instance;
}
this.screen has a button with CustomActivity.getInstance().methodCall() on tap. And one of beta testers said he just tapped that button and got crash with this method in stacktrace: Attempt to invoke virtual method '...CustomActivity.getInstance().methodCall()' on a null object reference.
I don't understand - how it's possible due to Activity lifecycle.
According to stacktrace, onCreate shouldn't be called after some previous onDestroy. Even if the last one can happen when we were in another Activity (and yep, this.screen is not nullified anywhere), but this.screen fragment can't be rendered without Activity recreation. Am I right?
P.S.: there is no more instance variable management at all and SomeOtherClass has no custom parent class (just default object).
P.P.S.: nope, device wasn't locked / app just launched. Tester worked with it, rotated phone to remove sim card, removed, rotated back and saw crash alert.
P.P.P.S: don't know why null in super.onCreate() but anyhow this Activity has no code to support saved states.
Everytime the application go in background, and the RAM start filling out, the application start loosing variables (if im not wrong, this is the normal behavior of Android, it loose the instances of variables)
When I bring back the app from background the application crash, because I loose the MainActivity.
There is a way to relaunch this activity if there is not activity at all?
Is an ambiguous question but I found this question but I don't think is the best choice
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
if(taskList.get(0).numActivities == 1 &&
taskList.get(0).topActivity.getClassName().equals(this.getClass().getName())) {
Log.i(TAG, "This is last activity in the stack");
}
Im loosing the full activity, well I can get the activity
#Override
public void onAttach(Activity activity) {
LocalNotification.registerReceiver(reloadReceiver, LocalNotification.RELOAD_CARTMG);
try {
mListener = (OnFragmentInteractionListener) activity;
MainActivity ma = (MainActivity)activity; //THIS IS LOOSE
ma.openMyList = false;
ma.openMyAddress = false;
ma.hideSearchButton();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
}
super.onAttach(activity);
}
This is the Log inside the app
com.XXXX.mg E/Uncaught Exception detected in thread {}: Unable to start activity ComponentInfo{com.XXXX.mg/com.XXXX.view.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.XXXX.view.MainActivity.showShoppingCartIcon()' on a null object reference
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.XXXX.mg/com.XXXX.view.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.XXXX.view.MainActivity.showShoppingCartIcon()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2426)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.XXXX.view.MainActivity.showShoppingCartIcon()' on a null object reference
at com.XXXX.view.shoppingCart.ShoppingCartFragment.onDetach(ShoppingCartFragment.java:411)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1202)
at android.support.v4.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1349)
at android.support.v4.app.BackStackRecord.popFromBackStack(BackStackRecord.java:915)
at android.support.v4.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1722)
at android.support.v4.app.FragmentManagerImpl$3.run(FragmentManager.java:593)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1617)
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:339)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:602)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1260)
at android.app.Activity.performStart(Activity.java:6261)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2389)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
I'm not sure if your app crashes just because you loose your MainActivity, you should be coding expecting this kind of behaviour, using onSaveInstanceState like #pablobu suggested is the right approach.
Can we see the logs? If you're loosing the state of your instances that's fine, you should save those states and restore them Recreating an Activity is it because of Fragments? you can also save those too, Handling Fragments Tutorial this is a good read.
Please, can you give us your logcat output, that might help a lot for us to see whats really going on.
EDIT
With your Logcat edit, try to take a look to the fragment lifecycle, onAttach executes first than onActivityCreated(), so if your Activity is already destroyed, your onAttach method will not have your activity, you should wait until your Activity is recreated .
Furthermore, you have your
super.onAttach(activity);
at the end of your code block, put it before your code block.
/**
This method was deprecated in API level 23.
Use onAttach(Context) instead.
*/
#Override
public void onAttach(Context context) {
super.onAttach(context);
MainActivity mainActivity;
if (context instanceof Activity) {
mainActivity = (MainActivity) context;
try {
mListener = (OnFragmentInteractionListener) mainActivity;
/**
Since you are getting a reference and accesing attributes you should be careful
with NullPointerException, check if not null first.
Or better yet refactor a little your code using an interface to handle this behaviour or use
the one you already created and just tell the activity what to do.
*/
/*if(mainActivity != null) {
ma.openMyList = false;
ma.openMyAddress = false;
ma.hideSearchButton();
}*/
} catch (ClassCastException e) {
throw new ClassCastException(mainActivity.getClass().getSimpleName() + " must implement OnFragmentInteractionListener");
}
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//since your Listener is global use mListener behaviour that I suggested here.
if (mListener != null){
mListener.openListOfSomething(false);
mListener.hideSearch();
}
}
You can use the Bundle savedInstatceState, wich is given in OnCreate and similar methods:
private String aVariable;
#Override
public void onCreate (Bundle savedInstantceState) {
super.onCreate(savedInstanceState);
// Get the variable from savedInstanceState, if it isn't empty.
// If it is empty, it could be the first Activity run.
try {
aVariable = savedInstanceState.get("Key");
} catch (NullPointerException e) {
aVariable = "Baum";
}
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString(aVariable, "Key");
}
These methods set the Variable "aVariable" to "Baum" on the first run
(NullPointerException, because there is no String to "Key").
Later, when the Activity is going to be destroyed, onSaveInstanceState will be called. There you save your variables (aVariable) under a Key ("Key").
I have an app on Google Play that tends to make the app crash if it is in the background when the app is updated and then re-opened by the user. The crash only happens sometimes, which is hinting towards a race condition somewhere. I am having a hard time debugging this and have only been able to reproduce this a few times myself, though I have got some stack traces reported by users. The stack traces are never pointing to the same line in the code, but the stack trace always enters my code through performResumeActivity() in Android and thus onResume() in my code. Note that this has never occurred to me or been reported by users in any other context than when updating the app (as far as I am able to tell). This does not mean that it only occurs when updating the app, but this is the only way I have seen the crash occur.
Steps to (sometimes) reproduce:
Install an old app version, e.g. by adb install app-release-previous-version.apk.
Open the app, play around a little bit and then minimize the app.
Open Google Play and update the app to the latest version.
Open the app from Google Play (we can open it from there after update).
The app crashes in performResumeActivity()/onResume() from time to time.
I'll add the code for the Activity where the crash occurs. Non-important code for this crash is not pasted, but let me know if I should add more code. The various stack traces I have are pasted at the end of the question.
MyActivity (important parts only)
public class MyActivity extends AppCompatActivity
{
private MyMainFragment myMainFragment = null;
private FilteredRecipes myFilteredRecipes = null;
private MyShoppingList myShoppingList = null;
private boolean mShouldReadAutomaticSave = false;
// <snip> other members
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mShouldReadAutomaticSave = savedInstanceState == null;
setContentView(R.layout.my_activity);
// Setup Toolbar / ActionBar.
Toolbar toolbar = (Toolbar) findViewById(R.id.my_main_toolbar);
setSupportActionBar(toolbar);
NavigationView navigationView = (NavigationView) findViewById(R.id.my_navigationview);
if (navigationView != null)
{
// <snip> Setup navigationView
}
DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.my_drawer_layout);
if (drawerLayout != null)
{
// <snip> Setup DrawerLayout
}
if (myShoppingList == null) { myShoppingList = new MyShoppingList(this); }
if (myFilteredRecipes == null) { myFilteredRecipes = new MyFilteredRecipes(this); }
if (myMainFragment == null) { myMainFragment = new MyMainFragment(); }
if (savedInstanceState == null)
{
// Runs a thread to read recipes from an SQLite database. This is not the culprit.
filterAllRecipes(null);
}
else
{
restoreState(savedInstanceState); // See method below
}
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.fade_in, R.anim.none)
.replace(R.id.my_content_frame, myMainFragment, "main_fragment")
.commit();
}
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
if (myFilteredRecipes != null)
{
myFilteredRecipes.onSaveInstanceState(outState); // Restores from Bundle
}
if (myShoppingList != null)
{
myShoppingList.onSaveInstanceState(outState); // Restores from Bundle
}
if (myMainFragment != null)
{
getSupportFragmentManager().putFragment(outState, "myFragment", myMainFragment); // Restores from Bundle
}
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
}
private void restoreState(Bundle savedInstanceState)
{
myShoppingList.onRestoreInstanceState(savedInstanceState);
myFilteredRecipes.onRestoreInstanceState(savedInstanceState);
myMainFragment = (MyMainFragment)
getSupportFragmentManager().getFragment(savedInstanceState, "myFragment");
}
#Override
public void onResume()
{
super.onResume();
// These tests were added as an attempt to cure the crash.
// Probably fixed some of the crashes (stack traces below), but not the entire issue.
if (myShoppingList == null) { myShoppingList = new MyShoppingList(this); }
if (myFilteredRecipes == null) { myFilteredRecipes = new MyFilteredRecipes(this); }
if (myMainFragment == null)
{
myMainFragment = new MyMainFragment();
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.fade_in, R.anim.none)
.replace(R.id.my_content_frame, myMainFragment, "main_fragment")
.commit();
}
if (mShouldReadAutomaticSave) // Set in onCreate() when applicable
{
mShouldReadAutomaticSave = false;
final MyActivity mThis = this;
Thread loadAutomaticSaveThread = new Thread()
{
#Override
public void run()
{
// Load automatic save if any. Must be done last to avoid race condition in LoadShoppingListTaks.
JSONObject automaticSave = ShoppingListFileHandler.readAutomaticSavedShoppingList(mThis);
if (automaticSave != null)
{
// This task takes the JSON data from the file read above, reads data from an SQLite database and sets up the myShoppingList data.
// Calls back to MyActivity.onShoppingListLoaded() when done.
// NOTE: There has been a crash in here when myShoppingList was null. This was before the "== null" tests above was added.
new ShoppingListFileHandler.LoadShoppingListTask(mThis, myShoppingList, automaticSave, false).execute();
}
}
};
loadAutomaticSaveThread.setName("LoadAutomaticSaveThread");
loadAutomaticSaveThread.start();
}
}
// This is called from the LoadShoppingListTask when done.
#Override
public synchronized void onShoppingListLoaded()
{
if (myShoppingList != null)
{
// MyShoppingList.notifyDataSetChanged will call notifyDataSetChanged on a
// RecyclerView adapter in the main Fragment. There is a stack trace for this below.
myShoppingList.notifyDataSetChanged();
}
}
}
So that's the Activity where the crashes occur. Here are the stack traces I have got, some of them are partly obfuscated, since I hadn't understood what it did at that time.
Stack trace for a missing Fragment
Notes: It seems that we get a Fragment that is null, i.e. it is probably myMainFragment that is null when trying to restart the Activity.
java.lang.RuntimeException: Unable to resume activity {com.my.app/com.my.app.activities.MyActivity}: java.lang.IllegalArgumentException: No view found for id 0x7f0f0089 (com.my.app/my_content_frame) for fragment a{215e6dc #0 id=0x7f0f0089 main_fragment}
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3403)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3434)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2772)
at android.app.ActivityThread.access$900(ActivityThread.java:177)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1449)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5951)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
Caused by: java.lang.IllegalArgumentException: No view found for id 0x7f0f0089 (com.my.app/my_content_frame) for fragment a{215e6dc #0 id=0x7f0f0089 main_fragment}
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1098)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1286)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:758)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1671)
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:388)
at android.support.v4.app.FragmentActivity.onPostResume(FragmentActivity.java:512)
at android.support.v7.app.AppCompatActivity.onPostResume(AppCompatActivity.java:178)
at android.app.Activity.performResume(Activity.java:6430)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3392)
... 11 more
Stack trace for Shopping List == null
Notes: Crash in the LoadShoppingListTask since the myShoppingList member was null in the task. This happened before adding the tests in the start of onResume().
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:304)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] com.my.app.shoppinglist.j.a(android.content.Context, java.util.ArrayList, java.lang.String, android.util.SparseIntArray, org.json.JSONArray, java.util.ArrayList, boolean)' on a null object reference
at com.my.app.filehandling.ShoppingListFileHandler$LoadShoppingListTask.doInBackground(ShoppingListFileHandler.java:882)
at com.my.app.filehandling.ShoppingListFileHandler$LoadShoppingListTask.doInBackground(ShoppingListFileHandler.java:770)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 4 more
Stack trace from RecyclerView in the main Fragment
Notes: There are multiple of these in various places in the RecyclerView when binding data. This particular RecyclerView lives in the main Fragment in the Activity. These crashes are the only times I have had problems with it. The RecyclerView reads its data from the myShoppingList member in the Activity. It seems like the Views are not created yet.
java.lang.NullPointerException: Attempt to read from field 'android.widget.TextView com.my.app.gui.fragments.shoppinglist.k.a' on a null object reference
at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.bindRecipeName(ShoppingListAdapter.java:401)
at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.onBindViewHolder(ShoppingListAdapter.java:249)
at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.onBindViewHolder(ShoppingListAdapter.java:31)
at com.my.app.gui.fragments.shoppinglist.ShoppingListAdapter.access$000(ShoppingListAdapter.java:31)
at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5471)
at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5504)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4741)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4617)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1994)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1390)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353)
<snip> Long stack trace inside Android classes...
Stack trace with Fragment == null
Notes: This was the first stack trace I got. Unfortunately I had not realized that I had to add deobscufation files at this point, so the stack trace is not that useful. But it seems obvious that the member myMainFragment was null at the crash.
java.lang.RuntimeException: Unable to resume activity {com.my.app/com.my.app.activities.MyActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.my.app.gui.fragments.c.a.L()' on a null object reference
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3403)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3434)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2772)
at android.app.ActivityThread.access$900(ActivityThread.java:177)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1449)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5951)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.my.app.gui.fragments.c.a.L()' on a null object reference
at com.my.app.activities.MyActivity.t(Unknown Source)
at com.my.app.activities.MyActivity.onResume(Unknown Source)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1255)
at android.app.Activity.performResume(Activity.java:6412)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3392)
... 11 more
I cannot see where I have taken the wrong turn. Any hints and comments are appreciated.