handler消息处理机制

移动开发中,我们要处理好主线程与子线程之间的关系,耗时的操作应安排到子线程中,避免阻塞主线程,导致ANR
异步处理技术是提高应用性能,解决主线程和子线程之间通信的关键。
异步处理技术有很多种,常见的有Thread,AsyncTask,Handler&Looper,Executor等

QQ截图20170729093531.png

Thread

线程是java的一个概念,实际是执行任务的基本单元
创建线程的两种方法
继承Thread类并重写run方法。

实现Runnable接口并实现run方法

Android中各种类型的线程本质上都是基于Linux系统的pthreads,在应用层可分为三种线程
主线程
主线程称为UI线程,随着应用启动而启动,主线程用来描述Android组件,同时刷新屏幕上的UI元素。
Android系统如果检测到非主线程更新UI组件,那么会报出CalledFromWrongThreadException异常,只有在主线程才能操作UI,是因为Android的UI工具包不是线程安全的。主线程中创建Handler会顺序执行接收到的消息,包括从其他线程发送的消息。
Binder线程
Binder线程用于不同进程间线程通信,每个进程都维护了一个线程池,用来处理其他进程中线程发送消息,这些包括系统服务,intents,ContentProviders,和service等
应用不需要关心需要Binder线程,因为系统会优先将请求转换为只用主线程。
一个典型的需要使用Bnder线程的场景是应用提供一个给其他进程通过AIDL接口绑定的service
后台线程
在应用中显示创建的线程都是后台线程,也就是当刚创建出来时,这些线程的执行体是空的,需要手动添加任务在Linux系统层面,主线程和后台线程是一样的。在Android框架中,通过WindowManager赋予了主线程只能处理UI更新以及后台线程不能直接操作UI的限制。

HandlerThread

HandlerThread集成了Looper和MessageQueue的线程,并启动HandlerThread时,会共同生成Looper和MessageQueue,然后等待消息进行处理,它的run方法如下

使用HandlerThread的好处是开发者不需要自己创建和维护Looper

HandlerThread handlerThread = new HandlerThread("123");
handlerThread.start();

handler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

Handler中只有一个消息队列,队列中消息的是顺序执行的,因此是线程安全的,吞吐量会收到一定影响,队列中的任务可能会被前面没有执行完的任务阻塞。
HandlerThread的内部机制确保创建Looper和发送消息之间不存在竞态条件,这个是通过将HandlerThread.getLooper()实现为一个阻塞操作,只有当HandlerThread准备好接受消息之后才返回。

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        //如果线程已经启动,那么looper准备好之前应先等待
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

如果具体的业务要求在HandlerThread开始接受消息之前要进行默写初始化操作,可以重写HandlerThread的onLooperPrepared函数。

intentService

intentService具有service一样的生命周期,同时也提供了后台线程中处理异步机制。
intentService在一个后台线程中顺序执行所有任务。
通过Context.startService传递一个Intent类型的参数启动intentService的异步执行。
如果此时intentService正在运行中,那么这个新的intent将会进入队列进行排队,直到后台处理完队列前面的任务;
如果此时intentService没有在运行,那么将会启动一个新的intentService,
当后台线程队列的所有任务执行完成之后,intentService将结束它的生命周期,因此intentService不需要手动结束。
intentService是通过HandlerThread来实现后台处理任务。

使用
intentService是一个抽象类,使用前需要继承它并实现onHandlerIntent方法,这个方法中实现具体的后台处理业务逻辑,同时需要在子类的构造方法中需要调用super(String name)传入子类的名字。
语法如下

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super(MyIntentService.class.getName());
        setIntentRedelivery(true);
        //setIntentRedelivery设置为true,那么intentService的onStartCommand方法返回START_REDELIVER_INTENT.
        //如果onHandleIntent返回之前进程死掉了,那么进程会重新启动,intent将会重新投递。
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        //这个方法是在后台线程中调用
    }
}

当然在AndroidManifest.xml文件中进行注册

Executor

创建和销毁对象们都是存在开销的。
如果应用中频繁的创建和销毁线程,改善应用体验。
Executor的框架定义是一个名为Executor接口定义,Executor主要目的是分离任务的创建和它的执行。

public interface Executor {
    void execute(Runnable command);
}

可以自己实现Executor并重写execute直接创建线程来执行Runnable

public class MyExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        new Thread(command).start();
    }
}

预定义线程池

实际开发中execute,需要增加类似队列,任务优先级等功能,最终实现一个线程池。

ThreadPoolExecutor

预定义线程池都是基于ThreadPoolExecutor 类上创建的,通过ThreadPoolExecutor 开发者可以自定义线程池的一些线程池的一些行为。
**构造函数的五个参数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

//参 1corePoolSize 核心线程池
核心线程会一直存在于线程池中,即使当前没有任务需要处理。
当线程数小于核心线程数时,即使当前有空闲的线程,线程池也会优先创建线程来处理任务。
//参2 maximumPoolSize 最大线程数
当线程数大于核心线程数,且任务队列已经满了,这时线程池会创建新的线程,直到线程数量达到最大线程数为止。
//参3 keepAliveTime 线程的空闲存活时间
当线程的空闲时间超过这个值时,线程会被销毁,直到线程数等于核心线程数。
//参4 unit keepAliveTime的单位
可选的有TimeUnit.NANOSECONDS,MICROSECONDS,MILLISECONDS,SECONDS。
//参5 workQueue 线程池所使用的任务缓冲队列**
-放等待任务的--有效的缓存序列BlockingQueue<Runnable>

执行

//3. 创建自定义的线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
        3,//核心线程池
        6,//最大运行的线程数量
        1,//存活时间,指定是等待任务的存活时间
        TimeUnit.HOURS,//时间单位
        new LinkedBlockingQueue<Runnable>()//缓冲队列,用来存放那些处于等待中的 任务---一般都这样写
);
threadPoolExecutor.execute(new DownloadTask(1));
//移除线程池
//threadPoolExecutor.remove()
//glide也是多任务的
}

/**
* 创建下载任务--继承Runnable
* 下载任务,负责完成去服务器下载文件
* 它里面的作用就是http下载模块的功能
*/
class DownloadTask implements Runnable {
private int num;
public DownloadTask(int num) {
    this.num = num;//这个参数是为了区分线程设立的
    Log.e("tag", "线程" + num + " waitting。。。");
}
//执行耗时操作--run方法执行说明已经开始进行操作了
@Override
public void run() {
    Log.e("tag", "线程" + num + "开始运行。。。");
    SystemClock.sleep(5000);
    Log.e("tag", "线程" + num + "over了----------");
}
}

Executor框架为开发者提供了预定义的线程实现

1:FixedThreadPool-线程数量固定的线程池。
特点:当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭。当所有线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来
创建:Executors.newFixedThreadPool(3); 参数表示线程的个数
2:CachedThreadPool缓存型池子线程数量不定的线程池
特点:它只有非核心线程,当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。空闲线程会等待60s来执行新任务
能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
创建:Executors.newCachedThreadPool();
3:ScheduleThreadPool - 核心线程数量是固定的
特点:核心线程固定,非核心线程没有限制,并且当非核心线程闲置时,被立即回收
创建:Executors.newScheduledThreadPool(3);
4:SingleThreadExcutor - 线程池内部只有一个核心线程
特点:它确保所有的任务都在同一个线程中按顺序执行。
创建: Executors.newSingleThreadExecutor();

AsyncTask

AsyncTask是在框架基础上进行的封装,它实现将耗时任务移到工作线程中执行,同时提供方便的接口实现工作线程和主线程通信
一个应用中使用所有AsyncTask实例会共享全局的属性,也就是说AsyncTask中的任务使串行执行,那么应用中的所有的AsyncTask都会进行排队,只有等前面的任务执行完成之后才会执行下一个AsyncTask中的任务,在mTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,"aa");或者在APILevel大于13的系统上execute都有这个效果
如果是异步执行,AsyncTask中的ThreadPoolExecutor 指定的核心线程数是CPU+1。
即:在四核CPU上,最多只有5个任务可以同时进行,其他任务需要在队列中排队,等待空闲的线程。(昨天看貌似变了)

public abstract class AsyncTask<Params, Progress, Result> {
    private static final String LOG_TAG = "AsyncTask";

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;
    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
}

使用

VirusTask mTask = new VirusTask();
// 执行任务—参数可变参数
mTask.execute("haha", "hehe");

//AsyncTask执行任务每次只能执行一次.因此再次调用要重新new对象
//定义子类继承此抽象类
// 泛型1 运行中 指定doInBackground的参数类型 也是execute方法的参数类型
// 泛型2 进度更新 onProgressUpdate的参数类型 publishProgress的参数类型
// 泛型3 结束 onPostExecute的参数类型 doInBackground的返回值类型

private class VirusTask extends AsyncTask<String, Integer, String> {
    //执行任务前会指定的方法 运行在UI线程做一些准备工作
    @Override
    protected void onPreExecute() {
        super.onPreExecute();

    }

    //doInBackground处理耗时任务 运行在子线程(默认只重写此方法)(其他方法都在ui线程)
    @Override
    protected String doInBackground(String... params) {
        return "aaaaa";
    }

    //onPostExecute任务结束后执行的方法 运行在UI线程一般用来更新UI
    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
    }

    //onProgressUpdate更新进度 运行在UI线程
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }
}

Handler消息异步处理机制

执行流程
Handler在创建时会与当前所在的线程的Looper对象相关联(如果当前线程的Looper为空或不存在,则会抛出异常。
此时需要在线程中主动调用Looper.prepare()来创建一个Looper对象)。handler通过send或post发送消息,sendmessage中调用messageQueue的enqueueMesssage将消息传入messageQueue中,Looper的loop方法调用MessageQueue的next方法返回信息,通过msg.Target.DispatchMessage来处理信息,callback中实现处理。一般会调用handler的handlermessage方法

Handler使用

方式一: post(Runnable)
创建一个工作线程,实现 Runnable 接口,实现 run 方法,处理耗时操作

new Thread(new Runnable() {
   @Override
   public void run() {
       /**
          耗时操作
        */
      handler.post(new Runnable() {
          @Override
          public void run() {
              /**
                更新UI
               */
          }
      });
   }
 }).start();

创建一个 handler,通过 handler.post/postDelay,投递创建的 Runnable,在 run 方法中进行更新 UI 操作。

方式二: sendMessage(Message)
创建一个工作线程,继承 Thread,重新 run 方法,处理耗时操作
创建一个 Message 对象,设置 what 标志及数据
通过 sendMessage 进行投递消息
创建一个handler,重写 handleMessage 方法,根据 msg.what 信息判断,接收对应的信息,再在这里更新 UI。

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {      //判断标志位
            case 1:
                /**
                 获取数据,更新UI
                */
                break;
        }
    }
};


public class WorkThread extends Thread {

    @Override
    public void run() {
        super.run();
       /**
         耗时操作
        */

        //从全局池中返回一个message实例,避免多次创建message(如new Message)
        Message msg =Message.obtain();  
        msg.obj = data;
        msg.what=1;   //标志消息的标志
        handler.sendMessage(msg);
    }

}

new WorkThread().start();

Handler 存在的问题

内存方面
Handler 被作为 Activity 引用,如果为非静态内部类,则会引用外部类对象。当 Activity finish 时,Handler可能并未执行完,从而引起 Activity 的内存泄漏。故而在所有调用 Handler 的地方,都用静态内部类。
异常方面
当 Activity finish 时,在 onDestroy 方法中释放了一些资源。此时 Handler 执行到 handlerMessage 方法,但相关资源已经被释放,从而引起空指针的异常。
避免
如果是使用 handlerMessage,则在方法中加try catch。
如果是用 post 方法,则在Runnable方法中加try catch。

Handler 的改进

内存方面:使用静态内部类创建 handler 对象,且对 Activity 持有弱引用
异常方面:不加 try catch,而是在 onDestory 中把消息队列 MessageQueue 中的消息给 remove 掉。则使用如下方式创建 handler 对象:

/**
 * 为避免handler造成的内存泄漏
 * 1、使用静态的handler,对外部类不保持对象的引用
 * 2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
 */
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityReference;    

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = (MainActivity) mActivityReference.get();  //获取弱引用队列中的activity
        switch (msg.what) {    //获取消息,更新UI
            case 1:
                byte[] data = (byte[]) msg.obj;
                activity.threadIv.setImageBitmap(activity.getBitmap(data));
                break;
        }
    }
}

并在 onDesotry 中销毁:

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
    handler.removeCallbacks(postRunnable);   //清除runnable对应的message
    //handler.removeMessage(what)  清除what对应的message
}

Handler 的使用实现

耗时操作采用从网络加载一张图片

继承 Thread 或实现 Runnable 接口的线程,与 UI 线程进行分离,其中 Runnable 与主线程通过回调接口进行通信,降低耦合,提高代码复用性。
在 Activity 中创建 handler 对象,调用工作线程执行

public class MainActivity extends AppCompatActivity {

ImageView threadIv;
ImageView runnableIv;
SendThread sendThread;
PostRunnable postRunnable;
private final MyHandler handler = new MyHandler(this);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    threadIv = (ImageView) findViewById(R.id.thread_iv);
    runnableIv = (ImageView) findViewById(R.id.runnable_iv);

    sendThread = new SendThread(handler);
    sendThread.start();

    postRunnable = new PostRunnable(handler);
    postRunnable.setRefreshUI(new PostRunnable.RefreshUI() {
        @Override
        public void setImage(byte[] data) {
            runnableIv.setImageBitmap(getBitmap(data));
        }
    });
    new Thread(postRunnable).start();
}

/**
  为避免handler造成的内存泄漏
  1、使用静态的handler,对外部类不保持对象的引用
  2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
 /
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityReference;

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = (MainActivity) mActivityReference.get();  //获取弱引用队列中的activity
        switch (msg.what) {    //获取消息,更新UI
            case 1:
                byte[] data = (byte[]) msg.obj;
                activity.threadIv.setImageBitmap(activity.getBitmap(data));
                break;
        }
    }
}

private Bitmap getBitmap(byte[] data) {
    return BitmapFactory.decodeByteArray(data, 0, data.length);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
    handler.removeCallbacks(postRunnable);   //清除runnable对应的message
    //handler.removeMessage(what)  清除what对应的message
}
}

方式一:实现 runnable 接口,通过 post(Runnable)通信,并通过给定的回调接口通知 Activity 更新

public class PostRunnable implements Runnable {

    private Handler handler;
    private RefreshUI refreshUI;
    byte[] data = null;

    public PostRunnable(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        /**
         * 耗时操作
         */
        final Bitmap bitmap = null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet("http://i3.17173cdn.com/2fhnvk/YWxqaGBf/cms3/FNsPLfbkmwgBgpl.jpg");
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                data = EntityUtils.toByteArray(httpResponse.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //返回结果给UI线程
        handler.post(new Runnable() {
            @Override
            public void run() {
                refreshUI.setImage(data);
            }
        });
    }

    public interface RefreshUI {
        public void setImage(byte[] data);
    }

    public void setRefreshUI(RefreshUI refreshUI) {
        this.refreshUI = refreshUI;
    }
}

方式二:继承Thread,通过handler的sendMessage通信

public class SendThread extends Thread {

    private Handler handler;

    public SendThread(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        super.run();
        /**
         * 耗时操作
         */
        byte[]data=null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet("https://d36lyudx79hk0a.cloudfront.net/p0/descr/pc27/3095587d8c4560d8.png");
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            if(httpResponse.getStatusLine().getStatusCode()==200){
                data= EntityUtils.toByteArray(httpResponse.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //返回结果给UI线程
        doTask(data);
    }

    /**
     * 通过handler返回消息
     * @param data
     */
    private void doTask(byte[] data) {
        Message msg =Message.obtain();  //从全局池中返回一个message实例,避免多次创建message(如new Message)
        msg.obj = data;
        msg.what=1;   //标志消息的标志
        handler.sendMessage(msg);
    }
}

Handler 通信机制

创建Handler,并采用当前线程的Looper创建消息循环系统;
Handler通过sendMessage(Message)或Post(Runnable)发送消息,调用enqueueMessage把消息插入到消息链表中;
Looper循环检测消息队列中的消息,若有消息则取出该消息,并调用该消息持有的handler的dispatchMessage方法,回调到创建Handler线程中重写的handleMessage里执行。
Handler 如何关联 Looper、MessageQueue
1、Handler 发送消息

Message msg =Message.obtain(); 
msg.obj = data;
msg.what=1;   //标志消息的标志
handler.sendMessage(msg);

sendMessageQueue开始追踪,函数调用关系:sendMessage -> sendMessageDelayed ->sendMessageAtTime,在sendMessageAtTime中,携带者传来的message与Handler的mQueue一起通过enqueueMessage进入队列了。
对于postRunnable而言,通过post投递该runnable,调用getPostMessage,通过该runnable构造一个message,再通过 sendMessageDelayed投递,接下来和sendMessage的流程一样了。
2、消息入队列
在enqueueMessage中,通过MessageQueue入队列,并为该message的target赋值为当前的handler对象,记住msg.target很重要,之后Looper取出该消息时,还需要由msg.target.dispatchMessage回调到该handler中处理消息。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

在MessageQueue中,由Message的消息链表进行入队列

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

3、Looper 处理消息
再说处理消息之前,先看Looper是如何构建与获取的:
构造Looper时,构建消息循环队列,并获取当前线程

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

但该函数是私有的,外界不能直接构造一个Looper,而是通过Looper.prepare来构造的:

public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

这里创建Looper,并把Looper对象保存在sThreadLocal中,那sThreadLocal是什么呢?

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

它是一个保存Looper的TheadLocal实例,而ThreadLocal是线程私有的数据存储类,可以来保存线程的Looper对象,这样Handler就可以通过ThreadLocal来保存于获取Looper对象了
TheadLocal 如何保存与获取Looper?

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

public T get() {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
       values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

在 set 中都是通过 values.put 保存当前线程的 Looper 实例,通过 values.getAfterMiss(this)获取,其中put和getAfterMiss都有key和value,都是由Value对象的table数组保存的,那么在table数组里怎么存的呢?

table[index] = key.reference;
table[index + 1] = value;

很显然在数组中,前一个保存着ThreadLocal对象引用的索引,后一个存储传入的Looper实例。
接下来看Looper在loop中如何处理消息
在loop中,一个循环,通过next取出MessageQueue中的消息
若取出的消息为null,则结束循环,返回。
设置消息为空,可以通过MessageQueue的quit和quitSafely方法通知消息队列退出。
若取出的消息不为空,则通过msg.target.dispatchMessage回调到handler中去。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
            Long.toHexString(ident) + " to 0x"
            Long.toHexString(newIdent) + " while dispatching to "
            msg.target.getClass().getName() + " "
            msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

4、handler处理消息
Looper把消息回调到handler的dispatchMessage中进行消息处理:
若该消息有callback,即通过Post(Runnable)的方式投递消息,因为在投递runnable时,把runnable对象赋值给了message的callback。

若handler的mCallback不为空,则交由通过callback创建handler方式去处理。

否则,由最常见创建handler对象的方式,在重写handlerMessage中处理。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

子线程中创建handler,处理消息

public class MainActivity extends Activity {
    TextView valeTv;
    // 定义键值对
    private String KEY = "key";
    private String VALUE = "value";

    // 定义一个自己的线程
    class MyThread extends Thread {
        public Handler mHandler;

        @Override
        public void run() {
            System.out.println("线程开始运行");
            Looper.prepare();// 在线程中必须建立一个自己的looper,不能用ui线程中的
            mHandler = new Handler() {// 在新线程中创建Handler时必须创建Looper
                public void handleMessage(Message msg) {
                    if (msg.what == 111) {
                        String str = msg.getData().getString(KEY);
                        // valeTv.setText(str);//不能更新ui
                        Toast.makeText(MainActivity.this, str, 0).show();
                    }
                };
            };
            Looper.loop();
        }

    }

    private MyThread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        valeTv = (TextView) findViewById(R.id.vale_textView);
        thread = new MyThread();
        // 启动线程
        thread.start();
        //sentMsg();//发在这很危险,因为发送消息的动作可能在线程执行前,这样就出错了
    }

    public void buttonListener(View v) {
        sentMsg();//因为点击事件肯定在线程开始执行后才进行,所以这里是正确的
    }

    private void sentMsg() {
        // 创建消息
        Message msg = new Message();
        msg.what = 111;
        Bundle bundle = new Bundle();
        bundle.putString(KEY, VALUE);
        // 设置数据
        msg.setData(bundle);

        System.out.println("向线程发送消息");// 这句话必须在“线程开始运行”后打印才表示正确
        // 发送消息,由于线程执行的时间不固定,这句话必须放在线程start后的一段时间才行。这里放在点击事件中,确保线程已经开始执行了。
        thread.mHandler.sendMessage(msg);
    }
}

内存泄漏
一是在Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null);取消所有的消息的处理,包括待处理的消息;
二是声明handler的内部类为static。

参考

《Android高级进阶》
http://www.jianshu.com/p/0a274564a4b1
http://www.cnblogs.com/tianzhijiexian/p/3880581.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容