进阶之路 | 奇妙的四大组件之旅

进阶之路 | 奇妙的四大组件之旅

前言

本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍:

我的GIthub博客

学习清单:

  • Activity的工作过程
  • Service的工作过程
  • Service的启动过程
  • Service的绑定过程
  • BroadcastReceiver的工作过程
  • BroadcastReceiver的注册过程
  • BroadcastReceiver的发送和接收过程
  • ContentProvider的工作过程

一.为什么要学习四大组件?

何为“四大”:

  • Activity
  • Service
  • BroadcastReceiver
  • ContentProvider

谈到四大组件,相信在座各位都再熟悉不过了,光闻其名,未见其声,“四大”二字一出,足见其在安卓系统中的地位,可谓是安卓界的F4

其地位之崇高,在某种程度上也可以体现他的重要性,所以说,光会使用四大组件还是不能体现我们对他的重视(ai hu)的,我们还要分析其工作过程,能够更好地理解系统内部的运行机制,从而加深对Android体系结构的认识;同时,四大组件还是面试必问的知识点之一。

综上,掌握好四大组件相关的知识,对于一个Android开发者来说是非常重要的!

以下内容紧张赤鸡,请系好保险带,我们要开车(hu you)了。— No picture,say a J8!

老司机开车

二.核心知识点归纳

2.1 概述

2.1.1 Activity

  • 类型:展示型组件

  • 作用:展示一个界面并和用户交互

  • 使用:

    A.需要在AndroidManifest中注册

    B.需要借助Intent启动,两种方式:

  • 显示Intent:

    Intent intent=new Intent(xxx.this,xxx.class); startActivity(intent);

  • 隐式Intent:

    Intent intent=new Intent(); intent.setAction(xxx); intent.addCategory(xxx); startActivity(intent);

  • 四种启动模式:
  • standard:标准模式
  • singleTop:栈顶复用模式
  • singleTask:栈内复用模式
  • singleInstance:单实例模式

想了解启动模式的读者,可以看下笔者写的一篇文章:进阶之路 | 奇妙的Activity之旅中的2.2部分

  • 通过finish()结束一个Activity

2.1.2 Service

  • 类型:计算型组件

  • 作用:在后台执行一系列计算任务,耗时的后台计算建议在单独的线程中执行

  • 使用:

    A.需要在AndroidManifest中注册

    B.需要借助Intent启动:Intent intent = new Intent(xxx.this, xxx.class); startService(intent);

    C.两种运行状态:

  • 启动状态:通过startService()
  • 绑定状态:通过bindService()

​ D.停止方式:unBindService();stopService();

2.1.3 BroadcastReceiver

  • 类型:消息型组件
  • 作用:在不同的组件乃至不同的应用之间传递消息
  • 使用:
  • 两种注册方式:

    A.动态注册:通过Context.registerReceiver()& Context.unRegisterReceiver(),必须要启动应用才能注册并接收广播。

    B.静态注册:在AndroidManifest文件中注册,不需要启动应用即可接收广播。

  • 需要借助Intent发送广播:Intent intent = new Intent("xxx"); sendBroadcast(intent);

  • 四种广播类型:

    A.普通广播

    B.有序广播

    C.本地广播

    D.粘性广播

  • 没有停止概念

2.1.4 ContentProvider

  • 类型:共享型组件
  • 作用:向其他组件乃至其他应用共享数据(安卓IPC的一种方式)
  • 使用:
  • 需要在AndroidManifest中注册

  • 无需借助Intent启动

  • 四种操作:注意需要处理好线程同步(因为这些操作运行在Binder线程)

    A.insert():添加数据

    B.update():更新数据

    C.delete():删除数据

    D.query():查询数据

  • 无需手动停止

想详细了解IPC机制的读者,可以看下笔者写的一篇文章:进阶之路 | 奇妙的 IPC 之旅

2.2 工作过程

差不多该进入今天的主题了,为了逼格,为了高薪,大伙往前冲!

2.2.1 Activity

Activity启动过程流程图:

一眼看上去有点晕晕的,墙裂建议配合源码一起服用,效果极佳,笔者推荐一篇文章:图解Activity启动流程,进阶高级

`Activity`启动过程

Q1:结论:

  • ActivityManagerServiceApplicationThread都是Binder
  • Application的创建也是通过Instrumentation来完成的,这个过程和Activity对象一样,都是通过类加载器来实现的
  • Activity的启动过程最终回到ApplicationThread中,通过ApplicationThread.scheduleLaunchActivity() 将启动Activity的消息发送并交由Handler H处理。
  • Handler H对消息的处理会调用handleLaunchActivity()->performLaunchActivity()得以最终完成Activity的创建和启动。

Q2:重点类

  • Instrumentation

instrumentationAndroid系统里面的一套控制方法或者”钩子“。 这些钩子可以在正常的生命周期(正常是由操作系统控制的)之外控制Android控件的运行;它们同时可以控制Android如何加载应用程序。

  • ActivityManagerService「AMS」

AMS是系统的引导服务,应用进程的启动、切换和调度、四大组件的启动和管理都需要AMS的支持。

  • ActivityStackSupervisor:
  • ActivityStackSupervisorAMS中的构造方法中被创建。

  • AMS 通过操作ActivityStackSupervisor来管理Activity

  • ActivityStack:
  • ActivityStack从名称来看是跟栈相关的类,其实它是一个管理类,用来管理系统所有Activity的各种状态
  • 它由ActivityStackSupervisor来进行管理的
  • ApplicationThread:
  • ActivityThread的私有内部类,也是一个Binder对象
  • 在此处它是作为IApplicationThread对象的Server端,等待Client端的请求然后进行处理,最大的Client就是AMS

2.2.2 Service

源码流程分析:Service的工作过程

1.启动过程:

Service启动过程

2.绑定过程:

Service绑定过程

结论:

  • ContextImplContext的具体实现,通过Activity.attach()Activity建立关联。Activity.attach()中还会完成Window的创建并和Activity&Window的关联,由此事件可传递给Window
  • ActivityServices是一个辅助ActivityManagerService(AMS)进行Service管理的类,包括Service的启动、绑定和停止。
  • Activity类似的,Service的启动/绑定过程最终回到ApplicationThread中,通过ActivityThread.handleCreateService()/ActivityThread.handleBindService完成Service的启动/绑定,注意绑定Service的后续还必须告知客户端已经成功连接Service的这一流程,由ActivityManagerService.publishService()去完成。

2.2.3 BroadcastReceiver

源码流程分析:BroadcastReceiver 的工作过程分析

1.注册

四大组件的静态注册都是在应用安装时由PackageManagerService(PMS)解析注册,当动态注册BroadcastReceiver时流程为:

BroadcastReceiver动态注册

2.发送和接收

发送和接收流程

结论:

  • 动态注册广播最终会跨进程交给AMS,并把远程Receiver( 实际上传的是IIntentReceiver,是个Binder 对象)和远程IntentFilter保存起来,完成注册任务
  • 发送广播时,系统为intent添加了两个标记位:
  • FLAG_EXCLUDE_STOPPED_PACKAGES :广播不会发送给已经停止的APP(系统为所有广播默认添加该标记)
  • FLAG_INCLUDE_STOPPED_PACKAGES :广播也会发送到已经停止的APP(两个标记共存时,以该标记为准
  • 最终在ReceiverDispatcher .performReceive ()里回调了ReceiveronReceive(),使得广播得以接收并处理

Q2:实现原理:

从实现原理看上,广播使用了观察者模式,基于消息的发布/订阅事件模型

具体实现流程要点粗略概括如下:

  • 广播接收者BroadcastReceiver通过Binder机制向AMS进行注册
  • 广播发送者通过Binder机制向AMS发送广播
  • AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中
  • 消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法

2.2.4 ContentProvider

1.启动流程总概

ContentProvider启动流程总概

  • 启动的入口为ActivityThread.main():创建ActivityThread实例并创建主线程消息队列
  • ActivityThread.attach():远程调用AMS.attachApplication()并提供ApplicationThread用于和AMS的通信
  • AMS.attachApplication():通过ActivityThread.bindApplication()方法和Handler H来调回ActivityThread.handleBindApplication()
  • ActivityThread.handleBindApplication():先创建Application、再加载ContentProvider、最后回调Application.onCreate()
2.Query过程流程

insert()delete()update()的实现原理和query()类似,限于篇幅,这里不展开,感兴趣的读者可以主动去探究

源码流程分析:ContentProvider的工作过程

3gYU1K.jpg

结论:

  • ContentProvidermultiprocess属性:ContentProvider是否是单例,一般用单例

  • 访问ContentProvider需要ContentResolver,其真正实现类是ApplicationContentResolver。当ContentProvider所在进程未启动时,第一次访问它会触发ContentProvider的创建以及进程启动

  • ContentProvider所在的进程启动时,会同时被启动并被发布到AMS

注意:ContentProvider.onCreate()Application.onCreate()执行

  • 同样的,最终通过ActivityThread.handleBindApplication()完成ContentProvider的创建。

三.课堂小测试

恭喜你!已经看完了前面的文章,相信你对四大组件已经有一定深度的了解,下面,进行一下课堂小测试,验证一下自己的学习成果吧!

Q1:为什么要使用ContentProvider?它和SQL在实现上有什么区别?

  • ContentProvider 屏蔽了数据存储的细节,内部实现透明化,用户只需关心URI即可(是否匹配)
  • ContentProvider能实现不同APP的数据共享,SQL只能是自己程序才能访问
  • ContentProvider还能增删本地的文件,XML等信息

Q2:Android引入四大组件的用意

这个问题在笔者刚开始学习Android的时候就一直困惑,直到看了一篇Google Android 团队:Dianne Hackborn发表在Google+上的一篇post译文

见解:Google Android Framework团队决定,不要让一个明确的Main方法作为APP的入口,因为需要让系统对APP怎样运行有更多的控制权,在该系统中,用户永远不需要考虑开启和停止一个APP,而把这些事交给系统去管理。所以他们设计了四大组件以作为APP功能的载体和入口

  • Activity

    一个APP与用户交互的入口

  • BroadcastReceiver
  • 一种让系统在正常的用户流(user flow)之外,传递事件给APP的机制。
  • 最重要的是,因为这是另一个被精心定义的APP的入口,即使APP当前并不在运行,系统也可以将Broadcasts传递给APP
  • Service

APP由于各种各样的原因需要在后台运行时,Service就是一个这样的入口

  • ContentProvider
  • 人们通常会将它当作对数据库的抽象,因为有许多的API和支持库就是这样使用ContentProvider
  • 但是从系统设计的角度,这并不是ContentProvider的初衷。对于系统来说,ContentProvider实际上是一个入口,用于获取一个APP内部的公开的被命名的数据项(data items),每个数据项都被一个URI scheme所标识。

如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力

本文参考链接:


 上一篇
进阶之路 | 奇妙的Handler之旅 进阶之路 | 奇妙的Handler之旅
在Android平台上,主要用到的通信机制有两种:Handler和Binder,前者用于进程内部的通信,后者主要用于跨进程通信。在多线程的应用场景中,Handler将工作线程中需更新UI的操作信息传递到UI主线程,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。
2020-03-06
下一篇 
进阶之路 | 奇妙的Window之旅 进阶之路 | 奇妙的Window之旅
Window是一个顶层窗口查看和行为的一个抽象基类,这个类的实例作为一个顶级View添加到Window Manager。它提供了一套标准的UI方法,比如添加背景,标题等等。
2020-02-26
  目录