Android Jetpack 六大架構組件全面瞭解

項目地址

一.項目簡介

本項目實現了對Lifecycle,LiveData,ViewModel,Room,Paging,Navigation這六個官方構架組件的全面使用,組件的單獨使用或者合作使用都有(PS:WorkManager  bug 太多,不建議使用,我這裏也不會提到他,因爲我反正是有坑,邁不過去)

貼一張項目主界面圖,大家可以先下載項目自己運行一下

 

二.對於六大組件的簡介

1.Lifecycle(監聽activity生命週期)

Lifecycle主要作用是方便監聽activity和Fragment生命週期

在監聽activity和Fragment時,在代碼裏沒有什麼區別,和下面一樣

getLifecycle().addObserver(new IPresenter());


    class IPresenter implements LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        void onCreate( LifecycleOwner owner){
            tv.setText(tv.getText()+"onCreate\n");
        }
。。。。。。。
    }

但是他有一個枚舉類,有點意思

Lifecycle.State 是一個枚舉類,用於描述當前 生命週期擁有者 的狀態,與onStop之類的有點不一樣,更加寬泛點 ,
通過此函數獲取getLifecycle().getCurrentState(),

DESTROYED:onDestroy執行中和之後    
INITIALIZED : onCreate執行前包括執行時   
CREATED:onCreate與onStop之間
STARTED:onStart與onPause之間  
RESUMED:onResume執行中和 到 onPause開始執行前

還有一個特殊的比較方法,getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED),這個比較的當前值的等級是否等於或高於給的值,
也就是說State枚舉類裏的值越往後面,值越大,如果當前是 RESUMED ,那上面這個判斷是 true

所以在我們刷新界面時,可以添加一個判斷  if(getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)),保證當前activity 可見時才刷新

 

2.LiveData(監聽自身變化的數據類)

LIveData是一個抽象類,實現類有MutableLiveData、MediatorLiveData

MutableLiveData:這個數據類有着監聽自身變化的能力,並且通過監聽者模式告訴 其他組件數據更新。這個能夠與ViewModel、Room配合,這個後面說

 

    MutableLiveData<Integer> num=new MutableLiveData<>();
    MyObserver observer;        

        observer=new MyObserver();
        num.observe(this,observer);

    class MyObserver implements Observer<Integer> {

        @Override
        public void onChanged(@Nullable Integer integer) {
            tv.setText(""+integer);
        }
    }

MediatorLiveData 與 MutableLiveData的不同之處在於,他能統合 MutableLiveData,就像是一個ArrayList添加一個list一樣,不僅數據添加進去了,而且如果添加進去的 MutableLiveData 有Observer ,而MediatorLiveData 也有Observer,在這個MutableLiveData 數據發生改變時, MediatorLiveData 的Observer也會觸發。

以下代碼就是將MutableLiveData添加到 MediatorLiveData,

       mediatorLiveData.addSource(num,mediatorLiveData::setValue);

 

3.ViewModel(跨組件通訊)

我們先看看我們如何獲取ViewModel這個類的對象
ViewModelProviders.of(getActivity()).get(MyViewModel.class);

而這個getActivity()也可以換成Fragment的實例

現在我說一下這個爲何能夠做到跨組件通訊,因爲它能跨組件獲取同一個實例

比如:在ActivityA裏有FragmentA、FragmentB,你在FragmentA通過給getActivity()得到ViewModel的實例,和你在FragmentB也這樣做得到的實例是一樣的。說白了,在FragmentA你如果改變這個ViewModel的LIveData這樣的屬性,在FragmentB能夠監聽的到

        viewModel= ViewModelProviders.of(getActivity()).get(MyViewModel.class);
        btn_add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewModel.setNum(viewModel.getNum().getValue()+1);
            }
        });
        viewModel= ViewModelProviders.of(getActivity()).get(MyViewModel.class);
        viewModel.getNum().observe(this,new MyObserver());

 

4.Room(數據庫框架)

Room有三個部分:實體類、數據庫操控類、數據庫創建類

實體類,和GreenDao、Realm一樣通過註解來創建表,和修改屬性

這個是實體類的屬性能夠是LIveData

@Entity(tableName = "test")
public class TestBean {

    @PrimaryKey
    private long id;

    private String name="";

......
}

數據庫操控類,你別看他是接口類,但是他通過Room框架的註解卻有着真正的操控數據的能力,

@Dao
public interface TestDao {

    @Query("SELECT * FROM test")
    List<TestBean> getAll();

    @Query("SELECT * FROM test WHERE id = (:id)")
    TestBean getById(int id);

    @Insert
    void insert(TestBean testBean);

    @Delete
    void delete(TestBean testBean);

    @Update
    void update(TestBean testBean);

}

數據庫創建類,能夠創建數據庫,並且能夠修改數據庫版本

@Database(entities = {TestBean.class},version = 1)
public abstract class TestDataBase extends RoomDatabase{

    public abstract TestDao testDao();


}

Room數據庫的數據可以通過 給MutableLiveData,來完成對界面刷新的綁定

        data.observe(this, new Observer<List<TestBean>>() {
            @Override
            public void onChanged(@Nullable List<TestBean> testBeans) {
             
            }
        });     
   data.setValue(testDao.getAll());

5.Paging(分頁加載)

Paging實現的分頁加載指的是:你如果有100條數據,但是你的屏幕能夠顯示出來的只有10條,那你把100條item都加載到RecyclerView裏,就會浪費內存,那你先給RecyclerView加載20條數據,當你把RecyclerView拉到底部,Paging再給RecyclerView 20條數據,讓他再加載出20條item給用戶看。

還是三個步驟實現Paging的基礎使用

第一個LiveData數據類,這個PageSize就是你每一次給RecyclerView的數據數量

val allCheeses = LivePagedListBuilder(dao.allCheesesByName(), PagedList.Config.Builder()
                    .setPageSize(PAGE_SIZE)
                    .setEnablePlaceholders(ENABLE_PLACEHOLDERS)
                    .build()).build()

第二個實現PagedListAdapter,其中還用了DiffUtil這個幫助RecyclerView優化更新的工具類

class CheeseAdapter : PagedListAdapter<Cheese, CheeseViewHolder>(diffCallback) {
    override fun onBindViewHolder(holder: CheeseViewHolder, position: Int) {
        holder.bindTo(getItem(position))
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CheeseViewHolder =
            CheeseViewHolder(parent)

    companion object {

        private val diffCallback = object : DiffUtil.ItemCallback<Cheese>() {
            override fun areItemsTheSame(oldItem: Cheese, newItem: Cheese): Boolean =
                    oldItem.id == newItem.id

            override fun areContentsTheSame(oldItem: Cheese, newItem: Cheese): Boolean =
                    oldItem == newItem
        }
    }
}

第三個將Adapter與數據庫連接起來

viewModel.allCheeses.observe(this, Observer(adapter::submitList))

說實話代碼太多了,大家還是看看項目代碼比較好,這個例子是Kotlin的語言,我是從官方例子裏弄出來的

 

6.Navigation(Fragment控制框架)

首先用NavHostFragment佔個位,並且設置xml來控制Fragment的顯示和跳轉

    <fragment
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph_main" />

然後看看這個 nav_graph_main 寫了啥,開頭 這個app:startDestination 設置了默認顯示Fragment

<navigation 
    app:startDestination="@id/page1Fragment">

然後接下來 在fragment標籤裏 指定了該fragment 的具體實現類和 action(活動)

通過id 來唯一標識, name來指定 實現類

    <fragment
        android:id="@+id/page1Fragment"
        android:name="com.example.lilingzhi.llvr.fragment.MainPage1Fragment"
        android:label="fragment_page1"
        tools:layout="@layout/fragment_main_page1">
        <action
            android:id="@+id/action_page2"
            app:destination="@id/page2Fragment"
            app:enterAnim="@anim/slide_right_in"
            app:exitAnim="@anim/slide_left_out"
            app:popEnterAnim="@anim/slide_left_in"
            app:popExitAnim="@anim/slide_right_out" />
    </fragment>

action標籤描述的是Fragment跳轉的 目的地和跳轉動畫(目的地 進入動畫、出發地退出、出發地返回時 進入、目的地返回時退出),   而action標籤裏,id是唯一標識,app:destination指定了跳轉目的地

,在Java代碼通過以下觸發(從出發地到目的地)

Navigation.findNavController(it).navigate(R.id.action_page2)

想要返回時(目的地到出發地)

Navigation.findNavController(it).navigateUp()

在activity點擊返回鍵可以觸發 返回動畫

    override fun onSupportNavigateUp() =
            findNavController(this, R.id.my_nav_host_fragment).navigateUp()

 

 

 

參考文章

Android架構組件(二)——LiveData

Android架構組件Room的使用

Android官方架構組件Navigation:大巧不工的Fragment管理框架

Android.Arch.Paging: 分頁加載的新選項

Android 架構組件(一)——Lifecycle-Aware Components

Android架構組件(三)——ViewModel

官網

官方github 源碼

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章