sockeqwe / mosby

A Model-View-Presenter / Model-View-Intent library for modern Android apps
http://hannesdorfmann.com/mosby/
Apache License 2.0
5.49k stars 841 forks source link

unable to recreate the state of killed application #227

Closed Kyba1985 closed 7 years ago

Kyba1985 commented 7 years ago

Mosby Version: compile 'com.hannesdorfmann.mosby:viewstate-conductor:0.8.2' Expected behavior the controller that are added to router are recreated Actual behavior (include a stacktrace if crash) the saveinstancestate of the application is null when recover from force stopped application and the application reset to first controller, all the backstack is lost. Steps to reproduce the behavior or link to a sample repository From the root controller, go to a viewpager controller , than go to a child controller of folder details and than to another deeper controller of inner folder. when i go back to home and force kill the application from android studio, when i select the application from list of apps, the backstack is lost and the app restart from the first controller, and the saveinstancestate is null.

The demo app of conductor without mosby works well, the backstack is recreated and the saveinstancestate is not null when recreate the app state. So i'm thinking that the problem is from mosby.

Do you have any clue about this strange behavior?

sockeqwe commented 7 years ago

the backstack is lost and the app restart from the first controller

So the problem is that the whole back stack got lost? If yes, then this is not a Mosby issue. Mosby is not handling or changing conductor's router somehow internally. Mosby doesn't know about Conductor's Router at all. Then you should continue the discussing at https://github.com/bluelinelabs/Conductor/issues/253 as this seems to be a conductor issue.

If the back stack is restored properly by Conductor, but you are wondering why your ViewState is like starting the app for the first time, then this is the expected behavior.

Per default, Mosby is not saving any ViewState information in Bundle. So Mosby is per default not surviving process death.
Per default Mosby only save instance through screen orientation change and back stack navigation.

To do so you have to use a ViewState instance of interface RestorableViewState extends ViewState like ParcelableListLceViewState (if you use the LCE package) which may requires that all your "model" data implements the Parcelable interface too.

Btw. I strongly recommend to update to viewstate-conductor:3.0.0

Kyba1985 commented 7 years ago

ok, i had migrated to 3.0.0. Now, my question is, wich is the best practice to recreate the state of the backstack of the forced application closed like conductor demo application?

sockeqwe commented 7 years ago

again, what exactly is the problem you are facing? If I understand your description correctly:

when i go back to home and force kill the application from android studio, when i select the application from list of apps, the backstack is lost and the app restartfrom the first controller, and the saveinstancestate is null.

app restartfrom the first controller, this is not a Mosby issue. As already said, Mosby is not changing Conductor Router. Mosby is not the cause that your app is starting with the first controller.

Kyba1985 commented 7 years ago

Seems that i'm missing some point; the conductor sample application can recover the backstack, my application can't. When i recover from stopped application, the savedinstancestate of main activity is null (strange!), in the demo app of conductor is !=null, and i think is used to recreate the backstack.

In my application the code that handle navigation from a controller to another is the same that is used in the demo conductor. So the only difference that i see in my code is that i'm using mosby. I'm not sure that the problem is mosby, but at the moment, the application works well in navigation from the controllers, but when i force stop the app, when i select it from apps in background the app restart from the first controller with in the mainrouteractivity savedinstancestate == null. My question is: who set the savedinstancestate to null? I can't understand. If i can't find the problem the only way is remove all dependencies from mosby and test the recreation of back stack with my own presenters, but is a lot of work and i want avoid it.

Are you sure that i don't do anything for handle the recreation of backstack when i use mosby?

sockeqwe commented 7 years ago

My question is: who set the savedinstancestate to null

Not Mosby. (nor conductor I guess. i think this is managed by the android OS / ActivityManager).

Are you sure that i don't do anything for handle the recreation of backstack when i use mosby?

I am sure

Do you have your app open source on github so that I can take a closer look?

Kyba1985 commented 7 years ago

No , the application is not mine but is for a company that i work, is on private svn server.

Another clue for investigation:

i'm going deeper in your sample code, the application can handle the backstack recover, so my another question is: wich are the classes that handle the recreation of the backstack when the app is recovered? I see in the createtaskcontroller : MvpViewStateController<CreateTaskView, CreateTaskPresenter, CreateTaskViewState>.

has MvpViewStateController class the responsability in the recreation backstack? At the moment my controller base is

public abstract class BaseController extends MvpController

wich is the difference of responsability between Mvpcontroller and MvpViewStateController ?

sockeqwe commented 7 years ago

wich are the classes that handle the recreation of the backstack when the app is recovered?

Mosby does not recreate the back stack. It's conductor who is recreating the backstack and instantites the controllers on the back stack. Again, Mosby has zero knowledge of the backstack or Router or any other Conductor components involved in backstack management.

MvpViewStateController adds a feature so that the state of the UI Elements is not lost during screen orientation changes. More info here: http://hannesdorfmann.com/mosby/viewstate/ (the docs talk about fragments, but Conductor controller also recreates the view, so it's the same issue that is solved with ViewState feature)

Can you post the code of your MainActivity?

Kyba1985 commented 7 years ago

sure, now i post the mainactivity and application most important code:

MAIN:

public class MainRouterActivity extends AppCompatActivity implements ActionBarProvider {
......
    private static final String TAG = "MainRouterActivity";
    private Router router;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= 21) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        }

        setContentView(R.layout.main_router_activity);
        ButterKnife.bind(this);

        ApplicationStatus.initialize(this.getApplication());
        if (savedInstanceState != null) {
            Log.d("initapp", savedInstanceState.toString());
        } else {
            Log.d("initapp", "saveinstancestate is null");
        }
        router = Conductor.attachRouter(this, flRootContainer, savedInstanceState);
        if (!router.hasRootController()) {
            RouterTransaction firstTransaction = RouterTransaction.with(new BrowserViewController());
            firstTransaction.tag(BrowserViewController.TAG_ROUTER);
            router.setRoot(firstTransaction);
        }

    }

    @Override
    public ActionBar getSupportActionBar() {
        return super.getSupportActionBar();
    }

    @Override
    public void onBackPressed() {
        if (!router.handleBack()) {
            super.onBackPressed();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        Log.d("state", "activity saveinstancestate");
        super.onSaveInstanceState(outState);
    }
}

APPLICATION:


i don't see strange code in the application code, only that is multidex application.

sockeqwe commented 7 years ago

¯\_(ツ)_/¯

I can't see any issue here. Sorry, I can't help you. You should continue to ask on conductor but if savedInstanceState is really null than this rather seems like some strange device behavior, because as far as I know conductor is not setting the savedInstanceState to null somehow. Actually, I think that it is not possible to set a bundle to null at all because this bundle is handed over to the Activity by the operating system and then activity is passing it to conductor. Same workflow when saving things (like the back stack of controllers) into a bundle. The bundle is created by the operating system and then hand over to Activity.onSaveInstanceState() and forwarded internally to Conductor which then saves the Router. I can't really see how a bundle could be "set" somehow to null.

Double check:

Last but not least, try to build a little sample example with just 3-4 Controllers that reproduces this issue and attach it on https://github.com/bluelinelabs/Conductor/issues/253 Otherwise, it will be super hard to reproduce this issue somehow. Also try to build the same navigation hierarchy as you have in your real app like ViewPager and child routers as you have in your real app.

Sorry, I can't help you.

Kyba1985 commented 7 years ago

Finally i got the issue, not mosby , not conductor, but my first class (a browser like chrome) and his complex saveinstancestate that break the save of the state of the application ... when i replace that class with another fake controller the app works well. Thanks for your time and your patience @sockeqwe