1、Android中多进程通信方式有哪些
- Socket、Binder、共享内存。常用的时binder。当然还有管道,信号,信号量,消息队列。
进程隔离
- 操作系统有虚拟内存和物理内存。前者真实存在,后者是内存管理技术,通过虚拟映射手段让每个应用进程认为它有连续可用内存。在使用了虚拟存储器的情况下,通过MMU完成虚拟到物理的转换
- 虚拟内存有两块,用户控件和内存控件,两块隔离的。用户程序崩了,内核不影响。同样A app崩了 B app会没事。也避免相互操作。总的来说就是用户空间隔了,但内核空间没隔。
IPC通信
进程间用户空间要隔离 ,但内核空间被所有进程共享,所以绝大部分IPC机制就利用这个特点来来实现跨进程通信。
管道
UNIX古老的进程通信方式,在内核空间管理一个固定大小的缓冲区,一端用户空间进,一端用户空间出。 - 匿名,4k,半双工,只支持父子和兄弟进程间通信 还有个FIFO实名管道,支持双向同行,建立时给它个名字,任何进程都可以通过名字打开管道另外一端,同时多个的话就有点力不从心
信号
- 主要用于通知接收进程某个事件的发生,原理时中断模拟,进程收到信号和收到中断请求一样
- 信号时一种异步通信机制,不需要阻塞,进程可以通过sigaction注册接收信号,就执行响应函数
- 在Android中,如果程序出现ANR问题会发出:SINGALQUIT信号,aoo可以注册此信号监听ANR,爱奇艺xcrash、umeng、matrix都实现了该方式。
信号量
- 可以看成计数器,是个进程间或者同进程不同线程间的同步手段。
- 信号量有初始值,每当进程使用信号量,就-1。当计数器减少到0就没资源了,其它进程访问就必须等待,当该进程执行完工作,就会执行V操作来对信号量+1;
共享内存
就是多个进程共享一块物理内存区域。Android中提供了匿名共享内存,基于Linux的共享内存,都是在临时文件系统上创建虚拟文件,再映射到不同进程。能让多进程操作同一块内存区域,除了物理内存限制,没有其他大小限制。相比于Linux的共享内存,它加了互斥锁。
消息队列
- 消息队列是一个链表,放在内核中。有异步功能,信号量信息少,它大。管道只能承载无格式字节流,消息队列内存有格式的。
- 但是更耗费内存,消息队列传送数据上限一般是16kb
- Android中大量的场景都市消息队列来设计的,比如handler,就是消息队列
socket
socket原本是为网络通信设计的,后来UNIX给用了只用了进程拷贝到另外一个进程。Zygote就是通过 LocalSocket接收启动应用进程通知的
Binder
在Android中Binder更多用在system_server进程和上层App之间的IPC就用的binder。Intent、contentProvider、messager、Broadcast、AIDL都是Binder机制完成的跨进程通信。
总结
- 管道:创建一个page 4KB大小的内存,缓存区域大小比较有限
- 信号:不适合信息交换,更适合进程中断控制
- 信号量:常作为锁机制,防止某进程正在访问资源时,其他资源也访问。主要作为同步手段
- 共享内存:无需赋值,无缓冲区直接附加到进程虚拟地址空间,速度快,但是进程间的同步问题,操作系统没实现,必须利用同步工具解决。
- 消息队列,消息赋值两次,额外CPU消耗,不适合频繁或者信息量大的通信
- socket:作为更通用的接口,消息消息低;
- Binder
- 性能:只要拷贝1次。共享内存0次,socket管道,消息队列都要2次。
- 安全:Binder有可靠的身份表示,只有由IPC机制本身在内核中添加,而不是用户添加。
- 操作性:基于CS架构,操作简单,socket也是。共享内存操作复杂,易用性差。 所以吧,Binder最适合system_server进程与App上层IPC通信
2、描述一下Binder进制原理
- Binder时Android提供的IPC框架。基于CS架构,效率高、本质也是调用系统底层的共享内存。
进程隔离
不同进程无法是接通信,内核空间是被不同进程共享。那么用户到内核到用户不就可以了?所以现在进程到进程的问题就是,用户和内核之间通信的问题。
进程划分
32位虚拟内存空间4g。高1GB给内核,低3GB给到各个进程也就是用户空间。至于通信主要两个函数.copy_from_user和copy_to_user
Linux下的传统给IPC原理
发送方把要发送的内容放到内存缓存区中,通过系统调用进入内核态,然后内核程序在内核空间分配内存,开辟一块内核缓存区
,调用copyfromuser函数将数据从用户空间的内核缓冲区。接收方进程在接收时在自己用户空间开辟一块内存缓冲区,然后调用copytouser将数据从内核缓冲区拷贝到接收进程的内存缓冲区。
- 拷贝2次,性能低下
- 由于接收进程不知道需要多大的空间存放传递过来的数据,所以尽可能大或者先调API来接收消息头获取消息体大小,不是浪费空间就是浪费事件。
Binder跨进程通信原理
- 动态内核可加载模块&& 内存映射
- 跨进程通信需要内核空间支持。但是binder不是linux内核一部分。有个linux的动态内核可加载模块。运行时被链接到内核作为内核的一部分运行。这样,Android通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块来实现通信。
- 这个运行在内核空间负责各个用户进程通过binder实现通信的内核模块就叫Binder 驱动
内存映射
- Binder通过mmap来实现内存映射。将用户空间的一块内存区域映射到内核空间,关系建立之后,修改,内核和用户空间都一起变。减少了拷贝次数
Binder IPC实现原理
- Binder IPC基于内存映射mmap来实现的,但是mmap通常时在由物理介质的文件系统上的
- 用户空间不能直接和物理设备通信,磁盘读取到用户空间需要2次拷贝(磁盘-》内核空间-》用户空间),而mmap则只要一次。将内核空间和用户空间建立映射,磁盘拷贝到内核就行
一次Binder
- Binder驱动在内核空间创建一个数据接收缓冲区
- 建立内核缓冲区,内核缓冲区和数据接收缓冲区映射建立,数据缓冲区 和接收进程用户空间地址映射建立。
- 发送方通过copyFromuser将数据拷贝到内核缓冲区,因为映射,所以接收进程的用户空间。
3 为什么Anroid 要采用binder作为IPC机制?
- 性能
- 只要1次拷贝。共享内存0次,socket2次。
- 速度上仅次于共享内存,优于Socket 消息队列,管道,信号,信号量
- 可用性
- BInder基于C/S架构,可用性高
- 共享内存,需要同步机制,搞不好容易死锁
- socket,效率低,开销大
- 安全性
- Binder安全高,为每个App分配不同UID用来鉴别进程身份。
- 支持实名Binder,有支持匿名binder
- 传统IPC不安全
- 依赖上层协议,写UID/pid
- 访问接入点开放,任何程序都可以与其建立连接
- Binder安全高,为每个App分配不同UID用来鉴别进程身份。
4、 Binder线程池的工作过程(未完成)
- 每个进程都只有一个processState俩描述当前进程在binder通信时binder的状态。
- 有一个默认的binder主线程 Pool Thread(在processState.cpp中)和底层Binder Driver进行通信,这个线程主体就是一个IPCThreadState对象。
- Binder线程创建
- java层 Process.start() - 》
- 向Zygote进程发送出创建进程的socket消息-》
- Zygote收到消息后调用Zygote.forkAndSpecialize来fork新进程-》
- 新进程调用RuntimeInit.nativeZygoteInit方法,该方法经过jni映射,-》
- 最终会调用到app_main.cpp中的onZygoteInit
- onZygoteInit
- ProcessState::self()
- open打开/dev/binder驱动设备
- mmap映射内核地址空间,将binder驱动的fd赋值给ProcessState对象中的变量mDriverFD
- startThreadPool()是创建一个新的binder线程,不断进行talkWithDriver()。
- ProcessState::self()
- PS.startThreadPool
- 通过变量mThreadPoolStarted来保证每个应用进程只允许启动一个binder线程池
- 并且本次创建的是binder主线程,其它都是Binder驱动创建的子线程
- PS.spawnPooledThread
- startThreadPool 内部执行了spawnPooledThread
- 获取BInder线程名,makeBinderThreadName
- PoolThread.run
- makeBinderThreadName,获取线程名
- PoolThread.run-》IPC.joinThreadPool
- IPC.joinThreadPool
- isMain=true。代表是主线程。 - BC_ENTER_LOOPER发送给binderDriver之后,代表的是Binder主线程,不会退出的线程
- isMain=false。代表是非主线程。 - BC_REGISTER_LOOPER发送给binderDriver后,表示是由binder驱动创建的线程。
- processPendingDerefs(//清除队列的引用)
- getAndExecuteCommand(处理下一条指令)
- executeCommand
- talkWithDriver(//false代表bwr数据的read_buffer为空)
- IPC.joinThreadPool –> IPC.getAndExecuteCommand() ->IPC.talkWithDriver() ,但talkWithDriver收到事务之后, 便进入IPC.executeCommand(), 接下来,从executeCommand说起
- executeCommand
- //创建新的binder线程
- mProcess->spawnPooledThread(false);
- java层 Process.start() - 》
5、AIDL的全称是什么,如何工作,能处理哪些类型的数据?
- AIDL,Android interface definition lanuage.Android接口描述语言。编译可以封装aidl文件生成一段代码,生成的代码封装了binder,可以 堪称是binder的延伸。
- AIDL的使用是指就是对Binder机制的封装,主要 将Binder封装成一个代理对象proxy,从客户角度看,就像是客户端直接调用了服务端代码
怎么使用
- 服务端:创建aidl接口-》创建一个类继承aidl接口名下的Stub,并实现方法,假设名为MyBInder->创建一个Service,在服务的onBInder中返回MyBinder对象。
- 客户端:创建aidl接口,和服务端一样,包名也要一样。绑定服务-》通过service的action来服务端的服务-》并实现ServiceConnection绑定监听-》将ServiceConnection返回的IBinder。通过AIDL文件同名.Stub.asInterface(IBinder),返回得到代理对象。代理对象调用方法。就会得到服务端实现的内容了。
客户端和服务段绑定成功之后,就可以通过AIDL的接口代理对象,像调本地方法一言给,调用服务端的方法了。要注意的是AIDL间传递的对象要实现Parcelable接口
- AIDL绑定过程
- 客户端调用bindService,发起绑定服务的请求,通过ServiceManager,拿到ActivityManagerService(AMS),通过AMS像服务端发起bindService。
- 然后服务端接收到绑定请求,以Handler消息机制的方式,发送一个绑定服务的Message,然后在ActivityThread中处理这个绑定请求,调用onBind函数,并返回了对应的IBinder对象。这个返回IBinder对象的操作,基本就和绑定过程的通过ServiceManager\AMS类似了。
AIDL支持的数据类型
- Java基本数据类型
- 引用类型比如String和CharSequence
- 实现Parcelabale接口的数据类型
- List Map。内包含的只能是上述几种。
6、Android 中pid&uid的区别和联系
- 一个pid代表一个进程,一个uid代表一个应用程序。所以存在一个UID存在多个PID。但一个PID只能有一个uid
- 完全暴露:android:exported= true。 activity service contentProvider申明true时。标识类允许外界的数据访问。如果加了intentFilter属性,则默认true。当然也能强制false。
- 权限提示暴露。 访问 activity service contentProvider时要有某个权限
- 私有暴露
- 需要sharedUserId+同一套签名,才能在统一沙箱中相互访问。
- 不同的pid之间用私有暴露和权限暴露。不同的uid之间用完全暴露的方法。
- 如果一个应用是系统应用。则不需要其他应用暴露,便可直接访问应用数据,
7、Handler怎么进行线程通信?原理是什么?
怎么做:
- handler对象和looper绑定。调用handler.post(runnable)或者handler.dispatchMessage(msg)
- 当然前后得做looper.prepare和looper.loop。不过主线程是默认给你做了,就不用
原理:
- 其实就是在发送端线程创建一个handler对象与接收端线程的looper绑定
- 因为绑定了,所以handler发送的msg回到接收端的messagequeue
- 接收端looper转动去从messagequeue中取出消息,
- 取出的消息传递给handler的dispatcherMessage方法
--
dispatchMessage()
方法进一步处理消息,可能是通过Runnable对象或handleMessage。
8、ThreadLocal的原理/以及在Looper是如何应用的?
- ThreadLocal,提供了线程局部变量的实现。每个线程都有自己独立的数据副本,而不需要共享数据。
ThreadLocal的原理
- 举例说明吧,Thread就是每个人,threadlocalmap就是每个人衣服上的口袋。threadlocal是口袋的标签,比如左上口袋,右下口袋。那么每个人只要通过东西在坐上口袋里,然后去翻对应口袋就能找到。
在Looper是如何应用的
LOOPEr和线程是一对一,那么在looper.prepare的时候,回去通过threadllocal< looper >去找该线程的ThreadLocalmap中是否有该looper。没有就新建,如果有了,说明重复创建,我就报错。
9\Handler如果没有消息处理是阻塞的还是非阻塞的?
没有消息MessageQueue会执行nativepollonce阻塞,直到有消息来就通知。这个阻塞是异步阻塞,主要用到了epoll(IO复用模型)
10、handler.post(new Runnable)是如何执行的?
runnable被封装成为一个message,然后添加到messagequeue里
- runnable首先被放到sendMessageDelayed(getPosterMessage(r),0)
- getPosterMessage中Message.obtain. m.callback = r
- sendMessageDelayed(msg,0)
- 消息被执行的时候,先判断有没有callback.如果有就执行msg.callback.run
11、handler的callbck存在,但是返回true,消息会不会执行?
如果msg有callback会执行,如果不是则不会执行
12、Handler的sendMessage和postDelay的区别?
后者使用到了sendMessageDelay函数,有多了个延时操作而已,没啥多大区别
13、Looper.looper会不会阻塞主线程?
- 主线程Looper.looper当然有可能阻塞主线程啦,但是不用担心
- ActivityThread是App的入口,ActivityThread.attach就是运行在主线程looper.prepare和looper.looper当中。
- 那么既然是looper,那么没有消息,那么messagequeue.next调用nativepollonce当然会阻塞。不过不用担心,我们写得代码就是handler驱动起来的,每16ms都有vsync。不停会有消息过来。这些UI绘制信号都封装成为一个个message。我们通过looper来调用handleMessage来回调我们的各Activity、fragment。我们的代码就在循环里执行。主线程一切皆是Message
- 如果主线程中操作时间过长,可能引起UI线程刷新卡顿。
死循环问题
死循环问题不会导致CPU空转,因为Messagqueue里有一个nativepollonce方法,没有消息就阻塞,它利用了epoll机制,epoll_wait。没消息就休眠。不会空转。
14、Looper无限循环的阻塞为什么没有ANR
什么是ANR
- 应用无响应,Android对于一些事件要在一定事件内完成,不然就会ANR。
- 服务20s。前台广播,contentProvider 10s。输入触摸事件分发5s。
- 类似于拆炸弹,做这些事情在system_server进程中埋下倒计时,如果没有定时拆则会引发爆炸。对于输入事件是超过5s,必须要新事件来,才爆炸。
Looper无限循环如何导致阻塞的
- Looper的无限循环是Looper不停去取MessageQueue中的messge,生命周期切换,长按点击啥的,都依赖这
- 当messagequeue为空,messagequeue.next中会触发nativePollOnce阻塞,原理是epoll。主线程休眠释放CPU,有消息来时,通过向管道写数据来唤醒主线程。
Looper无限循环为什么没有ANR?
- ANR是一个事件操作超过规定事件,而Looper无限循环就是为了去取消息操作事件,其阻塞时因为无消息让CPU停下来做其它事,避免空转。这两者不是一个维度。。
15、Looper如何在子线程中创建?
- 先Looper.prepare
- new handler(Looper.myLooper())
- 最后Looper.loop
16、Looper、handler、线程间的关系。例如一个线程可以有几个Looper可以对应几个Handler?
- 一个线程对应一个Looper。在prepare的时候有在threadlocalmap里检查的。
- 但是可以对应N个handler
17、子线程发送消息到主线程更新UI,除了handler和Asynctask,还有什么?
- 子线程中可以用runOnUiThread,本质上还是Handler的post方法
- view.post()方法更新,本质上是getRunQueue().post。内部是先sendMessageDealyed方法后sendMessageAtTime方法,最后是queue.enqueueMessage,最终还是用到了handler
18、idleHandler是什么?怎么使用?能解决什么问题?
- idleHandler是Messaagequeue内自定义的一个接口,一般用于性能优化。就是当消息队列中没有需要立即执行的message时,会主动触发idlehandler的queueIdle方法。放回值为 false,只会执行一次。返回值为true,每次消息队列内没有需要立即执行的消息时,都会触发该方法。
怎么使用
class MyIdleHandler implements IdleHandler {
@Override
public boolean queueIdle() {
// 在这里执行空闲状态时的操作
// 例如关闭数据库连接、清理资源等
return false; // 返回false表示处理完成,true表示还有更多任务要处理
}
}
Looper.myQueue().addIdleHandler(new MyIdleHandler());
能解决什么问题?
Idlehandler是在主线程空闲的时候被执行,那我们可以将相对耗时的或者一些 影响线程运行的事务放到IdleHandler里面处理。比如我们需要调用GC,一般会带来STW问题,于是可以将这个动作放到IdleHandler里面执行,而android源码确实也是这样进行的。
19、Android 系统启动流程(重要)
- 按下电源键,引导芯片代码从预定的地方(固化在rom)开始执行,加载bootloaader加载到RAM,然后执行。Bootloader是一小段程序,主要功能是将内核映像从闪存加载到内存。
- 生成第一个进程idle(pid=0),会初始化进程管理,内存管理和一些驱动
- idle会生成两个进程。
-
pid=1的init进程,用户空间的鼻祖
- 创建和挂在 文件系统
- 初始化和启动系统服务
- 给进程创建信号处理函数防止僵尸进程出现
- 通过(init.rc配置文件) fork java进程的鼻祖 zygote
- zygote
- 启动Java虚拟机
- 注册jni方法和预加载类和资源。
- 使用jni方法调用zygoteinit的main方法
- zygote通过 zygoteinit fork 一个systemserver,两者用socket通信,传递一些初始化啊和配置信息,systemserver创建AMS,两用binder通信,AMS通过socket和zygote 通信。
- systemserver通过binder告诉AMS,然后AMS通过socket告诉zygote去创建app进程
- zygote
-
pid=2的kthreadd的内核空间鼻祖
- 它会创建内核工作线程、软中断等内核守护进程。
-
20、Zygote进程的启动流程(重要)
- 收集开机后会执行init文件,启动init.rc脚本,在脚本里启动zygote
- 来到app_main.cpp
- 初始化AndroidRuntime
- 设置zygote为启动模式,strcmp(arg,"--zygote")
- 给start函数的参数args赋值
- runtime.start启动Zygoteinit
- zygoteinit
- 创建虚拟机
- 注册jni
- 启动zygote的main函数
- 预加载
- 创建socket
- 创建systemServer
- 启动systemserver的main方法
- 无限循环
21、Android中进程的优先级
从低到高 空进程-后台进程-服务进程-可见进程-前台进程。空侯福贱钱
22、SystemServer进程的启动流程(重要)
SystemServer进程是由zygote启动的,管理app运行的所需的AMS WMS PMS等等。
- SystemServer被创建后
- 启动Binder线程池,这样就可以与其它进程进行Binder跨进程通信。
- SystemServer在启动过程总给,先初始化一些系统变量,加载类库,创建Context对象。
- 创建SystemServiceManager,对系统服务进行创建、启动和生命周期管理。
- 启动各种系统服务,AMS PMS WMS等
- SystemServer启动前,尝试与Zygote建立socket通信,成功才启动服务
- 启动的服务单独运行在systemserver的各线程中,同属于Systemserver进程。
23、AMS启动流程
AMS是ActivityManagerService。Android 10之前,主要对四大组件管理和调度,也对进程、电池、内存、权限进行管理。Android 10及之后,就把Activity的管理下方给了ATMS,activity task manager service。他们不是独立进程,都在systemServer进程中运行。
AMS&ATMS的启动
- 在systemServer的main函数会有个run方法,startBootstrapServices,启动AMS和ATMS。他们只是一个服务,并非一个进程。
- atms&ams启动后将自己的本地服务公布到Localservices列表,进程内别的服务要使用他们就可以去localservice中找。
- atms&ams将自己的binder公布给ServiceManager进程,由这个进程存储,以后方便其它进程获取AMS和ATMS的binder代理。
24、SystemServer进程为什么要在Zygote中fork启动,而不是在init进程中直接启动
- fork函数能创建两个几乎一样的进程,pid不一样,子线程没了。主进程会返回一个子进程pid(打印子和自己pid),子线程返回0(打印自己pid)
- Zygote作为一个孵化器,可以提前加载一些资源,这个fork时基于Copy-On-Write机制创建的其它进程就能直接使用这些资源,不用重新加载,比如system_server就可以直接使用Zygote中的JNI函数、共享库、常用的类以及主题资源。然而init进程中没有这些资源提供,所以就没设计init进程船舰SystemServer
25、为什么要用Zygote进程去孵化app进程,而不是让SystermServer去孵化?
zygote进程:
- 虚拟机初始化与启动
- JNI函数的注册
- 加载公用的各种资源
- 创建socket服务器并在runSelectionLoop中死循环等待socket消息,fork了systemServer进程等操作
SystemServer进程作为Zygote进程的大儿子
- 主要是启动和管理 引导服务 核心服务啥的 AMS WMS PMS 等90多种服务专门给App进程使用的。
- App的运行需要什么?
- 虚拟机
- 能够调用framework中的资源和函数。Zygote加载了
- AMS WMS等服务给app运行和管理提供支持。systemServer做了
- 所以对应用程序来说 zygote内做的事情用的到 ,systemserver中的AMS WMS 等服务用不到。浪费了
- 另外fork对多线程不友好,可能死锁,而system_server有很多子线程。
- fork,复制整个用户空间数据(copy_on_write),然后仅仅复制当前线程到子进程中。那么其它线程就蒸发了。假设蒸发的这个子线程拿到了锁,锁被复制,但是哪个持锁的线程没了。那不就死锁了。
26、Zygote为什么不采用Binder机制进行IPC通信呢
- zygote采用binder会导致fork出来的进程产生死锁。当然Zygote在fork子进程时也会进行子线程的锁释放,以确保子进程的独立运行和避免死锁的产生。
- UNIX上有个准则:多线程程序里不准使用fork。因为容易死锁。
- Binder支持多线程,Binder线程池有多个线程运行。binder中自然会出现子线程锁等待状态,如果fork。锁还在,子线程没了,嚯,死锁。
27、Android app的进程怎么启动的
- 冷启动:后台没有改应用的进程。这时系统会重新创建一个新的进程分配给应用。必须先实例化Application
- 热启动:应用启动时,后台已有该应用的进程,比如back或者home。就直接启动,不用创建Application。
启动App进程
- 点击launcher桌面程序的App图标
- laucner程序调用startActivity函数,通过Binder发给System_server进程,通过binder发给其中的AMS,AMS通过socket告诉Zygote进程fork出一个子进程(App进程)
详细步骤
- 点击了launcher通过ServiceManager.getService("system_server")拿到system_server的实例systemManager代理对象,再通过systemManager.getService("activity")获取AMS的代理对象,Android10及之后是通过systemManager.getService("activity_task_manager")拿到ActivityTaskManagerProxy。
- ActivityTaskManagerProxy和ActivityTaskManager通信,ActivityTaskManager和ATMS通信,ATMS检测判断,看App进程是否村咋爱,如果存在就热启动,如果不存在就是冷启动,然后ATMS发给送 socket请求给到Zygote进程,通知zygote去创建App进程
- Zygote进程收到socekt请求就会去fork函数,fork会产生一个子进程,这个 就是需要启动的app进程,它具备Zygote进程几乎一切,没有子线程凉了没有。
- 开启App主线程
- App进程启动后,
- native层面反射执行其main函数
- 例化一个ActivityThread
- 触发Activitythread.attch函数,前后Looper.prepareMainLooper()和Looper.loop()
- ApplicationThread(处理App和安卓系统服务之间通信,非主线程)、Looper、Handler对象。开启主线程消息循环Looper.loop()
28、Android Application为什么是单例
- application的创建来自于handleBindApplication
- AppBindData.info.makeApplication(...)
- 如果mApplication不为空就返回
- 如果为空就通过mActivityThread.mInstrumentation.newApplication(...)
29、Intent的原理,作用,可以传递哪些类型的参数?
原理
系统启动时,PMS扫描所有已安装apkmu解析其中AndroidManifest清单文件得到App信息,都存起来构建完成apk信息树。Intent去各个组件通信的时候,会调用PMS去查查找对应的组件列表,找到相关组件进行类似用于启动的操作
作用
- Activity之间传递信息
- App之间传递信息
- 发送广播
能传递哪些类型参数?
通过binder实现的
- 基本数据类型和String
- 其它类必须实现Parcelable或者Serializable
- 如何是集合或者数组,其中的元素要么是基本数据类型的包装类、String。或者实现Parcelable或者Serializable
30、Activity启动流程分析
大概流程
- 桌面系统其实时一个Launcher,点击后
- 它调用ATMS(Android 10之前是AMS后面就不说了),
- ATMS通过socket发给zygote,
- 请求fork一个app进程,
- app进程启动后,反射执行ActivityThread的main函数,
- 然后Looper.prepare .新建一个ActivityThread对象,然后执行这个对象的attach函数。然后Looper.loop.
- 然后执行这个对象的attach函数
- ATMS的attachApplication方法,
- ATMS通过AMS与ApplicationThread通信
- ApplicationThread的bindeApplication函数执行,开始应用初始化,加载资源和设置
- application的生命中后期,完成初始化和涉资
- 启动Activity并执行相关流程
详细版本
- 主线程中执行execStartActivity中有个instrumentation做检测。
- ActivityManagerProxy startActivity和AMS通信,
- AMS-》ATMS.execute-》ActivityStarter 解读activity启动模式在内的各种参数,然后 启动个黑白屏
- 交给ActivityStack处理。因为其中存储了应用中所有的Activity
- 为了更好管理Activity的各个生命周期,调用startSpecificActivity到ActivityStackSupervisor,把生命周期事件封装成clientTransaction
- 交给ApplicationThreadProxy,通过schedulerLauchActivity跨进程binder访问App今晨给,然后给App进程的transactionExecutor统一执行。直到完成Activity的onResume
32、如果需要在Activtiy间传递大量数据怎么办?
Intent传递数据最大时1M-8K.一般更少因为还有包装,因为Binder锁映射的内存大小就是这么大。actvity间出纳第数据一般时用到binder。
- 文件或者数据库
- 将要传递的信息封装在静态类中,然后只传名字
- 匿名共享内存
- ViewModel和LiveData也不是不行
33、打开页面,如何实现一键退出
- 获取当前系统的任务栈,逐步退出Activity
- 同上,Applicaton中子类建立一个Activity链表,保存正在运行的Activity实例,需要时遍历退出。
- 行System.exit(0) 强制退出/killProcess
- 广播/viewModel/eventbus 每个Activity中都接收并退出。
34、startActivity(MainActivity.this,LoginActivity.class);LoginActivity配置的launchMode是何时解析的?
- 在AMS中检查是否存在实例,通过检查activity包名、类名和标志等信息来判断
- 确定启动模式
- standard:标准
- singleTop:如果在栈顶有就不会创建新的,不在栈顶就复用,没有就创建新的
- singleTask:整个个系统中只有这一个activity,如果栈内有了,会清除它栈顶部的activity
- singleInstance:在singletask的基础上,所在的task只有一个activity,就是它
- 新建栈或者复用已存在栈的充分必要条件是什么?
- singleTask/singleInstance会为mLaunchFlags自动添加FLAG_ACTIVITY_NEW_TASK,也就是说他们都有存在不使用当前栈的可能。
- FLAG_ACTIVITY_NEW_TASK + taskAffinity(不同标识不同包名)一定会出新栈,优先级最高,及时是single'task这个模式也会被覆盖
35、清单文件中配置receiver,系统是合适会注册此广播接收者的?
- 一般是由pkms来完成的。触发执行分为两个部分
- 系统启动时,会启动system_server进程,而SystemServer进程会启动各种服务,这些服务包括PKMS,启动PKMS就会扫描apk安装目录解析Androidmanifest,做持久化存储。
- app安装的时候,也会触发PKMS对apk进行检查,调用类似流程解析AndroidManifest
- Receiver的计息,都只是整个AndroidManifest解析过程中的一环
36、如何通过WindowManager添加Window?
- 权限设置
- 获取windowmanagerservice的代理对象,一个WindowManagerImpl实例
- 获取需要添加的view,设置一下参数-type/FLAG/宽高/坐标系
- WindowManagerImpl.addView(view.params)
37、为什么Dialog不能用Application的Context
- Dialog添加到Window中需要一个 合法的Token,这个在Activity运行后是可以获得的
- Window、WM、WMS、Token的概念
- Window是窗口的意思,对应屏幕上一块显示区域。实现类是PhoneWindow。Window有一个属性值Type(应用窗口、子窗口、系统窗口)
- WM,WindowManager,WIndowManager是应用于窗口之间的桥梁。实现类是WindowManagerImpl
- WMS,WindowManagerService单独的进程。WindowManagerImpl通过IWindowSession与其通过Binder IPC通信。它是窗口的管理这,负责窗口的启动、添加和删除。窗口的大小层级也是WMS进行管理的
- Token:窗口令牌,是一种特殊的Binder令牌,WMS用它标识唯一识别系统中的一个窗口
- Application或者Service的context去获取WindowManager服务的话,会得到一个WindowManagerImpl实例,这个实例的token是空的。
- 因为这个令牌只会在Activity,Activity作为context去拿mWIndowManager,这个mWindowmanager在Activity的attach方法中被创建,Token指向此Activity的Token
38、WIndowManagerSErveice中的token到底是什么?token存在意义是什么?
-
Activity有自己的PhoneWindow,WindowManager同时PhoneWindow含有token
-
而Application并没有自己的PhoneWindow,它返回的WindowManager是应用服务windowManager,并没有赋值token的过程。
-
当我们使用Activity来弹出dialog时,此时Activity的DecorView已经是显示到屏幕上了。有界面了。这个dialog作为子窗口类型被添加到PhoneWindow上,它的token就是DecorView的token。此时DecorView已经被显示到屏幕上了,它本身是拥有token的;
- 当一个view被添加到 屏幕后,它对应的 viewRootIMpl就有一个token对象。这个token来自WindowManagerGlobal,是 一个IWindowSEssion对象 PhoneWindow的DecorView添加到屏幕后,后续添加的子window的token,就都是这个IWindowSession 对象了
WMS是如何验证token的?
- Activity的PhoneWindow是有token,而Application使用的是应用级服务windowManager,并不存在token。
- mTokenMap 是一个 HashMap<IBinder, WindowToken> 对象,这里就可以保存一开始初始化的token以及后来创 建的windowToken两者的关系。WMS就可以根据IBinder对象拿到windowToken进行信息 对比。