Jetpact-activity組件完全決議
文章目錄
- Jetpact-activity組件完全決議
- 前言
- 原始碼決議
- 構造器
- Activity停止時取消View還未執行的事件
- View層處理
- 移除點擊事件回呼
- 移除長按事件
- Activity銷毀時清除ViewMode中資料
- isChangingConfigurations() 相關
- getViewModelStore()
- ViewModelStore.clear()
- 解決InputMethodManager中 mNextServedView 持有Activity導致記憶體泄漏
- 對Lifecycle的支持
- 對fragment回傳鍵調度支持
- 使用BackPressedDispatcher
- 回傳呼度原始碼決議
- OnBackPressedCallback 回呼
- Cancellable
- OnBackPressedDispatcher 回傳呼度路由
- activity-ktx擴展庫功能
- OnBackPressedDispatcherKt
- ActivityViewModelLazyKt
- 總結
- 參考
前言
Jetpack-activity/fragment 是jetpack架構組件中最基礎的部分,
底層對jetpack-lifecycle組件做了支持、為開發者能夠直接使用jetpack架構組件提供了支持,因此要想徹底了解jetpack系列,先學習activity/fragment組件很有必要,
注意:
本文中源代碼均使用 1.1.0穩定版本
參考
def activity_version = "1.1.0"
// Java language implementation
implementation "androidx.activity:activity:$activity_version"
// Kotlin
implementation "androidx.activity:activity-ktx:$activity_version"
繼承結構:

原始碼決議
/**
* Base class for activities that enables composition of higher level components.
* <p>
* Rather than all functionality being built directly into this class, only the minimal set of
* lower level building blocks are included. Higher level components can then be used as needed
* without enforcing a deep Activity class hierarchy or strong coupling between components.
*/
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner {
//....
}
從 ComponentActivity 類的注釋上可以得出兩條資訊:
ComponentActivity是一個支持組合高級組件的基類Activity- 并沒有將所有的組件都構建到這個類中,只是包含最基礎的底層組件,開發者可以根據需要使用更高級別的組件,無需在組件之間強耦合,
構造器
ComponentActivity在構造器中針對Android不同版本進行了簡單兼容處理
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
//noinspection ConstantConditions
//如果在使用 Lifecycle 物件的時候還沒有初始化則直接拋錯,對于重寫 getLifecycle() 方法需要注意
if (lifecycle == null) {
throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
+ "constructor. Please make sure you are lazily constructing your Lifecycle "
+ "in the first call to getLifecycle() rather than relying on field "
+ "initialization.");
}
//針對API 19以上兼容
if (Build.VERSION.SDK_INT >= 19) {
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_STOP) {
Window window = getWindow();
final View decor = window != null ? window.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
}
}
});
}
//Activity銷毀時清除ViewMode中資料
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
//針對 19~23 版本 解決 InputMethodManager中 mNextServedView 記憶體泄漏問題
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
從構造器中代碼來看做的事情比較多,但都是兼容性處理,有三個方面的處理:
- 針對API19以上兼容Activity停止時取消View還未執行的事件
- 針對API19以上Activity銷毀時清除ViewMode中資料
- 針對API19~23版本Activity銷毀時解決InputMethodManager中 mNextServedView持有Activity導致記憶體泄漏問題
Activity停止時取消View還未執行的事件
在 Activity 處于stop 時用戶是看不到界面的,也沒有必要再處理 View的點擊、長按、影片等事件,所以有必要將這些事件移除掉,
我們跟隨原始碼看一下具體是怎么做的:
View層處理
上述代碼會呼叫View中 cancelPendingInputEvents() 這是個取消事件的總調度方法,它沒有具體做事情,而是呼叫了 dispatchCancelPendingInputEvents() 來完成作業
public final void cancelPendingInputEvents() {
dispatchCancelPendingInputEvents();
}
void dispatchCancelPendingInputEvents() {
//位操作設定標志位
mPrivateFlags3 &= ~PFLAG3_CALLED_SUPER;
//執行清除事件作業
onCancelPendingInputEvents();
//檢查標志位是否正確 以確保完成了清除作業
if ((mPrivateFlags3 & PFLAG3_CALLED_SUPER) != PFLAG3_CALLED_SUPER) {
throw new SuperNotCalledException("View " + getClass().getSimpleName() +
" did not call through to super.onCancelPendingInputEvents()");
}
}
在 dispatchCancelPendingInputEvents() 方法中呼叫了 onCancelPendingInputEvents() 來完成具體的清除作業:
onCancelPendingInputEvents() 會清除已發送到訊息隊列的事件,延遲事件等 如果是自定義View 是可以重寫此方法,來自定義指定那些事件是需要清除或保留的,但是需要注意要 super.onCancelPendingInputEvents() 要呼叫父類方法 完成 mPrivateFlags3變數的位操作
public void onCancelPendingInputEvents() {
//移除點擊事件回呼
removePerformClickCallback();
//取消等待的長按事件
cancelLongPress();
//對標識變數進行操作 在 dispatchCancelPendingInputEvents()對此變數有檢查操作
mPrivateFlags3 |= PFLAG3_CALLED_SUPER;
}
移除點擊事件回呼
在 removePerformClickCallback() 中直接呼叫 removeCallbacks 將 mPerformClick點擊事件傳入
@UnsupportedAppUsage
private void removePerformClickCallback() {
if (mPerformClick != null) {
removeCallbacks(mPerformClick);
}
}
removeCallbacks(Runnable action)才是真正移除事件處理的方法,凡是以下幾種方式添加的事件或延遲事件都會移除
post()postDelayed()postOnAnimation()postOnAnimationDelayed()
public boolean removeCallbacks(Runnable action) {
if (action != null) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
//移除指定回呼
attachInfo.mHandler.removeCallbacks(action);
//移除Choreographer中影片回呼
attachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
Choreographer.CALLBACK_ANIMATION, action, null);
}
//移除等待佇列中事件
getRunQueue().removeCallbacks(action);
}
return true;
}
移除長按事件
在 cancelLongPress()中會分別呼叫 removeLongPressCallback() 清除長按回呼 和 removeTapCallback()移除長按產生的超時事件
removeLongPressCallback() 和 removeTapCallback() 都會呼叫 removeCallbacks(Runnable action) 來移除指定的事件,前面我們已經分析過了,就不再贅述了,
public void cancelLongPress() {
//移除長按回呼事件
removeLongPressCallback();
//移除長按超時回呼事件
removeTapCallback();
}
//移除長按回呼
private void removeLongPressCallback() {
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
}
//移除長按超時事件、修改標志位
private void removeTapCallback() {
if (mPendingCheckForTap != null) {
mPrivateFlags &= ~PFLAG_PREPRESSED;
removeCallbacks(mPendingCheckForTap);
}
}
Activity銷毀時清除ViewMode中資料
Activity銷毀時清除ViewMode中資料,需要依賴另一個組件-Lifecycle支持,不僅是 ViewModel 在Jetpack 架構組件中很多組件都需要依賴 Lifecycle組件,
我們重新看一下以下代碼
//Activity銷毀時清除ViewMode中資料
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
我們看到 !isChangingConfigurations() 為 true ,也就是Activity配置沒有修改情況下,在Activity銷毀時會呼叫 getViewModelStore().clear()
這里我們先將ViewModel放一放,來看一下為什么在清除 ViewModel中資料還有一個前置條件?這個條件什么時候滿足條件?
isChangingConfigurations() 相關
isChangingConfigurations()是 Activity類中方法,用來判斷 Activity 的配置資訊是否更改了,(比如 橫豎屏切換、語言發生變化等)需要重新啟動該Activity的時候 這個方法會回傳 true 、沒有更改和默認情況都是 false
/** true if the activity is being destroyed in order to recreate it with a new configuration */
/*package*/ boolean mChangingConfigurations = false;
public boolean isChangingConfigurations() {
return mChangingConfigurations;
}
我們看到 mChangingConfigurations變數是包級訪問權限,我們知道 Activity資源發生變化時會重新啟動,在 framework層經過一系列呼叫,最侄訓呼叫 ActivityThread中 handleRelaunchActivity() 將 mChangingConfigurations 設定為 true
@Override
public void handleRelaunchActivity(ActivityClientRecord tmp,
PendingTransactionActions pendingActions) {
//.....
int configChanges = 0;
ActivityClientRecord r = mActivities.get(tmp.token);
r.activity.mConfigChangeFlags |= configChanges;
r.mPreserveWindow = tmp.mPreserveWindow;
//將標識設定為修改
r.activity.mChangingConfigurations = true;
//重新啟動Activity
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
//.....
}
所以這就是為什么ViewModel能夠在Activity橫豎屏切換,還能保存資料不丟失的原因,
getViewModelStore()
再回到 ComponentActivity 中我們看一下 getViewModelStore() 是如何實作的
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
//從上一次保存的配置修改中恢復 ViewModelStore 實體
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果還是null new出一個ViewModelStore實體
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
我們看到 mViewModelStore是個全域變數,在進行實體化時會先從 上一次保存的 NonConfigurationInstances 物件中恢復,如果為null 最侄訓重新 new 出來一個新的 ViewModelStore 實體并賦值給 mViewModelStore
而 NonConfigurationInstances 是 ComponentActivity 中的靜態內部類 定義如下
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
在 Activity 非正常銷毀時會觸發 onRetainNonConfigurationInstance() 來保存一些資料,上面 NonConfigurationInstances 類中 viewModelStore 實體就是這樣保存的
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
//取出自定義資料
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
//創建NonConfigurationInstances物件保存 自定義資料和 viewModelStore 實體物件
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
我們看到 onRetainNonConfigurationInstance() 已被標記為 final,官方不建議我們自己再復寫此方法,而 onRetainCustomNonConfigurationInstance() 和與之對應的 getLastCustomNonConfigurationInstance()也都被標記為廢棄,可以看出官方還沒有提供成熟方案,
@Deprecated
@Nullable
public Object onRetainCustomNonConfigurationInstance() {
return null;
}
@Deprecated
@Nullable
public Object getLastCustomNonConfigurationInstance() {
NonConfigurationInstances nc = (NonConfigurationInstances)
getLastNonConfigurationInstance();
return nc != null ? nc.custom : null;
}
ViewModelStore.clear()
重新回到 ViewModelStore類的 clear()這里,ViewModelStore類代碼比較簡單,我們著重看一下 clear(),其實就是遍歷 HashMap,并呼叫ViewModel中的clear()
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModel中clear()方法如下:
@MainThread
final void clear() {
//設定標志位
mCleared = true;
//清除快取的tag map集合
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
//供子類使用清除子類資料
onCleared();
}
我們看到 clear()一共干了三件事:
- 設定清除標志位
- 清除快取在
HashMap中的tag資料 - 呼叫
onCleared()子類可以重寫此方法完成清除資料
解決InputMethodManager中 mNextServedView 持有Activity導致記憶體泄漏
在Android 4.4~6.0之間一直存在一個比較常見的系統bug,那就是 InputMethodManager類中 mNextServedView在activity銷毀后也會一直持有Activity參考從而導致記憶體泄漏,使用LeakCanary很容易檢測出來

常見的解決方式是通過反射得到 InputMethodManager 中 mNextServedView 在 Activity銷毀后置為null,把參考鏈給斷開 比如可以參考這篇文章傳統解決方式、下面我們看一下 ComponentActivity 是怎么解決這個問題的
前面我們已經在構造器中看到如下代碼:
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
利用 Lifecyle添加一個觀察者物件,創建了一個 ImmLeaksCleaner并將當前 Activity物件傳入
@RequiresApi(19)
final class ImmLeaksCleaner implements LifecycleEventObserver {
//變數初始化狀態列舉值
private static final int NOT_INITIALIAZED = 0;
private static final int INIT_SUCCESS = 1;
private static final int INIT_FAILED = 2;
//初始化狀態
private static int sReflectedFieldsInitialized = NOT_INITIALIAZED;
//反射對應的欄位值
private static Field sHField;
private static Field sServedViewField;
private static Field sNextServedViewField;
private Activity mActivity;
ImmLeaksCleaner(Activity activity) {
mActivity = activity;
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
//activity生命周期走到 onDestory() 時才會往下執行
if (event != Lifecycle.Event.ON_DESTROY) {
return;
}
//發現沒有初始化進行初始化反射出指定欄位
if (sReflectedFieldsInitialized == NOT_INITIALIAZED) {
initializeReflectiveFields();
}
//反射成功
if (sReflectedFieldsInitialized == INIT_SUCCESS) {
//獲取當前InputMethodManager物件
InputMethodManager inputMethodManager = (InputMethodManager)
mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
//拿到當前lock鎖物件
final Object lock;
try {
lock = sHField.get(inputMethodManager);
} catch (IllegalAccessException e) {
return;
}
if (lock == null) {
return;
}
//進入同步鎖
synchronized (lock) {
final View servedView;
try {
servedView = (View) sServedViewField.get(inputMethodManager);
} catch (IllegalAccessException e) {
return;
} catch (ClassCastException e) {
return;
}
if (servedView == null) {
return;
}
if (servedView.isAttachedToWindow()) {
return;
}
//將mNextServedView物件設定為null
try {
sNextServedViewField.set(inputMethodManager, null);
} catch (IllegalAccessException e) {
return;
}
}
inputMethodManager.isActive();
}
}
@MainThread
private static void initializeReflectiveFields() {
try {
//設定標識位標識開始反射
sReflectedFieldsInitialized = INIT_FAILED;
sServedViewField = InputMethodManager.class.getDeclaredField("mServedView");
sServedViewField.setAccessible(true);
sNextServedViewField = InputMethodManager.class.getDeclaredField("mNextServedView");
sNextServedViewField.setAccessible(true);
//對應是Handler實作類
sHField = InputMethodManager.class.getDeclaredField("mH");
sHField.setAccessible(true);
//反射成功重置標識位
sReflectedFieldsInitialized = INIT_SUCCESS;
} catch (NoSuchFieldException e) {
// very oem much custom ˉ\_(ツ)_/ˉ
}
}
}
以上就是 ImmLeaksCleaner類解決方式,代碼比較簡單有詳細的注釋,就不再贅述了
對Lifecycle的支持
我們上面看到 ComponentActivity實作了 LifecycleOwner介面,內部創建了 LifecycleRegistry 物件并將當前Activity實體傳入
//創建 LifecycleRegistry 物件
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
而 getLifecycle()回傳的值即 mLifecycleRegistry
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
getLifecycle()實作很簡單,就是將new出來的mLifecycleRegistry回傳,我們從getLifecycle() 的注釋上我們提取到兩點資訊:
- 官方不推薦重寫
getLifecycle()而且會在未來高版本中會將此方法標記為final - 如果你想重寫
getLifecycle()就需要遵循以下兩條- 必須回傳一個
LifecycleRegistry物件 - 對
LifecycleRegistry物件進行懶初始化
注意:在LifecycleRegistry物件初始化完成之前,這個物件將會在父類的構造器中呼叫
- 必須回傳一個
對fragment回傳鍵調度支持
什么是對fragment回傳鍵的調度支持? 其本質就是讓fragment 能像Activity一樣在按下回傳鍵時能夠回呼onBackPressed(), 所以BackPressedDispatcher調度器本質也是將onBackPressed()回呼到fragment里面實作而已
下面我們先看一個在fragment里面具體如何使用回傳呼度?
使用BackPressedDispatcher
1.創建Activity
第一步創建一個測驗Activity內部布局和相關代碼如下:
class BackMainActivity : BaseEasyActivity() {
override fun getLayoutId(): Int {
return R.layout.activity_jetpack_back_main
}
override fun initView() {
supportFragmentManager.beginTransaction()
.replace(R.id.backContent, BackListFragment())
.commitNowAllowingStateLoss()
}
override fun onBackPressed() {
super.onBackPressed()
Logger.d("onBackPressed")
}
}
activity_jetpack_back_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/backContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
BackListFragment:
class BackListFragment : BaseEasyListFragment() {
override fun initView() {
super.initView()
requireActivity().onBackPressedDispatcher
.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Logger.i("back 1")
back()
}
})
requireActivity().onBackPressedDispatcher.addCallback(this,object :OnBackPressedCallback(true){
override fun handleOnBackPressed() {
Logger.i("back 2")
back2()
}
})
}
private fun back2() {
activity?.let {
AlertDialog.Builder(it).setTitle("EasyTitle 2")
.setMessage("你確定退出嗎?")
.setNegativeButton(
"取消"
) { dialog, _ ->
dialog?.dismiss()
}
.setPositiveButton(
"確定"
) { dialog, _ ->
dialog?.dismiss()
requireActivity().finish()
}
.create()
.show()
}
}
private fun back() {
activity?.let {
AlertDialog.Builder(it).setTitle("EasyTitle 1")
.setMessage("你確定退出嗎?")
.setNegativeButton(
"取消"
) { dialog, _ ->
dialog?.dismiss()
}
.setPositiveButton(
"確定"
) { dialog, _ ->
dialog?.dismiss()
requireActivity().finish()
}
.create()
.show()
}
}
}
在fragment里面我們呼叫requireActivity().onBackPressedDispatcher.addCallback()添加了兩個回呼,并在handleOnBackPressed()回呼中我們彈出一個確認彈框
addCallback() 這個方法有兩個引數含義分別是:
@NonNull LifecycleOwner owner: 當前的lifecycle實作物件Actiivty和Fragment頂級類都實作了LifecycleOwner介面,所以第一個引數一般傳入this就可以了@NonNull OnBackPressedCallback onBackPressedCallback:OnBackPressedCallback接識訓傳鍵回呼抽象類,子類需要繼承此類,其中構造方法中的boolean enabled引數必須傳入true如果 傳入false此回呼不會執行,默認值為false
以上就是我們Demo全部代碼了,當我們運行程式,點擊回傳鍵 我們看到 back2()里的彈框顯示出來了,點擊確定按鈕將會呼叫finish()關閉當前頁面
你可能會疑問我們注冊了兩個回呼,但是back()彈框并沒有顯示,是怎么回事呢?那就只能看一下原始碼才能知道答案
回傳呼度原始碼決議
回傳鍵調度代碼的源頭還是在 ComponentActivity中,讓我們重新將注意力轉移到此類中,前文中我們看到 ComponentActivity實作的介面有一個 OnBackPressedDispatcherOwner :
public interface OnBackPressedDispatcherOwner extends LifecycleOwner {
/**
* Retrieve the {@link OnBackPressedDispatcher} that should handle the system back button.
*
* @return The {@link OnBackPressedDispatcher}.
*/
@NonNull
OnBackPressedDispatcher getOnBackPressedDispatcher();
}
可以看到 OnBackPressedDispatcherOwner繼承與 LifecycleOwner 那么他自然也擁有lifecycle相關的功能 getOnBackPressedDispatcher() 是回傳一個回傳鍵路由類,這個類會將系統回傳鍵觸發路由到指定回呼中
下面我們看一下 ComponentActivity類回傳鍵路由相關代碼和實作邏輯
private final OnBackPressedDispatcher mOnBackPressedDispatcher =
new OnBackPressedDispatcher(new Runnable() {
@Override
public void run() {
ComponentActivity.super.onBackPressed();
}
});
@Override
@MainThread
public void onBackPressed() {
mOnBackPressedDispatcher.onBackPressed();
}
@NonNull
@Override
public final OnBackPressedDispatcher getOnBackPressedDispatcher() {
return mOnBackPressedDispatcher;
}
- 在
ComponentActivity創建出回傳鍵路由類,并傳入了一個默認任務,run()中并將此次點擊回傳鍵任務交由父類來實作,這個默認邏輯只有在不存在任何自定義回呼的情況下執行 - 在
onBackPressed()中就是將任務交由OnBackPressedDispatcher來執行 getOnBackPressedDispatcher()只是將當前創建出來的實體進行回傳,不過這個方法被標記為final了
OnBackPressedCallback 回呼
下面看一下 OnBackPressedCallback的具體實作
public abstract class OnBackPressedCallback {
private boolean mEnabled;
//存盤Cancellable介面集合
private CopyOnWriteArrayList<Cancellable> mCancellables = new CopyOnWriteArrayList<>();
public OnBackPressedCallback(boolean enabled) {
mEnabled = enabled;
}
@MainThread
public final void setEnabled(boolean enabled) {
mEnabled = enabled;
}
@MainThread
public final boolean isEnabled() {
return mEnabled;
}
//呼叫所有 Cancellable的cancel()函式
@MainThread
public final void remove() {
for (Cancellable cancellable: mCancellables) {
cancellable.cancel();
}
}
//子類需要實作的回傳鍵邏輯
@MainThread
public abstract void handleOnBackPressed();
//添加和移除 Cancellable 的方法,主要是組件庫代碼內部呼叫(包訪問權限)
void addCancellable(@NonNull Cancellable cancellable) {
mCancellables.add(cancellable);
}
void removeCancellable(@NonNull Cancellable cancellable) {
mCancellables.remove(cancellable);
}
}
上述OnBackPressedCallback 代碼邏輯比較簡單,有比較詳細的注釋就不再贅述了
Cancellable
Cancellable 是一個組件庫代碼內部(包訪問權限),取消介面定義如下:
interface Cancellable {
/**
* Cancel the subscription. This call should be idempotent, making it safe to
* call multiple times.
*/
void cancel();
}
OnBackPressedDispatcher 回傳呼度路由
下面我們看一下 OnBackPressedDispatcher 類具體實作
public final class OnBackPressedDispatcher {
//默認回傳任務
@Nullable
private final Runnable mFallbackOnBackPressed;
//回傳鍵任務佇列
@SuppressWarnings("WeakerAccess") /* synthetic access */
final ArrayDeque<OnBackPressedCallback> mOnBackPressedCallbacks = new ArrayDeque<>();
//默認、有參構造器
public OnBackPressedDispatcher() {
this(null);
}
public OnBackPressedDispatcher(@Nullable Runnable fallbackOnBackPressed) {
mFallbackOnBackPressed = fallbackOnBackPressed;
}
//添加回傳鍵任務
@MainThread
public void addCallback(@NonNull OnBackPressedCallback onBackPressedCallback) {
//呼叫addCancellableCallback()下面方法,將回傳任務包裝成可需要性質的任務,
//子類可以呼叫 OnBackPressedCallback中remove() 將此任務移除掉
addCancellableCallback(onBackPressedCallback);
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
@MainThread
@NonNull
Cancellable addCancellableCallback(@NonNull OnBackPressedCallback onBackPressedCallback) {
//添加到全域集合中
mOnBackPressedCallbacks.add(onBackPressedCallback);
//將普通任務包裝成可需要性質的任務
OnBackPressedCancellable cancellable = new OnBackPressedCancellable(onBackPressedCallback);
onBackPressedCallback.addCancellable(cancellable);
return cancellable;
}
//添加任務,并指定了lifecycle物件
@SuppressLint("LambdaLast")
@MainThread
public void addCallback(@NonNull LifecycleOwner owner,
@NonNull OnBackPressedCallback onBackPressedCallback) {
Lifecycle lifecycle = owner.getLifecycle();
//不能在 DESTROYED 狀態時注冊
if (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) {
return;
}
//添加一個LifecycleOnBackPressedCancellable 具有生命周期觀察能力,可需要性質的任務
onBackPressedCallback.addCancellable(
new LifecycleOnBackPressedCancellable(lifecycle, onBackPressedCallback));
}
//判斷是否存在打開的回呼任務
@MainThread
public boolean hasEnabledCallbacks() {
Iterator<OnBackPressedCallback> iterator =
mOnBackPressedCallbacks.descendingIterator();
while (iterator.hasNext()) {
if (iterator.next().isEnabled()) {
return true;
}
}
return false;
}
//ComponentActivity 類中onBackPressed()會代理到這個方法里執行
@MainThread
public void onBackPressed() {
//倒序遍歷
Iterator<OnBackPressedCallback> iterator =
mOnBackPressedCallbacks.descendingIterator();
while (iterator.hasNext()) {
OnBackPressedCallback callback = iterator.next();
//如果OnBackPressedCallback中mEnabled值為 true才會執行
//且只會執行任務佇列中第一個任務,所以一個fragment如果添加多個任務,只會執行最后添加的任務
if (callback.isEnabled()) {
callback.handleOnBackPressed();
return;
}
}
//上述任務佇列中沒有找到可執行的自定義任務,則會將此次事件交給ComponentActivity來執行
if (mFallbackOnBackPressed != null) {
mFallbackOnBackPressed.run();
}
}
//對普通回傳任務進行包裝成可取消性質的
private class OnBackPressedCancellable implements Cancellable {
private final OnBackPressedCallback mOnBackPressedCallback;
OnBackPressedCancellable(OnBackPressedCallback onBackPressedCallback) {
mOnBackPressedCallback = onBackPressedCallback;
}
@Override
public void cancel() {
//從佇列中移除和移除自身回呼
mOnBackPressedCallbacks.remove(mOnBackPressedCallback);
mOnBackPressedCallback.removeCancellable(this);
}
}
//對指定Lifecycle實作類進行包裝,內部自動處理生命周期相關狀態
private class LifecycleOnBackPressedCancellable implements LifecycleEventObserver,
Cancellable {
private final Lifecycle mLifecycle;
private final OnBackPressedCallback mOnBackPressedCallback;
@Nullable
private Cancellable mCurrentCancellable;
LifecycleOnBackPressedCancellable(@NonNull Lifecycle lifecycle,
@NonNull OnBackPressedCallback onBackPressedCallback) {
mLifecycle = lifecycle;
mOnBackPressedCallback = onBackPressedCallback;
//添加lifecycle監聽
lifecycle.addObserver(this);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_START) {
//在fragment啟動的時候將任務添加進去,并將任務包裝成可需要的任務
mCurrentCancellable = addCancellableCallback(mOnBackPressedCallback);
} else if (event == Lifecycle.Event.ON_STOP) {
//在fragment stop的時候取消任務
if (mCurrentCancellable != null) {
mCurrentCancellable.cancel();
}
} else if (event == Lifecycle.Event.ON_DESTROY) {
//fragment銷毀時將任務取消
cancel();
}
}
@Override
public void cancel() {
//移除lifecycle回呼
mLifecycle.removeObserver(this);
//移除回呼
mOnBackPressedCallback.removeCancellable(this);
if (mCurrentCancellable != null) {
mCurrentCancellable.cancel();
mCurrentCancellable = null;
}
}
}
}
以上就是OnBackPressedDispatcher回傳呼度路由類的全部代碼,有詳細的注釋應該看明白,
OnBackPressedDispatcher 是實作回傳呼度人主要類,內部處理了添加任務,移除任務,將任務路由到指定的 fragment中,這里在添加任務時 推薦使用addCallback(@NonNull LifecycleOwner owner, @NonNull OnBackPressedCallback onBackPressedCallback) 來添加任務,這樣就能和 lifecycle關聯起來,內部已經處理了和fragment生命周期相關的邏輯了,
activity-ktx擴展庫功能
activity-ktx擴展庫是 Google 使用kotlin語言開發的輔助庫,同時支持了kotlin協程,對于使用jetpack庫很有幫助
activity-ktx擴展庫主要包含兩個kotlin檔案:
- OnBackPressedDispatcherKt
- ActivityViewModelLazyKt
OnBackPressedDispatcherKt
是專門對 OnBackPressedDispatcher類的一個擴展和包裝來看看具體怎么做的
//繼承OnBackPressedCallback并對 OnBackPressedDispatcher的addCallback()進行擴展
fun OnBackPressedDispatcher.addCallback(
owner: LifecycleOwner? = null,
enabled: Boolean = true,
onBackPressed: OnBackPressedCallback.() -> Unit
): OnBackPressedCallback {
//內部實作類
val callback = object : OnBackPressedCallback(enabled) {
override fun handleOnBackPressed() {
//執行傳過來的函式式方法
onBackPressed()
}
}
//對LifecycleOwner不同情況呼叫不同API
if (owner != null) {
addCallback(owner, callback)
} else {
addCallback(callback)
}
return callback
}
從上述原始碼中我們看到這個方法功能還是比較多的,再具體使用時就比較方便了
使用:
//添加回傳回呼
requireActivity().onBackPressedDispatcher.addCallback(owner = this,enabled = true,{
//...
})
當然根據kotlin具名函式的特點,也可以省略前兩個引數:
requireActivity().onBackPressedDispatcher.addCallback(onBackPressed = {
//...
})
所以在使用上比之前的方式要簡單很多
ActivityViewModelLazyKt
這個擴展類是為了幫助我們方便的使用ViewModel類,想想一下我們是如何創建ViewModel的
這里先創建出來一個自定義ViewModel,看一下有多少種方式創建實體
class BackViewModel(application: Application) : AndroidViewModel(application) {}
ViewModelProviders 方式
viewModel = ViewModelProviders.of(this).get(BackViewModel::class.java)
使用ViewModelProviders呼叫of()并呼叫get()就能創建實體,很方便的,但是很不幸在后來的版本中Google 先是將 ViewModelProviders標記為過時,再后來就直接洗掉了
所以Google推薦直接使用ViewModelProvider來創建實體,其實 ViewModelProviders的of()和get()也是對 ViewModelProvider的簡單封裝
ViewModelProvider 方式
val viewModel = ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory.getInstance(application)
).get(BackViewModel::class.java)
看著是有點麻煩哈…不過代碼邏輯還是很好懂的
viewModels() 方式
下面看看利用 ActivityViewModelLazyKt 擴展組件創建ViewModel實體
只需要呼叫 viewModels() 函式就可以了
val backViewModel = viewModels<BackViewModel> {
ViewModelProvider.AndroidViewModelFactory.getInstance(application)
}
當然上面方式是有點麻煩,還需要傳入一個 lambda 運算式感徑訓不好理解,不過使用默認的 ViewModelFactory 就比較簡單了
val viewModel: BackViewModel by viewModels()
或者這樣寫
val viewModel by viewModels<BackViewModel>()
不過意思是一樣的相對以上方式就簡單很多了,幾乎感受不到 ViewModelProvider 和 AndroidViewModelFactory 等類的存在
接下來看一下viewModels()是如何實作的
viewModels()簡要原始碼分析
//實作了Lazy介面具備懶加載欄位的功能
//ComponentActivity類的擴展函式
@MainThread
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
//①
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
//②
return ViewModelLazy(VM::class, {
viewModelStore
}, factoryPromise)
}
上述代碼雖然簡短但是功能比較多,我們具體看一下
- 根據引數是否為null來選擇是使用自定義 ViewModelProviderFactory 還是 默認的 ViewModelProviderFactory ,
defaultViewModelProviderFactory變數對應是mDefaultFactory(SavedStateViewModelFactory型別) - 根據引數構建一個
ViewModelLazy物件回傳
注意: 原始碼中有如下注釋
This property can be accessed only after the Activity is attached to the Application,
and access prior to that will result in IllegalArgumentException.
這里的意思是,如果使用擴展函式初始化的屬性只能在Actiivty添加了Application后才能訪問,在此之前的訪問將會拋出IllegalArgumentException例外
我們知道在啟動Activity 會呼叫 Activity的attach()將Application背景關系物件賦予Activity上,所以我們應該保證變數不能在 Activity 的onCreate()之前呼叫就可以了
總結
本文主要以 jetpack-activity 組件為切入點分析了該組件的主要功能,并根據原始碼了解了內部實作原理,
簡單來說 jetpack-activity 組件有如下功能:
- 解決一些Android碎片化適配問題
- 對
Lifecycle系列組件提供了支持 - 提供了回傳鍵路由,對
Fragment處理回傳鍵提供了支持
同時 ktx 擴展組件也是對jetpack-activity 組件庫的一個補充,在其他的組件庫中 ktx 更是比較重要,
參考
https://developer.android.com/jetpack
https://developer.android.com/jetpack/androidx/releases/activity
https://www.jianshu.com/p/f2aa4cf53abd
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/213307.html
標籤:其他
