Jetpact-activity组件完全解析
Jetpact-activity组件完全解析
前言
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://blog.csdn.net/ForwardSailing/article/details/109639992