新款安卓架構設計-Architecture Components介紹
架構模板篇介紹了mvp,mvi,mvvm(databinding),今天介紹的這款安卓架構框架是Google在今天5月17號推出的:Architecture Components. 它的第一版本解決如下問題:
1)自動管理activity和fragment的生命週期,從而避免資源和記憶體溢位問題
2)持續化java物件到sqlite資料庫
主要包括組成部分:
1)Lifecycle Components
2)LiveData
3)ViewModel
4)Rom (Database, entity, DAO)
它的兩個主要設計原則:
1)單一原則,不要把所有程式碼都寫在Activity/Fragment, 隔離獨立的模組
2)model(資料)驅動UI
完整的架構圖:
1.新增到專案工程
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
}
}
Lifecycles,LiveData, ViewModel,新增依賴:
compile "android.arch.lifecycle:runtime:1.0.0-alpha3"
compile "android.arch.lifecycle:extensions:1.0.0-alpha3"
annotationProcessor "android .arch.lifecycle:compiler:1.0.0-alpha3"
Room,新增依賴
compile "android.arch.persistence.room:runtime:1.0.0-alpha3"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha3"
新增Room的test依賴:
testCompile "android.arch.persistence.room:testing:1.0.0-alpha3"
新增room的rxjava支援依賴:
compile "android .arch.persistence.room:rxjava2:1.0.0-alpha3"
2.處理Lifecycles
Lifecycle持有Activity/Fragment的lifecycle狀態,允許其他物件觀察這些狀態變化。使用下面兩種列舉類跟蹤關聯元件的lifecycle狀態:
1)Event
2)State
其對應的活動圖:
在程式碼中,使用@Annotation的方式標識對應的關聯元件的生命週期
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
}
}
3.LiveData
LiveData是一個儲存value和觀察value變化的物件。不像其他的observable,它可以關聯lifecycle.如果Observer的生命週期處於STARTED或RESUMED狀態,則LiveData會將Observer視為活動狀態。看下對面的程式碼片段:
public class LocationLiveData extends LiveData<Location> {
private LocationManager locationManager;
private SimpleLocationListener listener = new SimpleLocationListener() {
@Override
public void onLocationChanged(Location location) {
setValue(location);
}
};
public LocationLiveData(Context context) {
locationManager = (LocationManager) context.getSystemService(
Context.LOCATION_SERVICE);
}
@Override
protected void onActive() {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
@Override
protected void onInactive() {
locationManager.removeUpdates(listener);
}
}
LiveData有三個方法:
1)onActive() 當LiveData有一個啟用的observer時呼叫;
2)onInactive() 當LiveData沒有任何啟用的observer時呼叫;
3)setValue() 呼叫該方法更新LiveData的value,並通知相關的observer
在Fragment中使用LiveData:
public class MyFragment extends LifecycleFragment {
public void onActivityCreated (Bundle savedInstanceState) {
LiveData<Location> myLocationListener = ...;
Util.checkUserStatus(result -> {
if (result) {
myLocationListener.addObserver(this, location -> {
// update UI
});
}
});
}
}
它具有如下優勢:
1)沒有記憶體洩漏:因為它的observers繫結到對應的Lifecycle物件,所以當Lifecycle銷燬了,對應的LiveData也會被清除
2)不會導致Crash而停止activities,同樣因為Lifecycle影響observer,當observer處於inactive狀態,不會去接收event
3)總是更新到最新資料,這個很方便
4)正確處理configuration change(比如螢幕旋轉),當螢幕旋轉後,他仍然可以獲取最新的可用資料
5)共享資源,可以把LiveData弄成單例模式
6)因為它綁定了Lifecycle,所以不需要手動管理Activity/Fragment對應的生命週期了。
4.ViewModel
它的設計就是為了儲存和管理UI關聯的資料,這樣使得configuration change(比如螢幕旋轉)發生時,資料可以恢復。
回顧下Activity恢復資料,如果Activity被重新建立或者發生螢幕旋轉,儲存和恢復週期
onCreate()
onSaveInstanceState()
onRestoreInstanceState()
但是它有缺點:只允許儲存少量的數量,對於需要儲存大量的資料:比如介面上所有的使用者列表資訊,無能為力。
Lifecycle提供了ViewModel,它負責為UI提供資料,ViewModel會自動保留當發生螢幕旋轉時。這樣就實現了程式碼分離:ViewModel負責準備資料,而不是放在Activity/Fragment中。
下面打程式碼示例演示了它是怎麼使用的:
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// do async operation to fetch users
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
它的實現原理:activity被重新建立後,它會使用同樣的ViewModel例項(上一個Activity建立的),當對應的activity被銷燬了,框架會 呼叫ViewModel的onCleared方法來銷燬資源。
注意:由於ViewModel超出了具體的Activity和Fragment例項生命週期,所以它不應該引用View或任何可能持有對活動上下文的引用的類。 如果ViewModel需要應用程式上下文(例如,找到系統服務),則可以擴充套件AndroidViewModel類,並在建構函式中接收應用程式的建構函式(因為Application類擴充套件了Context)
4.1實現fragment之間資料共享
很常見,一個Activity有多個Fragment, 而實現fragment之間的資料共享或者叫資料傳遞比較麻煩
fragment.setArgs(Bundle)
Bundle bundle = fragment.getArgs()
而ViewModel幫我們簡化了,看下下面的程式碼示例
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}
這種機制的優勢:
1)對activity來說,它是完全隔離,不知道任何fragment之間的細節
2)fragment之間不需要互相關注,除了ViewModel的細節
3)fragment實現互相獨立。
5.Room
Room是一個持續library,Room在SQLite上提供了一個抽象層,以便在利用SQLite的全部功能的同時使流暢的資料庫訪問。它包括了三個主要元件:
1)Database
2)Entity
3)DAO
Room的架構設計圖如下:
從左到右的順序:database, DAO, entity
下面幾個程式碼判斷,展示了怎麼編寫這幾個元件,以及怎麼使用
1)Entity
@Entity
public class User {
@PrimaryKey
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
// Getters and setters are ignored for brevity,
// but they're required for Room to work.
}
2)DAO
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
3)Database
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
4)使用
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
AppDatabase.class, "database-name").build();
5.總結
Lifecycle, LiveData,ViewModel,Room看起來是獨立的,確實它們各自負責各自的功能,但是它們一旦串聯起來,你會發現很多頭疼的問題,比如:程式碼分離,資料持續化,非同步載入結束確保Activity/Fragment沒有被銷燬後重新整理UI,這些問題都透過架構的方式幫我們處理好了,既然它有這麼多的優勢還是值得一用的。
但是需要注意的是:它怎麼樣也只是一個架構模板,如果你有比它更好用的架構模板,可以比較之後再做出選擇。