Binder、Handler、synchronized


一、Binder

1、Binder是什么?

  • 机制:Binder是一种进程间通信的机制
  • 驱动:Binder是一个虚拟物理设备驱动
  • 应用层:Binder是一个能发起进程间通信的JAVA类
  • Binder就是Android中的血管,在Android中我们使用Activity,Service等组件都需要和AMS(system_server)进行通信,这种跨进程的通信都是通过Binder完成。
  • Activity,Service等组件和AMS不是同一个进程,其实也是多进程通信。

2、为什么要用多进程?

  • 虚拟机给每一个进程分配的内存是有限制的,LMK会优先回收对系统资源占用多的进程
  • 为了突破内存限制,防止占用内存过多被杀
  • 功能稳定性,一个进程崩溃对另外进程不造成影响:将不稳定功能放入独立进程
  • 规避内存泄漏,独立的WebView进程阻隔内存泄漏导致问题

3、为什么使用Binder?
在这里插入图片描述

4、进程空间划分:用户空间(User Space) ——内核空间(Kernel Space)

  • 每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。例如,对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,内核空间的大小是可以通过参数配置调整的
  • 对于用户空间,不同进程之间是不能共享的,而内核空间却是可共享的
  • Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的
  • Client端与Server端进程往往采用ioctl等方法与内核空间的驱动进行交互。(ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。)

5、 Android 系统中用户进程之间是如何通过这个内核模块(Binder 驱动)来实现通信的呢?

  • 当然不是之前的:将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程。而是通过内存映射:
  • 内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间
    反之内核空间对这段区域的修改也能直接反应到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动
    在这里插入图片描述
  • 首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;然后再申请1个page大小的物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer和内核空间的Buffer同步操作的功能。

6、Binder 实现原理:

  • Binder IPC 正是基于内存映射(mmap)来实现的,但是 mmap() 通常是用在有物理介质的文件系统上,进程中的用户区域是不能直接和物理设备打交道的。
  • 如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘–>内核空间–>用户空间),通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率

在这里插入图片描述

二、StringBuffer与StringBuilder区别

  • StringBuffer:线程安全,StringBuilder:线程不安全。因为 StringBuffer 的所有公开方法都是 synchronized 修饰的,而 StringBuilder 并没有 synchronized 修饰。

三、Synchronized 和 Lock的概念

  • synchronized 是Java 并发编程中很重要的关键字,另外一个很重要的是 volatile。Syncronized 的目的是一次只允许一个线程进入由他修饰的代码段,从而允许他们进行自我保护。Synchronized 很像生活中的锁例子,进入由Synchronized 保护的代码区首先需要获取 Synchronized 这把锁,其他线程想要执行必须进行等待。Synchronized 锁住的代码区域执行完成后需要把锁归还,也就是释放锁,这样才能够让其他线程使用。
  • Lock 是 Java并发编程中很重要的一个接口,它要比 Synchronized 关键字更能直译"锁"的概念,Lock需要手动加锁和手动解锁,一般通过 lock.lock() 方法来进行加锁, 通过 lock.unlock() 方法进行解锁。与 Lock 关联密切的锁有 ReetrantLock 和 ReadWriteLock。

区别

  • 存在层面:Syncronized 是Java 中的一个关键字,存在于 JVM 层面,Lock 是 Java 中的一个接口
  • 锁的释放条件:1. 获取锁的线程执行完同步代码后,自动释放;2. 线程发生异常时,JVM会让线程释放锁;Lock 必须在 finally 关键字中释放锁,不然容易造成线程死锁
  • 锁的获取: 在 Syncronized 中,假设线程 A 获得锁,B 线程等待。如果 A 发生阻塞,那么 B 会一直等待。在 Lock 中,会分情况而定,Lock 中有尝试获取锁的方法,如果尝试获取到锁,则不用一直等待
  • 锁的状态:Synchronized 无法判断锁的状态,Lock 则可以判断
  • 锁的类型:Synchronized 是可重入,不可中断,非公平锁;Lock 锁则是 可重入,可判断,可公平锁
  • 锁的性能:Synchronized 适用于少量同步的情况下,性能开销比较大。Lock 锁适用于大量同步阶段:

应用场景

  • 在方法上使用 Synchronized。方法声明时使用,放在范围操作符之后,返回类型声明之前。即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候。
  • 在某个代码段使用 Synchronized。你也可以在某个代码块上使用 Synchronized 关键字,表示只能有一个线程进入某个代码段。
  • 使用 Synchronized 锁住整个对象。synchronized后面括号里是一对象,此时线程获得的是对象锁
  • lock 方法可能是平常使用最多的一个方法,就是用来获取锁。如果锁被其他线程获取,则进行等待。如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。

四、Handler机制

1、什么是Handler?有什么作用?

  • Handler是Android定义的一套 子线程与主线程间通讯的消息传递机制 。
    作用是把子线程中的UI更新信息,传递给主线程(UI线程),以此完成UI更新操作。
  • Handler中有四个较重要的类:
    (1)处理器类(Handler),相当于快递员
    (2)消息队列类(MessageQueue),相当于快递分拣中心
    (3)循环器类(Looper),相当于快递公司
    (4)消息类(Message),相当于包裹

2、它的工作原理
一个Android应用程序被创建时就会创建一个进程,该进程用应用的包名作为进程名。该进程会启动主要线程,也叫UI主线程,但有时需要做些耗时操作,为了不能够去阻塞UI主线程的正常运行,我们将它放在子线程中进行操作,操作完成后需要绘制UI,但Android子线程不能直接操作UI线程,所以通过Handler来进行通信。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3、Handler导致的内存泄露原因及其解决方案

  • 在Activity内将Handler声明成非静态内部类或者匿名内部类,这样Handle默认持有外部类Activity的引用。如果Activity在销毁时,Handler还有未执行完或者正在执行的Message,而Handler又持有Activity的引用,导致GC无法回收Activity,导致内存泄漏。如以下两种情形可能导致内存泄漏
    1、在Activity内将Handler声明成匿名内部类
    2、在Activity内将Handler声明成非静态内部类

解决方法

  • Activity销毁时,清空Handler中,未执行或正在执行的Callback以及Message。
  • 静态内部类+弱引用

4、什么是弱引用?

  • 软引用是在system.gc 不一定回收 软引用回收只会在jvm内存不足时回收 弱引用 system.gc方法执行后就会回收
  • WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。

5、一个线程可以有几个Handler,几个Looper,几个MessageQueue对象

  • 一个线程只能有一个looper 每一个Thread都有一个threadLocalMap变量 里面存着<key,value>
    key是当前线程(默认会是主线程) value为loop
  • 一个线程可以有多个handler ,但是他们使用的消息队列都是同一个,一个线程的looper只会创建一次,只有一个looper对象,一个looper下只有一个MessageQueue属性,所以一个线程只有一个MessageQueue

6、Message对象创建的方式有哪些?

  • Message msg = new Message() 直接创建一个message对象
    Message msg2 = Message.obtain(); 从message池中返回一个message对象
    Message msg1 = handler1.obtainMessage(); 作用和第二步一样,

7、通过Handler如何实现线程的切换

  • 当在A线程中创建handler的时候,同时创建了Looper与MessageQueue,Looper在A线程中调用loop进入一个无限的for循环从MessageQueue中取消息。当B线程调用handler发送一个message的时候,会通过msg.target.dispatchMessage(msg);将message插入到handler对应的MessageQueue中,Looper发现有message插入到MessageQueue中,便取出message执行相应的逻辑,因为Looper.loop()是在A线程中启动的,所以则回到了A线程,达到了从B线程切换到A线程的目的
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值