安卓学习笔记之Handler

来源:互联网 发布:无间道知乎细节 编辑:程序博客网 时间:2024/06/10 22:24

UI线程

当系统启动的时候,就会创建一个主线程(Main Thread),然后这个主线程向UI组件分发事件,主线程和UI的组件进行交互,故称UI线程。


线程安全

Android的UI线程是不安全的。引用一下,百度百科的解释

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
from 百度百科

既然这样,Google给我们提供了更新ui界面的Handler类。


Handler

一个处理异步消息的类,创建一个子线程发送消息至主线程,在主线程更新相应的UI界面。比如在子线程进行长时间的网络操作,然后更新UI界面上的TextView。
使用的方法有两种,

1.post(runnable)&2.sendMessage(message)

先演示一下,所谓不能直接在UI线程更新TextView的操作。在XML布局文件里创建一个TextView。

package com.example.myapplication;import android.app.Activity;import android.os.Bundle;import android.support.annotation.Nullable;import android.widget.Button;import android.widget.TextView;/** * Created by Does on 2017/7/20. */public class HandlerExample extends Activity {    private TextView tv_content;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv_content= (TextView) findViewById(R.id.tv_content);        new Thread(){            @Override            public void run() {                try {                    Thread.sleep(5000);                    tv_content.setText("更新UI");                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }.start();    }}

写好之后,运行之后,
这里写图片描述

Exception:只有创建了视图层次结构的原始线程才能触及它的视图

为了解决这种问题,我们使用Handler


post(runnable)?

这里模拟一个简单的下载功能。XML文件里面添加两个组件,一个Button和一个Textview

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private TextView tv_content;    private Button btn_onclick;    private Handler handler=new Handler();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv_content= (TextView) findViewById(R.id.tv_content);        btn_onclick= (Button) findViewById(R.id.btn_onclick);        btn_onclick.setOnClickListener(this);    }    @Override    public void onClick(View view) {        DownLoadThread downLoadThread=new DownLoadThread();        downLoadThread.start();    }    class DownLoadThread extends  Thread{            @Override            public void run() {                try {                    System.out.println("文件正在下载...");                    Thread.sleep(2000);                    System.out.println("文件下载成功");                    Runnable runnable=new Runnable() {                        @Override                        public void run() {                            System.out.println("当前线程的id是:  "+Thread.currentThread().getId());                            MainActivity.this.tv_content.setText("textview已经改变");                        }                    };                    handler.post(runnable);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }    }}

运行效果如下,点击Button之前
这里写图片描述

点击Button之后
这里写图片描述


sendMessage(message)

public class HandlerExample extends Activity {    private TextView tv_content;    private Handler handler=new Handler(){        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case 1:                    tv_content.setText("UI变化了");                    break;                default:                    break;            }        }    };    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv_content= (TextView) findViewById(R.id.tv_content);        new Thread(){            @Override            public void run() {                try {                    Thread.sleep(5000);                    Message message=new Message();                    message.what=1;                    handler.sendMessage(message);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }.start();    }}

效果和上面的是一样的。

这个明显和上一个有些不同,多了几样的东西。Message(),sendMessage(),handleMessage()
首先要子线程中,创建一个Handler的一个对象handler,然后执行handler.sendMessage(message),
handler携带message的一个对象,message.what=1,给它一个标识,随便取。在handleMessage()方法里,就会接受到传过去的值,进而在handleMessage里进行UI的更新操作。


Handler运行机制

接下来我们说说内部机制的运行。主要有用到Looper,Handler,MessageQueu(消息队列).从源码入手,
当我们手机一启动的时候,系统默认主线程会先调用prepareMainLooper()方法。先执行prepare(false)

 public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            ...            sMainLooper = myLooper();        }    }

接着创建一个Looper对象,将ThreadLocal设置为线程安全的对象。

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));    }

接着sThreadLocal向Looper类里传过去quitAllowed,并在Looper()构造器里创建了一个MessageQueue的对象mQueue。

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

最后,再调用prepareMainLooper()中的myLooper(),取出线程安全的Looper对象。

public static Looper myLooper() {        return sThreadLocal.get();    }

这是系统帮我们做的事情!!
接着我们需要做一些事情更新UI的操作,我们在MainActivity方法中创建了一个Handler对象,

public Handler() {        this(null, false);    }    public Handler(Callback callback, boolean async) {        ...        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;    }

handler取出了系统为我们创建的Looper对象,并取出系统创建的mQueue对象。此时我们创建的handler就持有了系统刚创建的Looper对象和MessageQueue对象。
**

发送消息

**
当我们发送消息的时候,sendMessage

    sendMessage(Message msg)    sendMessageDelayed(Message msg, long delayMillis)    public final boolean sendMessageAtFrontOfQueue(Message msg) {        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, 0);    }    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

首先是取出Looper对象中MessageQueue在enqueueMessage()方法中,handler携带我们要发送的Message,然后放入消息队列MessageQueue中

boolean enqueueMessage(Message msg, long when) {        ...        boolean needWake;        synchronized (this) {            ...            msg.when = when;            Message p = mMessages;            if (p == null || when == 0 || when < p.when) {                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;                 prev.next = msg;            }        }        ...        return true;    }

循环取出

接着Looper调用loop()方法

public static void loop() {        final Looper me = myLooper();        ...        final MessageQueue queue = me.mQueue;        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            Message msg = queue.next();             ...            // This must be in a local variable, in case a UI event sets the logger            Printer logging = me.mLogging;            msg.target.dispatchMessage(msg);            ...            final long newIdent = Binder.clearCallingIdentity();            ...            msg.recycle();        }    }

myLooper()取出当前Looper对象
me.mQueue拿到当前的MessageQueue对象
queue.next();取出下一个消息
如果消息存在 则调用消息的msg.target.dispatchMessage(msg);

处理消息

这就是我们重写的方法,

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