Android 架構組件 - LiveData

介紹

LiveData 是一款基於觀察者模式的可感知生命週期的核心組件。LiveData 爲界面代碼 (Observer)的監視對象

(Observable),當 LiveData 所持有的數據改變時,它會通知相應的界面代碼進行更新。同時,LiveData 持有

界面代碼 Lifecycle 的引用,這意味着它會在界面代碼(LifecycleOwner)的生命週期處於 started 或 resumed

時作出相應更新,而在 LifecycleOwner 被銷燬時停止更新。通過 LiveData,開發者可以方便地構建安全性更高、

性能更好的高響應度用戶界面 .

https://developer.android.google.cn/topic/libraries/architecture/livedata.html

使用必知

推薦在 ViewModel , 而不是在 Activity / Fragment 中保存 LiveData 對象

  • 防止 Activity / Fragment 過於臃腫, 這些 UI 控制器只負責顯示數據, 而不負責保存數據.
  • 將 LiveData 實例與 Activity / Fragment 實例分離, 使得 LiveData 對象在 Activity / Fragment 實例銷燬時仍能保存數據.

觀察/訂閱 LiveData 對象的最佳時機是 onCreate() 方法中

  • 防止因 Activity / Fragment 的 onStart() / onResume() 多次回調, 導致 LiveData 多次被 observe
  • 確保 Activity / Fragment 一旦變成活躍狀態,就有可展示的數據

LiveData 使用

一般按照以下步驟:

  1. 在 ViewModel 類中, 創建 LiveData 的實例, 用來保存指定類型的數據.

    /**
    * 實現每隔一秒鐘,調用 postValue() 設置一次數據 
    */
    // step1
    public class TimerViewModel extends ViewModel {
    
        private static final String TAG = "TimerViewModel";
        private static final int ONE_SECOND = 1000;
        private MutableLiveData<Long> mutableLiveData = new MutableLiveData<>();
        private final Long initTime;
    
        public TimerViewModel() {
            initTime = SystemClock.elapsedRealtime();
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    long num = (SystemClock.elapsedRealtime() - initTime) / 1000;
                  	// setValue()會拋出異常 : Cannot invoke setValue on a background thread
                    mutableLiveData.postValue(num);
                }
            }, ONE_SECOND, ONE_SECOND);
        }
    
        public MutableLiveData<Long> getMutableLiveData() {
            return mutableLiveData;
        }
    
        /**
         * 當 Activity 或 Fragment 銷燬時,會回調該方法
         */
        @Override
        protected void onCleared() {
            super.onCleared();
            Log.e(TAG, "onCleared: ");
        }
    }
    
  2. 在 Activity / Fragment 創建 Observer 對象, 實現 onChanged() 方法, 當 LiveData 對象中保存的數據發生改變時會回調 onChanged() 方法, 我們可以在該方法中進行更新 UI 操作.

    // step2 
    Observer<Long> observer = new Observer<Long>() {
        @Override
        public void onChanged(@Nullable Long aLong) {
          textView.setText(String.valueOf(aLong));
        }
    };
    
  3. 使用 LiveData 對象的 observe(LifecycleOwner owner, Observer observer) 方法, 將 Observer 對象訂閱到 LiveData 對象上; 也可以使用 observeForever(Observer oberver) 方法不傳 LifecycleOwner 參數訂閱 Observer, 這時 Observer 被認爲始終處於活動狀態.

    public class LiveDataActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_live_data);
              textView = findViewById(R.id.tv_title);
              // step2
              Observer<Long> observer = new Observer<Long>() {
                  @Override
                  public void onChanged(@Nullable Long aLong) {
                  		textView.setText(String.valueOf(aLong));
              		}
       				};
          		// step3 
          		timerViewModel = ViewModelProviders.of(this).get(TimerViewModel.class);
            	timerViewModel.getMutableLiveData().observe(this, observer);
        }
    }
    

LiveData 更新數據

MutableLiveData 類提供了兩個公開方法: setValue(T) 和 postValue(T) , 如果需要更新 LiveData 對象存儲的數據, 必須需要使用這兩個方法.

public void click(View view) {
    timerViewModel.getMutableLiveData().postValue(100L);
}

setValue(T) 和 postValue(T) 區別 :

setValue(T) 必須在主線程中調用 , 而 postValue(T) 既可以在主線程中調用, 也可以在子線程中調用 .

LiveData 擴展

需求: 在 Activity 處於前臺 started 時 , 子線程開始計數, 並更新顯示到 UI 上, 在 Activity 處於後臺 paused 時, 暫停計數, 再次回到前臺時,繼續計數.

  1. 繼承擴展 MutableLiveData ,覆蓋 onActive() 和 onInactive() 方法, 根據標識是否 postValue 更新數據.

    public class CountLiveData extends MutableLiveData<Integer> {
    
        private static final String TAG = "CountLiveData";
        private int count;
        private boolean isRun = true;
        private final CountTask countTask;
    
        public CountLiveData() {
            countTask = new CountTask();
            countTask.start();
        }
    
    
        @Override
        protected void onActive() {
            super.onActive();
            Log.e(TAG, "onActive: ");
            countTask.interrupt();
            isRun = true;
        }
    
        @Override
        protected void onInactive() {
            super.onInactive();
            Log.e(TAG, "onInactive: ");
            isRun = false;
        }
    
        private class CountTask extends Thread {
            @Override
            public void run() {
                super.run();
                while (true) {
                    if (!isRun) {
                        try {
                            Thread.sleep(Integer.MAX_VALUE);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    count++;
                    postValue(count);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  2. 創建 ViewModel , 存儲 LiveData 對象中的數據.

    public class CountViewModel extends ViewModel {
    
        private static final String TAG = "CountViewModel";
        CountLiveData countLiveData;
    
        public CountViewModel() {
            countLiveData = new CountLiveData();
        }
    
        public CountLiveData getCountLiveData() {
            return countLiveData;
        }
    
        /**
         * Activity / Fragment 銷燬時回調該方法
         */
        @Override
        protected void onCleared() {
            super.onCleared();
            Log.e(TAG, "onCleared: ");
        }
    }
    
  3. Activity 中創建 Observer , 訂閱監聽 LiveData 中的數據變化 , 並更新 UI

    public class LiveDataActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) { 
    				// 計數
            CountViewModel countViewModel = ViewModelProviders.of(this).get(CountViewModel.class);
            Observer<Integer> countObserver = new Observer<Integer>() {
                @Override
                public void onChanged(@Nullable Integer integer) {
                    countTextView.setText("計數: " + String.valueOf(integer));
                }
            };
            countViewModel.getCountLiveData().observe(this, countObserver);
        }
        @Override
     protected void onStart() {
         super.onStart();
         Log.e("CountLiveData", "LiveDataActivity -> onStart(): " );
     }
    
     @Override
     protected void onResume() {
         super.onResume();
         Log.e("CountLiveData", "LiveDataActivity -> onResume(): " );
     }
    
     @Override
     protected void onRestart() {
         super.onRestart();
         Log.e("CountLiveData", "LiveDataActivity -> onRestart(): " );
     }
    
     @Override
     protected void onPause() {
         super.onPause();
         Log.e("CountLiveData", "LiveDataActivity -> onPause(): " );
     }
    
     public void click(View view) {
         startActivity(new Intent(this, LiveDataSecondActivity.class));
         timerViewModel.getMutableLiveData().postValue(100L);
     }
    }  
    

在這裏插入圖片描述

效果圖

由 GIF 圖可知: Activity 活躍時, 子線程每隔一秒鐘 postValue() 一次數據給 observer, 當 Activity 變成不活躍時, 子線程暫停了 postValue() , 再次回到活躍狀態時, 接着計數開始 postValue.

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