本文就“谷歌開發(fā)者”最近推出的Android 架構組件做簡單介紹和使用娇昙,如果需要更好的理解架構組件的原理象泵,可以到官網《Guide to App Architecture》船殉。
(一)Lifecycle
Lifecycle組件的引入
一項新的技術的提出肯定是為了解決痛點問題,如果使用過MVP模式的話咽斧,有個問題:Presenter感知Activity或者Fragment的生命周期堪置?你可能會這樣做,Presenter中定義多個和Activity或者Fragment相應的生命周期方法张惹,然后在Activity或者Fragment中調用Presenter中定義的方法舀锨。以下暫且用MVP模式場景舉例:
public class MainPresenter implements IPresenter {
public MainPresenter(Context context){
}
public void onCreate() {
}
public void onStart() {
}
...
public void onDestroy() {
}
}
???然后,Activity中的生命周期回調主動調用Presenter的public函數宛逗,如下:
public class MainActivity extends AppCompatActivity {
private IPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new MainPresenter(this);
mPresenter.onCreate();
}
@Override
protected void onStart() {
super.onStart();
mPresenter.onStart();
}
...
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.onDestroy();
}
}
???上面簡單的例子很明顯暴露出代碼冗余的短板坎匿,實際開發(fā)中業(yè)務如果稍加復雜,這個問題體現的就更加明顯雷激,對于追求代碼結構完美的程序員最不能接受替蔬。因此,Lifecycle組件就誕生了屎暇。
Lifecycle組件的簡單使用
public interface IPresenter extends DefaultLifecycleObserver{
//....
}
public class MainActivity extends AppCompatActivity {
private IPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new MainPresenter(this);
mPresenter.onCreate();
//將mPresenter加入宿主生命周期觀察者隊列
getLifecycle().addObserver(mPresenter);
}
@Override
protected void onStart() {
super.onStart();
}
...
@Override
protected void onDestroy() {
super.onDestroy();
}
}
??作為觀察者Presenter即可選擇性的重寫相關生命周期回調
public class MainPresenter implements IPresenter {
public MainPresenter(Context context){
}
@Override
public void onCreate(LifecycleOwner owner) {
}
@Override
public void onStart(LifecycleOwner owner) {
}
...
@Override
public void onDestroy(LifecycleOwner owner) {
}
}
???所謂沒有對比就沒有傷害承桥,關于Lifecycle組件原理簡單的由下面的圖來詮釋:
???我們以V4包中的Fragment(AppCompatActivity類似)為例,看下Fragment和LifecycleOwner根悼、LifecycleObserver凶异、Lifecycle之間的類關系圖。
Lifecycle組件成員Lifecycle被定義成了抽象類挤巡,LifecycleOwner剩彬、LifecycleObserver被定義成了接口;
Fragment實現了LifecycleOwner接口矿卑,該只有一個返回Lifecycle對象的方法getLifecyle()喉恋;
Fragment中getLifecycle()方法返回的是繼承了抽象類Lifecycle的LifecycleRegistry。
LifecycleRegistry中定義嵌套類ObserverWithState母廷,該類持有GenericLifecycleObserver對象轻黑,而GenericLifecycleObserver是繼承了LifecycleObserver的接口。
?
??最后琴昆,建議明白Lifecycle組件的框架和結構后氓鄙,親自實踐慢慢體會。
(二)LiveData
LiveData組件的引入
從LiveData具有的特點椎咧,我們就能聯想到它能夠解決我們遇到的什么問題玖详。LiveData具有以下優(yōu)點:
- 能夠保證數據和UI統(tǒng)一
- 減少內存泄漏
- 當Activity停止時不會引起崩潰
- 不需要額外的手動處理來響應生命周期的變化
- 組件和數據相關的內容能實時更新
- 針對configuration change(比如語言把介、屏幕方向變化)時勤讽,不需要額外的處理來保存數據
- 資源共享
LiveData組件的簡單使用
LiveData常見的幾種使用方式:
- 使用LiveData對象
- 繼承LiveData類
方式一:使用LiveData對象
主要有一下步驟:
- 創(chuàng)建保存特定數據類型的LiveData實例;
- 創(chuàng)建Observer對象拗踢,作為參數傳入LiveData.observe(...)方法添加觀察者;
- 更新LiveData對象存儲的數據;
創(chuàng)建LiveData實例
???Android文檔中建議LiveData配合ViewModel使用更佳犯建,其實炸宵,你也可以不使用ViewModel券膀,但是一定要做到LiveData中保存的數據和組件分離,原因前面我們已經提到過了驯遇。下面是在ViewModel中創(chuàng)建LiveData實例的例子芹彬,至于ViewModel后面會做詳細介紹:
public class NameViewModel extends ViewModel{
// Create a LiveData with a String
private MutableLiveData<String> mCurrentName;
// Create a LiveData with a String list
private MutableLiveData<List<String>> mNameListData;
public MutableLiveData<String> getCurrentName() {
if (mCurrentName == null) {
mCurrentName = new MutableLiveData<>();
}
return mCurrentName;
}
public MutableLiveData<List<String>> getNameList(){
if (mNameListData == null) {
mNameListData = new MutableLiveData<>();
}
return mNameListData;
}
}
???在NameViewModel中創(chuàng)建了兩個MutableLiveData(MutableLiveData是LiveData的子類)實例,分別存儲當前姓名叉庐、姓名列表舒帮;兩個實例通過NameViewModel中的Getter方法得到。
創(chuàng)建Observer對象陡叠,添加觀察者
public class LiveDataFragment extends Fragment {
private static final String TAG = "LiveDataFragment";
private NameViewModel mNameViewModel;
@BindView(R.id.tv_name)
TextView mTvName;
public static LiveDataFragment getInstance(){
return new LiveDataFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNameViewModel = ViewModelProviders.of(this).get(NameViewModel.class);
mNameViewModel.getCurrentName().observe(this,(String name) -> {
mTvName.setText(name);
Log.d(TAG, "currentName: " + name);
}); // 訂閱LiveData中當前Name數據變化
mNameViewModel.getNameList().observe(this, (List<String> nameList) -> {
for (String item : nameList) {
Log.d(TAG, "name: " + item);
}
}); // 訂閱LiveData中Name列表數據變化
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_livedata, container, false);
ButterKnife.bind(this, view);
return view;
}
}
???在onCreate()方法中通過LiveData.observe()方法添加觀察者玩郊,當數據變化時會通過回調方法通知觀察者,在Observer中更新當前姓名和打印姓名列表枉阵。
更新LiveData中的數據
???在上面我們已經訂閱了LiveData數據變化译红,現在我們看下如果LiveData數據變化時,上面的Observer中是否會受到更新的通知兴溜。我們在LiveDataFragment中增加兩個按鈕來改變LiveData中的數據侦厚。
@OnClick({R.id.btn_change_name, R.id.btn_update_list})
void onClicked(View view){
switch (view.getId()){
case R.id.btn_change_name:
mNameViewModel.getCurrentName().setValue("Uzi");
break;
case R.id.btn_update_list:
List<String> nameList = new ArrayList<>();
for (int i = 0; i < 10; i++){
nameList.add("Mlxg<" + i + ">");
}
mNameViewModel.getNameList().setValue(nameList);
break;
}
}
???代碼很簡單,在兩個按鈕的點擊事件中通過LiveData.setValue()方法來改變LiveData中保存的數據昵慌。當點擊這兩個按鈕的時候假夺,我們會發(fā)現在onCreate()方法中會收相應到數據改變的回調。
方式二:繼承LiveData類
????除了直接使用LiveData對象外斋攀,還可以通過集成LiveData類來定義適合特定需求的LiveData已卷。下面舉個繼承LiveData類的例子, 來驗證下LiveData的其中一個優(yōu)點——資源共享淳蔼。
public class MyLiveData extends LiveData<Integer> {
private static final String TAG = "MyLiveData";
private static MyLiveData sData;
private WeakReference<Context> mContextWeakReference;
public static MyLiveData getInstance(Context context){
if (sData == null){
sData = new MyLiveData(context);
}
return sData;
}
private MyLiveData(Context context){
mContextWeakReference = new WeakReference<>(context);
}
@Override
protected void onActive() {
super.onActive();
registerReceiver();
}
@Override
protected void onInactive() {
super.onInactive();
unregisterReceiver();
}
private void registerReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
mContextWeakReference.get().registerReceiver(mReceiver, intentFilter);
}
private void unregisterReceiver() {
mContextWeakReference.get().unregisterReceiver(mReceiver);
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "action = " + action);
if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
int wifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
int wifiLevel = WifiManager.calculateSignalLevel(wifiRssi, 4);
sData.setValue(wifiLevel);
}
}
};
}
???MyLiveData是個繼承了LiveData的單例類侧蘸,在onActive()和onInactive()方法中分別注冊和反注冊Wifi信號強度的廣播。然后在廣播接收器中更新MyLiveData對象鹉梨。在使用的時候就可以通過MyLiveData.getInstance()方法讳癌,然后通過調用observe()方法來添加觀察者對象,訂閱Wifi信息強度變化存皂。
- onActive(),此方法是當處于激活狀態(tài)的observer個數從0到1時晌坤,該方法會被調用。
- onInactive() ,此方法是當處于激活狀態(tài)的observer個數從1變?yōu)?時旦袋,該方法會被調用骤菠。
???關于LiveData的原理,從以下圖理解:
????LiveData的類關系圖相對比較簡單疤孕,從上面的類圖我們就能看到商乎。和LiveData組件相關的類和接口有:LiveData類、Observer接口祭阀、GenericLifecycleObserver接口鹉戚。LiveData類是個抽象類鲜戒,但是它沒有抽象方法,抽象類有個特點是:不能在抽象類中實例化自己抹凳。為什么LiveData會被定義成abstract而又沒有抽象方法呢遏餐,這個…我也不知道,看了下LiveData的提交記錄赢底,是在將hasObservers()替換getObserverCount()方法時將LiveData改成了abstract境输,在此之前它是被定義為public,可以翻墻的可以看下這里的修改記錄颖系。
- MediatorLiveData繼承自MutableLiveData嗅剖,MutableLiveData繼承自LiveData。MediatorLiveData可以看成是多個LiveData的代理嘁扼,當將多個LiveData添加到MediatorLiveData信粮,任何一個LiveData數據發(fā)生變化時,MediatorLiveData都會收到通知趁啸。
- LiveData有個內部類LifecycleBoundObserver强缘,它實現了GenericLifecycleObserver,而GenericLifecycleObserver繼承了LifecycleObserver接口不傅。在這里可以回顧下Lifecycle組件相關的內容旅掂。當組件(Fragment、Activity)生命周期變化時會通過onStateChanged()方法回調過來访娶。
- Observer接口就是觀察者商虐,其中定義了LiveData數據變化的回調方法onChanged()。
?
當然崖疤,LiveData還有更多的使用方法秘车,這里僅做大致的理解并簡單使用。
(三)ViewModel
ViewModel組件的引入
ViewModel劫哼,它是負責準備及管理UI組件(Fragment/Activity)相關的數據類叮趴,也就是說ViewModel是用來管理UI相關的數據的。同時ViewModel還可以用來負責UI組件間的通信权烧。既然ViewModel是用來管理和UI組件有關的數據的眯亦,而LiveData又是這些數據的持有類,所以在使用LiveData的時候般码,就自然想到要使用ViewModel了妻率。另外,ViewModel還可以用于UI組件間的通信侈询。
ViewModel的基本使用
前面在講解LiveData時舌涨,我們已經使用了ViewModel糯耍,所以它的基本使用扔字,在這里就不在贅述了囊嘉。
ViewModel的生命周期
ViewModel的生命周期,在官方文檔中是用下面這張圖來描述ViewModel的生命周期革为。
???上圖是用Activity作為例子扭粱,左側表示Activity的生命周期狀態(tài),右側綠色部分表示ViewModel的生命周期范圍震檩。當屏幕旋轉的時候琢蛤,Activity會被recreate,Activity會經過幾個生命周期方法抛虏,但是這個時候ViewModel還是之前的對象博其,并沒有被重新創(chuàng)建,只有當Activity的finish()方法被調用時迂猴,ViewModel.onCleared()方法會被調用慕淡,對象才會被銷毀。這張圖很好的描述了是當Activity被recreate時沸毁,ViewModel的生命周期峰髓。
??? 另外,有個注意的地方:在ViewModel中不要持有Activity的引用息尺。為什么要注意這一點呢携兵?從上面的圖我們看到,當Activity被recreate時搂誉,ViewModel對象并沒有被銷毀徐紧,如果Model持有Activity的引用時就可能會導致內存泄漏。那如果要使用到Context對象怎么辦呢炭懊,那就使用ViewModel的子類AndroidViewModel吧浪汪。
ViewModel的組件間通信
下面看下ViewModel用于Fragment之間通信的例子:
public class CommunicateViewModel extends ViewModel {
private MutableLiveData<String> mNameLiveData;
public LiveData<String> getName(){
if (mNameLiveData == null) {
mNameLiveData = new MutableLiveData<>();
}
return mNameLiveData;
}
public void setName(String name){
if (mNameLiveData != null) {
mNameLiveData.setValue(name);
}
}
@Override
protected void onCleared() {
super.onCleared();
mNameLiveData = null;
}
}
?
public class FragmentOne extends Fragment {
private CommunicateViewModel mCommunicateViewModel;
public static FragmentOne getInstance(){
return new FragmentOne();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_one, container, false);
ButterKnife.bind(this, view);
return view;
}
@OnClick(R.id.btn_set_name)
void onViewClicked(View v){
switch (v.getId()){
case R.id.btn_set_name:
mCommunicateViewModel.setName("Jane");
break;
}
}
}
?
public class FragmentTwo extends Fragment {
@BindView(R.id.tv_name)
TextView mTvName;
private CommunicateViewModel mCommunicateViewModel;
public static FragmentTwo getInstance(){
return new FragmentTwo();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class);
mCommunicateViewModel.getName().observe(this, name -> mTvName.setText(name));
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_two, container, false);
ButterKnife.bind(this, view);
return view;
}
}
代碼很簡單,在FragmentOne中改變CommunicateViewModel中LiveData保存的數據凛虽,然后在FragmentTwo中會收到數據改變的通知死遭。但是這個前提兩個Fragment是共享的同一個LiveData數據對象。如何保證兩個Fragment里的ViewModel是同一個對象呢凯旋,ViewModelProviders.of(Activity/Fragment)中的對象一致呀潭,獲得的ViewModel就是同一個對象,源碼如下:
/**
* Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
* is alive. More detailed explanation is in {@link ViewModel}.
* <p>
* It uses {@link ViewModelProvider.AndroidViewModelFactory} to instantiate new ViewModels.
*
* @param activity an activity, in whose scope ViewModels should be retained
* @return a ViewModelProvider instance
*/
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this {@code ViewModelProvider}.
* <p>
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @param <T> The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
@NonNull
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this {@code ViewModelProvider}.
* <p>
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param key The key to use to identify the ViewModel.
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @param <T> The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
以下是原理圖:
- ViewModelProviders是ViewModel工具類至非,該類提供了通過Fragment和Activity得到ViewModel的方法钠署,而具體實現又是有ViewModelProvider實現的。ViewModelProvider是實現ViewModel創(chuàng)建荒椭、獲取的工具類谐鼎。在ViewModelProvider中定義了一個創(chuàng)建ViewModel的接口類——Factory。ViewModelProvider中有個ViewModelStore對象趣惠,用于存儲ViewModel對象狸棍。
- ViewModelStore是存儲ViewModel的類身害,具體實現是通過HashMap來保存ViewModle對象。
- ViewModel是個抽象類草戈,里面只定義了一個onCleared()方法塌鸯,該方法在ViewModel不在被使用時調用。ViewModel有一個子類AndroidViewModel唐片,這個類是便于要在ViewModel中使用Context對象丙猬,因為我們前面提到是不能在ViewModel中持有Activity的引用。
- ViewModelStores是ViewModelStore的工廠方法類费韭,它會關聯HolderFragment茧球,HolderFragment有個嵌套類——HolderFragmentManager。
?
最后星持,Lifecycle袜腥,LiveData,ViewModel結合起來使用钉汗,優(yōu)化項目整體架構羹令。
?
?
?
?
?