8.4耗时操作的通用方式—多线程与异步处理

Android通过一个主线程对用户界面进行更新,这个线程是UI线程。如果程序不使用任何并发构建、Android的所有代码都会在这个线程中运行。当我们在进行网络连接等比较耗时的操作时,如果此连接动作直接在主线程,也就是UI线程中处理,会发生什么情况呢?整个程序处于等待状态,界面似乎是“假死”掉了。如果5秒钟以上没有响应,系统就会弹出对话框提示是否要强制关闭应用。为了给用户更好的用户体验,必须把这个任务放置到单独线程中运行,避免阻塞UI线程,这样就不会对主线程有任何影响。

8.4.1 多线程和异步处理简介

一般的,网络请求都需要一定的时间,所以在网络开发的过程中,会考虑使用多线程来实现网络请求,配合异步处理完成UI线程的更新。所以我们在本章用一个小节来详细讲下Android中的多线程和异步处理。

Android实现多线程与异步处理一般有下面两种方式:

1Handler方式。

2AsyncTask类实现。

下面我们就会对这两种方式做详细的说明。


经验分享:

一般的,如果应用程序中会大量的创建新的线程,就要考虑使用线程池了。使用线程池可以有效的管理线程。由于本节主要介绍如何在Android网络应用中实现异步处理,所以就不对线程池做展开说明了。


8.4.2 Handler方式

Handler允许用户发送、处理消息和与线程的消息队列相关联的runnable对象。每一个handler实例都与一个单独的线程相关联。每次创建一个新的Handler对象时,它都与创建该对象的线程或者该线程中的消息队列绑定在一起,这样Handler就可以发送消息和runnable对象到消息队列中,并在从消息队列中取出的时候处理它们。详细的说明可以参考“4.3.1消息的传递—Handler的使用”。

下面是通过Handler来异步加载网络图片的完整例子:

//import

publicclass HandlerTest extends Activity {


publicstatic final int SHOW_PROGRESS = 0;

publicstatic final int REFRESH = 1;

publicstatic final int REMOVE_PROGRESS = 2;

privateProgressBar mProgressBar;

privateImageView img;

privateButton btn;

privateBitmap mBitmap;


privateView.OnClickListener mOnClickListener = new View.OnClickListener(){

@Override

publicvoid onClick(View v) {

intid = v.getId();

switch(id) {

caseR.id.download:

sendMessage(SHOW_PROGRESS);

Thread t =

newThread(newDownload("http://www.google.com/images/google_favicon_128.png"));

// 开启一个线程下载图片

t.start();

break;

}

}

};

privateHandler mHandler = new Handler() {

@Override

publicvoid handleMessage(Message msg) {

switch(msg.what) {

caseSHOW_PROGRESS:

//显示等待界面

mProgressBar.setVisibility(View.VISIBLE);

break;

caseREMOVE_PROGRESS:

//隐藏等待界面

mHandler.removeMessages(SHOW_PROGRESS);

mProgressBar.setVisibility(View.INVISIBLE);

break;

caseREFRESH:

//更新UI

onRefresh();

break;

}

}

};


protectedvoid onRefresh() {

if(mBitmap!= null){

//更新UI

img.setImageBitmap(mBitmap);

}

//隐藏等待界面

sendMessage(REMOVE_PROGRESS);

}


protectedfinal void sendMessage(int what) {

mHandler.sendMessage(mHandler.obtainMessage(what));

}


@Override

publicvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.handlertest_layout);

img= (ImageView)findViewById(R.id.imgic);

btn= (Button)findViewById(R.id.download);

btn.setOnClickListener(mOnClickListener);

mProgressBar= new ProgressBar(this);

FrameLayout.LayoutParamsparams = new FrameLayout.LayoutParams(

LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);

params.gravity= Gravity.CENTER;

//添加一个等待的view

addContentView(mProgressBar,params);

//开始时候设置为隐藏

mProgressBar.setVisibility(View.INVISIBLE);

}


/**

* 下载的线程

*/

publicclass Download implements Runnable {

privateString uri;

publicDownload(String uri) {

this.uri= uri;

}

@Override

publicvoid run() {

try{

URL url = new URL(uri);

HttpURLConnectionconn

=(HttpURLConnection)url.openConnection();

conn.setDoInput(true);

conn.connect();

InputStreaminputStream=conn.getInputStream();

mBitmap= BitmapFactory.decodeStream(inputStream);

//下载完成后,发送消息给主线程刷新UI

sendMessage(REFRESH);

}catch (Exception e) {

//

}

}

}

}


具体的Layout文件handlertest_layout.xml代码如下:

<?xmlversion="1.0" encoding="utf-8"?>

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="@android:color/white">


程序运行结果如下图8-3所示。

8-3异步加载网络图片的运行效果图


经验分享:

Message是线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。Message中包含了两个额外的intwhat字段(该字段是用来区分每条信息)和一个object字段,这样在大部分情况下,使用者就不需要再做内存分配工作了。虽然Message的构造函数是public的,但是最好是使用Message.obtain()Handler.obtainMessage()函数来获取Message对象,因为Message的实现中包含了回收再利用的机制,可以提高效率。


8.4.3AsyncTask类实现后台任务的处理

Android提供了一个较线程更简单的处理多任务的方法——AsyncTask异步任务类,相对于线程来说,AsyncTask对于简单的任务处理更安全。AsyncTask类是一个抽象类,你要使用它,必须继承它并实现doInBackground()方法。

AsyncTask使用三种泛型和可变参数类型ParamsProgressResult

  • Params启动任务执行的输入参数,比如HTTP请求的URL

  • Progress后台任务执行的百分比。

  • Result后台执行任务最终返回的结果,比如String

AsyncTask主要有三个操作方法

1doInBackground(Params...),将在onPreExecute方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作,例如网络获取图片。可以调用publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。

2onProgressUpdate(Progress...),publishProgress方法被调用后,UIthread将调用这个方法从而在界面上展示任务的进展情况,例如(刷新前台的图片)。

3onPostExecute(Result),doInBackground执行完成后,onPostExecute方法将被UIthread调用,后台的计算结果将通过该方法传递到UIthread

下面我们来看一个下载网页的例子。

首先建一个layout文件asynctask_layout.xml

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:orientation=”vertical”>

android:id=”@+id/readWebpage”

android:layout_width=”match_parent”

android:layout_height=”wrap_content”

android:onClick=”readWebpage”

android:text=”LoadWebpage” >

android:id=”@+id/TextView01”

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:text=”ExampleText” >


下面建一个执行的Activity

//import

publicclass AsyncTaskTest extends Activity {

privateTextView textView;

@Override

publicvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.asynctask_layout);

textView= (TextView) findViewById(R.id.TextView01);

}

privateclass DownloadWebPageTask extends AsyncTask {

@Override

protectedString doInBackground(String... urls) {

Stringresponse = "";

for(String url : urls) {

// 循环的获取Params中的参数

DefaultHttpClientclient = new DefaultHttpClient();

HttpGethttpGet = new HttpGet(url);

try{

HttpResponseexecute = client.execute(httpGet);

InputStreamcontent = execute.getEntity().getContent();

BufferedReaderbuffer = new BufferedReader(

newInputStreamReader(content));

Strings = "";

while((s = buffer.readLine()) != null) {

response+= s;

}

}catch (Exception e) {

e.printStackTrace();

}

}

returnresponse;

}

@Override

protectedvoid onPostExecute(String result) {

//更新UI

//这里的参数是doInBackground返回过来的

textView.setText(result); }

}

publicvoid readWebpage(View view) {

//按钮的动作

DownloadWebPageTasktask = new DownloadWebPageTask();

task.execute(newString[] { "http://www.baidu.com"});

}

}


经验分享:

在使用AsyncTask中需要注意以下几点:

1AsyncTask的实例必须在UIthread中创建。

2AsyncTask.execute方法必须在UIthread中调用。

3)不要手动的调用onPreExecute(),onPostExecute(Result)

doInBackground(Params...),onProgressUpdate(Progress...)这几个方法。

4)该task只能被执行一次,否则多次调用时将会出现异常。


更多相关文章

  1. Android(安卓)MVP模式 初步理解
  2. Android进阶知识树——Android(安卓)多进程、Binder 你必须知道
  3. android解析XML文件的三方法之DOM
  4. android里的线程和进程
  5. Android(安卓)界面滑动实现---Scroller类 从源码和开发文档中学
  6. Android中线程与进程的理解
  7. Android的事件处理机制之基于监听的事件处理
  8. Android(安卓)对View的一些理解
  9. 如何使用Android中的OpenGL ES媒体效果

随机推荐

  1. 解决了在Android原生TextView中,当text长
  2. Eclipse 点击 Run 自动生成 out 文件的错
  3. Android定时器AlarmManager和Timer的区别
  4. Dialog使用findViewById 报空指针异常
  5. 浅析Android中build.gradle的实用技巧
  6. ImageView宽度填满屏幕,高度自适应
  7. Android(安卓)中文API (68) ―― Bluetooth
  8. Android入门开发之SD卡读写操作
  9. Android中在代码中设置控件的宽和高
  10. HttpURLConnection和HttpClient区别