Skip to content

Commit b0efd7b

Browse files
author
Daniel Novak
committed
Remove singleton and retain viewmodels in activity
1 parent ad652bb commit b0efd7b

File tree

11 files changed

+175
-90
lines changed

11 files changed

+175
-90
lines changed

library/src/main/java/eu/inloop/viewmodel/AbstractViewModel.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import android.support.annotation.NonNull;
55
import android.support.annotation.Nullable;
66

7+
import eu.inloop.viewmodel.IView;
8+
79
public abstract class AbstractViewModel<T extends IView> {
810

911
@Nullable

library/src/main/java/eu/inloop/viewmodel/IView.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,4 @@
22

33
public interface IView {
44

5-
public void showLoading();
6-
public void hideProgress();
75
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package eu.inloop.viewmodel;
2+
3+
/**
4+
* Your {@link android.app.Activity} must implement this interface if
5+
* any of the contained Fragments the {@link eu.inloop.viewmodel.ViewModelHelper}
6+
*/
7+
public interface IViewModelProvider {
8+
9+
/**
10+
* See {@link eu.inloop.viewmodel.base.ViewModelBaseActivity} on how to implement.
11+
* @return
12+
*/
13+
public ViewModelProvider getViewModelProvider();
14+
}

library/src/main/java/eu/inloop/viewmodel/ViewModelHelper.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import android.app.Activity;
44
import android.os.Bundle;
5+
import android.support.annotation.NonNull;
6+
import android.support.annotation.Nullable;
57
import android.support.v4.app.Fragment;
68
import android.util.Log;
79

@@ -12,7 +14,8 @@ public class ViewModelHelper<T extends IView, R extends AbstractViewModel<T>> {
1214
private String mScreenId;
1315
private R mViewModel;
1416

15-
public void onCreate(Bundle savedInstanceState, Class<? extends AbstractViewModel<T>> viewModelClass) {
17+
public void onCreate(@NonNull Activity activity, @Nullable Bundle savedInstanceState,
18+
@NonNull Class<? extends AbstractViewModel<T>> viewModelClass) {
1619
if (viewModelClass == null) {
1720
//no viewmodel for this fragment
1821
mViewModel = null;
@@ -24,7 +27,7 @@ public void onCreate(Bundle savedInstanceState, Class<? extends AbstractViewMode
2427
mScreenId = savedInstanceState.getString("identifier");
2528
}
2629

27-
final ViewModelService.ViewModelWrapper<T> viewModelWrapper = ViewModelService.getInstance().getViewModel(mScreenId, viewModelClass);
30+
final ViewModelProvider.ViewModelWrapper<T> viewModelWrapper = getViewModelProvider(activity).getViewModelProvider().getViewModel(mScreenId, viewModelClass);
2831
mViewModel = (R) viewModelWrapper.viewModel;
2932
if (savedInstanceState != null && viewModelWrapper.wasCreated) {
3033
Log.e("model", "Application recreated by system - restoring viewmodel");
@@ -40,37 +43,37 @@ public void initWithView(T view) {
4043
mViewModel.initWithView(view);
4144
}
4245

43-
public void onDestroyView(Fragment fragment) {
46+
public void onDestroyView(@NonNull Fragment fragment) {
4447
if (mViewModel == null) {
4548
//no viewmodel for this fragment
4649
return;
4750
}
4851
mViewModel.clearView();
4952
if (fragment.getActivity() != null && fragment.getActivity().isFinishing()) {
50-
removeViewModel();
53+
removeViewModel(fragment.getActivity());
5154
}
5255
}
5356

54-
public void onDestroy(Fragment fragment) {
57+
public void onDestroy(@NonNull Fragment fragment) {
5558
if (mViewModel == null) {
5659
//no viewmodel for this fragment
5760
return;
5861
}
59-
if (fragment.getActivity() != null && fragment.getActivity().isFinishing()) {
60-
removeViewModel();
62+
if (fragment.getActivity().isFinishing()) {
63+
removeViewModel(fragment.getActivity());
6164
} else if (fragment.isRemoving()) {
6265
Log.d("mode", "Removing viewmodel - fragment replaced");
63-
removeViewModel();
66+
removeViewModel(fragment.getActivity());
6467
}
6568
}
6669

67-
public void onDestroy(Activity activity) {
70+
public void onDestroy(@NonNull Activity activity) {
6871
if (mViewModel == null) {
6972
//no viewmodel for this fragment
7073
return;
7174
}
7275
if (activity.isFinishing()) {
73-
removeViewModel();
76+
removeViewModel(activity);
7477
}
7578
}
7679

@@ -94,15 +97,22 @@ public R getViewModel() {
9497
return mViewModel;
9598
}
9699

97-
public void onSaveInstanceState(Bundle bundle) {
100+
public void onSaveInstanceState(@Nullable Bundle bundle) {
98101
bundle.putString("identifier", mScreenId);
99102
if (mViewModel != null) {
100103
mViewModel.saveState(bundle);
101104
}
102105
}
103106

104-
protected boolean removeViewModel() {
105-
boolean removed = ViewModelService.getInstance().remove(mScreenId);
107+
private IViewModelProvider getViewModelProvider(@NonNull Activity activity) {
108+
if (!(activity instanceof IViewModelProvider)) {
109+
throw new IllegalStateException("Your activity must implement IViewModelProvider");
110+
}
111+
return ((IViewModelProvider)activity);
112+
}
113+
114+
protected boolean removeViewModel(@NonNull Activity activity) {
115+
boolean removed = getViewModelProvider(activity).getViewModelProvider().remove(mScreenId);
106116
mViewModel.onModelRemoved();
107117
return removed;
108118
}

library/src/main/java/eu/inloop/viewmodel/ViewModelService.java renamed to library/src/main/java/eu/inloop/viewmodel/ViewModelProvider.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,28 @@
11
package eu.inloop.viewmodel;
2-
32
import android.support.annotation.NonNull;
43

5-
import java.util.concurrent.ConcurrentHashMap;
4+
import java.util.HashMap;
65

7-
public class ViewModelService {
6+
/**
7+
* Create and keep this class inside your Activity. Store it
8+
* in {@link android.support.v4.app.FragmentActivity#onRetainCustomNonConfigurationInstance()
9+
* and restore in {@link android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)} before
10+
* calling the super implemenentation.
11+
*/
12+
public class ViewModelProvider {
813

9-
private static volatile ViewModelService sInstance;
10-
private final ConcurrentHashMap<String, AbstractViewModel<? extends IView>> mViewModelCache;
14+
private final HashMap<String, AbstractViewModel<? extends IView>> mViewModelCache;
1115

12-
@NonNull
13-
public static ViewModelService getInstance() {
14-
if (sInstance == null) {
15-
synchronized (ViewModelService.class) {
16-
if (sInstance == null) {
17-
sInstance = new ViewModelService();
18-
}
19-
}
20-
}
21-
return sInstance;
16+
public ViewModelProvider() {
17+
mViewModelCache = new HashMap<>();
2218
}
2319

24-
private ViewModelService() {
25-
mViewModelCache = new ConcurrentHashMap<>();
26-
}
27-
28-
public boolean remove(String key) {
20+
boolean remove(@NonNull String key) {
2921
return mViewModelCache.remove(key) != null;
3022
}
3123

3224
public static class ViewModelWrapper<T extends IView> {
25+
@NonNull
3326
public final AbstractViewModel<T> viewModel;
3427
public final boolean wasCreated;
3528

@@ -39,6 +32,13 @@ private ViewModelWrapper(@NonNull AbstractViewModel<T> mViewModel, boolean mWasC
3932
}
4033
}
4134

35+
/**
36+
* Call this in {@link android.app.Activity#onStop()} if {@link android.app.Activity#isFinishing()}
37+
*/
38+
public void removeAllViewModels() {
39+
mViewModelCache.clear();
40+
}
41+
4242
@SuppressWarnings("unchecked")
4343
@NonNull
4444
public synchronized <T extends IView> ViewModelWrapper<T> getViewModel(@NonNull String key, @NonNull Class<? extends AbstractViewModel<T>> viewModelClass) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package eu.inloop.viewmodel.base;
2+
3+
import android.os.Bundle;
4+
import android.support.v4.app.FragmentActivity;
5+
import android.util.Log;
6+
7+
import eu.inloop.viewmodel.IViewModelProvider;
8+
import eu.inloop.viewmodel.ViewModelProvider;
9+
10+
public class ViewModelBaseActivity extends FragmentActivity implements IViewModelProvider {
11+
12+
private ViewModelProvider mViewModelService;
13+
14+
@Override
15+
protected void onCreate(Bundle savedInstanceState) {
16+
//This code must be execute prior to super.onCreate()
17+
if (getLastCustomNonConfigurationInstance() == null) {
18+
mViewModelService = new ViewModelProvider();
19+
} else {
20+
mViewModelService = (ViewModelProvider) getLastCustomNonConfigurationInstance();
21+
}
22+
super.onCreate(savedInstanceState);
23+
}
24+
25+
@Override
26+
public Object onRetainCustomNonConfigurationInstance() {
27+
return mViewModelService;
28+
}
29+
30+
@Override
31+
public ViewModelProvider getViewModelProvider() {
32+
return mViewModelService;
33+
}
34+
35+
@Override
36+
protected void onStop() {
37+
if (isFinishing()) {
38+
mViewModelService.removeAllViewModels();
39+
}
40+
super.onStop();
41+
}
42+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package eu.inloop.viewmodel.base;
2+
3+
import android.os.Bundle;
4+
import android.support.annotation.Nullable;
5+
import android.support.v4.app.Fragment;
6+
import android.view.View;
7+
8+
import eu.inloop.viewmodel.AbstractViewModel;
9+
import eu.inloop.viewmodel.IView;
10+
import eu.inloop.viewmodel.ViewModelHelper;
11+
12+
public abstract class ViewModelBaseFragment<T extends IView, R extends AbstractViewModel<T>> extends Fragment {
13+
14+
private ViewModelHelper<T, R> mViewModeHelper = new ViewModelHelper<T, R>();
15+
16+
@Override
17+
public void onCreate(Bundle savedInstanceState) {
18+
super.onCreate(savedInstanceState);
19+
mViewModeHelper.onCreate(getActivity(), savedInstanceState, getViewModelClass());
20+
}
21+
22+
public abstract Class<R> getViewModelClass();
23+
24+
@Override
25+
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
26+
super.onViewCreated(view, savedInstanceState);
27+
mViewModeHelper.initWithView((T) this);
28+
}
29+
30+
@Override
31+
public void onSaveInstanceState(Bundle outState) {
32+
super.onSaveInstanceState(outState);
33+
mViewModeHelper.onSaveInstanceState(outState);
34+
}
35+
36+
@Override
37+
public void onStart() {
38+
super.onStart();
39+
mViewModeHelper.onStart();
40+
}
41+
42+
@Override
43+
public void onStop() {
44+
super.onStop();
45+
mViewModeHelper.onStop();
46+
}
47+
48+
@Override
49+
public void onDestroyView() {
50+
mViewModeHelper.onDestroyView(this);
51+
super.onDestroyView();
52+
}
53+
54+
@Override
55+
public void onDestroy() {
56+
mViewModeHelper.onDestroy(this);
57+
super.onDestroy();
58+
}
59+
60+
public R getViewModel() {
61+
return mViewModeHelper.getViewModel();
62+
}
63+
}

sample/src/main/java/eu/inloop/viewmodel/sample/activity/ProjectBaseActivity.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
import android.os.Bundle;
44
import android.support.v4.app.FragmentActivity;
55

6-
public class ProjectBaseActivity extends FragmentActivity{
6+
import eu.inloop.viewmodel.IViewModelProvider;
7+
import eu.inloop.viewmodel.ViewModelProvider;
8+
import eu.inloop.viewmodel.base.ViewModelBaseActivity;
9+
10+
public class ProjectBaseActivity extends ViewModelBaseActivity {
711

812
@Override
913
protected void onCreate(Bundle savedInstanceState) {
Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,15 @@
11
package eu.inloop.viewmodel.sample.fragment;
22

3-
import android.os.Bundle;
4-
import android.support.annotation.Nullable;
5-
import android.support.v4.app.Fragment;
6-
import android.view.View;
7-
83
import butterknife.ButterKnife;
94
import eu.inloop.viewmodel.AbstractViewModel;
105
import eu.inloop.viewmodel.IView;
11-
import eu.inloop.viewmodel.ViewModelHelper;
12-
13-
public abstract class ProjectBaseFragment<T extends IView, R extends AbstractViewModel<T>> extends Fragment {
14-
15-
private ViewModelHelper<T, R> mViewModeHelper = new ViewModelHelper<T, R>();
16-
17-
@Override
18-
public void onCreate(Bundle savedInstanceState) {
19-
super.onCreate(savedInstanceState);
20-
mViewModeHelper.onCreate(savedInstanceState, getViewModelClass());
21-
}
6+
import eu.inloop.viewmodel.base.ViewModelBaseFragment;
227

23-
public abstract Class<R> getViewModelClass();
24-
25-
@Override
26-
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
27-
super.onViewCreated(view, savedInstanceState);
28-
mViewModeHelper.initWithView((T) this);
29-
ButterKnife.inject(this, view);
30-
}
31-
32-
@Override
33-
public void onSaveInstanceState(Bundle outState) {
34-
super.onSaveInstanceState(outState);
35-
mViewModeHelper.onSaveInstanceState(outState);
36-
}
37-
38-
@Override
39-
public void onStart() {
40-
super.onStart();
41-
mViewModeHelper.onStart();
42-
}
43-
44-
@Override
45-
public void onStop() {
46-
super.onStop();
47-
mViewModeHelper.onStop();
48-
}
8+
public abstract class ProjectBaseFragment<T extends IView, R extends AbstractViewModel<T>> extends ViewModelBaseFragment<T,R> {
499

5010
@Override
5111
public void onDestroyView() {
5212
ButterKnife.reset(this);
53-
mViewModeHelper.onDestroyView(this);
5413
super.onDestroyView();
5514
}
56-
57-
@Override
58-
public void onDestroy() {
59-
mViewModeHelper.onDestroy(this);
60-
super.onDestroy();
61-
}
62-
63-
public R getViewModel() {
64-
return mViewModeHelper.getViewModel();
65-
}
6615
}

sample/src/main/java/eu/inloop/viewmodel/sample/fragment/UserListFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class UserListFragment extends ProjectBaseFragment<IUserListView, UserLis
3030
@Override
3131
public void onCreate(Bundle savedInstanceState) {
3232
super.onCreate(savedInstanceState);
33-
mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, new ArrayList<String>());
33+
mAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1, new ArrayList<String>());
3434
}
3535

3636
@Override

0 commit comments

Comments
 (0)