【Android】Handler机制及其手写实现

1,UI只能在主线程中进行操作,否则程序异常。


以下就是一个错误的用法

public class DemoActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service_1);
 
        Button button = (Button) findViewById(R.id.service_button);
        final TextView textView = (TextView) findViewById(R.id.service_text);
 
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("改变的结果");//子线程中修改了UI,错误!
                    }
                }).start();
            }
        });
    }
}

 android提供正确方式之一是使用Handler,或者AsyncTask。

private Handler handler = new Handler() {
 
        @Override
        public void handleMessage(@NonNull Message msg) {
            //此处在主线程中执行,可以更新UI
            switch (msg.what) {
 
                case UPDATE_TEXT:
                    //在此处进行UI更新
                    textView.setText("has changed!");
                    break;
                default:
                    break;
            }
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service_1);
 
        Button button = (Button) findViewById(R.id.service_button);
        button.setOnClickListener(this);
        textView = (TextView) findViewById(R.id.service_text);
 
    }
 
    @Override
    public void onClick(View v) {
        Log.d("Activity1", "click!");
 
        switch (v.getId()) {
 
            case R.id.service_button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        //发送信息
                        handler.sendMessage(message);
                    }
                })
        }
    }
}


或者使用AsyncTask进行异步任务,UI更新重写onProgressUpdate函数即可,

class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
 
 
    private ProgressDialog progressDialog = new ProgressDialog(null);
 
    @Override
    protected void onPreExecute() {
        //任务之前的准备
        progressDialog.show();
 
    }
 
    @Override
    protected void onProgressUpdate(Integer... values) {
       //任务同步更新UI
        progressDialog.setMessage("Downloaded"+values[0]+"%");
 
    }
 
    @Override
    protected Boolean doInBackground(Void... voids) {
 
        //具体任务
 
        return null;
    }
 
    @Override
    protected void onPostExecute(Boolean aBoolean) {
       //任务完成后的工作
       //关闭进度条
        progressDialog.dismiss();
    }
}

2,Handler是什么?咱们来研究实现原理,并且尝试手写一个Handler的实现。

(1) Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。
(2) Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。
(3) MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
(4) Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。

简而言知,Java层Handler Api是给某个线程处理消息的,一般主线程处理UI消息。而消息的处理需要消息队列,即MessageQueue,本质是一个优先级队列,根据Message.when字段排序。而消息的提取需要Looper,该类线程唯一,通过ThreadLocal<Looper>保证。该类调用MessageQueue的next方法,如果有消息则发给对应handler处理,否则阻塞(有消息后唤醒)。

ThreadLocal实现很简单,通过唯一的ThreadLocal对象,将其保存在各个线程的Map中,ThreadLocal.this作为key,即保证了线程唯一。因为每个线程的map都不同,这点可通过get方法知道。

 那么如何正确使用Handler呢?查看ActivityThread.main方法(prepareMainLooper本质还是调用了prepare方法),先在线程中准备Looper,然后运行Looper.loop()进入消息循环,此处就是用户进程的创建入口,也是UI线程。该入口是从Zygote进程fork出来的,具体可以查看我写的Activity-R启动流程。

 ActivityThread中有一个handler H,该类重写handelrMessage方法,用于处理特定的消息。同时,Looper.perpareMainLooper静态变量mainLooper保存了主线程的Looper,即可与主线程通信,实现相应逻辑。

咱们来手写实现一个简单的Handler机制吧!首先Handler机制包含四个核心类:

Handler、Looper、Message、MessageQueue。

Handler即处理消息的具体执行者,一个Looper下可以有多个Handler哈;

package _AndroidFramework.Handler机制;
 
 
//手写一个android handler
public abstract class Handler {
 
    private Message mMessage;
    private Looper mLooper;
 
 
    public Handler() {
        Looper looper = Looper.looperThreadLocal.get();
        if (looper == null) {
            throw new Error("looper not exist where current thread !");
        }
        this.mLooper = looper;
    }
 
    public Handler(Looper looper) {
        if (looper == null) {
            throw new NullPointerException("");
        }
        this.mLooper = looper;
    }
 
    public void post(Runnable runnable) {
        this.mMessage = Message.obtain();
        mMessage.callback = runnable;
        mMessage.when = 0L;
        mMessage.what = null;
        this.sendMessage(mMessage);
    }
 
    public void sendMessage(Message message) {
        message.sendWhen = System.currentTimeMillis();
        message.target = this;
        this.mLooper.getMessageQueue().offer(message);
    }
 
    protected abstract void handleMessage(Message message);
 
    public Looper getLooper() {
        return mLooper;
    }
 
    public void setLooper(Looper mLooper) {
        this.mLooper = mLooper;
    }
}

Looper需要保持线程唯一,通过ThreadLocal实现;

package _AndroidFramework.Handler机制;
 
import java.util.ArrayList;
import java.util.List;
 
//循环读取者
public class Looper {
 
    public static ThreadLocal<Looper> looperThreadLocal;
    private static Looper mMainLooper;
 
    static {
        looperThreadLocal = new ThreadLocal<>();
    }
 
    private Message mMessage;
    private MessageQueue mMessageQueue = new MessageQueue();
    private volatile boolean quit = false;
 
    private Looper() {
    }
 
    public static Looper getLooper() {
        return looperThreadLocal.get();
    }
 
    public static Looper getMainLooper() {
        return mMainLooper;
    }
 
    public static void prepare() {
        if (looperThreadLocal.get() != null) {
            throw new Error("current thread already has prepared!");
        }
 
        looperThreadLocal.set(new Looper());
    }
 
    public static void prepareMain() {
        prepare();
        mMainLooper = getLooper();
    }
 
    public static void loop() {
        //do something
        Looper looper = looperThreadLocal.get();
        if (looper == null) {
            throw new Error("please use prepare() to create looper!");
        }
 
        while (true) {
 
            looper.mMessage = looper.mMessageQueue.peek();
            if (looper.mMessage == null || !looper.mMessage.doHandleMessage()) {
                continue;
            }
 
            //开始处理消息
            looper.mMessage = looper.mMessageQueue.poll();
            looper.mMessage.target.handleMessage(looper.mMessage);
            //处理完消息后,放入缓存池
            Message.addCache(looper.mMessage);
            looper.mMessage = null;
            if (looper.quit) return;
        }
    }
 
    public void quit() {
        this.quit = true;
    }
 
    public MessageQueue getMessageQueue() {
        return mMessageQueue;
    }
}

处理的具体实例就是Message,我们通过享元模式实现;

package _AndroidFramework.Handler机制;
 
import java.util.LinkedList;
 
//消息
public final class Message {
 
 
    private static LinkedList<Message> cache;
    private static final int CACHE_SIZE = 30;
 
    static {
        cache = new LinkedList<>();
        for (int i = 0; i < CACHE_SIZE; i++) {
            cache.addLast(new Message());
        }
    }
 
    public String what;
    public Long when;
    public Runnable callback;
    public Long sendWhen;
    public Object arg1;
    public Object arg2;
    public Handler target;
 
    //是否可以处理当前消息?
    public boolean doHandleMessage() {
        return System.currentTimeMillis() >= sendWhen + when;
    }
 
    /**
     * 享元模式
     **/
    public static Message obtain() {
        if (cache.size() >= CACHE_SIZE) {
            return new Message();
        }
 
        return cache.removeFirst();
    }
 
    public static void addCache(Message message) {
        if (message == null) return;
        if (cache.size() >= CACHE_SIZE) return;
 
        message.when = null;
        message.sendWhen = null;
        message.what = null;
        message.callback = null;
        message.target = null;
        cache.addLast(message);
    }
 
    @Override
    public String toString() {
        return "Message{" +
                "what='" + what + '\'' +
                ", when=" + when +
                ", sendWhen=" + sendWhen +
                ", arg1=" + arg1 +
                ", arg2=" + arg2 +
                '}';
    }
}

存放消息的地方就是消息队列,也是Looper观察读取的地方,和Looper一样保持线程唯一,并且保持消息插入的有序性,本质就是优先级队列。

package _AndroidFramework.Handler机制;
 
//优先级队列
public class MessageQueue {
 
 
    //队列大小
    private int size;
 
    //队列头
    private Node head;
 
    //队列尾
    private Node tail;
 
 
    public MessageQueue() {
        this.size = 0;
        this.head = null;
        this.tail = null;
    }
 
    /**
     * 维持的有序列表节点
     **/
    private static class Node {
        Node pre;
        Node next;
        Message message;
 
        public Node(Message message) {
            this.message = message;
        }
    }
 
 
    private boolean insertToLinkTail(Node node) {
 
        if (this.head == null) {
            this.head = this.tail = node;
            return true;
        }
 
        //顺序插入
        Node p = head;
        while (p != null) {
            if (p.message.when > node.message.when) {
                break;
            }
            p = p.next;
        }
        if (p == head) {
            //头部
            p.pre = node;
            node.next = p;
            head = node;
            return true;
        } else if (p == null) {
            //尾部
            tail.next = node;
            node.pre = tail;
            tail = node;
            return true;
        } else {
            //中间
            Node pre = p.pre;
            pre.next = node;
            node.next = p;
            node.pre = pre;
            p.pre = node;
            return true;
        }
    }
 
    private Node removeFromLinkHead() {
 
        if (head == null) return null;
 
        Node p = head;
        Node newHead = head.next;
        if (newHead == null) {
            head = tail = null;
        } else {
            newHead.pre = null;
            head = newHead;
        }
        return p;
    }
 
 
    /**
     * 消息api
     **/
 
    public boolean offer(Message message) {
        synchronized (this) {
            if (this.insertToLinkTail(new Node(message))) {
                this.size++;
                return true;
            }
            return false;
        }
    }
 
    public Message poll() {
        synchronized (this) {
            Node node = this.removeFromLinkHead();
 
            if (node == null) {
                return null;
            }
            this.size--;
            return node.message;
        }
    }
 
    public Message peek() {
        synchronized (this) {
            if (head == null) return null;
            return head.message;
        }
    }
 
    public int getSize() {
       synchronized (this){
           return this.size;
       }
    }
}

咱们测试一下吧!测试代码如下。

package _AndroidFramework.Handler机制;
 
public class Test {
    public static void main(String[] args) {
        Looper.prepareMain();
 
        sendMessageInOtherThread();
 
        //主线程创建完毕,开始接听new Loop消息
        Looper.loop();
    }
 
    public static void sendMessageInOtherThread() {
 
        new Thread(() -> {
 
            Handler handler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message message) {
                    if (message.callback != null) {
                        message.callback.run();
                    }
                }
            };
 
 
            Message message = Message.obtain();
            message.callback = () -> System.out.println(Thread.currentThread().getName() + "   handler1  我是消息1---" + System.currentTimeMillis());
            message.when = 1000L;
 
            Message message2 = Message.obtain();
            message2.callback = () -> System.out.println(Thread.currentThread().getName() + "   handler1  我是消息2---" + System.currentTimeMillis());
            message2.when = 3000L;
 
            Message message3 = Message.obtain();
            message3.callback = () -> {
                System.out.println(Thread.currentThread().getName() + "   handler1  我是消息3---我要销毁主程序" + System.currentTimeMillis());
                Looper.getMainLooper().quit();
            };
            message3.when = 7000L;
 
            handler.sendMessage(message);
            handler.sendMessage(message2);
            handler.sendMessage(message3);
            System.out.println(Thread.currentThread().getName() + "   handler1 发送时间---" + System.currentTimeMillis());
 
        }).start();
 
        new Thread(() -> {
 
            Handler handler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message message) {
                    if (message.callback != null) {
                        message.callback.run();
                    }
                }
            };
 
 
            Message message = Message.obtain();
            message.callback = () -> System.out.println(Thread.currentThread().getName() + "   handler2  我是消息1---" + System.currentTimeMillis());
            message.when = 4000L;
 
            Message message2 = Message.obtain();
            message2.callback = () -> System.out.println(Thread.currentThread().getName() + "   handler2  我是消息2---" + System.currentTimeMillis());
            message2.when = 2000L;
 
            Message message3 = Message.obtain();
            message3.callback = () -> {
                System.out.println(Thread.currentThread().getName() + "   handler2  我是消息3---我要销毁主程序" + System.currentTimeMillis());
                Looper.getMainLooper().quit();
            };
            message3.when = 6000L;
 
            handler.sendMessage(message);
            handler.sendMessage(message2);
            handler.sendMessage(message3);
            System.out.println(Thread.currentThread().getName() + "   handler2 发送时间---" + System.currentTimeMillis());
 
        }).start();
    }
}

结果:

 ok,本次的实现就写到这,注意上述代码只是对Java层Handler的模拟,Android的Looper使用的监听机制是epoll,通过native层实现。

-------------------------------------------------------------------------

补充一个知识点,消息屏障

消息屏障本质是没有target属性的message,当消息队列遇到屏障消息后,阻塞所有同步消息,优先执行异步消息,以下是messageQueue相关代码,方法名postSyncBarrier就名副其实,异步消息的屏障呢,

private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
            //根据时间,找到插入队列位置,保存到prev
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            // 插入屏障消息
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
            //队头,直接放到消息队列头部
                msg.next = p;
                mMessages = msg;
            }
            //返回mNextBarrierToken++ 结果
            return token;
        }
    }

那么,进入next函数,咱看看屏障消息如何使用。

如果target字段为null,代表这是个屏障消息,此后迭代消息队列,找到最近异步消息,满足条件后就执行,

 那么如何移除屏障?通过removeSyncBarrier方法,很简单的,

 //传入屏障消息唯一识别taken,
 public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            //从队头迭代消息,找到屏障消息
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            //没有屏障,抛错
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            //移除屏障的链表操作
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

那么,如何传入异步消息?获取message后,通过调用setAsyncchronous方法,设置异步即可,

 public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

注意,MessageQueue中,消息屏障相关方法被标记为@hide,可以通过反射调用,

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值