文章目录
问题引入
Android用户界面是与用户交互的接口,对于用户的操作,Android迅速响应用户输入(200ms内)是一个重要目标。
若用户界面长时间对用户的操作未作出响应,那么这样的应用程序,肯定不会受到用户的广泛青睐。
此类问题案例很多:如后台下载、异步加载图片等等。
对于这类耗时比较多的工作,一般是使用多线程的方法来解决的。
线程知识
主线程(UI线程)
Android应用刚启动时,会在当前应用所对应的进程中启动一个主线程(也叫UI线程);
该UI线程处理与UI相关的事件,如:用户的按键事件,把相关的事件分派到对应的组件进行处理等。
子线程
1.对于UI线程中比较耗时的工作,开启一个子线程来处理这些工作:首先创建一个Thread 对象,然后调用start( )方法 启动新的子线程。
2.使用子线程解决异步执行:
(1)带来的新问题:在Android中,只有 UI线程 (即主线程)才可以更新主UI界面,而 子线程不能更新UI界面。
(2)解决方案(既要异步执行,又要解决更新UI界面的问题):
①使用多线程实现Thread+Handler
②使用AsyncTask
解决方案
一,使用Thread+Handler处理
1.概念
Thread:子线程,用来处理耗时的操作。
Handler:接受子线程发送的数据,并用此数据配合主线程更新UI
- Handler定义在主线程中(UI线程中)
- Handler充当主线程和子线程中间交互的中介:
• Handler在新启动的子线程中发送消息
• Handler在主线程中获取并处理子线程所发送的消息
2.流程图
3.过程概述
①首先UI线程创建的同时,会初始化一个Looper对象以及其关联的MessageQueue。
②创建一个工作子线程进行耗时工作的完成。
③通过引用的MyHandler,来发送携带信息的Message对象到MessageQueue。
④MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;
⑤Looper管理MessageQueue, 取出Message分发给MyHandler处理(每个主线程只有一个Looper)
4.代码实现
①创建Thread对象,在Thread对象的run方法中发送消息。
Thread thread= new Thread() {
public void run() {
Message msg = handler.obtainMessage();
msg.what = MSG_CURRENT;
msg.obj=1;
handler.sendMessage(msg);
}
};
thread.start();
②创建Handler对象(自定义的匿名子类方法),并添加handleMessage方法
private Handler handler = new Handler() {
public void handleMessage (Message msg) {
switch (msg.what) {
case MSG_CURRENT: // TODO
TextView.setText(msg.obj);
break;
}
}
};
二,用AsyncTask处理
1.概念(异步执行的四个方法)
①onPreExecute():任务被执行之前调用UI线程。
• 运行在UI线程中,任务执行前的准备,比如弹出进度条
②doInBackground(Params…):onPreExecute()执行完成,立刻调用后台线程。
• 这步被用于执行较长时间的后台任务;
• 子线程中运行,将执行结束的结果返回onPostExecute()参数中
③onProgressUpdate(Progress…):运行在UI线程中,更新当前进度条信息,被publishProgress回调。
• 这个方法用于在用户界面显示进度,当后台计算还在进行时。
• 例如:这个方法可以被用于一个进度条动画或在文本域显示日志。
④onPostExecute(Result):当后台计算结束时,调用UI线程。
• 运行在UI线程中,处理异步线程的任务结果。
2.流程图
3.AsyncTask异步任务使用时的注意事项
①AsyncTask必须被子类继承。
②子类至少重写其中的doInBackground(Params…)方法,一般还会重写onPostExecute(Result)。
③任务实例必须创建在UI线程。
④execute(Prams…)必须在UI线程上调用。
⑤不要手动调用onPreExecute(), onPostExecute(), doInBackground(), onProgressUpdate()。
⑥不能在doInBackground(Params… params)中更改UI界面。
4.AsyncTask异步任务代码实现进度条
• MainActivity.java
package sn.demo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class AsyncTaskDemoActivity extends Activity {
/** Called when the activity is first created. */
private Button download;
private TextView tv;
private ProgressBar pb;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initView();
}
private void initView() {
// TODO Auto-generated method stub
tv=(TextView)findViewById(R.id.tv);
pb=(ProgressBar)findViewById(R.id.pb);
download=(Button)findViewById(R.id.download);
download.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
DownloadTask dt=new DownloadTask(AsyncTaskDemoActivity.this,pb,tv);
dt.execute(100);
}
});
}
}
• DownloadTask.java
package sn.demo;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;
public class DownloadTask extends AsyncTask<Integer, Integer, String> {
//后面尖括号内分别是参数(线程休息时间),进度(publishProgress用到),返回值 类型
private Context mContext=null;
private ProgressBar mProgressBar=null;
private TextView mTextView=null;
public DownloadTask(Context context,ProgressBar pb,TextView tv){
this.mContext=context;
this.mProgressBar=pb;
this.mTextView=tv;
}
/*
* 第一个执行的方法
* 执行时机:在执行实际的后台操作前,被UI 线程调用
* 作用:可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化,这个方法可以不用实现。
* @see android.os.AsyncTask#onPreExecute()
*/
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
Log.d("sn", "00000");
super.onPreExecute();
}
/*
* 执行时机:在onPreExecute 方法执行后马上执行,该方法运行在后台线程中
* 作用:主要负责执行那些很耗时的后台处理工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
* @see android.os.AsyncTask#doInBackground(Params[])
*/
@Override
protected String doInBackground(Integer... params) {
// TODO Auto-generated method stub
Log.d("sn", "1111111");
for(int i=0;i<=100;i++){
mProgressBar.setProgress(i);
publishProgress(i);
try {
Thread.sleep(params[0]);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return "执行完毕";
}
/*
* 执行时机:这个函数在doInBackground调用publishProgress时被调用后,UI 线程将调用这个方法.虽然此方法只有一个参数,但此参数是一个数组,可以用values[i]来调用
* 作用:在界面上展示任务的进展情况,例如通过一个进度条进行展示。此实例中,该方法会被执行100次
* @see android.os.AsyncTask#onProgressUpdate(Progress[])
*/
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
Log.d("sn", "2222222222");
mTextView.setText(values[0]+"%");
super.onProgressUpdate(values);
}
/*
* 执行时机:在doInBackground 执行完成后,将被UI 线程调用
* 作用:后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户
* result:上面doInBackground执行后的返回值,所以这里是"执行完毕"
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
Log.d("sn", "3333333333");
super.onPostExecute(result);
}
}
• activity_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="Hello , Welcome to Andy's Blog!"/>
<Button
android:id="@+id/download"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Download"/>
<TextView
android:id="@+id/tv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="当前进度显示"/>
<ProgressBar
android:id="@+id/pb"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"/>
</LinearLayout>
总结
AsncyTask适合处理一些简单的异步任务,然而对于比较复杂的或多个同时存在异步任务,还是使用Thread+Handler异步消息机制更加方便和清晰。