
本文共 4953 字,大约阅读时间需要 16 分钟。
关于Android的多线程机制,面试的时候总是问到,今天专门写这个博客,目的是把handler说清楚。
分别从下面四个方向说清楚。
由来
问题:为什么有handler?
我们知道java是支持多线程的,而一个APP只有一个UI(即屏幕只有一个),如果每个线程都可以更新UI,呵呵,估计我们的APP就乱套了,所以Android的设计者就想着,更新UI只能在主线程中,子线程是不能更新UI的,子线程如果更新UI,系统就会抛出异常。所以Android就需要提供一种机制,子线程通过这种机制可以在主线程中更新UI。
那么java有没有提供在子线程之间实时通讯的机制??? 由于线程的特性,一个进程内的所有线程够共享同一存储空间,即在多线程中,线程是可以访问同一内存单元,这样很可能出现数据不同步的问题,因此java提供了 synchronized关键字 和 Object 类的wait和notify方法(当然java还提供别的方法),但是此方法对Android更新UI这种特殊场合不是很适用。
因此Android专门提供了异步消息处理机制,用来解决在Android子线程中更新UI的操作。即handler机制。
应用
handler机制是为了在子线程中更新UI的,但是handler的作用不仅仅是为了更新UI的,准确的说是为了异步消息处理,因此从以下三种情况说明消息是如何发送和接受的。
子线程向主线程发送消息
Android是基于Java的,所以也分主线程,子线程!
(1)主线程:实现业务逻辑、UI绘制更新、各子线程串连,类似于将军;
(2)子线程:完成耗时(联网取数据、SD卡数据加载、后台长时间运行)操作,类似于小兵;
对于使用者来说,代码默认执行在主线程中,子线程的代码在new thread内执行的代码。
第一步:在主线程中创建一个handler
private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT: // 在这里可以进行UI操作 text.setText("Nice to meet you"); break; default: break; } } };
第二步:开启一个子线程,在子线程里直接使用Handler发送消息即可
new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.what = UPDATE_TEXT; handler.sendMessage(message); // 将Message对象发送出去 } }).start();
主线程向子线程发送消息
分两种情况:
方法1在子线程里初始化Looper
主线程向子线程发送消息的话,我们需要在子线程里初始化Looper,并在主线程里创建的Handler引用子线程的Looper(Handler中引用的是哪个线程的Looper,就在哪个线程里处理消息),下面看代码:
//方法1 public void method1(){ MyThread thread = new MyThread(); thread.start();//千万别忘记开启这个线程// delay(1000); //在Handler初始化的时候,thread.looper还没有初始化,所以加一个延时 // 或者使用 wait() 方法。 synchronized (thread) { if(thread.looper == null) { try{ thread.wait(); }catch (Exception e){ e.printStackTrace(); } } } handler1 = new Handler(thread.looper){ public void handleMessage(Message msg) { switch (msg.what) { case UPDATE: // 在这里可以进行UI操作 Log.d("当前子线程是--1--->",Thread.currentThread()+""); break; default: break; } }; }; } //子线程 class MyThread extends Thread{ private Looper looper;//取出该子线程的Looper public void run() { Looper.prepare();//创建该子线程的Looper looper = Looper.myLooper();//取出该子线程的Looper // 使用 notify() 方法唤醒。 synchronized (this) { this.notify(); } Looper.loop();//只要调用了该方法才能不断循环取出消息 } }
主线程发送消息:
//下面是主线程发送消息Message message1 = new Message();message1.what = UPDATE;handler1.sendMessage(message1);break;
这种方法有个问题,就是Handler初始化的时候,thread.looper还没有初始化,会导致Crash,上面的代码中加了一个延时解决问题了,或者使用 wait()和notify()方法解决,但有更好的解决办法,就是使用HandlerThread。
方法2使用HandlerThread
Android提供了HandlerThread类,子线程中有handler,代码如下:
//方法2 public void method2(){ //实例化一个特殊的线程HandlerThread,必须给其指定一个名字 HandlerThread thread = new HandlerThread("handler thread"); thread.start();//千万不要忘记开启这个线程 //将mHandler与thread相关联 handler2 = new Handler(thread.getLooper()){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case UPDATE: // 在这里可以进行UI操作 Log.d("当前子线程是--2--->",Thread.currentThread()+""); break; default: break; } }; }; }
子线程向子线程发送消息
创建子线程,该子线程接受并处理消息,(注意,该子线程的创建也可以应用上面的情况)
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); childHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case SEND: // 在这里可以进行UI操作 Log.d(TAG,"这个消息是从-->>" + msg.obj + "过来的,在" + Thread.currentThread()+ "子线程当中执行的"); break; default: break; } } }; Looper.loop();//开始轮循 }}).start();
发送消息线程:
new Thread(new Runnable() { @Override public void run() { Message msg = childHandler.obtainMessage(); msg.what = SEND; msg.obj = ""+ Thread.currentThread(); childHandler.sendMessage(msg); }}).start();
以上分析主要参考,但是这偏文章没有写好的源代码,在总结这篇文章的基础上,实现代码并上传github,可直接运行。
原理
原理分析,主要查看郭霖的《第一行代码,第二版》。
(1)ActivityThread.main() 函数中,调用 Looper.prepareMainLooper();
(2)Looper.prepareMainLooper(); 的作用是初始化当前线程作为一个主线程。具体内容是,创建一个Looper对象,并将该对象赋值给sThreadLocal变量。 (2.1)在创建Looper对象的时候,其构造函数会创建MessageQueue对象。 (3)最后,ActivityThread.main() 会调用Looper.loop(); 函数。 (4)而Looper.loop();函数会从队列中取出message,并调用Handler. dispatchMessage()函数。从上面可以看出,其关键是Looper对象。
java实现
理解了handler机制后,我们可不可以用java手写一个handler机制呢?
发表评论
最新留言
关于作者
