第八章 网络的时代—网络开发(3)

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"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

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

<Button

android:id="@+id/download"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="开始下载图片"/>

<ImageView

android:id="@+id/imgic"

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

</LinearLayout>


程序运行结果如下图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<Params,Progress, Result>使用三种泛型和可变参数类型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

<LinearLayoutxmlns:android=”http://schemas.android.com/apk/res/android”

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:orientation=”vertical”>

<Button

android:id=”@+id/readWebpage”

android:layout_width=”match_parent”

android:layout_height=”wrap_content”

android:onClick=”readWebpage”

android:text=”LoadWebpage” >

</Button>

<TextView

android:id=”@+id/TextView01”

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:text=”ExampleText” >

</TextView>

</LinearLayout>


下面建一个执行的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<String, Void,String> {

@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只能被执行一次,否则多次调用时将会出现异常。


阅读更多
上一篇第八章 网络的时代—网络开发(2)
下一篇第八章 网络的时代—网络开发(4)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭