Handler的使用

Handler的使用

一、Handler的两种使用场景

  • 在主线程(UI线程)使用
  • 在子线程中使用

1.在主线程(UI线程)创建使用Handler

private Handler mHandler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.d(TAG, "handleMessage: "+msg.what);
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mHandler.sendEmptyMessage(0);
        }
    }).start();

}

2.在子线程中创建使用Handler

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new Thread(new Runnable() {
        /**
         * handler使用步骤:
         * 1.Looper.prepare(); 先调用,并且只执行一次
         * 2.创建Handler,使用Handler
         * 3.Looper.loop();
         */
        @Override
        public void run() {

            /**
             * 给ThreadLocal设置了一个looper对象
             * 在looper的构造方法中,创建了一个messageQueue对象(信息队列)
             */
            Looper.prepare();

            /**
             * handler构造方法中做了两件事
             * 1.获得looper
             * 2.获得looper中的messageQueue
             */
            Handler handler=new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };

            /**
             * 最终将message发送到messageQueue里面
             * 在messageQueue里面,所有的message都是一时间为顺序从小到大排列
             */
            handler.sendEmptyMessage(0);

            /**
             * 顺次取出messageQueue里的message
             * 并执行message.target.dispatchMessage();其中message.target就是发送这个message的handler
             */
            Looper.loop();
        }
    }).start();
}

二、Handler的消息处理机制

  • 每个Handler实例都与一个线程和该线程的消息队列,Looper相关联。
  • 每个线程中只会有一个MessageQueue对象和一个Looper。
  • 消息不会直接添加到MessageQueue中,而是通过与Looper相关的Handler对象。
  • 通过ThreadLocal来保存每个线程中有且仅有一个的MessageQueue对象,Looper对象。

Message

定义一个序列化的消息对象可以发送给Handler,消息对象包含描述信息和任意数据对象。这个对象包含两个额外的int字段和一个额外的对象字段,允许您在许多情况下不进行分配。

源码解读:

/**
 * 用户定义的消息代码,以便接收者能够识别该消息的含义。
 */
public int what;

/**
 * 如果您只需要存储一些整数值的话,arg1和arg2是使用setData()的成本较低的替代方法。
 */
public int arg1; 
public int arg2;

/**
 * 发送给接收方的任意对象。
 */
public Object obj;

/** Constructor (but the preferred way to get a Message is to call Message.obtain()).
*/
public Message() {
}

//Looper的loop()方法中执行dispatchMessage()的Handler对象,也就是最终处理这条Message信息的Handler
Handler target;

private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;

private static final int MAX_POOL_SIZE = 50;

注意:源码中提到构造Message更好的方式是通过下面这种方式:

Message msg = Message.obtain()

Handler

Handler允许你发送和处理与线程的MessageQueue相关联的消息和Runnable对象。每个Handler实例都与一个线程和该线程的消息队列相关联。当您创建一个新的Handler时,它被绑定到正在创建它的线程的线程消息队列——从这时开始,它将向该消息队列发送消息和runnables,并在消息队列中执行它们。

public Handler(Callback callback, boolean async) {
    ...

    mLooper = Looper.myLooper();
    if (mLooper == null) {//不能在线程内部创建Handler,它没有调用looper.prepar()
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

/**
 * 子类必须实现这个来接收消息,并在这个方法中处理消息
 */
public void handleMessage(Message msg) {
}

/**
 * 将Runnable r添加到消息队列中。runnable将运行在该handler所绑定的线程上。
 *  
 * @param r The Runnable that will be executed.
 * 
 * @return Returns true if the Runnable was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 */
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

/**
 * sendMessage等sendXX()方法,除了sendMessageAtFrontOfQueue()最终都调用该方法
 */
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

MessageQueue(消息队列)

Low-level class holding the list of messages to be dispatched by a Looper.

MessageQueue是保存由Looper发送的消息列表的低级类。消息不会直接添加到MessageQueue中,而是通过与Looper相关的Handler对象,
每个线程中只会有一个MessageQueue对象。

源码解读:

// 如果消息队列可以退出,则是true
private final boolean mQuitAllowed;

@SuppressWarnings("unused")
private long mPtr; // 使用本机代码

Message mMessages;

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

Message next() {
    ...
}

void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        ...
    }
}

Looper

用于为线程运行消息循环的类。默认情况下,线程没有与它们相关联的消息循环(message loop);要创建一个线程,在该线程中调用prepare(),然后调用loop()开始无限循环。每当发现Message Queue中存在一条消息,就会将它取出并传递到Handler的handleMessage()方法中处理,直到停止循环为止。每个线程中只会有一个Looper对象。

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class

final MessageQueue mQueue;
final Thread mThread;

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {//每一个线程只能创建一个Looper
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

/**
 * 将当前线程初始化为一个looper,将其标记为应用程序的主要looper。
 * 您的应用程序的主要用户是由Android环境创建的,因此您不需要自己调用这个函数。
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

/**
 * 返回与当前线程相关联的Looper对象。如果调用线程没有关联到Looper,则返回null。
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

/**
 * 在这个线程中运行消息队列。一定要调用quit()来结束循环。
 */
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...

        try {//msg.target即是handler对象,dispatchMessage(msg)是分发给指定的handler处理该条message
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        msg.recycleUnchecked();
    }

}

/**
 * Quits the looper.
 */
public void quit() {
    mQueue.quit(false);
}

ThreadLocal

Thread Local是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储到数据,对于其他线程来说则无法获取到数据。通过ThreadLocal来保存每个线程中有且仅有一个的MessageQueue对象,Looper对象。

/**
 * 在当前线程的这个线程局部变量的副本中返回值。如果该变量对当前线程没有任何值,
 * 那么它将首先被初始化为initialValue()方法所返回的值。
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

三、主线程及UI线程的相关概念

当应用程序启动时,Android首先会开启一个主线程 (UI线程) , 主线程为管理界面中的UI控件,进行事件分发。不能把耗时操作放在主线程中,否则,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 “强制关闭”。

Activity源码解读:

ActivityThread mMainThread;
private Thread mUiThread;
// 在构建碎片控制器之前,必须有一个Handler
final Handler mHandler = new Handler();

final void attach(Context context, ActivityThread aThread,...) {
    mUiThread = Thread.currentThread();

    mMainThread = aThread;
    ...
}

/**
 * 在UI线程上运行指定的操作。如果当前线程是UI线程,则立即执行操作。
 * 如果当前线程不是UI线程,则将操作发送到UI线程的事件队列
 *
 * @param action 要在UI线程上运行的操作,
 */
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

四、从消息创建到消息处理

创建消息

每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。

创建消息

发送消息

UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。

发送消息

处理消息

UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

处理消息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值