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
是一個支援組合高階元件的基礎類別ActivityComponentActivity
在構造器中針對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));
}
}
從構造器中程式碼來看做的事情比較多,但都是相容性處理,有三個方面的處理:
在 Activity
處於stop 時使用者是看不到介面的,也沒有必要再處理 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
中資料,需要依賴另一個元件-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()
是 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
橫豎屏切換,還能儲存資料不丟失的原因。
再回到 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()
,其實就是遍歷 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()
子類可以重寫此方法完成清除資料在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
類解決方式,程式碼比較簡單有詳細的註釋,就不再贅述了
我們上面看到 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
能像Activity
一樣在按下返回鍵時能夠回撥onBackPressed()
。 所以BackPressedDispatcher
排程器本質也是將onBackPressed()
回撥到fragment
裡面實現而已
下面我們先看一個在fragment
裡面具體如何使用返回撥度?
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
的具體實現
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
是一個元件庫程式碼內部(包存取許可權),取消介面定義如下:
interface Cancellable {
/**
* Cancel the subscription. This call should be idempotent, making it safe to
* call multiple times.
*/
void cancel();
}
下面我們看一下 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擴充套件庫是 Google 使用kotlin語言開發的輔助庫,同時支援了kotlin協程,對於使用jetpack庫很有幫助
activity-ktx擴充套件庫主要包含兩個kotlin檔案:
是專門對 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 = {
//...
})
所以在使用上比之前的方式要簡單很多
這個擴充套件類是為了幫助我們方便的使用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)
}
上述程式碼雖然簡短但是功能比較多,我們具體看一下
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 元件有如下功能:
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