FrameWork层的Handler和Binder;
通信分为:
App间的通信——Binder
App内部通信——Handler
Handler
Handler学好解决的问题:
1.面试
2.防止内存泄露
3.防止内存抖动
Handler原理:
Handler原理:是为了实现线程间的通信;
线程间的通信,两个线程使用公共的变量或者公共的其他东西都可以进行通信,但是这种方式不是自主的,不能够自主切换线程执行,所以Handler的最终是为了线程间的切换。
Handler的使用会涉及到message、looper、messageQueue;Message的作用是携带数据,handle通过调用sendMessage的方式发送数据到MessageQueue队列,looper通过循环遍历的方式从messageQueue中取出数据;
Handler四大部分:
Handler:提供传输数据的作用(handler.sendMessage() / handler.handlerMessage() 发送/获取数据)
Message:具体的数据
MessageQueue:具体的数据的列表
Looper:获取传输数据
Handler调用:
handler发送消息关键方法调用最终的会到handler.enqueueMessage -> MessageQueue.enqueueMessage
将数据通过handle发送数据到MessageQueue中。
handler调用了sendMessage,立刻就会调用handlerMessage()方法;
sendMessage是生产者,handlerMessage是消费者;消息在队列中排队(MessageQueue),这样解决大量的消息过来的问题,不会造成主线程sendMessage阻塞,所有消息都会直接放在队列中排队等候执行;
一个线程可以有几个Handler,几个looper?
有多个handler,每个handler都会配一个队列MessageQueue。
注:Lopper和MessageQueue绑定,为防止创建多个messageQueue,Looper创建也只能被调用一次;
一个Looper,因为looper里面是死循环执行,后面的代码是执行不到的,所以只有一个looper;looper的生命周期是当前线程的生命周期长度,如何保证一个线程中只有一个Looper,可以通过线程——ThreadLocal,ThreadLocal中会有一个ThreadLocalMap保存一个Looper,通过调用ThreadLocal的get方法来获取是否能得到Looper,如果能得到说明已经有了Looper,就直接返回一个异常通知已经有了Looper。
Looper.looper()就是循环提取消息并调用handlerMessage()去处理;
Looper.prepare()就是保证只有一个looper;
为什么handler会造成内层泄露?
长生命周期持有短生命周期,造成短生命周期得不到释放就会造成内存泄露;
线程的生命周期长,Activity的生命周期短,activity运行完得不到释放;
1.在Activity中实例化Handler导致了,Activity中持有handler对象;
2.Message和Handler的持有是由于在Lopper中进行循环遍历的时候,Message需要被执行所以要使用handler的handleMessage,但是此时获取不到handler对象,所以,通过Message中实例化一个Handler对象;
3.MessageQueue是Message的集合对象,所以造成持有关系;
4.Looper又和MessageQueue进行了绑定,造成了Looper对Message的持有;
最终 :线程----->Looper----->MessageQueue----->Message----->Handler------>Activity,一系列持有造成的内存泄露;
解决内存泄露可以使用static+弱引用的方式解决。
主线程会一直阻塞?
是的,如果主线程不进行looper.loop阻塞,一下子执行完成,整个程序就直接结束了,不可能有机会去执行其他的任务了。
Android是事件为驱动的操作系统,事件过来就去handler里执行,没有事件就阻塞在那里显示界面;
Message
Message的创建方式有两种:
1.new Message()
2.obtainMessage
涉及:想要了解优化内存抖动,可以看看message的原理,内存抖动是为啥?因为短时间创建大量的对象并销毁。
使用obtainMessage创建一个Message,会有复用的作用,涉及到一个回收池,回收池中存放的是Message,会有一定的数量,使用单项链表来存放这些Message。每个message对象指向下一个Message对象;
obtainMessage创建对象是从回收池中获取,没有的才会进行创建,回收池中获取一个Message需要将管理回收池的列表同这个取出来的Message的关联进行切断,所以需要将此获取的Message的next引用置为空。并且spool变量的引用将会变成下一个Message,同时单向列表的size-1,如下代码:
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message();
new Message()产生的对象是不会进回收池的。
安卓中的Looper.loop()阻塞为什么不会有问题?
Android是事件为驱动的操作系统,事件过来就去handler里执行(handle里处理包括了创建服务,创建广播,结束服务,等等事件处理),如果没有事件过来就阻塞在那里,显示静止界面,有事件就去执行事件;
利用epoll机制可以定位到是哪个app接受这个事件
运用到红黑树,事件过来之后查找app来执行这个事件,事件带有一个标记,查找对应的app。
Binder
binder是Android独有的,其他平台没有,但是移植到其他平台上进行编译通信。
Android是继承自Linux,在Linux基础上进行封装一层;
学习Binder作用
1.面试
2.解决bug
3.了解android运行原理
Android中为什么会用Binder?
Android系统为每个app提供服务,就是类似于C/S模型;为什么不沿用Linux的进程通信方式呢,以下对Linux的集中方式进行分析:
linux中有哪几种进程通信:管道、共享内存、Socket、File;
管道:
1.效率:管道不高,App1的数据copy到管道,管道copy到App2里面,进行了两次拷贝;
2.安全:管道比较安全
3.模型:1V1
共享内存:
效率是最高的,不会涉及任何copy;不安全,多线程下难以控制;多个程序有一个共享的内存,NVN模式;
Socket:
进行两次copy,还要用户和内核的cpu状态切换,比管道效率还低;安全;C/S模型;
File:
磁盘读写,更不安全;
Android中Binder机制:
Binder进行了一次copy(用户空间copy到内核空间);基于C/S模型,易用性高;为每个App分配UID,支持实名和匿名;
通信的时候通过Android系统进行验证两个App的UID进行通信,这样防止其他app拦截;
内核进程:里面装的系统代码、驱动代码(这些进程号为0的就是系统代码),防止,每个app都进行编译一次系统代码浪费资源;
App:里就是运行用户代码;
Binder为何能实现一次copy?
Binder的一次copy是利用了mmap(内存映射文件:目的是开辟物理地址),内存映射文件是在堆和栈的空余空间;mmap是在linux中的api,可以通过mmap去开辟物理地址空间。
而涉及到MMU,MMU(MemeoryManangerUnit)将mmap开辟的物理内存地址转化成虚拟内存地址;
物理地址:内存条上的地址;
虚拟地址:实际上是MMU提供的虚拟地址;
Binder采用的是C/S模式的,其中提供服务的进程成为Server进程,访问服务的进程成为Client进程;
Server和Client进程通信:要依靠运行在内核空间的Binder驱动程序来进行;
Service组件在开启时,会将自己注册到一个Service Manager里,以便于Client进程在ServiceManager中找到它;因此ServiceManager也称为Binder进程间通信的上下文管理者;同时它也需要和普通的Server进程和Client进程通信,所以也是可以看做一个特殊的Service组件;
注:Binder通信机制中的Service组件和Android中的Service组件是不同的概念;Service Manager里注册服务是在进程间的注册;
Binder
1.服务注册
2.服务发现
3.服务查找
扩展:在系统进程中init(pid = 1)是守护关键服务进程、启动关键服务进程,守护那些关键的服务进程(父进程是ppid = 1),
两个进程间的通信Binder原理:
Binder的通信是 进程A 调用copy_form_user,到内核空间,内核空间 同 进程B建立了链接,copy_to_user会将数据传给B进程;而进程间的通信大小是1M-8K(copy到内核空间),8K是由于有请求头等信息,一页是4K,需要是4的整数倍;
扩展:了解什么是页
cpu为了高效执行以及内存管理,每次需要拿一个页的代码,这个页表示一块连续的储存空间。常见的4kb,也称为块。
假如页的大小为P,那么在虚拟内存中就是VP称为虚拟页;从虚拟内存中拿到的一页的代码放在物理内存中,那么物理内存中也得有一个同样大小可以页存放虚拟页的代码,物理内存中的页称为物理页(PP);
在任何时刻,虚拟页都有三种以下状态中的一种:
未分配的:VM还未分配页(或者未创建),未分配的页还没有任何数据与代码与他们关联,因此也就不占任何磁盘;
已缓存的:当前缓存在物理内存中已分配页;
未缓存的:当前未缓存在屋里内存中已分配页;
以上状态都是在MMU中体现。
为什么会出现物理地址和虚拟地址呢?
由于现在的程序app大小都很大了,如果全部加载到内存中去运行需要很多内存,而手机的内存是有限的,又由于当你在运行一个app的时候不是所有的代码都会被加载到内存中去运行,只会加载一部分正在活动的代码,为了满足程序局部性原则,这时出现的物理地址和虚拟地址刚好能解决,能省下不用的内存空间供其他app使用。
物理地址和虚拟地址
为什么会存在MMU,因为app的整体大小假如是100M,但是实际的活跃代码在内存中只有1M,其他的代码处于磁盘中,所以,cpu在运行代码的时候不可能说只给1M的内存让cpu在里面运行,所以需要给一个MMU中间件,让CPU感觉运行在512M的内存中。
MMU(Memory Management Unit)内存管理单元又涉及到一个转换物理地址和虚拟地址;
物理地址:内存条上的地址;
虚拟地址:MMU提供的虚拟的地址;
MMU里有页表:页表里有效位+地址,有效位为0表示未缓存,为1表示已缓存,只要有效位是1就肯定有地址;
虚拟地址和物理地址,如果虚拟地址中没有的话,MMU就会读取磁盘并拿出来在内存中开辟一块新的空间,并在MMU中存放物理地址和对应的虚拟地址,cpu就会拿到虚拟地址;
共享内存:实现跨进程通信(也是mmap使用方式):
两个不同进程,通过native-lib.cpp文件,里面用C代码:通过mmap的api方式开辟物理内存并写入到物理内存,另外一个进程可以通过mmap获取到相同的那块物理内存地址,这样就可以获取内容了,实现了跨进程通信,和binder相比,没有进行copy,依赖于物理内存做通信。
BInder和共享内存的区别在于:
进程A和内核空间发生了一次copy,而进程B(类似服务端)和内核空间之间的关联类似于共享内存的形式,没有发生copy。
进程A(类似客户端)和内核空间的copy又是怎样的?
数据接收方Stub类,数据发送方Proxy,就是创建aidl时自动生成的文件里面的两个类,Proxy类是Stub类的内部类,如下代码:
```
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.zx.annotationtest;
/**
* 工程目录直接反键,自动创建在aild文件目录下,创建一个AIDL文件用于生成代码
* 生成的是Stub类数据接受方和Proxy类数据发送方
*/
public interface IMyAidlIPC extends android.os.IInterface
{
/** Default implementation for IMyAidlIPC. */
public static class Default implements com.zx.annotationtest.IMyAidlIPC
{
@Override public int add(int a, int b)throws android.os.RemoteException
{
return 0;
}
@Override
public android.os.IBinderasBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.zx.annotationtest.IMyAidlIPC
{
private static final java.lang.StringDESCRIPTOR ="com.zx.annotationtest.IMyAidlIPC";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.zx.annotationtest.IMyAidlIPC interface,
* generating a proxy if needed.
*/
public static com.zx.annotationtest.IMyAidlIPCasInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iininstanceof com.zx.annotationtest.IMyAidlIPC))) {
return ((com.zx.annotationtest.IMyAidlIPC)iin);
}
return new com.zx.annotationtest.IMyAidlIPC.Stub.Proxy(obj);
}
@Override public android.os.IBinderasBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)throws android.os.RemoteException
{
java.lang.String descriptor =DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_add:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result =this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.zx.annotationtest.IMyAidlIPC
{
private android.os.IBindermRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinderasBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int add(int a, int b)throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
boolean _status =mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
if (!_status &&getDefaultImpl() !=null) {
return getDefaultImpl().add(a, b);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.zx.annotationtest.IMyAidlIPCsDefaultImpl;
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION +0);
public static boolean setDefaultImpl(com.zx.annotationtest.IMyAidlIPC impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl !=null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl !=null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.zx.annotationtest.IMyAidlIPC getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public int add(int a, int b)throws android.os.RemoteException;
}
```
内核进程开启了init进程,init是守护进程,守护那些系统进程比如蓝牙、电话……
init进程又会开启service-manager和zygote进程,而zygote进程会开启system-server进程,system-server会开启应用服务AMS、PMS、WMS……,而开启的这些应用进程又归service-manager管理
SystemServer类:
```
SystemServiceManager mSystemServiceManager;
mActivityManagerService=mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();
```
其中Lifecycle.class是ActivityManagerService类的内部类:
```
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new ActivityManagerService(context);
}
public ActivityManagerService getService() {
return mService;
}
```
Lifecycle中包装了ActivityManagerService,并且将用getService方法把mService返回出去,就是上面的SystemServer类中获取ActivityManagerService的实例。
ActivityManagerService类:
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
将ActivityManagerService 通过addService给ServiceManager。
网友评论