一、线程通讯问题

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对象@Overridepublic void handleMessage(android.os.Message msg) {// 覆写此方法switch (msg.what) {                              // 判断操作类型case SET:                                      // 为设置文本操作MyMessageDemo.this.info.setText("Hello - " + count++);}}};private TextView info = null;                         // 文本显示组件@Overridepublic 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 {               // 定义定时调度的具体实现类@Overridepublic void run() {                                    // 启动线程 Message msg = new Message();                   // 定义Messagemsg.what = SET ;                               // 操作为设置显示文字MyMessageDemo.this.myHandler.sendMessage(msg);  // 发送消息到子线程}}
}

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

private class MyTask extends TimerTask {@Overridepublic 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()));// 其它线程中新建一个handlerLooper.prepare();// 创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个LoopermyThreadHandler = 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{ // 子线程类@Overridepublic void run(){Looper.prepare(); // 初始化LooperMyThreadDemo.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(); // 创建MessagetoMain.obj = "\n\n[B] 这是子线程发给主线程的信息:" + super.getLooper().getThread().getName(); // 设置显示文字toMain.what = SETMAIN; // 设置主线程操作的状态码MyThreadDemo.this.mainHandler.sendMessage(toMain); // 发送消息break;}}};Looper.loop(); // 启动该线程的消息队列}}@Overridepublic 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{@Overridepublic void onClick(View view){if (MyThreadDemo.this.childHandler != null){ // 已实例化子线程HandlerMessage childMsg = MyThreadDemo.this.childHandler.obtainMessage(); // 创建一个消息childMsg.obj = MyThreadDemo.this.mainHandler.getLooper().getThread().getName() + " --> Hello MLDN ."; // 设置消息内容childMsg.what = SETCHILD; // 操作码MyThreadDemo.this.childHandler.sendMessage(childMsg); // 向子线程发送}}}@Overrideprotected void onDestroy(){super.onDestroy();MyThreadDemo.this.childHandler.getLooper().quit(); // 结束队列}
}

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

转载于:https://www.cnblogs.com/hehe520/p/6330027.html

Handler用法总结相关推荐

  1. Android消息机制Handler用法

    这篇文章介绍了Android消息机制Handler用法总结,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 1.简述 Handler消息机制主要包括: Messa ...

  2. Android中对Handler用法的总结

    Android之Handler用法总结 方法一:(java习惯,在android平台开发时这样是不行的,因为它违背了单线程模型) 刚刚开始接触android线程编程的时候,习惯好像java一样,试图用 ...

  3. android studio 定时器,Android定时器和Handler用法实例分析

    本文实例讲述了Android定时器和Handler用法.分享给大家供大家参考.具体分析如下: 一.环境: 主机:WIN8 开发环境:Android Studio 二.定时器使用示例: 初始化: //定 ...

  4. android实现10ms定时器,本文实例讲述了Android定时器和Handler用法。分享给大家供大家参考。具体分析如下:一、环境:主机:WIN8开发环境:Android Studio...

    本文实例讲述了Android定时器和Handler用法.分享给大家供大家参考.具体分析如下: 一.环境: 主机:WIN8 开发环境:Android Studio 二.定时器使用示例: 初始化: //定 ...

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

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

  6. [置顶] Android之Handler用法总结

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

  7. Android Thread/Runnable/Handler用法

    public class MainActivity extends Activity implements OnClickListener {//1.Runnable,实现Runnable类的方式 p ...

  8. Handler用法及解析

    目录 1.handler作用: 1)传递消息Message 2)子线程通知主线程更新ui 2.常用api 3.handler使用避免内存泄露 1)handler怎么使用会产生内存泄露? 2)如何避免h ...

  9. android的handler使用方法,android中handler用法总结

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

最新文章

  1. 带你用深度学习虚拟机进行文本迁移学习(附代码)
  2. 利用FreeNas创建AFP共享
  3. 数据库中的时间数据在页面显示时后面会多一个.0的问题
  4. 10个你必须知道的Python内置函数
  5. Ubuntu下面的Keras可视化+权重维度获取-Netron的安装使用
  6. 给SAP Spartacus B2B list增加用户提示信息
  7. 项目中获取系统的用例的基本步骤
  8. 哪些情况下sql索引会失效
  9. 语言身高预测实验注意事项_?新预训练模型CodeBERT出世,编程语言和自然语言都不在话下...
  10. JavaScript中的数组 1
  11. BootStrap的介绍与案例使用
  12. HTML 修真录------深渊三君王
  13. linux双显卡配置_linux下的双显卡切换
  14. 外卖行业现状分析_2020年中国外卖行业市场现状和发展趋势分析 外卖下沉趋势明显【组图】...
  15. python汇率换算程序_Python编写一个汇率兑换程序
  16. 笨笨图片批量下载器[C# | WinForm | 正则表达式 | HttpWebRequest]
  17. 利用1.1.1.1进行DNS网络加速,仅需2分钟让网络更快
  18. 小米商城app端项目
  19. (二)ElasticSearch实战基础教程(ElasticSearch入门)
  20. 学习Linux七(Linux必学60个命令之【系统管理】)

热门文章

  1. [Unity3d]Unity Mathf 数学运算(C#)
  2. 【转载】.NET设计模式之抽象工厂模式(Abstract Factory)
  3. VFP中轻松绑定 Windows 事件
  4. 微服务怎么部署到服务器的_微服务的部署与发布:持续交付与持续部署微服务...
  5. mysql 导入百万级数据 几种 java_百万级数据,如何迁移到MySQL?
  6. mysql 2100,MySQL 实现准实时的表级别DML计数
  7. Vue与class和style
  8. Nacos简介和安装
  9. 查看linux 系统情况,LINUX 查看当前系统的负载情况
  10. 计算机二级考试网易教程,学姐分享 | 计算机二级来袭,你准备好了吗?