在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);
- }
- };
- }
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);
- }
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);
- }
- }
- };
- }
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>
<?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);
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);
- }
- };
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();
- }
- }
- };
- }
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();
//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());
- }
- }
- }
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 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();
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());
}
}
}
运行结果: