一、线程通讯问题

1.1 Message、Handler、Looper

在Android中提供了一种异步回调机制Handler,我们可以它来完成一个很长时间的任务。

Handler基本使用:

在主线程中,使用它很简单,new一个Handler对象实现其handleMessage方法,在handleMessage中,提供收到消息后相应的处理方法即可。

Message基本使用:

Message主要是进行消息的封装,并且同时可以指定消息的操作形式。

Looper基本使用:

当使用Handler处理Message的时候,实际上都是需要依靠一个Looper通道完成的,在一个Activity类中,会自动帮助程序员启动好Looper对象,而如果是一个用户自定义的类中,则需要用户手工使用Looper类中的若干方法之后才可以正常启动Looper对象。

1.2 基本用法: 定时更新文本内容:

public class MyMessageDemo extends Activity {
private static int count = 0; // 定义全局变量
public static final int SET = 1 ; // 设置一个what标记
private Handler myHandler = new Handler() { // 定义Handler对象
@Override
public void handleMessage(android.os.Message msg) {// 覆写此方法
switch (msg.what) { // 判断操作类型
case SET: // 为设置文本操作
MyMessageDemo.this.info.setText("Hello - " + count++);
}
}
};
private TextView info = null; // 文本显示组件
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.main);
this.info = (TextView) super.findViewById(R.id.info);
Timer timer = new Timer(); // 定义调度器
timer.schedule(new MyTask(), 0, 1000); // 立即开始,1秒一增长
}
private class MyTask extends TimerTask { // 定义定时调度的具体实现类
@Override
public void run() { // 启动线程
Message msg = new Message(); // 定义Message
msg.what = SET ; // 操作为设置显示文字
MyMessageDemo.this.myHandler.sendMessage(msg); // 发送消息到子线程
}
}
}

通过上述程序,我们已可以发现,UI界面中的数字在不停的自增。这是我们思考哇,为啥这么麻烦呢,非要在任务调度器中去发送消息,然后在消息中更新UI呢?我们直接在任务调度器中更新UI试下:

private class MyTask extends TimerTask {
@Override
public void run() {
MyMessageDemo.this.info.setText("MLDN - " + count++);
}
}

运行之后,系统报错:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

意思是,子线程无法更新主线程中各个组件的状态。 所以,我们必须在子线程返回要操作的消息,然后利用Handler处理消息。

1.3 通过上述程序可以发现,我们根本没有Looper对象,那么什么要Looper了,跟我们要用的Handler啥关系呢?

前面我们强调需要在主线程中使用Handler,为什么要这么说呢,因为你在自己new一个新线程中去像我前面那样简单建立一个Handler,程序执行是会报错的:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at com.cao.android.demos.handles.HandleTestActivity$MyThread$1.<init>(HandleTestActivity.java:86)
at com.cao.android.demos.handles.HandleTestActivity$MyThread.run(HandleTestActivity.java:86)

为什么在主线程中不会报错,而在自己新见的线程中就会报这个错误呢?很简单,因为主线程它已经建立了Looper,你可以打开ActivityThread的源码看一下:

public static final void main(String[] args) {
SamplingProfilerIntegration.start();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
Looper.loop();
if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}
thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}

在main函数中它已经做了这个事情了,为什么要调用 Looper.prepareMainLooper(); Looper.loop();我们可以进去看一下,在prepareMainLooper方法中新建了一个looper对象,并与当前进程进行了绑定,而在Looper.loop方法中,线程建立消息循环机制,循环从MessageQueue获取Message对象,调用 
msg.target.dispatchMessage(msg);进行处理msg.target在myThreadHandler.sendEmptyMessage(0)设置进去的,因为一个Thead中可以建立多个Hander,通过msg.target保证MessageQueue中的每个msg交由发送message的handler进行处理,那么Handler又是怎样与Looper建立联系的呢,在Handler构造函数中有这样一段代码:

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

在新建Handler时需要设置mLooper成员,Looper.myLooper是从当前线程中获取绑定的Looper对象:

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

若Looper对象没有创建,就会抛异常"Can't create handler inside thread that
has not called Looper.prepare()"

这跟我前面讲的是一致的。所以我们在一个新线程中要创建一个Handler就需要这样写:

 class MyThread extends Thread {
public void run() {
Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]-- run...", Thread
.currentThread().getName()));
// 其它线程中新建一个handler
Looper.prepare();// 创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个Looper
myThreadHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler handleMessage run...", Thread
.currentThread().getName()));
}
};
Looper.myLooper().loop();//建立一个消息循环,该线程不会退出
}
}

如何使用Looper呢,我们可以看一个复杂些的,主线程和子线程通讯的示例:

public class MyThreadDemo extends Activity
{
    public static final int SETMAIN = 1; // 设置一个what标记     public static final int SETCHILD = 2; // 设置一个what标记     private Handler mainHandler, childHandler; // 定义Handler对象     private TextView msg; // 文本显示组件     private Button but; // 按钮组件     class ChildThread implements Runnable
    { // 子线程类
        @Override
        public void run()
        {
            Looper.prepare(); // 初始化Looper
            MyThreadDemo.this.childHandler = new Handler()
            {
                public void handleMessage(Message msg)
                {
                    switch (msg.what)
                    { // 判断what操作
                        case SETCHILD: // 主线程发送给子线程的信息
                            System.out.println("*** Main Child Message : " + msg.obj); // 打印消息
                            Message toMain = MyThreadDemo.this.mainHandler.obtainMessage(); // 创建Message
                            toMain.obj = "\n\n[B] 这是子线程发给主线程的信息:" + super.getLooper().getThread().getName(); // 设置显示文字
                            toMain.what = SETMAIN; // 设置主线程操作的状态码
                            MyThreadDemo.this.mainHandler.sendMessage(toMain); // 发送消息
                            break;
                    }
                }
            };
            Looper.loop(); // 启动该线程的消息队列
        }
    }     @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.main); // 调用布局文件
        this.msg = (TextView) super.findViewById(R.id.msg); // 取得组件
        this.but = (Button) super.findViewById(R.id.but); // 取得按钮
        this.mainHandler = new Handler()
        { // 主线程的Handler对象
            public void handleMessage(Message msg)
            { // 消息处理
                switch (msg.what)
                { // 判断Message类型
                    case SETMAIN: // 设置主线程的操作类
                        MyThreadDemo.this.msg.setText("主线程接收数据:" + msg.obj.toString()); // 设置文本内容
                        break;
                }
            }
        };
        new Thread(new ChildThread(), "Child Thread").start(); // 启动子线程
        this.but.setOnClickListener(new OnClickListenerImpl()); // 单击事件操作
    }     private class OnClickListenerImpl implements OnClickListener
    {
        @Override
        public void onClick(View view)
        {
            if (MyThreadDemo.this.childHandler != null)
            { // 已实例化子线程Handler
                Message childMsg = MyThreadDemo.this.childHandler.obtainMessage(); // 创建一个消息
                childMsg.obj = MyThreadDemo.this.mainHandler.getLooper().getThread().getName() + " --> Hello MLDN ."; // 设置消息内容
                childMsg.what = SETCHILD; // 操作码
                MyThreadDemo.this.childHandler.sendMessage(childMsg); // 向子线程发送
            }
        }
    }     @Override
    protected void onDestroy()
    {
        super.onDestroy();
        MyThreadDemo.this.childHandler.getLooper().quit(); // 结束队列
    }
}

我们可以发现,在ChildThread子线程中,我们必须借助Looper,才可以完成与主线程的通讯。

Handler用法总结的更多相关文章

  1. Handler用法

    1.子线程创建handler 方法一 HandlerThread handlerThread = new HandlerThread(" sub thread name");  / ...

  2. Android之Handler用法总结(1)

    方法一:(java习惯,在android平台开发时这样是不行的,因为它违背了单线程模型) 刚刚开始接触android线程编程的时候,习惯好像java一样,试图用下面的代码解决问题 new Thread ...

  3. Android之Handler用法总结

    方法一:(java习惯,在android平台开发时这样是不行的,因为它违背了单线程模型) 刚刚开始接触android线程编程的时候,习惯好像java一样,试图用下面的代码解决问题 new Thread ...

  4. android中handler用法总结

    一.Handler的定义: Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用.比如可以用handler发送一个message,然后在handler的线程中 ...

  5. Android(java)学习笔记134:Handler用法总结 和 秒表案例

    一.Handler的定义: Handler主要接收子线程发送的数据, 并用此数据配合主线程更新UI,用来跟UI主线程交互用.比如可以用handler发送一个message,然后在handler的线程中 ...

  6. Android学习笔记--Handler用法总结

    不错的例子:http://www.cnblogs.com/menlsh/archive/2013/06/07/3125341.html 转自:一叶知秋的博客 http://blog.sina.com. ...

  7. 异步消息处理机制——Handler用法

    Handler 1. Message Messsge是线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据,Message的what字段,除此之外还可以使用arg1和arg2字段 ...

  8. andorid 多线程handler用法

    .xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...

  9. Android之Handler用法总结/安卓中只有主线程可以修改UI

    Handler传递消息的方式可以实现实时刷新以及长按连续响应事件. 按钮响应 btnadd_fcl.setOnTouchListener(new View.OnTouchListener() { pr ...

随机推荐

  1. 从0开始学习 GitHub 系列之「07.GitHub 常见的几种操作」

    之前写了一个 GitHub 系列,反响很不错,突然发现竟然还落下点东西没写,前段时间 GitHub 也改版了,借此机会补充下. 我们都说开源社区最大的魅力是人人多可以参与进去,发挥众人的力量,让一个项 ...

  2. 前端与编译原理——用JS写一个JS解释器

    说起编译原理,印象往往只停留在本科时那些枯燥的课程和晦涩的概念.作为前端开发者,编译原理似乎离我们很远,对它的理解很可能仅仅局限于"抽象语法树(AST)".但这仅仅是个开头而已.编 ...

  3. fc_net.py cs231n

    n如果有错误,欢迎指出,不胜感激 import numpy as np from cs231n.layers import * from cs231n.layer_utils import * cla ...

  4. IE下的双外边距浮动bug

    最常见且最容易发现的额一个bug是IE 6和最低版本中的双外边距浮动bug.这个bug是任何浮动元素上的外边距加倍. 上面代码中,div盒子向左边浮动,设置的margin-left的值是10px.在c ...

  5. (转) Hibernate持久化类与主键生成策略

    http://blog.csdn.net/yerenyuan_pku/article/details/65462930 Hibernate持久化类 什么是持久化类呢?在Hibernate中持久化类的英 ...

  6. AlertDialog提示对话框练习

    闲来无事,就练习了下AlertDialog对话框. 首先写了三个button,分别打开一般对话框,单选列表对话框和复选对话框. xml代码 <LinearLayout xmlns:android ...

  7. 【JZOJ4804】【NOIP2016提高A组模拟9.28】成绩调研

    题目描述 输入 输出 样例输入 5 3 1 2 3 1 2 1 2 1 1 1 1 样例输出 4 数据范围 解法 考虑设置左指针l和右指针r: 维护[l,r]的关于等第的桶. 初始l=r=0: 每次右 ...

  8. ansible Ansible Galaxy ansible-playbook 安装 使用 命令 笔记 生成密钥 管控机 被管控机 wget epel源

    笔记 ansible 安装 与salt对比 相同 都是为了同时在多台机器上执行相同的命令 都是python开发 不同 agent(saltstack需要安装.ansible不需要) 配置(salt配置 ...

  9. 大数据技术之Zookeeper

    第1章 Zookeeper入门 1.1 概述 Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目. 1.2 特点 1.3 数据结构 1.4 应用场景 提供的服务包括:统 ...

  10. python 定义子类