Android官方架构组件之LiveData + ViewModel + Room 源码分析

Android官方架构组件之LiveData + ViewModel + Room 源码分析,第1张

简单使用案例:

MainActivity:

MessageViewModel:

其中viewModelgetMessageObserver()observe(this, new Observer() )

中的this即SupportActivity :LifecycleOwner

SupportActivity implements LifecycleOwner

SupportActivity

SupportActivity就是通过getLifecycle()获取 mLifecycleRegistry来标记当前Activity或Fragment的各种状态,其中ReportFragmentinjectIfNeededIn(this)内部源码也是与mLifecycleRegistrymarkState(LifecycleStateCREATED) 类似,状态的信息记录在mLifecycleRegistry对象内部。Activity的其他类型的事件如onCreate,onPause等都是通过getLifecycle()获取 mLifecycleRegistry对象调用mLifecycleRegistry内部方法来改变其状态的。

Fragment的状态更加容易看到,FragmentActivity即在Activity的生命周期中获取

FragmentActivity部分源码:

通过上面的简单分析,两个重要的类即 LifecycleRegistry extends Lifecycle:

下面是Lifecycle抽象类:

回到开始的案例:

LiveDataobserve(this, new Observer);

这里我们传入的Observer和 ownergetLifecycle()addObserver()即 Activity中的Lifecycle 是不同的。

我们上面已经知道Activity中的Lifecycle是与生命周期相关的,通过LifecycleaddObserver()可以监听到 Activity的生命周期 然后在LifecycleBoundObserver作出

相应的处理,具体的实现在LifecycleRegistryaddObserver中(Lifecycle实现类),最终会根据事件变化调用 mLifecycleObserveronStateChanged(owner, event),

LifecycleBoundObserveronStateChanged -> activeStateChanged -> dispatchingValue -> considerNotify(initiator) -> observerobserveronChanged((T) mData);

最终调用的是我们传入的observer。

这样看来 LiveData<T> 就没有什么特殊的了,把它看做一个普通的观察者模式的管理者即可,比如EventBus。

>

本文将对Activity的工作过程进行分析。

主要学习以下内容:

(1)系统内部是如何启动一个Activity的

(2)新Activity的对象是何时创建的

(3)Activity的各个生命周日是被系统何时回调的

Activity启动流程分两种,一种是启动正在运行的app的Activity,即启动子Activity。如无特殊声明默认和启动该activity的activity处于同一进程。如果有声明在一个新的进程中,则处于两个进程。另一种是打开新的app,即为Launcher启动新的Activity。后边启动Activity的流程是一样的,区别是前边判断进程是否存在的那部分。

Activity的启动流程整体如下:

一Activity启动阶段

(一)涉及到的概念

进程:Android系统为每个APP分配至少一个进程

IPC:跨进程通信,Android中采用Binder机制。

(二)涉及到的类

ActivityStack:Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程。

ActivitySupervisor:管理 activity 任务栈

ActivityThread:ActivityThread 运行在UI线程(主线程),App的真正入口。

ApplicationThread:用来实现AMS和ActivityThread之间的交互。

ApplicationThreadProxy:ApplicationThread 在服务端的代理。AMS就是通过该代理与ActivityThread进行通信的。

IActivityManager:继承与IInterface接口,抽象出跨进程通信需要实现的功能

AMN:运行在server端(SystemServer进程)。实现了Binder类,具体功能由子类AMS实现。

AMS:AMN的子类,负责管理四大组件和进程,包括生命周期和状态切换。AMS因为要和ui交互,所以极其复杂,涉及window。

AMP:AMS的client端代理(app进程)。了解Binder知识可以比较容易理解server端的stub和client端的proxy。AMP和AMS通过Binder通信。

Instrumentation:仪表盘,负责调用Activity和Application生命周期。测试用到这个类比较多。

(三)涉及到的进程

(1)Launcher所在的进程

(2)AMS所在的SystemServer进程

(3)要启动的Activity所在的app进程

如果是启动根Activity,就涉及上述三个进程。

如果是启动子Activity,那么就只涉及AMS进程和app所在进程。

(四)具体流程

Launcher:Launcher通知AMS要启动activity。

startActivitySafely->startActivity->InstrumentationexecStartActivity()(AMPstartActivity)->AMSstartActivity

AMS:PMS的resoveIntent验证要启动activity是否匹配。如果匹配,通过ApplicationThread发消息给Launcher所在的主线程,暂停当前Activity(即Launcher)。

暂停完,在该activity还不可见时,通知AMS,根据要启动的Activity配置ActivityStack。然后判断要启动的Activity进程是否存在

存在:发送消息LAUNCH_ACTIVITY给需要启动的Activity主线程,执行handleLaunchActivity

不存在:通过socket向zygote请求创建进程。进程启动后,ActivityThreadattach

判断Application是否存在,若不存在,通过LoadApkmakeApplication创建一个。在主线程中通过threadattach方法来关联ApplicationThread。

在通过ActivityStackSupervisor来获取当前需要显示的ActivityStack。

继续通过ApplicationThread来发送消息给主线程的Handler来启动Activity (handleLaunchActivity)。

handleLauchActivity:调用了performLauchActivity,里边Instrumentation生成了新的activity对象,继续调用activity生命周期。

IPC过程:

双方都是通过对方的代理对象来进行通信。

1app和AMS通信:app通过本进程的AMP和AMS进行Binder通信

2AMS和新app通信:通过ApplicationThreadProxy来通信,并不直接和ActivityThread通信

(五)参考函数流程

Activity启动流程(从Launcher开始):

第一阶段: Launcher通知AMS要启动新的Activity(在Launcher所在的进程执行)

第二阶段:AMS先校验一下Activity的正确性,如果正确的话,会暂存一下Activity的信息。然后,AMS会通知Launcher程序pause Activity(在AMS所在进程执行)

第三阶段:pause Launcher的Activity,并通知AMS已经paused(在Launcher所在进程执行)

第四阶段:检查activity所在进程是否存在,如果存在,就直接通知这个进程,在该进程中启动Activity;不存在的话,会调用Processstart创建一个新进程(执行在AMS进程)

第五阶段: 创建ActivityThread实例,执行一些初始化 *** 作,并绑定Application。如果Application不存在,会调用LoadedApkmakeApplication创建一个新的Application对象。之后进入Loop循环。(执行在新创建的app进程)

第六阶段:处理新的应用进程发出的创建进程完成的通信请求,并通知新应用程序进程启动目标Activity组件(执行在AMS进程)

第七阶段: 加载MainActivity类,调用onCreate声明周期方法(执行在新启动的app进程)

从另一个角度下图来概括:

下面简要介绍一下启动的过程:

        Step 1 无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerServicestartActivity接口; 

        Step 2 ActivityManagerService调用ActivityStackstartActivityMayWait来做准备要启动的Activity的相关信息;

        Step 3 ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerServicestartActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;

        Step 4 ApplicationThread不执行真正的启动 *** 作,它通过调用ActivityManagerServiceactivityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;

        Step 5 对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;

        Step 6 ActivityManagerServic调用ApplicationThreadscheduleLaunchActivity接口,通知相应的进程执行启动Activity的 *** 作;

        Step 7 ApplicationThread把这个启动Activity的 *** 作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。

最近学习安卓开发activity的生命周期,对单个活动的生命周期较为熟悉。此时师兄抛出问题,当一个activity进入 onStop() 时,如何得知时由于用户点击了主页键,还是由于进入了另一个本程序的activity?

查阅网络资源后,得知可以通过安卓本身的多种方法来判断。不过某些方法的使用需要获取用户授权,或者需要较高版本的安卓系统。不过本文主要介绍通过activity本身的生命周期回调函数来判断。

由于设计两个activity的跳转,所以我们应该去考虑多个activity的生命周期回调函数顺序。查阅并测试可知,当进行activity跳转时,先执行原来activity的 onPause() 和,然后执行第二个activity的 onCreate() 、 onStart() 、 onResume() ,再执行第一个activity的 onStop 。如图所示,红色是第一个activity的回调函数执行,蓝色是第二个。

我们可以利用这个特点,通过一个static变量来记录当前run的activity数量,当这个数量为0的时候,即程序被压入后台,当这个数量不为0,则表明当前执行了一个跳转 *** 作。

首先新建一个 BaseActivity 类,声明一个static变量,让别的activity全部继承。并在 onStart() 和 onStop() 中修改 count 。

创建 MainActivity 类,继承自 BaseActivity 类,同时修改 onStop() ,检查 count 的值,判断此次导致 onStop() 的原因。我在这个activity中添加了一个button用于跳转到第二个activity。

这个类很简单,没有什么功能,只是跳转过来后,执行基类的 onCreate() 方法。

至此,我们便实现了通过activity生命周期回调函数判断是否被压入后台。不过,后来学长针对我的这种方式提出了漏洞——当引用外部SDK,且外部SDK中可以d出未继承我们自定义的base基类的activity,那么怎么处理这种情况呢?这里就要用到安卓SDK提供的全局生命周期类了——ApplicationregisterActivityLifecycleCallbaks,这里我们之后再谈。

想要实现这种不同的分支,我们就要使用一种变量来存储我们是否是第一次进入应用,当然这种变量不可能是存储在应用里,而是要存储在应用包名底下的文件中

那么我们就来看看实现这种变量存储和修改的步骤吧

1、在应用的欢迎页面Splash Activity里定义一个变量isFirstIn(前一篇文章中讲了Splash),初始值无所谓,应为我们马上要从文件中读取这个值

2、定义SharedPreferences对象,并通过该对象获取文件中isFirstIn键(key)对应的值(value),这里默认为true,因为第一次进入应用时我们并没有创建该文件和该变量

3、在Splash中需要跳转的地方做一个if判断,如果是isFirstIn是true说明我们是第一次进入,那么跳转到GuideActivity1,如果是false说明我们不是第一次进入,那么跳转到主页面MainActivity

4、假如GuideActivity1是最后一个使用向导页,我们要在GuideActivity1结束之后默认跳转到MainActivity里,那么我们需要在跳转之前改变一下isFirstIn值为false,并存到文件里,这样下次进入应用时Splash可以从文件里获取值为false的isFirstIn值,这样就可以通过分支直接跳转到MainActivity了

下面关键部分的代码

SplashJava

boolean isFirstIn = false;

//onCreate中

SharedPreferences preferences = getSharedPreferences(“first_pref”,

MODE_PRIVATE);

isFirstIn = preferencesgetBoolean(“isFirstIn”, true);

new Handler()postDelayed(new Runnable() {

@Override

public void run() {

if (isFirstIn) {

// start guideactivity1

intent = new Intent(Splashthis, GuideActivity1class);

} else {

// start TVDirectActivity

intent = new Intent(Splashthis, TVDirectActivityclass);

}

SplashthisstartActivity(intent);

Splashthisfinish();

}

}, SPLASH_DISPLAY_LENGHT);

GuideActivity1java 在引导页面结束前一定要修改SharedPreferences里边的值,这样第二次进入的话才会跳转到主页面,不进引导页。

SharedPreferences preferences = getSharedPreferences(

“first_pref”, MODE_PRIVATE);

Editor editor = preferencesedit();

editorputBoolean(“isFirstIn”, false);

editorcommit();

最后可以在应用包名底下shared_prefs文件夹下找到我们在应用里定义的first_prefxml文件,打开可以看到里面

这种使用SharedPreferences存储变量的方法不仅可以使用与欢迎页面与使用向导,可以随意在程序任何位置使用,用来存储应用退出之后也要记录的某些关键变量或值

以上就是关于Android官方架构组件之LiveData + ViewModel + Room 源码分析全部的内容,包括:Android官方架构组件之LiveData + ViewModel + Room 源码分析、Android四大组件之Activity(2)组件间通信、全面解析Activity: Activity的工作过程等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9282524.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-26
下一篇 2023-04-26

发表评论

登录后才能评论

评论列表(0条)

保存