Google 2017 I/O開發者大會於近日召開,在開發者大會上谷歌除了發布了Android O等一些新產品之外,也對Android代碼的架構做出了一個官方的回應。
Google 2017 I/O開發者大會Android架構組件介紹現場視頻
下面是官方提供的Android App開發的架構圖:
從上圖可以看到一些關鍵字:ViewModel
,LiveData
,Room
等。其實看了上面視頻的會發現Google官方Android架構組件一共包括以下幾個:
- LifeCycle : 與Activity和Fragment的生命周期有關
- LiveData :異步可訂閱數據,也是生命周期感知
- ViewModel :視圖數據持有模型,也是生命周期感知
- Room :SQLite抽象層,用於簡化SQLite數據存儲
這篇文章主要講解LifeCycle在項目的簡單用法。
AS中添加依賴
首先在工程根目錄的build.gradle
中添加一下內容:
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' } //添加此行
}
}
然后在應用目錄下的build.gradle
中添加以下依賴:
//For Lifecycles, LiveData, and ViewModel
compile "android.arch.lifecycle:runtime:1.0.0-alpha1"
compile "android.arch.lifecycle:extensions:1.0.0-alpha1"
annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha1"
//For Room
compile "android.arch.persistence.room:runtime:1.0.0-alpha1"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha1"
LifeCycle相關使用
在我們平時的項目中經常會遇到很多需要依賴生命周期的邏輯處理,比如有這么一個需求。
在某個Activity我們需要在屏幕上展現用戶的地理位置。簡單的實現方法如下:
class MyLocationListener {
public MyLocationListener(Context context, Callback callback) {
// ...
}
void start() {
// connect to system location service
}
void stop() {
// disconnect from system location service
}
}
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, (location) -> {
// update UI
});
}
public void onStart() {
super.onStart();
myLocationListener.start();
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
雖然以上代碼看起來很簡潔,但在實際項目中的onStart,onStop方法可能會變得相當龐大。
此外,實際情況可能並不像上面這么簡單,例如我們需要在start位置監聽前做用戶狀態檢測,檢測是一個耗時的任務,那么很有可能在檢測結束前用戶提前退出了Activity,這時候就會導致myLocationListener.start()
在myLocationListener.stop()
后面調用,從而引起很多難以定位的問題。代碼如下:
class MyActivity extends AppCompatActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
myLocationListener = new MyLocationListener(this, location -> {
// update UI
});
}
public void onStart() {
super.onStart();
Util.checkUserStatus(result -> {
// what if this callback is invoked AFTER activity is stopped?
if (result) {
myLocationListener.start();
}
});
}
public void onStop() {
super.onStop();
myLocationListener.stop();
}
}
這時候就該今天的主角LifeCycle
出場了。它提供了一套接口幫助你處理這些問題。
LifeCycle
LifeCyle
類持有Activity或者Fragment的生命周期相關信息,並且支持其他對象監聽這些狀態。
LifeCyle
有兩個枚舉用於追蹤生命周期中的狀態。
Event
這是生命周期的事件類,會在Framework和LifeCycle間傳遞,這些事件映射到Activity和Fragment的回調事件中。
State
LifeCycle所持有Activity或Fragment的當前狀態。
一個類想要監聽LifeCycle的狀態,只需要給其方法加上注解:
public class MyObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
}
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());
LifeCycleOwner
LifeCycleOwner
是一個只有一個方法的接口用於表明其有一個LifeCycle對象。這個方法為getLifecycle()
。
這個對象給Acitivity,Fragment和LifeCycle提供了一個很好的抽象關系,Activity和Fragment只要實現這個接口就能配合LifeCycle實現生命周期監聽。
注意:由於目前LifeCycle處於alpha階段,所以Fragment和AppCompatActivity並不會實現這些方法,在此之前,可以使用
LifecycleActivity
和LifecycleFragment
。等LifeCycle趨於穩定后,Fragment和AppCompatActivity會默認實現這些。
對於之前的位置監聽的例子,我們可以讓MyLocationListener
繼承LifecycleObserver,在onCreate中使用LifeCycle進行初始化,剩下的問題則不必擔心了。因為MyLocationListener
有能力進行生命周期的判斷。
class MyActivity extends LifecycleActivity {
private MyLocationListener myLocationListener;
public void onCreate(...) {
//此處進行初始化getLifecycle()傳入LifeCycle對象
myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
// update UI
});
//檢測用戶狀態並啟用監聽
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.enable();
}
});
}
}
下面看一下MyLocationListener
class MyLocationListener implements LifecycleObserver {
private boolean enabled = false;
public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
...
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void start() {
if (enabled) {
// connect
}
}
public void enable() {
enabled = true;
//⓵
if (lifecycle.getState().isAtLeast(STARTED)) {
// connect if not connected
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void stop() {
// disconnect if connected
}
}
LifeCycle最重要的特性就是在⓵處,他可以提供主動查詢生命周期狀態的方法。這樣就避免了上面遇到的myLocationListener.start()
在myLocationListener.stop()
后面調用的問題。
通過以上的實現,我們的LocationListener
是完全的生命周期感知了,它可以進行自己的初始化和資源清理而不必受Activity或者Fragment的管理。這時候如果我們在其他Activity或者Fragment中使用LocationListener
,我們只需要初始化它就行了,不必再擔心生命周期對它的影響,因為它內部會做好這一切。
通過LefeCycle工作的類我們稱之為生命周期感知
。鼓勵需要使用Android生命周期的類的庫提供生命周期感知組件,以便客戶端可以輕松地在客戶端上集成這些類,而無需手動生命周期管理。
LiveData
就是生命周期感知組件的示例,將LiveData和ViewModel一起使用,可以在遵循Android生命周期的情況下,更容易的使用數據來填充UI。
生命周期的最佳實踐
- 保持你的UI(Activity和Fragment)盡可能簡潔。它們不應該試圖獲取它們的數據而是使用ViewModel來執行此操作,並通過LiveData的回調將數據更新到UI中。
- 嘗試編寫數據驅動的UI,你的UI的責任是在數據更改時更新視圖,或將用戶操作通知給ViewModel。
- 將你的數據邏輯放在ViewModel類中。 ViewModel應該作為UI和其他數據操作的連接器。值得注意的是,ViewModel並不負責提取數據(例如,從網絡)。相反,ViewModel應該調用其他接口來執行此工作,然后將結果提供給UI。
- 使用
Data Binding
可以讓你的的UI代碼變得相當干凈利落。這將使你的UI更具聲明性,並最大限度地減少書寫UI更新的代碼。如果您更喜歡在Java中執行此操作,請使用像Butter Knife
這樣的庫來避免使用樣板代碼並進行更好的抽象。 - 如果你有一個復雜的UI,請考慮創建一個Presenter類來處理UI修改。這通常是過度架構的,但可能有助於使你的UI更容易測試。
- 不要在ViewModel中引用View或Activity上下文。如果ViewModel在Activity或View銷毀的情況下依舊存活,這時將導致內存泄漏。
補充
在自定義的Activity或Fragment中實現LifeCycleOwner,可以實現LifecycleRegistryOwner
這個接口。而不是繼承(LifeCycleFragment和LifeCycleActivity)
public class MyFragment extends Fragment implements LifecycleRegistryOwner {
LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
@Override
public LifecycleRegistry getLifecycle() {
return lifecycleRegistry;
}
}
如果你要在自定義的類中實現LifeCycleOwner,可以使用LifecycleRegistry
,但是你需要主動向其轉發生命周期的事件。但如果你自定義類是Fragment和Activity的話並且它們實現的是LifecycleRegistryOwner,那么事件轉發都是自動完成的。