Handler消息传递机制

引言:

出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题。

为了解决这个问题,Android制定了一条简单的规则:只允许UI线程修改Acitivity里的组件,这样就会导致新启动的线程无法动态改变组建的属性值。

但在实际的应用开发中,需要让新启动的线程改变界面组件的属性值,这就需要借助与Handler的消息传递机制来实现了。

一、Handler的使用

Handler类的主要作用有两个:

》在新启动的线程中发送消息

》在主线程中获取,处理消息

为了让主线程能“适时”的处理新启动的线程发送的消息,只能通过回调的方式来实现。

重写Handler类中处理消息的方法;新启动的线程发送消息时消息会发送到与之关联的MessageQueue;而Handler会不断地从MessageQueue种获取并处理消息;这将导致handler类中处理消息的方法被回调。

new Thread(new Runnable() {
    @Override
    public void run() {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Message msg = handler.obtainMessage();
    msg.what = Constant.USER_LOGIN;
    msg.obj = "您尚未注册,是否快捷登录";
    handler.sendMessage(msg);
    }
}).start();
Handler mHandler = new Handler() {

    public void handleMessage(android.os.Message msg) {
        super.handleMessage(msg);
        if (activity.isFinishing()) {
          return;
        }
        if (msg.what == Constant.USER_LOGIN) {
          ToastUtils.showShort(activity, msg.obj.toString());
        }
    }
};

二、与Handler一起工作的几个组件,Looper、MessageQueue

》Looper:每个线程只有一个Looper,负责管理MessagQueue,会不断地总MessageQueue中取出消息,并将消息分给对应的Handler处理。

》MessageQueue:由Looper负责管理。采用先进先出的方式来管理Message。

》Handler:把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

三、使用Handler源码分析:

在线程中使用Handler的步骤如下。

private class LooperThread extends Thread {
    public Handler mHandler;

    @Override
    public void run() {
    Looper.prepare();
    mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
        Log.i(TAG, msg.toString());
        }
    };
    Looper.loop();
    }
}

-调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,构造器会创建与之配套的MessageQueue。

public final class Looper {
    private static final String TAG = "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 Printer mLogging;
}  
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");
    }
    //这里new一个Looper对象
    sThreadLocal.set(new Looper(quitAllowed));  }
}  

-有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。

//构造方法
public Handler() {this(null, false);}  

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }  

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}  

-调用Looper的loop()方法启动Looper。

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;

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); //获取消息队列的笑一个消息,没有消息将会阻塞
        if (msg == null) {
            // 如果消息为空,表明消息队列正在退出
            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);
        }

        // 使用final修饰标识符,保证在分发消息的过程中线程标识符不会被更改
        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.recycle();
    }
}
上一篇:第二节 Cobalt Strike使用重定器


下一篇:【渗透测试笔记】之【内网渗透——Cobalt Strike用户驱动攻击】