欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

Jetpact-activity组件完全解析

程序员文章站 2022-06-19 09:57:49
Jetpact-activity组件完全解析文章目录Jetpact-activity组件完全解析前言源码解析构造器Activity停止时取消View还未执行的事件View层处理移除点击事件回调移除长按事件Activity销毁时清除ViewMode中数据isChangingConfigurations() 相关getViewModelStore()ViewModelStore.clear()解决InputMethodManager中 mNextServedView 持有Activity导致内存泄漏对Life...

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"

继承结构:

Jetpact-activity组件完全解析

源码解析

/**
 * 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 类的注释上可以得出两条信息:

  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层处理

上述代码会调用ViewcancelPendingInputEvents() 这是个取消事件的总调度方法,它没有具体做事情,而是调用了 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() 中直接调用 removeCallbacksmPerformClick点击事件传入

 @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层经过一系列调用,最终会调用 ActivityThreadhandleRelaunchActivity() 将 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

NonConfigurationInstancesComponentActivity 中的静态内部类 定义如下

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();
    }
}

ViewModelclear()方法如下:

@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类中 mNextServedViewactivity销毁后也会一直持有Activity引用从而导致内存泄漏,使用LeakCanary很容易检测出来

Jetpact-activity组件完全解析

常见的解决方式是通过反射得到 InputMethodManagermNextServedViewActivity销毁后置为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实现对象 ActiivtyFragment*类都实现了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来创建实例,其实 ViewModelProvidersof()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>()

不过意思是一样的相对以上方式就简单很多了,几乎感受不到 ViewModelProviderAndroidViewModelFactory 等类的存在

接下来看一下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 会调用 Activityattach()Application上下文对象赋予Activity上,所以我们应该保证变量不能在 ActivityonCreate()之前调用就可以了

总结

本文主要以 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