zl程序教程

您现在的位置是:首页 >  其它

当前栏目

Handler用法总结

总结 用法 handler
2023-09-14 08:56:50 时间

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: Cant 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(

 "Cant 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对象没有创建,就会抛异常"Cant 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标记


            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(); // 启动该线程的消息队列         }     }
        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()); // 单击事件操作     }
            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); // 向子线程发送             }         }     }
        super.onDestroy();         MyThreadDemo.this.childHandler.getLooper().quit(); // 结束队列     }
我们可以发现,在ChildThread子线程中,我们必须借助Looper,才可以完成与主线程的通讯。


Handler源码解读——handler使用时的注意事项 工作中经常会遇到从子线程发送消息给主线程,让主线程更新UI的操作,常见的有handler.sendMessage(Message),和handler.post(runnable)和handler.postDelayed(runnable, milliseconds);一直在使用这些方法,却不知道他们的原理,今天就来解释一下他们的原理。
09准备将Handler的返回值写入ServletResponse 在RequestMappingHandlerAdapter初始化完成后设置默认的HandlerMethodReturnValueHandler HandlerMethodReturnValueHandler体系介绍 HandlerMethodReturnValueHandler的执行流程
Handler消息传递机制 出于性能优化考虑,Android的UI操作并不是线程安全的,这意义着如果有多个线程并发操作UI组件,则可能导致线程安全问题。