Everything negative-pressure,challenges-is all an opportunity for me to rise
08 October 2020
设计架构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
+-----------------------------------------------------------------------------------------------------+
|androidx.activity.ComponentActivity(or Derived Class) |
| onResume(new instance) |
| ^ |
| | |
| getViewModelStore() onStart(new instance)->getViewModelStore() |
| ^ ^ |
| | | |
| +-------------------------------------------+ onCreate(new instance)->getViewModelStore()|
| | androidx.lifecycle.ViewModelStore | ^ |
| | (Single instance & Lazy initialization) | | |
| | -------------------------------- | onDestroy |
| | |androidx.lifecycle.ViewModel - 1| | ^ |
| | |androidx.lifecycle.ViewModel - 2| | | |
| | |androidx.lifecycle.ViewModel - 3| | | |
| | |... | |------>onRetainNonConfigurationInstance() |
| | |androidx.lifecycle.ViewModel - N| | ^ |
| | -------------------------------- | | |
| | | | |
| | clear: 1. Call clear() of each ViewModel.| onStop |
| | ^ 2. Clear all viewModels | ^ |
| +---|---------------------------------------+ | |
| | | |
| | onPause(due to configuration changed) |
| | |
| onDestroy <---onStop <--- onPause(activity is done and should be closed.) |
+-----------------------------------------------------------------------------------------------------+
上面这张架构图描述了androidx.activity.ComponentActivity
, androidx.lifecycle.ViewModelStore
和 androidx.lifecycle.ViewModel
之间的关系:每个androidx.activity.ComponentActivity
的派生Activity都会持有1个androidx.lifecycle.ViewModelStore
,只不过androidx.lifecycle.ViewModelStore
直到getViewModelStore()
调用时才会惰性创建1次(需要注意的是,创建这个androidx.lifecycle.ViewModelStore
的前提有两个:当前Activity对象持有的mViewModelStore
为null
;如果当前Activity对象是由于配置改变(如旋转)重新创建的,而getViewModelStore()
并没有在onCreate或onStart时调用,则获取不到上次存储的androidx.lifecycle.ViewModelStore
实例),这个androidx.lifecycle.ViewModelStore
中会存储所有在这个派生Activity里创建的androidx.lifecycle.ViewModel
。当当前Activity正常需要销毁的时候,会调用androidx.lifecycle.ViewModelStore
的clear
方法:先去调用所有的androidx.lifecycle.ViewModel
的clear
方法以通知当前ViewModelStoreOwner
(androidx.activity.ComponentActivity
/androidx.fragment.app.Fragment
)需要销毁了,然后在清除调所有的androidx.lifecycle.ViewModel
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//androidx.activity.ComponentActivity.java
package androidx.activity;
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
// Lazily recreated from NonConfigurationInstances by getViewModelStore()
private ViewModelStore mViewModelStore;
/**
* Default constructor for ComponentActivity. All Activities must have a default constructor
* for API 27 and lower devices or when using the default
* {@link android.app.AppComponentFactory}.
*/
public ComponentActivity() {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
/**
* Retain all appropriate non-config state. You can NOT
* override this yourself! Use a {@link androidx.lifecycle.ViewModel} if you want to
* retain your own non config state.
*/
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
/**
* Returns the {@link ViewModelStore} associated with this activity
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of ComponentActivity.
*
* @return a {@code ViewModelStore}
* @throws IllegalStateException if called before the Activity is attached to the Application
* instance i.e., before onCreate()
*/
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
}
综上所述,下面列出几个的关键点:
androidx.activity.ComponentActivity
的派生类,如果需要存储一些将由配置改变(比如旋转)而丢失的昂贵数据,推荐使用 androidx.lifecycle.ViewModel
。这是因为androidx.activity.ComponentActivity
重写了onRetainNonConfigurationInstance()
这个方法并添加了final
修饰符,所以不允许其派生类继续重写。当然你也可以使用onRetainCustomNonConfigurationInstance
和getLastCustomNonConfigurationInstance
这两个方法实现,但这两个方法都已经废弃,所以不推荐使用。getViewModelStore()
不能在onCreate()
生命周期之前调用。getViewModelStore()
的调用除了不能在onCreate()
生命周期之前调用,还不能在onStart
之后调用,即只能在onCreate
和onStart
这两个生命周期中调用。androidx.lifecycle.ViewModel
。androidx.lifecycle.ViewModel
的clear
方法,然后在androidx.lifecycle.ViewModel
的内部调用onCleared
方法,这样我们就可以在我们自定义的androidx.lifecycle.ViewModel
的onCleared
方法中执行资源释放,网络请求取消等操作。设计架构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
+-----------------------------------------------------------------------------------------------------+
|androidx.fragment.app.Fragment(or Derived Class)| |
+------------------------------------------------+ |
|
getViewModelStore()
^
|
+-------|-----------------------------------------------------------------------------------+
| |androidx.fragment.app.FragmentManager| |
| +-------------------------------------+ |
| |
getViewModelStore(androidx.fragment.app.Fragment)
^
|
+-------|--------------------------------------------------------------------------------+
| |androidx.fragment.app.FragmentManagerViewModel| |
| +----------------------------------------------+ |
| |
getViewModelStore(androidx.fragment.app.Fragment)
^
|
+---------------------------------------------------------------------------------+
| |mViewModelStores(HashMap<String, androidx.lifecycle.ViewModelStore>)| |
| +--------------------------------------------------------------------+ |
| |
| androidx.fragment.app.Fragment@1 => androidx.lifecycle.ViewModelStore@1 |
| androidx.fragment.app.Fragment@2 => androidx.lifecycle.ViewModelStore@2 |
| androidx.fragment.app.Fragment@3 => androidx.lifecycle.ViewModelStore@3 |
| ... |
| androidx.fragment.app.Fragment@N => androidx.lifecycle.ViewModelStore@N |
| |
+---------------------------------------------------------------------------------+
在梳理androidx.lifecycle.ViewModel
在androidx.fragment.app.Fragment
中的设计流程之前,我们需要先了解一些FragmentManager
的知识:当我们创建了一个直接/间接继承自androidx.fragment.app.FragmentActivity
的Activity并交给系统,然后系统开始实例化这个Activity的时候,同时也会初始化了一个FragmentManager
(只不过这个FragmentManager
被androidx.fragment.app.FragmentHostCallback
直接持有,而androidx.fragment.app.FragmentController
又直接持有androidx.fragment.app.FragmentHostCallback
, androidx.fragment.app.FragmentController
又被androidx.fragment.app.FragmentActivity
直接持有),所以直接在这个Activity中通过事务提交的Fragments都由这个FragmentManager
直接管理,并且这些Fragment中持有的mFragmentManager
也是这个FragmentManager
。而在这些Fragment中创建的子Fragment由它的创建者Fragment(父Fragment)的mChildFragmentManager
(在父Fragment实例化的时候初始化)负责管理。
每个FragmentManager
持有一个androidx.fragment.app.FragmentManagerViewModel
,由它负责管理这些Fragments(由这个FragmentManager
直接管理)中使用的androidx.lifecycle.ViewModelStore
。
我们在Fragment中获取到的ViewModelStore来自于androidx.fragment.app.FragmentManagerViewModel,这是一个ViewModel
,它内部维护了一个K-V容器用来存储每个Fragment对应的ViewModelStore。那这个FragmentManagerViewModel
是怎么创建的呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// androidx.fragment.app.FragmentManager.java
package androidx.fragment.app;
public abstract class FragmentManager {
private FragmentManagerViewModel mNonConfig;
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
...
// Get the FragmentManagerViewModel
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
}
@NonNull
private FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
return mNonConfig.getChildNonConfig(f);
}
}
创建FragmentManagerViewModel
有三种情况:
第一种情况:
1
2
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
这种情况发生在androidx.fragment.app.FragmentActivity
执行到生命周期onCreate的时候,此时host
会attach到FragmentManager
,这样之后才可以用这个FragmentManager
管理Fragments。由上述代码可知,ViewModelStore
来自于host
,这个host
实现了ViewModelStoreOwner
这个接口以提供ViewModelStore
,然后我们通过将新创建的FragmentManagerViewModel
存储到这个ViewModelStore
中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// androidx.fragment.app.FragmentManagerViewModel.java
package androidx.fragment.app;
final class FragmentManagerViewModel extends ViewModel {
private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
@NonNull
@Override
@SuppressWarnings("unchecked")
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
return (T) viewModel;
}
}
@NonNull
static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
FACTORY);
return viewModelProvider.get(FragmentManagerViewModel.class);
}
}
现在我们只要知道host
在哪,我们就知道FragmentManagerViewModel
存储在哪:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// androidx.fragment.app.FragmentActivity.java
package androidx.fragment.app;
public class FragmentActivity extends ComponentActivity implements
ActivityCompat.OnRequestPermissionsResultCallback,
ActivityCompat.RequestPermissionsRequestCodeValidator {
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements
ViewModelStoreOwner,
OnBackPressedDispatcherOwner {
public HostCallbacks() {
super(FragmentActivity.this /*fragmentActivity*/);
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return FragmentActivity.this.getViewModelStore();
}
}
}
很清楚了,FragmentActivity
继承自ComponentActivity
,所以FragmentManagerViewModel
存储在这个Fragment的宿主Activity中。
第二种情况
1
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
我们知道,一个Fragment包含两个FragmentManager
:mFragmentManager
和mChildFragmentManager
,后者就是用来管理在这个Fragment中创建的所有子Fragment,当这个Fragment attach到host
的时候,mChildFragmentManager
会在父Fragment关联的FragmentManagerViewModel
中的mChildNonConfigs
创建一个新的FragmentManagerViewModel
(key:父Fragment)供这个mChildFragmentManager
所管理的所有Fragments使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// androidx.fragment.app.Fragment
package androidx.fragment.app;
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner {
void performAttach() {
mChildFragmentManager.attachController(mHost, new FragmentContainer() {
@Override
@Nullable
public View onFindViewById(int id) {
if (mView == null) {
throw new IllegalStateException("Fragment " + this + " does not have a view");
}
return mView.findViewById(id);
}
@Override
public boolean onHasView() {
return (mView != null);
}
}, this);
mState = ATTACHED;
mCalled = false;
onAttach(mHost.getContext());
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onAttach()");
}
}
}
这样意味这我们可以在androidx.fragment.app.FragmentActivity
的ViewModelStore
里存储的FragmentManagerViewModel
通过mChildNonConfigs
和mViewModelStores
直接或间接获取到附加到这个Activity上所有的Fragment和子Fragment的ViewModel
。
第三种情况
1
mNonConfig = new FragmentManagerViewModel(false);
这种已经废弃,可以不用考虑。
我们平时所使用的ViewModel
都需要继承这个类,这个类设计的很简单,对于我们来说有用的方法只有一个:onCleared
,要注意这个方法很重要,当我们的ViewModel
需要被移除的时候,会调用这个方法,所以我们可以在这个方法对正在观察的数据去取消订阅,关闭正在加载的资源等。
ViewModel
禁止持有一些如View/Activity/Fragment这些引用,想象一下旋转一个Activity的时候,如果ViewModel持有这个Activity的引用,导致这个Activity在需要被销毁的时候由于ViewModel的引用而导致无法被销毁,导致Activity内存泄漏。
ViewModel
的存在也简化了Fragment
与Fragment
之间的通信:我们只需要定义一个用于Fragment之间共享的ViewModel,然后将它存储在这些Fragment的宿主Activity的ViewModelStore中,然后这些Fragment就都可以获取到这个Fragment,然后相互通信,不需要定义交互接口,不要在宿主Activity中处理过多的逻辑。
ViewModel
也是官方推荐替换onRetainNonConfigurationInstance去保留所有适当的非配置状态(non-config state)的方法。
— Lenox Xian