android消息机制—Handler

栏目: IOS · Android · 发布时间: 5年前

内容简介:在android日常开发中我们经常会有从网上获取数据更新UI的需求,但是Goole出于安全考虑规定,只有android主线程才能更新UI,涉及到耗时操作的要放到子线程中处理。不过Google也为我们设计了Handler用于将子线程中的数据更新到UI线程。在android中Handler主要用来接受和发送消息,他的基本使用如下:

在android日常开发中我们经常会有从网上获取数据更新UI的需求,但是Goole出于安全考虑规定,只有android主线程才能更新UI,涉及到耗时操作的要放到子线程中处理。不过Google也为我们设计了Handler用于将子线程中的数据更新到UI线程。

项目源码

Handler的基本使用

在android中Handler主要用来接受和发送消息,他的基本使用如下:

//接受消息
@SuppressLint("HandlerLeak")
private Handler mHanlder = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 200:
               mTV_testHandler.setText((String) msg.obj);
            break;
        }
        super.handleMessage(msg);
    }
};

//在线程中发送消息
new Thread(new Runnable() {
    @Override
    public void run() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.what = 200;
                msg.obj = "这是Send发送的消息";
                mHanlder.sendMessage(msg);
            }
        }).start();
    }
}).start();
复制代码

子线程中能创建Handler吗

因为handler在创建的时候必须绑定Looper,否则回报 not called Looper.prepare() 异常,因此在线程中不能直接使用Handler空参构造方法;可以通过下面两种方式解决

第一种方式

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        Handler handler2 = new Handler();
        //一定要开启循环
        Looper.loop();
    }
});
复制代码

第二种方式

//传入一个Looper
Handler handler2=new Handler(looper);
复制代码

Handler如何与Looper关联

Handler关联Looper有两种方式,一种是在构造中传入Looper,另外是在线程中调用Looper.perpare()。

//构造1
public Handler() {
    this(null, false);
}
//构造2
public Handler(Callback callback) {
    this(callback, false);
}
//构造3
public Handler(Looper looper) {
    this(looper, null, false);
}
 //构造4
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
//构造5
public Handler(boolean async) {
    this(null, async);
}
//构造6
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());
        }
    }
    //关联Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //获取Looper中创建的MessageQueue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
 //构造7
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    //获取Looper中创建的MessageQueue
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

Handler总共有六种构造,其中 构造1、构造2、构造5 他们的本质都是调用了 构造6 在该构造中调用了Looper.myLooper()来关联Looper,另外从上面的代码中也可以知道 构造3、构造4 本质上调用了是 构造7 并且他们指定了关联的Looper。

Handler如何发送消息

Handler发送消息本质上有两种方式:

第一种handler.sendXXX()

通过mHandler.sendXXX()这种方式最后都会调用 sendMessageDelayed() ,下面我们来分析一下它的代码:

public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    //SystemClock.uptimeMillis()获取的是从开机到现在的毫秒数
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
复制代码

sendMessageDelayed的代码也很简单,就是将消息和计算的从开机到现在的时间加上我们设置的延迟发送时间(如果没有设置延迟时间默认为0)作为参数调用 sendMessageAtTime()方法 ,最后这个方法会将从Looper中获取的MessageQueue、消息和延迟时间传入enqueueMessage()方法中:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //将Hanlder赋值给Message的target
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //向MessageQueue插入消息
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

第二种mHandler.postXXX()

通过mHandler.postXXX()这种方式其本质和sendXXX()一样也是最后调用了sendMessageAtTime()方法发送消息,他们两个的区别是postXXX()方式要传入一个Runnable,这个Runnable会被包装成Message的callback。使用这种方式发消息比较方便一点。

//post发送消息
public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}
 //获取Post发送方式的信息
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    //将post传入的Runnable赋值给Message的callback变量
    m.callback = r;
    return m;
}
复制代码

handler如何接收消息

handler的消息是Looper的loop()方法中调用 msg.target.dispatchMessage(msg);方法传过来的,下面我们看看handler的dispatchMessage()方法。

public void dispatchMessage(Message msg) {
    //如果使用了postXXX方式发送消息,那么msg.callback就不会为空
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
复制代码

当程序执行dispatchMessage()方法时会做如下操作,首先判断Message的callback是否为空,如果不为空就会进入该方法调用handleCallback(msg)如果为空就会做第二个判断,即判断Handler的mCallback是否为空,如果不为空就会执行Callback的handleMessage()方法,如果这个函数返回true则跳出dispatchMessage(),否则就会执行Handler的handleMessage(msg)方法。如果上面的判断都不成立,那么系统将会调用Handler的handleMessage(msg)方法,该方法为一个空的函数,需要开发者根据需求重写该函数,。看到这里可能会感觉有些乱,没关系,下面我用三个例子来说明。

1. msg.callback != null

我们在上面postXXX()发送消息时候就提到过,postXXX()方法中的Runnable会被包装成Message的callback,因此该消息在Handler的dispatchMessage()方法中msg.callback != null判断成立,并且会调用handleCallback(msg)方法,该方法里面就一行代码 message.callback.run(); 即执行postXXX()的Runnable对象的run()方法。

mHanlder.post(new Runnable() {
    @Override
    public void run() {
        mTV_testHandler.setText("这是Post发送的消息");
    }
});
复制代码

2. mCallback != null

该方法是在创建Hanlder的时候在构造函数中传入Callback对象,Handler构造中会将这个Callback对象赋值给Handler的mCallback,因此mCallback != null这个判断成立。

Handler callbackHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case 202:
                //显示消息
                mTV_testHandler.setText((String) msg.obj);
                break;
        }
        return false;
    }
});
//发送消息
new Thread(new Runnable() {
    @Override
    public void run() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.what = 202;
                msg.obj = "这是Callback发送的消息";
                callbackHandler.sendMessage(msg);
            }
        }).start();
    }
}).start();
复制代码

3. 执行Hanlder的handleMessage(msg)

我们在创建Hanlder对象的时候,没有传入Callback对象,在发送消息的时候也没有使用postXXX()的方式发送,因此上面两中情况的判断都不会成立,dispatchMessage()最后回到用Hanlder的空函数handleMessage()执行里面的代码。

@SuppressLint("HandlerLeak")
private Handler mHanlder = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 200:
                //显示消息
                mTV_testHandler.setText((String) msg.obj);
                break;
        }
        super.handleMessage(msg);
    }
};

//发送消息
new Thread(new Runnable() {
    @Override
    public void run() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.what = 200;
                msg.obj = "这是Send发送的消息";
                mHanlder.sendMessage(msg);
            }
        }).start();
    }
}).start();
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

SCWCD Exam Study Kit Second Edition

SCWCD Exam Study Kit Second Edition

Hanumant Deshmukh、Jignesh Malavia、Matthew Scarpino / Manning Publications / 2005-05-20 / USD 49.95

Aimed at helping Java developers, Servlet/JSP developers, and J2EE developers pass the Sun Certified Web Component Developer Exam (SCWCD 310-081), this study guide covers all aspects of the Servlet an......一起来看看 《SCWCD Exam Study Kit Second Edition》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具