Android JetPack 原理浅析

前言

我就不在文章里面讲这一套框架的用法了,直接讲我在学习过程中的一些理解吧。讲得比较简单,比较喜欢深挖源码的伙伴可以去那些大佬的博客看看~

本篇文章涉及:Data Binding、Lifecycle、LiveData、ViewModel、Navigation

Data Binding

  • 自己写xml布局的时候,内容布局之外还嵌套了一层<layout>标签布局,但是编译之后,这一层标签会被去掉,里面使用到data binding的view都会被打上一个tag,xml布局上至下,标签顺序生成。

    Button例子

  • 原生ViewDataBinding类中有一个mapBinding()方法,会传入View viewObject[] bindings两个参数,前者就代表一个view,而后者则是代表用来装view的一个Object有序容器。当view是一个ViewGroup时,则会执行 viewGroup.getChildAt(i)遍历操作,遍历出的子view将通过标签辨别,有序放入Object容器内。

  • 编译之后,编译器会为我们的每一个<layout>标签生成两个专属类(是否还有其它的我也没去找),一个抽象类——XXXBinding,另一个实现类——XXXBindingImpl。

  • 在抽象类中,包含有使用到data binding 的view成员对象,还有我们的绑定数据的成员对象,例图:

    成员对象

    它们在构造器中被赋值,当然这是在它的实现类(子类)中实现的,例图:
    赋值操作

    这里解释了为什么之前说的Object[] bindings是一个有序数组。

  • 实现绑定数据是在实现类中实现的,我们直接看一个生成方法setUser()setUser(@Nullable com.jetpackstudy.MainUserData User)。通过一系列的委托、执行,中间经过了mUIThreadHandler.post(mRebindRunnable)这一句将绑定数据操作丢到主线程去执行(咦?好像是可以异步调用了)。

  • 去到实现的地方,发现给View进行更新的东西是一个Adapter,例如图:


    更新UI
  • 对一个setText()进行查看:

    更新文字

    这里有一个注意的点,就是一般XXXAdapter进行数据更新都会跟旧值进行比较,发生改变才更新,毕竟绘制视图可比代码执行慢多了。这也许能够减少我们在建一个很大的XXData类的时候的愧疚感。。。

Lifecycle

  • 简而言之,它的功能就是一个观察者可以订阅另外一个具有生命周期的被观察者。

  • 它在v4.Fragment和SupportActivity里面都有实现,下面我就只用SupportActivity来举例子了,包括LiveData那里。

  • Lifecycle——抽象类,代表生命周期,可以增添观察者(LifecycleObserver)。LifecycleOwner——接口,生命周期拥有者,它只有getLifecycle()一个抽象方法。LifecycleObserver——接口,作为观察者被Lifecycle添加,当生命周期发生改变时,它能够接收到消息。GenericLifecycleObserver——接口,继承自LifecycleObserver,只有一个onStateChange()抽象方法,后面的LifecycleObserver在Lifecycle中的对象都是它。

  • Activity的lifecycle架构是在SupportActivity实现的,SupportActivity作为生命周期的拥有者,自身实现了LifecycleOwner接口,然后自身维护着一个Lifecycle对象——LifecycleRegistry,用来管理观察者。

  • 需要注意的是LifecycleRegistry作为生命周期的对象,它是有着“生命周期拥有者(Activity)”的引用,但是LifecycleRegistry自身是通过弱引用的方式持有引用,这样可以防止内存泄漏,因为在有的地方我们可能能够getLifecycle()方法将LifecycleRegistry引用拿走了,在强引用的情况下可能导致Activity无法释放,内存泄漏。

  • SupportActivity和LifecycleRegistry的生命周期关联是通过一个ReportFragment的碎片进行关联的,通过ReportFragmentd的injectIfNeededIn(Activity activity)方法,使用Activity的FragmentManager的manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();方法使这个碎片的生命周期和当前Activity关联,当碎片生命周期发生改变时,则通过dispatch(Lifecycle.Event event)将状态发送出去。

  • 生命周期改变通知观察者自然是Lifecycle做的事情,也就是LifecycleRegistry。但是它将生命周期做了点改变,onCreate=onStop, onStart=onPause如图:


    生命周期的改变
  • 在分发事件的过程中,我们需要注意以下代码:


    向后和向前发送

    通过枚举值的比较,当前的生命周期若在第一个生命周期之后,则向后发送状态,若在最后一个生命周期之前,则向前发送状态。

  • SafeIterableMap ,这是刚才使用到的Map,它通过链表存储对象(内部实现Entry类,存储键值对),它的迭代器使用了WeakHashMap,好处是在值为空的时候,我们不需要手动移除它。这个Map的好处是,结构简单,一般观察者的数量比较少,所以在保证性能的情况下,占用空间是小于HashMap的。它还提供了加、减法迭代器。

  • 到我们的分发事件了,我们只看向后发送状态(backwardPass(LifecycleOwner owner)),这里直接上代码:

    image.png

    使用递减迭代器,如果存在跨越生命周期的情况,这里的while循环也保证了生命周期的完整性,防止出现状态跨越式变换。这里的dispatchEvent(lifecycleOwner, event)很简单,就是调用到了mLifecycleObserver.onStateChanged(owner, event);这一句代码实现了生命周期状态改变事件的分发。

  • 我们知道自己实现Observer使用的其实是注解,我们在一个方法上使用的注解,例如:

    观察者

    当活动的生命周期发生改变时,对应的方法就会被调用。那么我们还需要知道它的实现大体是怎样的,在onStateChange()方法被调用之后又实现了什么呢?

  • 前面讲漏了一点,GenericLifecycleObserver只是一个接口,它在Map中存在的实际上是FullLifecycleObserverAdapter对象,onStateChange()方法的实现实际上是这样的:

    GenericLifecycleObserver的实现类

  • 可是显然这里是直接调用的观察者的方法,跟我们自己的代码不符合,那这里的mObserver实际上是将我们自己的LifecycleObserver又给封装了一层,看我们LifecycleRegistry的addObserver()方法,能够发现我们的LifecycleObserver果然被一个叫做ObserverWithState的类给又包装了下。

  • 就不再废话了,因为中间又是绕过去绕过来的,直接 看下面一段代码:

    boolean hasLifecycleMethods(Class klass) {
        if (mHasLifecycleMethods.containsKey(klass)) {
            return mHasLifecycleMethods.get(klass);
        }

        Method[] methods = getDeclaredMethods(klass);
        for (Method method : methods) {
            OnLifecycleEvent annotation = method
                      .getAnnotation(OnLifecycleEvent.class);
            if (annotation != null) {
                createInfo(klass, methods);
                return true;
            }
        }
        mHasLifecycleMethods.put(klass, false);
        return false;
    }

每当我们添加一个观察者的时候都会调用到该方法呢,通过反射将观察者中被OnLifecycleEvent注解的方法找到,通过键值对的方式存在一个叫做mCallbackMap的HashMap中。

  • 那么在每一次生命周期发生改变时,最终会到该Map中去找被注解了的方法,符合条件则发起该方法。

LiveData

  • LiveData是一个抽象类,一般使用的是它的子类MutableLiveData,该类没有添加、改动LiveData的任何代码。MediatorLiveData是维护了一个SafeIterableMap容器的LiveData集合类。

  • LiveData.observe(LifecycleOwner, Observer<T>)方法: 添加Observer的思想在于将该Observer储存在HashMap中,在数据改变的时候,从Map中找到该观察者,发起onStateChange()调用。但是我们需要注意的点在于:在该方法中利用LifecycleBoundObserver类将Observer对象包装成了成了一个LifecycleBoundObserver对象,并将该对象和Lifecycle关联上了。

  • LifecycleBoundObserver:在该类中,有一个shouldBeActive()方法,当LifecycleOwner处于onStart()方法之后、onPause()之前才视为该Obsever是可活动的(mActive)。

  • observe()方法中利用LifecycleBoundObserver实现在每一次设置值的时候都访问一遍LifecycleOwner的生命周期,如果生命周期不符合条件,则改变mActive标志位,并且不会再回调Observer.onChange()方法。这里有一个小提示,LiveData提供了onActive()onInactive()两个空方法作为在ObservermActive状态改变时的回调。

  • 那如果我们不想跟活动的生命周期有关联呢?那我们就使用observeForever()方法,该方法的shouldBeActive()永远返回true,所以每次更新数据时,都能够触发onChange()方法。

  • postValue()setValue()的区别,setValue()只能在主线程调用,直接触发的onChange()方法。postValue()实现的还是setValue(),但是它会使用handler.post()方法将setValue()方法推送到主线程执行。

  • 补充:LifecycleBoundObserver作为一个GenericLifecycleObserver被添加进入在LifecycleOwer()中,因此它自身在onStateChange()方法中,会遍历调用removeObserver(mObserver);将观察者全部移除,因为观察者一般做的事情都是更新UI,是会含有环境引用,如果不进行观察者移除,将可能导致联用ViewModel时,ViewModel周期过长,引起内存泄漏。

ViewModel

  • ViewModel是一个抽象类,只有一个空方法onCleared(),这里的ViewModel抽象类就是一种规范的体现。AndroidViewModel类使用的环境是Application,需要在构造方法处传入Applicatio对象。ViewModelProvider内部有一个工厂模式,通过NewInstanceFactory是一个可以实例ViewModel的工厂,生产好的ViewModel被放在了ViewModelStore里面。

  • 上面实现的ViewModel没有任何特殊之处,因此我们还需要手动导入一个扩展包:
    implementation 'android.arch.lifecycle:extensions:1.1.1' 来引入谷歌对ViewModel的扩展。

  • 我们可以利用ViewModelProviders.of()方法来生成获取ViewModelProvider实例,获取ViewModelProvider实例需要传入ViewModelStore和一个ViewModelFactory,ViewModelFactory就是一个简单的工厂,里面使用反射进行ViewModel的生成,所以它被单例了起来。ViewModelStore比较特殊一点,接下来就要走一套流程了,务必跟紧。

  • 通过ViewModelStores.of(activity)静态方法生成一个ViewModelStore,回忆一下,ViewModelStore是存放ViewModel的地方。在of()方法里面,如果activity自身实现了ViewModelOwner这个接口的话,那么就直接return activity;(开发者也就能通过Activity,使用ViewModelStore直接管理ViewModel)。但一般情况下我们是没有自己去实现的,因此会执行代码return holderFragmentFor(activity).getViewModelStore();,简述一下,这里会生成一个Fragment,这个Fragment实现了ViewModelOwner接口,Fragment里面就有一个ViewModelStore。

  • 这个Fragment叫做HolderFragment,我们需要注意的是最终生成HolderFragment的地方是一个static final HolderFragmentManager sHolderFragmentManager的地方,它里面维护了:

        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
        private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();

这样两个容器,可能有的小伙伴开始紧张了,又有static又有activity强引用,这结果是啥?别急,我们接着往下看,简而言之,利用activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks)这一行代码可以向Application注册一个监听器,每当移除一个Activity的时候,就去mNotCommittedActivityHolders里面找对应的activity移除,以删除对Activity的强引用。Fragment也是同理,只不过Fragment使用得是parentFragment.getFragmentManager()来注册监听。

  • 这一步确实很巧妙,我们拿到了引用,但是也确保了不会内存泄漏。但是我们后面还会看到一个地方就是在HolderFragment的onCreate()方法中,也会对上面的两个容器进行remove()操作,也就是说我们Map中的key和value很有可能不会在Activity或者Fragment移除时才进行删除。

  • 但其实一个Activity或者一个Fragment的一个ViewModel一定是唯一的,这是怎么保证的呢?还有activity.getSupportFragmentManager()parentFragment.getChildFragmentManager()两句代码,不管当前活动处于哪个生命周期,它的FragmentManager一定是唯一的,在第一次使用FragmentManager生成HolderFragment的时候,我们给它打上一个TAG,所以每次我们都会检查TAG对应的Fragment的是否存在,以此保证HolderFragment在一个Activity或者一个Fragment中的唯一性。

  • 但是我们拿到唯一HolderFragment的作用是什么呢?原因有2,第一是保证ViewModel的唯一性,同一个Activity或者同一个Fragment从ViewModelStore里面多次拿到的ViewModel都是唯一的,而不是多个新的ViewModel,HolderFragment是唯一的,那么HolderFragment中的ViewModelStore也就是唯一的,这里就证明了为什么ViewModel可以用于两个Fragment共享一个Activity的数据。第二是可以实现数据保存功能,ViewModel在Activity屏幕方向改变的时候也能够实现保存数据的功能(屏幕方向改变其实是会重新触发Activity的onDestroy和onCreate()方法的,但是只要activity或者fragment这个对象还存在,它获取的FragmentManager一定是唯一的,ViewModel的数据也就不变化。

  • 分析完了持有Activity和Fragment引用的作用之后,我们再回到HolderFragmet上面,为什么这里又要生成一个碎片?很简单,类似于Lifecycle,目的也只是简单的想获取Activity或者Fragment的生命周期而已,那为什么这里不使用原生的Lifecycle组件,而自己再去生成一个碎片呢?原因可能在于原生的Lifecycle使用注解+反射的实现的,不如自己写来得直接、扩展性强,又或者说是为了兼容低版本(hhh,只是猜测~)。

  • 以Activity举例,HolderFragment获取了Activity的生命周期之后,在onCreate()方法中,会执行mNotCommittedActivityHolders.remove(holderFragment.getActivity());,提前去除对Activity和Fragment的引用。在onDeatroy()方法中,调用了mViewModelStore.clear();方法,也就是遍历调用vm.onCleared();,按照规范,我们应该在onCleared()方法中清除对Activity和Fragment的引用,因为在外部逻辑的影响下,导致ViewModel的生命周期可能会长于外界环境的生命周期。提醒一下,LiveData会主动释放引用,不用我们再操作LiveData释放。

  • 我们回到最开始的地方,我们通过HolderFragment拿到了ViewModelStore对象,然后和Factory组合即可生成ViewModelProvider,通过get()方法即可生成一个ViewModel对象。

  • 最后讨论下AndroidViewModel的作用,,,看了半天,确实能力有限,没能看出特殊之处,欢迎评论区讨论自己的想法。

Navigation

  • Navigation实现上包括了一个AS插件,应该是在AS3.2以上支持的。先回到上面的DataBinding中,它的设计思想是将部分Java层代码抽象迁移到xml布局中去(实质上还是Java代码完成的,只是对使用者隐藏了实现细节),这样就可以减小Java代码量,减少Java层的逻辑复杂性,在保证性能的前提下提升App开发的效率。那么Navigation的设计思想当然也包含了这很重要的一点,我们可以直接通过xml代码申明Fragment之间的跳转情况,使用时仅仅通过一行代码即实现Fragment的跳转,以及实现一些附加功能。Navigation我就分析了一点点,能力有限。推荐直接看FragmentManager那一套源码。

  • Navgation最终仍然是利用的Android自带的FragmentManager进行碎片管理,可以简单的理解成对FragmentManager体系进行了一层封装,通过创建不同的Navigator(分为Activity和Fragment)来实现不同的跳转,然后不同的Navigator由xml解析出来的NavGraphNavigator持有并管理,NavGraphNavigator当然应该被NavController持有,官方则提供了Navigation来供我们找到NavController,实际上NavController是在NavHostFragment中就创建好了的。

  • 正因为Navgation没有对碎片做多余的管理,因此Fragment的创建、销毁都是由自带的FragmentManager体系管理,虽然我们使用它还是需要自己做数据保存,但是这样也一定程度的降低了开发者的学习成本,毕竟Fragment还是原来的Fragment。

  • 我们先从xml布局中的NavHostFragment碎片开始分析,因为它接收的是我们的nav_xxxml导航文件,那么在活动创建的时候,NavHostFragment就会初始化,解析xml文件,展示第一个Fragment。

  • 说下Fragment生命周期的问题: 在活动setContentView时,Fragment会依次调用onInflate->onAttach->onCreate->onCreateView->onViewCreated,活动onCreated()之后会调用碎片的onActivityCreated(),在活动onStart()时,Fragment会调用onStart()

  • NavHostFragment在onCreate()方法就创建了该导航文件的NavController对象,并给它设置xml导航文件的id,设置Id的同时就会触发导航xml文件的解析,并且会触发导航文件中第一个Fragment(默认为HomeFragment吧)的初始化,并被添加到栈里面去。因为生命周期的调用会出现如下情况: 在活动的onCreate方法执行结束之后,HomeFragment的生命周期才从onAttach()开始,直到onActivityCreated()结束,才开始活动的onStart()原因在于NavHostFragment在执行onCreated()时,触发初始化操作,使用了FragmentManager来添加碎片,但是commit()中是将该事件提交到消息队列中,那么新碎片的创建自然会在活动的onCreate()结束后才开始,显然onStart()事件插入消息队列的时机是在创建新碎片之后。

  • 通过NavHostFragment,我们应该知道,在Fragment的onCreateView()中我们是拿不到NavController对象的,在onCreateView()结束之后视图才被添加进入根视图,所以我们可以在onViewCreated()方法之后进行NavController对象的获取和保存。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355