相关文章推荐
灰常酷的楼房  ·  十年后,万青依旧是无数人心中的“国摇之光”| ...·  6 月前    · 
豪气的炒饭  ·  航空GDS的发展和演变_手机凤凰网·  9 月前    · 
跑龙套的牛肉面  ·  【斗罗】海神为何选择蓝银草作为自己的传承武魂 ...·  9 月前    · 
调皮的杯子  ·  r语言excel操作 - CSDN文库·  10 月前    · 
聪明的金针菇  ·  印格用官 - 知乎·  1 年前    · 
Code  ›  Jetpact-activity组件完全解析_activity-ktx artifacts_Bumblebee1999的博客
https://blog.csdn.net/ForwardSailing/article/details/109639992
聪明的领带
2 年前
  • 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. * 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 类的注释上可以得出两条信息:

          1. ComponentActivity 是一个支持组合高级组件的基类Activity
          2. 并没有将所有的组件都构建到这个类中,只是包含最基础的底层组件。开发者可以根据需要使用更高级别的组件,无需在组件之间强耦合。

          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)才是真正移除事件处理的方法,凡是以下几种方式添加的事件或延迟事件都会移除

          1. post()
          2. postDelayed()
          3. postOnAnimation()
          4. 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()一共干了三件事:

          1. 设置清除标志位
          2. 清除缓存在HashMap中的tag数据
          3. 调用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() 就需要遵循以下两条
            1. 必须返回一个 LifecycleRegistry 对象
            2. 对 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() 这个方法有两个参数含义分别是:

          1. @NonNull LifecycleOwner owner : 当前的lifecycle实现对象 Actiivty和Fragment顶级类都实现了LifecycleOwner接口,所以第一个参数一般传入this就可以了
          2. @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)
          

          上述代码虽然简短但是功能比较多,我们具体看一下

          1. 根据参数是否为null来选择是使用自定义 ViewModelProviderFactory 还是 默认的 ViewModelProviderFactory , defaultViewModelProviderFactory 变量对应是 mDefaultFactory (SavedStateViewModelFactory类型)
          2. 根据参数构建一个 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

          在android中启动一个活动并获得结果是一个常见的模式,这种模式已经存在很长时间了。你开始一项活动,然后倾听结果,然后根据你得到的东西做一些事情。例如,您可以启动一个活动来选择一个文档,然后获得结果(在本例中是Uri)以你的应用程序需要的方式使用它。另一种方法是使用此模式请求对某些API级别的权限。按照上述过程的方式,所使用的api是允许您使用的startActivityForResult()和 onActivityResult()在你的UI中。 Android KTX 是包含在 Android Jetpack 及其他 Android 库中的一组 Kotlin 扩展程序。KTX 扩展程序可以为 Jetpack、Android 平台及其他 API 提供简洁的惯用 Kotlin 代码。简单来说 , ktx 就是:官方地址:https://developer.android.google.cn/kotlin/ktxmaven版本号:https://mvnrepository.com/artifact/androidx.core/core-ktx添加依赖: google 在 androidX 上对 activity 及 fragment 部分功能调整(startActivityForResult ,requestPermission,Save/Restore InstanceState,onBackPress)简化为 callback 的可回调方式 使用(暂时还会修改,建议 beta 版再更) Jetpack 之ViewModel源码分析 官方介绍:以注重生命周期的方式管理界面的相关数据. The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configurat... 这两天在准备写 fragment 返回栈的文章,但是发现必须先介绍一下 OnBackPressedDispatcher ,所以这是一篇介绍 what 的文章,喜欢一手资料的可以移步 官方文档 【背上Jetpack】Jetpack 主要组件的依赖及传递关系 【背上Jetpack】AdroidX下使用Activity和Fragment的变化 【背上Jetpack之Fragment】你真... OnBackPressedDispatcher 解决了什么问题: 在 Activity 里可以通过回调方法 onBackPressed() 处理,而 Fragment / View 却没有直接的回调方法。现在,我们可以使用 OnBackPressedDispatcher 替代 Activity#onBackP 在目前的比较常见的一种场景是,在 App 的首页如果点击 back 键会 toast 提示用户再点击一次是退出 App。 例如如下代码所示: # 首页Homectivity.java @Override public void onBackPressed() { if ((System.currentTimeMillis() - exitTime) > 2000) { ShowUtils.toast(R.string.exit_tost);//提示再按一次back
 
推荐文章
灰常酷的楼房  ·  十年后,万青依旧是无数人心中的“国摇之光”|乐队|摇滚|歌曲|万能青年旅店|杀死那个石家庄人_网易订阅
6 月前
豪气的炒饭  ·  航空GDS的发展和演变_手机凤凰网
9 月前
跑龙套的牛肉面  ·  【斗罗】海神为何选择蓝银草作为自己的传承武魂,却放弃了昊天锤_哔哩哔哩_bilibili
9 月前
调皮的杯子  ·  r语言excel操作 - CSDN文库
10 月前
聪明的金针菇  ·  印格用官 - 知乎
1 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号