1. 程式人生 > >新款安卓架構設計-Architecture Components介紹

新款安卓架構設計-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,這些問題都透過架構的方式幫我們處理好了,既然它有這麼多的優勢還是值得一用的。

但是需要注意的是:它怎麼樣也只是一個架構模板,如果你有比它更好用的架構模板,可以比較之後再做出選擇。