首先撇清一个概念:Android的单线程设计是指每个应用程序的UI线程(主线程)是单线程的,即和用户交互的界面是单线程的。但是,很显然,用户界面如果采用多线程处理效率会更高,Android为什么将UI线程限制为单线程呢?这是为了避免并发编程的复杂性,也是提高Android应用的健壮性的有效途径。
但是,主线程是单线程的,并不等于Android不支持多线程,比如两个Android应用程序之间的通讯。
Android中的Handler提供了线程之间通讯的简便手段,如下图所示:
Android的主线程(即UI线程)内部实现了消息队列机制,即一个主线程会自动创建一个消息队列(显然也只有一个消息队列),同时创建一个读取消息队列的Looper对象。主线程的所有用户交互都通过消息队列和Looper对象来实现:当有新的消息进入消息队列时,Looper会及时读取消息并调用相应的处理逻辑(更新用户界面)。
当其他线程希望更新用户界面时,可以通过Handler提供的sendMessage方法在消息队列中放入一个消息(Message对象,可以携带大量信息),这样Looper即可以及时获取该消息,并调用Handler相应的handleMessage方法处理该消息,这样就实现了线程之间的通讯。由此可以看出,Handler在线程通讯中起到了一个枢纽作用:Handler即负责和消息队列打交道,也负责处理相应的消息,其他线程通过Handler和主线程通讯,就可以不需要考虑和主线程的竞争和同步问题,极大的简化了线程的使用。
下面的示例代码演示了一个每隔1秒显示一条消息在主界面:
public class MainActivity extends Activity { private TextView label; private static int UPDATE = 1; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { label.setText(String.valueOf(msg.obj)); super.handleMessage(msg); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); label = (TextView) findViewById(R.id.label); new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Message msg = new Message(); msg.what = UPDATE; msg.obj = "当前循环变量:" + i; handler.sendMessage(msg); } } }.start(); } }
在上面的例子中,所谓的其他线程其实主线程的子线程,下面一个例子则演示了完全不同的两个组件通过Handler实现的交互:一个Service每隔1秒产生一个随机数,然后在主线程显示出来:
ServiceDemo.java文件:
public class ServiceDemo extends Service { @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } private Thread workThread; @Override public void onCreate() { super.onCreate(); Toast.makeText(this, "(1) onCreate()", Toast.LENGTH_LONG).show(); workThread = new Thread(null,backgroudWork,"WorkThread"); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); Toast.makeText(this, "(2) onStart()", Toast.LENGTH_SHORT).show(); if (!workThread.isAlive()){ workThread.start(); } } @Override public void onDestroy() { super.onDestroy(); Toast.makeText(this, "(3) onDestroy()", Toast.LENGTH_SHORT).show(); workThread.interrupt(); } private Runnable backgroudWork = new Runnable(){ @Override public void run() { try { while(!Thread.interrupted()){ double randomDouble = Math.random(); MainActivity.updateGUI(randomDouble); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } }; }
MainActivity.java文件:
public class MainActivity extends Activity { private static TextView label; private static int UPDATE = 1; protected static Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { label.setText(String.valueOf(msg.obj)); super.handleMessage(msg); } } }; public static void updateGUI(double doubleRadom){ Message msg = new Message(); msg.what = UPDATE; msg.obj = "当前随机数:" + doubleRadom; handler.sendMessage(msg); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); label = (TextView) findViewById(R.id.label); final Intent serviceIntent = new Intent(this, ServiceDemo.class); startService(serviceIntent); } }
可以看出,为了实现在两个组件间的通讯,主要的变化是:
- 将Handler对象定义为static类型的,便于在其他组件访问handler
- 在主线程中实现一个静态的updateGUI方法,以便通过调用handler.sendMessage添加一个消息到消息队列中
- 在Service的线程中,调用主线程的updateGUI并传入适当的参数
Handler和消息循环机制的设计很巧妙,在WEB应用和企业级应用中是否可以借鉴呢?