55import android .support .annotation .NonNull ;
66import android .support .annotation .Nullable ;
77import android .support .v4 .app .Fragment ;
8+ import android .support .v4 .app .FragmentManager ;
89import android .util .Log ;
910
1011import java .util .UUID ;
@@ -14,71 +15,110 @@ public class ViewModelHelper<T extends IView, R extends AbstractViewModel<T>> {
1415 private String mScreenId ;
1516 private R mViewModel ;
1617 private boolean mModelRemoved ;
17-
18- public void onCreate (@ NonNull Activity activity , @ Nullable Bundle savedInstanceState ,
19- @ NonNull Class <? extends AbstractViewModel <T >> viewModelClass ) {
18+ private boolean mOnSaveInstanceCalled ;
19+
20+ /**
21+ * Call from {@link android.app.Activity#onCreate(android.os.Bundle)} or
22+ * {@link android.support.v4.app.Fragment#onCreate(android.os.Bundle)}
23+ * @param savedInstanceState
24+ * @param viewModelClass
25+ */
26+ public void onCreate (@ Nullable Bundle savedInstanceState ,
27+ @ Nullable Class <? extends AbstractViewModel <T >> viewModelClass ) {
28+ // no viewmodel for this fragment
2029 if (viewModelClass == null ) {
21- //no viewmodel for this fragment
2230 mViewModel = null ;
2331 return ;
2432 }
33+
34+ // screen (activity/fragment) created for first time, attach unique ID
2535 if (savedInstanceState == null ) {
2636 mScreenId = UUID .randomUUID ().toString ();
2737 } else {
2838 mScreenId = savedInstanceState .getString ("identifier" );
39+ mOnSaveInstanceCalled = false ;
2940 }
3041
31- final ViewModelProvider .ViewModelWrapper <T > viewModelWrapper = getViewModelProvider (activity ).getViewModelProvider ().getViewModel (mScreenId , viewModelClass );
42+ // get model instance for this screen
43+ final ViewModelProvider .ViewModelWrapper <T > viewModelWrapper = ViewModelProvider .getInstance ().getViewModel (mScreenId , viewModelClass );
3244 mViewModel = (R ) viewModelWrapper .viewModel ;
45+
46+ // detect that the system has killed the app - saved instance is not null, but the model was recreated
3347 if (savedInstanceState != null && viewModelWrapper .wasCreated ) {
3448 Log .d ("model" , "Fragment recreated by system - restoring viewmodel" );
3549 mViewModel .restoreState (savedInstanceState );
3650 }
3751 }
3852
39- public void initWithView (T view ) {
53+ /**
54+ * Call from {@link android.support.v4.app.Fragment#onViewCreated(android.view.View, android.os.Bundle)}
55+ * or {@link android.app.Activity#onCreate(android.os.Bundle)}
56+ * @param view
57+ */
58+ public void initWithView (@ NonNull T view ) {
4059 if (mViewModel == null ) {
4160 //no viewmodel for this fragment
4261 return ;
4362 }
4463 mViewModel .initWithView (view );
4564 }
4665
66+ /**
67+ * Use in case this model is associated with an {@link android.support.v4.app.Fragment}
68+ * Call from {@link android.support.v4.app.Fragment#onDestroyView()}. Use in case model is associated
69+ * with Fragment
70+ * @param fragment
71+ */
4772 public void onDestroyView (@ NonNull Fragment fragment ) {
4873 if (mViewModel == null ) {
4974 //no viewmodel for this fragment
5075 return ;
5176 }
5277 mViewModel .clearView ();
5378 if (fragment .getActivity () != null && fragment .getActivity ().isFinishing ()) {
54- removeViewModel (fragment . getActivity () );
79+ removeViewModel ();
5580 }
5681 }
5782
83+ /**
84+ * Use in case this model is associated with an {@link android.support.v4.app.Fragment}
85+ * Call from {@link android.support.v4.app.Fragment#onDestroy()}
86+ * @param fragment
87+ */
5888 public void onDestroy (@ NonNull Fragment fragment ) {
5989 if (mViewModel == null ) {
6090 //no viewmodel for this fragment
6191 return ;
6292 }
6393 if (fragment .getActivity ().isFinishing ()) {
64- removeViewModel (fragment .getActivity ());
65- } else if (fragment .isRemoving ()) {
94+ removeViewModel ();
95+ } else if (fragment .isRemoving () && !mOnSaveInstanceCalled ) {
96+ // The fragment can be still in backstack even if isRemoving() is true.
97+ // We check mOnSaveInstanceCalled - if this was not called then the fragment is totally removed.
6698 Log .d ("mode" , "Removing viewmodel - fragment replaced" );
67- removeViewModel (fragment . getActivity () );
99+ removeViewModel ();
68100 }
69101 }
70102
103+ /**
104+ * Use in case this model is associated with an {@link android.app.Activity}
105+ * Call from {@link android.app.Activity#onDestroy()}
106+ * @param activity
107+ */
71108 public void onDestroy (@ NonNull Activity activity ) {
72109 if (mViewModel == null ) {
73110 //no viewmodel for this fragment
74111 return ;
75112 }
76113 mViewModel .clearView ();
77114 if (activity .isFinishing ()) {
78- removeViewModel (activity );
115+ removeViewModel ();
79116 }
80117 }
81118
119+ /**
120+ * Call from {@link android.app.Activity#onStop()} or {@link android.support.v4.app.Fragment#onStop()}
121+ */
82122 public void onStop () {
83123 if (mViewModel == null ) {
84124 //no viewmodel for this fragment
@@ -87,6 +127,9 @@ public void onStop() {
87127 mViewModel .onStop ();
88128 }
89129
130+ /**
131+ * Call from {@link android.app.Activity#onStart()} ()} or {@link android.support.v4.app.Fragment#onStart()} ()}
132+ */
90133 public void onStart () {
91134 if (mViewModel == null ) {
92135 //no viewmodel for this fragment
@@ -95,27 +138,29 @@ public void onStart() {
95138 mViewModel .onStart ();
96139 }
97140
141+
142+ @ Nullable
98143 public R getViewModel () {
99144 return mViewModel ;
100145 }
101146
102- public void onSaveInstanceState (@ Nullable Bundle bundle ) {
147+ /**
148+ * Call from {@link android.app.Activity#onSaveInstanceState(android.os.Bundle)}
149+ * or {@link android.support.v4.app.Fragment#onSaveInstanceState(android.os.Bundle)}.
150+ * This allows the model to save its state.
151+ * @param bundle
152+ */
153+ public void onSaveInstanceState (@ NonNull Bundle bundle ) {
103154 bundle .putString ("identifier" , mScreenId );
104155 if (mViewModel != null ) {
105156 mViewModel .saveState (bundle );
157+ mOnSaveInstanceCalled = true ;
106158 }
107159 }
108160
109- private IViewModelProvider getViewModelProvider (@ NonNull Activity activity ) {
110- if (!(activity instanceof IViewModelProvider )) {
111- throw new IllegalStateException ("Your activity must implement IViewModelProvider" );
112- }
113- return ((IViewModelProvider )activity );
114- }
115-
116- protected boolean removeViewModel (@ NonNull Activity activity ) {
161+ private boolean removeViewModel () {
117162 if (!mModelRemoved ) {
118- boolean removed = getViewModelProvider ( activity ). getViewModelProvider ().remove (mScreenId );
163+ boolean removed = ViewModelProvider . getInstance ().remove (mScreenId );
119164 mViewModel .onModelRemoved ();
120165 mModelRemoved = true ;
121166 return removed ;
0 commit comments