Android 同步和消息机制

Linux 下线程的运行模式是抢占式的,为了防止全局资源(可能是一个全局变量,也可能是系统的硬件资源,图扬声器)因为线程的切换出现错误,通常需要使用系统提供的同步机制来“独占”全局资源的访问权。虽然同步机制能解决资源访问的冲突问题,但也不可避免的带来了性能上的损失,因此,在不影响正确性的前提下,应当尽量避免使用同步机制。

1.原子操作


2.Android Native层的同步方法


3.Android Java层的同步方法:

Java语言提供了同步关键字,synchronized来支持线程间的同步操作。

synchronized锁住的是对象,不是一段代码。synchronized起到的是自动加锁和解锁的作用。

synchronized最常见的用法是保护一段代码

class Foo implements Runnable {

private String mLock;

public void lockedMethod () {

......

synchronized(mLock) {

......

 }

......

 }

 }

假设类Foo 有两个实例对象foo1 和foo2,如果在两个线程中分别调用foo1.lockedMethod()方法,在同一时间,只有一个线程能执行被synchronized括起来的代码,也就起到了互斥作用。但是如果另一个线程调用的是foo2.lockedMethod(),这样其实是两把不同的锁,不会起到互斥作用。

synchronized还可以用在类的方法前面

class FooA implements Runnable { 

public synchronized void lockedMethod () { ......}

public synchronized voidunlockedMethod () { ......}

 }

语法上等价于synchronized (this){...... },表明上锁的对象是类的实例对象本身。假定类FooA有一个实例对象foo_a,两个线程都访问foo_a.lockedMethod()一定会互斥,一个线程访问foo_a.lockedMethod(),另一个线程访问foo_a.unlockedMethod()同样会互斥。当然如果是两个实例类对象foo_a和foo_b,分别调用他们的函数,都不会互斥。

synchronized的第三种用法是修饰一个静态方法

class FooB implements Runnable { 

public synchronized static void lockedMethod () { ......}

 }

语法上等价于 synchronized (FooB.class){......},表明上锁的对象是类本身。一旦有一个线程调用FooB类的任何实例对象 lockedMethod () ,另一个线程对于FooB类的任何对象的 lockedMethod ()方法,都将会产生互斥。

synchronized 只能提供互斥访问的同步机制,如果需要完成一些更复杂的同步操作,需要Object 类来配合完成。Object类是所有Java对象的基类,Object类中提供了一些与同步相关的方法:

//唤醒挂起的线程

public final native void notify();

public final native void notifyAll();

// 挂起调用者所在的线程

public final void wait();

public final void wait(long millis);

public final native void wait(long millis, int nanos);

以上函数的调用必须是在本线程取得对Object 对象的控制权以后才能进行。

更准确的说,应该是在synchronized的作用域内调用。

Android 的消息机制

消息驱动是一种进程或线程的运行模式。内部、外部的各种事件都可以放到消息队列中按序处理。这种模式特别适合处理大量的交互事件。Android 应用的UI线程,同样采用了消息驱动模式,所有外部来的按键消息,触屏消息,各种系统的Intent,广播等都会转化为内部消息,然后在主线程中分发处理。

Android 没有全局的消息队列,消息队列是和某个线程关联在一起的,每个线程最多有一个消息队列,消息的取出和处理在线程中完成。处理前必须为线程构造消息队列,发送消息也必须先得到消息队列的Handler对象。

Android中与消息机制相关的类主要是

1.Looper,

线程的消息循环处理器,每个线程只能有一个Looper对象。Looper内部有一个消息队列MessageQueue,所有线程的消息都存放在这个队列中。新建一个线程时,系统并不会马上为这个线程创建一个Looper对象,需要程序自己创建。

2.Handler,

Handler对象时Message的接收者和处理者。用户使用Handler对象把Message添加到消息队列中,同时通过Handler的回调方法handleMessage()来对消息队列当中的Message进行处理。Handler对象在构造时和某个Looper对象关联在一起。Handler和Looper是多对一的关系,多个Handler对象可以和一个Looper对象关联在一起。

在一个线程中可以只用一个Handler对象来处理所有的消息,也可以使用多个。构造一个Handler对象需要两个参数,线程的Lopper对象和消息的处理函数。对于Handler对象而言,参数Looper是必须的,因为它只能给某个线程的Looper对象发送消息,如果构造方法不指定特定的Looper对象,它会使用当前线程的Looper对象。但是参数callback不是必须的,应用程序可以通过这个callback方法来实现对消息的集中处理。也可以把处理消息的callback方法直接放在消息对象中。

Handler类是消息框架的一部分,消息的定义和响应还需要在应用层的代码中完成。Android 把消息的定义和处理完全独立了出来,线程只是提供了一个消息队列和消息响应代码的运行环境。Android主线程的实现都是在Framework中,但是可以使用下面的方法来构造一个带有callback方法的消息发送给主线程。

public static Message obtain (Handler handler, Runnable callback)

这样callback方法将在主线程中执行。

Handler类的另一个功能是发送消息:

send类:send只是把消息插入到了消息队列中,同时指定消息处理的时间。如果指定的时间为0,表示要立即处理,MessageQueue会把这条消息插到队列的头部,MessageQueue类中接收消息的接口如下:

boolean enqueueMessage (Message msg, long when)

发送消息的接口虽然多,但是都是在时间上玩花样,让应用方便使用而已。

public final boolean sendMessage (Message msg) // 希望马上处理,但是不打算插队

public final boolean sendEmptyMessage (int  what) // 如果定义的消息只有ID, 不用附加参数,这个函数更佳方便。

public final boolean sendEmptyMessageDelayed (int  what, long delayMillis) //

public final boolean sendEmptyMessageAtTime (nt  what, long uptimeMillis) //

public final boolean sendMessageDelayed (Message msg, long delayMillis) // 希望延时一段时间处理

public boolean sendMessageAtTime (Message msg,  long uptimeMillis) // 希望在指定时间处理

public final boolean sendMessageAtFrontOfQueue (Message msg) //非常紧急,希望尽快处理

post类:也是在使用send类的方法在发送消息,只是它们的参数要求是Runnable类的对象,然后在方法中调用了getPostMessage(r)获取了一个Message对象来发送。

public final boolean post(Runnable r){

return sendMessageDelayed (getPostMessage(r), 0);

 }

public final boolean postAtTime(Runnable r, long uptimeMillis){

return sendMessageAtTime (getPostMessage(r), uptimeMillis);

 }

public final boolean postAtTime(Runnable r, Object token,  long uptimeMillis){

return sendMessageAtTime (getPostMessage(r, token), uptimeMillis);

 }

public final boolean postDelayed(Runnable r, long delayMillis){

return sendMessageDelayed (getPostMessage(r), delayMillis);

 }


public final boolean postAtFrontOfQueue(Runnable r){

return sendMessageAtFrontOfQueue (getPostMessage(r), 0);

 }

综上,post类型的方法用来发送带有处理方法的消息,send类型的方法则用于发送传统的带有消息ID的消息。

dispatchMessage():

消息回优先分发给消息中自带的回调方法。否则,如果Handler定义了回调方法,先调用这个方法处理,如果这个方法没有处理,还会调用Handler自己的HandleMessage()方法,这个方法缺省不做任何事情。如果实现了一个Handler类的继承类,也可以通过重载HandleMessage()方法来达到处理消息的目的。

消息的同步-——Message类的setAsynchronous()方法(给一条消息加上异步标志)。

一种情况:收到一条广播,或者在Activity刚启动时,可能需要完成一个比较耗时的操作,但是,如果消息处理函数长时间不返回,很容易发生ANR。常用的解决办法是发送一个消息,然后在消息处理函数中完成这个耗时操作。

当MessageQueue调用enqueueSyncBarrier(long when)后,使用setAsynchronous()方法来给一条消息做上标志,MessageQueue检测到消息的标志后,会正常处理这条消息,但是别的消息还是暂停处理,直到调用removeSynvBarrier()方法移走挡在消息队列前面的“SyncBarrier”

3.Message 

Message是消息的载体,Message设计为Parcelable类的派生类,这表明Message可以通过binder来夸进程发送。

4.MessageQueue。

不同线程间发送消息,有同步保护吗?如何实现的?


消息队列不同于普通队列,每条消息都有时间,如何实现按时间分发消息?



没有消息时消息队列会挂起吗?来了新的消息又是如何被唤醒的?


消息队列是如何组织的?新消息是如何插入的?都是在队尾吗?


SyncBarrier是如何工作的?
















你可能感兴趣的:(Android 同步和消息机制)