作者:李若昆
在Android平台中多线程应用很广泛,在UI更新、游戏开发和耗时处理(网络通信等)等方面都需要多线程技术。
当一个程序第一次启动的时候,Android会启动一个主线程。主线程(Main Thread)主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。
为了提供良好的用户体验,必须保证程序有高响应性,所以不能在UI线程中进行耗时的计算或IO操作。
当主线程正在做比较耗时的操作时,如正从网络上下载一个大图片,或者访问数据库,由于主线程被这些耗时的操作阻,无法及时的响应用户的事件,从用户的角度看会觉得程序已经死掉。
所以我们要创建单独的子线程,一些较费时的对象操作需交给独立的线程去执行。
(但如果子线程来执行UI对象,Android就会发出错误信息CalledFromWrongThreadException。)
这里介绍两种方式用多线程操作UI:
第一种是创建新线程Thread,用handler负责线程间的通信和消息。
线程间通信模型如下:
Thread:处理耗时操作的子线程,在需要时通过Handler发送消息(Message);
在Java中有两种方法实现线程体:一是继承线程类Thread,二是实现接口Runnable。
如果采用第1种方式,它继承线程类Thread并重写其中的方法run(),由于Java只支持单重继承,用这种方法定义的类不能再继承其他父类。之后在主线程中实例化该线程,通过start()方法启动子线程,子线程启动之后就开始调用run()方法,run()是一个线程体,我们在子线程中处理事情就是在这里编写代码实现的。
第2种方式可以提供一个实现接口Runnable的类作为一个线程的目标对象,在构造Thread类时候把目标对象(实现Runnable的类)传递给这个线程实例,由该目标对象(实现Runnable的类)提供线程体run()方法。这时候实现接口Runnable的类仍然可以继承其他父类。
Handler:
Handler在android里负责发送和处理消息。它的主要用途有:
1)按计划发送消息或执行某个Runnable(使用POST方法);
2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程)
MessageQueue: 负责给消息排队
Looper:负责不断的从MessageQueue中取出消息,分发给UI处理线程中的Handler来接收。主线程默认有一个Looper对象。
默认情况下,Handler接受的是当前线程下的消息循环实例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定线程),同时一个消息队列可以被当前线程中的多个对象进行分发、处理(在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个Handler来处理)。在实例化Handler的时候,Looper可以是任意线程的,只要有Handler的指针,任何线程也都可以sendMessage。Handler对于Message的处理不是并发的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。
进度条程序:
主线程控制Button响应和更新UI,子线程负责计算进度条进度,程序代码如下:
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Start" />
</LinearLayout>
HandlerAcitvity.java:
public class HandlerActivity extends Activity implements OnClickListener{
private ProgressBar progressBar;
Handler handler =new Handler(){
//Handler处理收到的子线程消息,更新UI
@Override
public void handleMessage(android.os.Message msg) {
progressBar.setProgress(msg.arg1);
handler.post(updateThread);
};
};
//子线程计算进度,这里Thread.sleep(1000)表示休眠1秒,达到每1秒进度跳增加10
Runnable updateThread= new Runnable() {
int i = 0;
public void run() {
// TODO Auto-generated method stub
System.out.println("Begin Thread");
i+=10;
if(i>=100)
i=100;
Message msg=handler.obtainMessage();
msg.arg1=i;
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
handler.sendMessage(msg);
if(i>=100)
handler.removeCallbacks(updateThread);
}
};
//Runnable
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.button1);
btn.setOnClickListener(this);
progressBar=(ProgressBar)findViewById(R.id.progressBar1);
}
public void onClick(View arg0) {
// TODO Auto-generated method stub
progressBar.setVisibility(View.VISIBLE);
handler.post(updateThread);
}
}
运行结果:
下面介绍多线程的第二种方式AsyncTask:
用Handler类来在子线程中更新UI线程虽然避免了在主线程进行耗时计算,但费时的任务操作总会启动一些匿名的子线程,太多的子线程给系统带来巨大的负担,随之带来一些性能问题。
因此android提供了一个工具类AsyncTask,顾名思义异步执行任务。这个AsyncTask生来就是处理一些后台的比较耗时的任务,给用户带来良好用户体验的,从编程的语法上显得优雅了许多,不再需要子线程和Handler就可以完成异步操作并且刷新用户界面。
AsyncTask的执行步骤:
1) 继承AsyncTask
2) 实现AsyncTask中定义的下面一个或几个方法
onPreExecute(), 该方法将在执行实际的后台操作前被UI 线程调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化,这个方法可以不用实现。
doInBackground(Params...), 将在onPreExecute方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台处理工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
onProgressUpdate(Progress...),在publishProgress方法被调用后,UI 线程将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute方法将被UI 线程调用,后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户。
onCancelled(),在用户取消线程操作的时候调用。在主线程中调用onCancelled()的时候调用。
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
1) Task的实例必须在UI 线程中创建
2) execute方法必须在UI 线程中调用
3) 不要手动的调用onPreExecute(),onPostExecute(Result),doInBackground(Params...),onProgressUpdate(Progress...)这几个方法,需要在UI线程中实例化这个task来调用。
4) 该task只能被执行一次,否则多次调用时将会出现异常
后台计数程序示例:
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
AsyncTaskActivity.java:
public class AsyncTaskActivity extends Activity implements OnClickListener{
private Button Btn;
private TextView txt;
private int count=0;
private boolean isRunning=false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Btn=(Button)findViewById(R.id.button1);
txt=(TextView)findViewById(R.id.textView1);
Btn.setOnClickListener(this);
}
public void onClick(View arg0) {
// TODO Auto-generated method stub
isRunning=true;
TimeTickLoad timetick=new TimeTickLoad();
timetick.execute();
}
private class TimeTickLoad extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... arg0) {
while (isRunning) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
count++;
publishProgress(null);
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
txt.setText("时间已经过去了" + String.valueOf(count) + "S");
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
}
运行结果:

1060

被折叠的 条评论
为什么被折叠?



