如果在需要在Activity中一个耗时的操作,例如当我们需要联网读取数据,或者读取本地较大的一个文件的时候,不能把这些操作放在主线程中。因为如果放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。这个时候,Handler就出现了,来解决这个复杂的问题。Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI。 ,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传递)Message对象(里面包含数据),把这些消息放入主线程队列中,配合主线程进行更新UI。Handler就是运行在Activity中的不可见的控件,它包括一个可以容纳主线程的子线程的队列容器以及一个子线程和主线程通信的Message消息队列容器。Handler可以把线程对象装入线程队列,也可以从线程队列中取出线程执行。
Handler的基本使用方法:
post(Runnable) 将线程对象添加到队列中
postAtTime(Runnable,long) 在指定的时间( uptimeMillis )执行 Runnable 对象
postDelayed(Runnable,long) 在指定的时间间隔( delayMillis )后执行 Runnable 对象
Handler使用的一般步骤为:
1. 首先在Activity中声明一个Handler对象。
2. 定义一个或多个线程对象,复写线程对象的run()方法,在run()方法中写上想要执行的操作。
3. 用Handler的post()(post()有多种重载形式)方法线程对象装入handler的线程队列。系统会在适当的时候取出当前线程队列中的队首线程并执行。
4. 当需要停止线程队列中某个线程的执行时,可以在主线程中调用removeCallbacks()方法,删除队列中未执行的线程对象。
示例:新建一个Android应用程序项目,main.xml总添加两个Button按钮。程序的流程为当我们点击Activity中的start按钮时,Activity中的Handler对象会将一个线程对象updateThread装入线程队列,在这个线程中会在控制台输出“updateThread”,并且将本线程再次装入handler的线程队列,形成循环调用。这样就可以不断输出文字内容。当我们单击end按钮时调用removeCallbacks()方法,删除线程对象,停止线程的执行,不再输出文字内容。
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/startButton" android:layout_width="300dp" android:layout_height="wrap_content" android:text="start" /> <Button android:id="@+id/endButton" android:layout_width="300dp" android:layout_height="wrap_content" android:text="end" /> </LinearLayout>
Android_Handler.java
package idea.org; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class Android_Handler extends Activity { /** Called when the activity is first created. */ private Button startButton=null; private Button endButton=null; Handler handler=new Handler(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startButton=(Button)findViewById(R.id.startButton); endButton=(Button)findViewById(R.id.endButton); startButton.setOnClickListener(new startButtonClickListener()); endButton.setOnClickListener(new endButtonClickListener()); } class startButtonClickListener implements OnClickListener { /* (non-Javadoc) * @see android.view.View.OnClickListener#onClick(android.view.View) */ @Override public void onClick(View v) { // TODO Auto-generated method stub //调用Handler的post()方法,将要执行的线程对象放到handler的线程队列当中 handler.post(updateThread); } } class endButtonClickListener implements OnClickListener { /* (non-Javadoc) * @see android.view.View.OnClickListener#onClick(android.view.View) */ @Override public void onClick(View v) { // TODO Auto-generated method stub //调用Handler的removeCallbacks()方法,删除队列当中未执行的线程对象 handler.removeCallbacks(updateThread); } } //新建一个线程对象,复写run()方法,将要执行的操作写在run()方法中 Runnable updateThread=new Runnable() { public void run() { System.out.println("updateThread"); handler.postDelayed(updateThread, 3000); } }; }
运行效果:
点击start按钮之后控制台不断输出,直到点击end按钮停止输出。
Handler消息队列的使用方法:
Message对象是操作系统提供的用于子线程和主线程之间通信的对象。Handler提供一个Message对象的消息队列。我们可以利用Message对象的int型成员变量arg1,arg2作为信息的载体进行通信。获得Message对象可以通过handler. obtainMessage()方法,可以用handler.sendMessage方法将消息加入到消息队列,系统会异步自动调用handler中的handMessage()方法从消息队列中取出队首消息进行处理。我们可以复写handMessage()方法,在这个方法中执行相应的操作。
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="300dp" android:layout_height="wrap_content" android:visibility="gone" /> <Button android:id="@+id/startButton" android:layout_width="300dp" android:layout_height="wrap_content" android:text="start" /> </LinearLayout>
Android_Handler.java
package idea.org; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; public class Android_Handler extends Activity { /** Called when the activity is first created. */ private Button startButton=null; private ProgressBar progressBar=null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startButton=(Button)findViewById(R.id.startButton); startButton.setOnClickListener(new startButtonClickListener()); progressBar=(ProgressBar)findViewById(R.id.progressBar); } class startButtonClickListener implements OnClickListener { /* (non-Javadoc) * @see android.view.View.OnClickListener#onClick(android.view.View) */ @Override public void onClick(View v) { // TODO Auto-generated method stub progressBar.setVisibility(View.VISIBLE); //调用Handler的post()方法,将要执行的线程对象放到handler的线程队列当中 handler.post(updateThread); } } //匿名内部类复写Handler的成员方法handlerMessage() Handler handler=new Handler() { /* (non-Javadoc) * @see android.os.Handler#handleMessage(android.os.Message) */ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub //设置进度条的进度 progressBar.setProgress(msg.arg1); handler.post(updateThread); } }; //新建一个线程对象,复写run()方法,将要执行的操作写在run()方法中 Runnable updateThread=new Runnable() { int i=0; public void run() { i=i+10; Message msg=handler.obtainMessage(); //i作为要设置进度条的进度参数复制给消息msg的arg1参数 msg.arg1=i; //将消息msg加入到消息队列 handler.sendMessage(msg); //线程睡眠1s try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //如果要设置的进度已经达到了100,去除未执行的线程对象 if(i==100) handler.removeCallbacks(updateThread); } }; }
示例:新建一个Android应用程序,在main.xml中添加一个ProgressBar和一个startButton按钮。当我们单击这个按钮时,显示ProgressBar并且将创建的一个线程对象添加到线程对列。在这个线程中我们定义一个不断增加的变量i作为设置进度条进度的参数,将这个参数复制给一个Message对象的arg1参数,然后将这个对象添加到消息队列。当进度达到100时移除线程对象。在处理Message对象的handMessage()方法中将arg1的值设置为进度条的进度,从而实现参数的传递,然后再把线程加入到线程队列。
运行效果:
点击start按钮之后,进度条的进度不断改变。