在android中,有很多功能是不能放在onCreate或者onStart方法里面,因为这些功能相对
来说费时比较长,比如说下载一个文件,下载的过程比较长,但是如果写在Activity中,
那么这段时间Activity是完全没有响应的,那么就可以将这种处理大量数据或者耗时比较
长的东西放在一个单独的线程中来完成,即Activity是一个线程,而下载的是在另外一个
线程,那么这样就可以使得下载跟Activity之间互不影响,从而得到了良好的用户体验
这里有两种队列,一种是线程队列,就是用postXX方法或者removeCallbacks方法对线程对象的操作。另一种是消息队列,用sendMessage和handleMessage方法来对消息对象进行处理
handler采用的是一个消息队列的方式,每一个handler都有一个与之关联的消息队列,而且是先进先出的方式执行,即:每次加入一个handler,然后拿出来,对其进行处理,然后再拿出另一个,再进行处理
例子一:这个例子仅仅是对线程对象进行操作的测试
package org.hualang.handler; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.widget.Button; public class HandlerTest extends Activity { private Button mybutton1; private Button mybutton2; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mybutton1 = (Button)findViewById(R.id.mybutton1); mybutton2 = (Button)findViewById(R.id.mybutton2); mybutton1.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View arg0) { /** * 调用Handler的post方法,将要执行的线程对象添加到 * 线程队列中 */ handler.post(updateThread); } }); mybutton2.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub handler.removeCallbacks(updateThread); } }); } //创建Handler对象 Handler handler = new Handler(); /** * 将要执行的操作卸载写入线程对象的run()方法当中 */ Runnable updateThread = new Runnable() { public void run() { System.out.println("更新线程"); //在run方法内部,执行postXX的方法,每隔3秒会执行一次 handler.postDelayed(updateThread, 3000); } }; }
运行结果如下:
程序解释:首先创建一个Handler对象,然后创建一个继承自Runnable接口的线程
程序首先点击按钮“开始”,于是会马上执行post方法,将执行的线程对象添加到线程队列中,这时会马上执行
public void run() { System.out.println("更新线程"); //在run方法内部,执行postXX的方法,每隔3秒会执行一次 handler.postDelayed(updateThread, 3000); }
然后,执行postDelayed方法,由于里面设置的间隔时间,所以每3秒会调价一个handler对象到线程队列中,并且一直执行,直到点击“结束”按钮,调用removeCallbacks方法将其从线程队列中移除
例子2:下面的例子将简单的对线程对象和消息对象进行处理
package org.hualang.handlertest2; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; public class HandlerTest2 extends Activity { private ProgressBar bar = null; private Button start = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); bar = (ProgressBar)findViewById(R.id.progress1); start = (Button)findViewById(R.id.start); start.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { bar.setVisibility(View.VISIBLE); handler.post(handlerThread); } }); } /** * 使用匿名内部类来复写hanlder当中的hanldrMessage方法 * 这里的msg对象就是从线程部分发送过来的对象 */ Handler handler = new Handler() { public void handleMessage(Message msg) { bar.setProgress(msg.arg1); handler.post(handlerThread); } }; //线程类,该类使用的是匿名内部类的方式进行声明 Runnable handlerThread = new Runnable() { int i = 0; public void run() { System.out.println("开始线程"); i = i + 10; /** * 得到一个消息对象,Message类是由android操作系统提供 * obtainMessage方法用来得到Message对象 */ Message msg = handler.obtainMessage(); /** * Message中有个成员变量,即msg独享的arg1参数 * 将其值设置为i。用arg1或arg2这两个成员变量传递 * 消息,优点是系统性能消耗较少 */ msg.arg1 = i; try { //当前线程休眠1秒 Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } /** * 发送一个消息,用sendMessage是将msg加入到消息 * 队列中。而post是将线程加入到线程队列中 */ handler.sendMessage(msg); if( i == 100) { /** * 如果i=100的时候,就将线程对象 * 从handler当中移除 */ handler.removeCallbacks(handlerThread); bar.setVisibility(View.GONE); } } }; }
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" > <ProgressBar android:id="@+id/progress1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:visibility="gone" style="?android:attr/progressBarStyleHorizontal" /> <Button android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="点击我" /> </LinearLayout>
运行结果:
程序说明:
1、当点击按钮后,会执行按钮的onClick方法中的
bar.setVisibility(View.VISIBLE); handler.post(handlerThread);
将进度条显示出来,并且将线程对象加入到线程队列中
2、线程对象对先打印出一个“开始线程”,然后i的值增加10,然后从系统中获取一个Message对象
3、将i赋给Message对象的参数arg1
4、当前线程休眠5秒,然后通过sendMessage方法发送一个Message对象发送到消息队列中
5、然后再执行,通过handleMessage方法设置进度条的值,并且将其加入到进程队列中
Handler handler = new Handler() { public void handleMessage(Message msg) { bar.setProgress(msg.arg1); handler.post(handlerThread); } };
6、循环执行,直到i=100,进度条隐藏,并将线程对象从线程队列中取出
对于Handler来说,它和与它调用它的Activity是出于同一线程的,上一篇并没有调用线程
的start方法,而是直接执行的run方法。而启动一个线程是调用的start方法
上一篇博客里的对Handler的调用时通过Runnable接口来实现的,并且是通过run()方法来启动那个线程的,而且是Activity和Handler是两个线程独立运行的,互补干扰,但是实际情况确实,Activity所在的线程和Handler的线程是同一个线程,下面进行一下实验
package org.hualang.handlertest3; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; public class HandlerTest3 extends Activity { private Handler handler = new Handler(); private String TAG = "System.out"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handler.post(r); setContentView(R.layout.main); //Thread t = new Thread(r); //t.start(); Log.d(TAG,"Activity id:"+Thread.currentThread().getId()); Log.d(TAG,"Activity name:"+Thread.currentThread().getName()); } Runnable r = new Runnable() { public void run() { Log.d(TAG,"Handler id:"+Thread.currentThread().getId()); Log.d(TAG,"Handler name:"+Thread.currentThread().getName()); try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; }
运行结果:
证明是同一个线程的两个依据:
①Activity的id或name和Handler的id或name是同样的
②我设置了
handler.post(r);
setContentView(R.layout.main);
也就是,如果执行后马上显示文本信息,那么可以证明它们不在同一个线程,但是实际情况是要先执行了handler后5秒,才显示文本信息,说明它们在同一线程
如果将代码改为
//handler.post(r); setContentView(R.layout.main); Thread t = new Thread(r); t.start();
再次执行,运行结果如下,通过start启动线程,它们不在同一个线程中
----------------------------------------------------------------------------------------------------------------
Looper即循环的从队列当中取得消息的功能,如果在线程中使用Looper
那么,就会循环的从线程队列当中取得消息并处理,如果队列当中没有消息的话
,线程就进入了休眠状态
Looper很少自己创建,在Android中给出了HandlerThread类,并且具有循环取得并处理消息的功能
下面来实现这种Activity和Handler分别在两个线程中执行,实现真正的异步处理
package org.hualang.handlertest; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.util.Log; public class HandlerTest4 extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.d("System.out","Activity所在线程的id:"+Thread.currentThread().getId()); /** * 生成一个HandlerThread对象,实现了使用Looper来处理消息队列的功能 * 这个类由Android应用程序框架提供 */ HandlerThread handlerThread = new HandlerThread("handlerThread"); handlerThread.start(); MyHandler handler = new MyHandler(handlerThread.getLooper()); Message msg = handler.obtainMessage(); /** * 将Message对象发送到目标对象 * 所谓的目标对象,就是生成该msg对象的handler对象 */ msg.sendToTarget(); } class MyHandler extends Handler { public MyHandler() { } public MyHandler(Looper looper) { super(looper); } public void handleMessage(Message msg) { Log.d("System.out", "handler所在线程的id:"+Thread.currentThread().getId()); } } }
运行结果:
可以看到,Activity和Handler是在两个不同的线程中执行的,这样就是实现了真正的异步处理
1、首先创建一个HandlerThread对象,这个HandlerThread类实现了循环的取得消息并处理
2、用start方法启动一个新线程
3、创建MyHandler类,里面传递的参数即Looper方法所获得的可以循环在队列中取得的消息
4、MyHandler类调用的是带参数Looper的构造方法,并且实现了handlerMessage方法
5、获取一个Message对象
6、将这个对象发送到生成该msg对象的handler对象,从而执行了handleMessage方法
-----------------------------------------------------------------------------------------------------
最后,将说一下Message里传送的数据的使用,这里的msg对象可以使用arg1,arg2或者obj
arg1 and arg2 are lower-cost alternatives to using setData()
if you only need to store a few integer values. 也就是相对于setData()方法,如果你仅仅保存一些简单的整形数的话,arg1,arg2对资源的要求较低,而setData()方法一般用于传递大量数据的时候会用到
如果是msg.obj,那么可以这样用
msg.obj = "Welcome to china";
然后在handleMessage()方法中用
String str = (String)msg.obj;来获得传递的值
如果使用getData()方法的话,需要用到Bundle对象来传递,下面用个例子来说明
Bundle b = new Bundle(); b.putInt("age", 22); b.putString("name", "loulijun"); msg.setData(b); msg.sendToTarget();
上面的代码用来设置要传递的数据
下面的代码用来获取Bundle传递过来的数据并且用Toast来显示
Bundle b = msg.getData(); int age = b.getInt("age"); String name = b.getString("name"); Toast toast = Toast.makeText(getApplicationContext(), "age="+age+"name="+name, Toast.LENGTH_LONG); toast.show();
package org.hualang.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
public class HandlerTest4 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d("System.out","Activity所在线程的id:"+Thread.currentThread().getId());
/**
* 生成一个HandlerThread对象,实现了使用Looper来处理消息队列的功能
* 这个类由Android应用程序框架提供
*/
HandlerThread handlerThread = new HandlerThread("handlerThread");
/**
* 使用HandlerThread的getLooper()方法之前,必须先调用该类的start()方法,否则是个null,会报错
*/
handlerThread.start();
MyHandler handler = new MyHandler(handlerThread.getLooper());
Message msg = handler.obtainMessage();
/**
* 将Message对象发送到目标对象
* 所谓的目标对象,就是生成该msg对象的handler对象
*/
//msg.obj = "Hello world";
Bundle b = new Bundle();
b.putInt("age", 22);
b.putString("name", "loulijun");
msg.setData(b);
msg.sendToTarget();
}
class MyHandler extends Handler
{
public MyHandler()
{
}
public MyHandler(Looper looper)
{
super(looper);
}
public void handleMessage(Message msg)
{
//String str = (String)msg.obj
Bundle b = msg.getData();
int age = b.getInt("age");
String name = b.getString("name");
Toast toast = Toast.makeText(getApplicationContext(), "age="+age+"name="+name, Toast.LENGTH_LONG);
toast.show();
Log.d("System.out", "handler所在线程的id:"+Thread.currentThread().getId());
}
}
}
运行结果: