您的位置:首页 > 理论基础 > 计算机网络

使用AsyncTask异步更新UI界面(加载网络图片)

2015-09-22 10:19 549 查看
写这个博客时抽抽了,顺便又用了一些WebView的东西,更多webview参见这里
1.简单介绍下AsyncTask
AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.比Handler更轻量级,但是在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.代码量上,如果只开一个后台进程AsyncTask是绝对首选,因为handler处理结构相对复杂。

2.使用

要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个doInBackground)。
参考:http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html
AsyncTask定义了三种泛型类型 Params,Progress和Result。

Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比, 比如Integer。
Result 后台执行任务最终返回的结果,比如String。

使用过AsyncTask 的同学都知道一个异步加载数据最少要重写以下这两个方法:

doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回

有必要的话你还得重写以下这三个方法,但不是必须的:

onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
onCancelled() 用户调用取消时,要做的操作

总结一句话:
在主线程中调用YouAsyncTask.execute("你的参数"),doInBackground(...)里面的参数就是你传的参数集,你可以在这里处理耗时操作,它会返回一个结果result给onPostExecute(...)在这里处理结果,如果你在doInBackground(...)中调用了publishProgress(progressNumber)方法,那么进度的显示在onProgressUpdate(...)中,里面的参数就是progressNumber。

使用AsyncTask类,以下是几条必须遵守的准则:

Task的实例必须在UI thread中创建;
execute方法必须在UI thread中调用;
不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
该task只能被执行一次,否则多次调用时将会出现异常;

3.看个栗子
建立在上一个博客项目基础上,创建了新的布局处理。

XMl:activity_async_task.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/download"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:padding="10dp"
        android:text="download" />
    <ImageView 
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/download"
        android:layout_marginTop="10dp"
        android:scaleType="centerCrop"/>
    
    <SeekBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_below="@id/image"
        android:layout_margin="5dp"
        android:max="100"
        android:progress="0"
        android:id="@+id/seekbar"/>
    
    <WebView 
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/seekbar"/>
</RelativeLayout>


AsyncTaskActivity.java(该文件弃用,因为后来优化遇到了些问题,作为对比故保留,详见第二个文件)

package com.lzy.exploremessagedemo;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;

public class AsyncTaskActivity extends Activity {
	private Button downloadButton;
	private ImageView mImageView;
	private ProgressDialog progressDialog;
	private SeekBar seekBar;
	private WebView webView;
	
	private final String url = "http://b.hiphotos.baidu.com/image/h%3D300/sign=1b921b860d24ab18ff16e" +
			"73705fbe69a/86d6277f9e2f070861ccd4a0ed24b899a801f241.jpg";
	@SuppressLint("SetJavaScriptEnabled")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_async_task);
		progressDialog = new ProgressDialog(this);
		
		mImageView = (ImageView) findViewById(R.id.image);
		seekBar = (SeekBar) findViewById(R.id.seekbar);
		webView = (WebView) findViewById(R.id.webview);
		
		WebSettings settings = webView.getSettings();
		settings.setJavaScriptEnabled(true);
		settings.setBuiltInZoomControls(true);
		settings.setDisplayZoomControls(true);
		settings.setSupportZoom(true);
		downloadButton = (Button) findViewById(R.id.download);
		downloadButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				new MyAsyncTask().execute(url);//异步加载网络图片
				webView.loadUrl(url);//使用webview加载网络图片
			}
		});
	}
	
	class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>{//参数,进度,结果

		@Override
		protected void onPreExecute() {
			progressDialog.setMessage("请稍后...");
			progressDialog.show();
		}

		@Override
		protected Bitmap doInBackground(String... prarm) {
			String url = prarm[0];
			Bitmap bitmap = null;
			HttpClient httpClient = new DefaultHttpClient();
			HttpGet httpGet = new HttpGet(url);
			try {
				HttpResponse httpResponse = httpClient.execute(httpGet);
				if (HttpStatus.SC_OK == httpResponse.getStatusLine().getStatusCode()) {
					HttpEntity entity = httpResponse.getEntity();
					InputStream in = entity.getContent();
					bitmap = BitmapFactory.decodeStream(in);
					publishProgress(100);//进度显示,这里作为一个知识点,结果显示回调在onProgressUpdate(Integer... values)方法中
				}
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				httpClient.getConnectionManager().shutdown();
			}
			return bitmap;
		}
		
		@Override
		protected void onProgressUpdate(Integer... values) {
			seekBar.setProgress(values[0]);
		}
		
		@Override
		protected void onPostExecute(Bitmap result) {
			progressDialog.dismiss();
			mImageView.setImageBitmap(result);
			Toast.makeText(getApplicationContext(), "下载成功!", Toast.LENGTH_SHORT).show();
		}
		
	}
}


vvvvvvvv22222222222222222222222222↓

package com.lzy.exploremessagedemo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;

public class AsyncTaskActivity extends Activity {
	private Button downloadButton;
	private ImageView mImageView;
	private ProgressDialog progressDialog;
	private SeekBar seekBar;
	private WebView webView;
	
	private final String url = "http://b.hiphotos.baidu.com/image/h%3D300/sign=1b921b860d24ab18ff16e" +
			"73705fbe69a/86d6277f9e2f070861ccd4a0ed24b899a801f241.jpg";
	@SuppressLint("SetJavaScriptEnabled")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_async_task);
		progressDialog = new ProgressDialog(this);
		
		mImageView = (ImageView) findViewById(R.id.image);
		seekBar = (SeekBar) findViewById(R.id.seekbar);
		webView = (WebView) findViewById(R.id.webview);
		
		WebSettings settings = webView.getSettings();
		settings.setJavaScriptEnabled(true);
		settings.setBuiltInZoomControls(true);
		settings.setDisplayZoomControls(true);
		settings.setSupportZoom(true);
		downloadButton = (Button) findViewById(R.id.download);
		downloadButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				new MyAsyncTask().execute(url);
				webView.loadUrl(url);
			}
		});
	}
	
	class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>{//参数,进度,结果

		@Override
		protected void onPreExecute() {
			progressDialog.setMessage("请稍后");
			progressDialog.show();
		}

		@SuppressWarnings("unused")
		@Override
		protected Bitmap doInBackground(String... prarm) {
			String url = prarm[0];
			Bitmap bitmap = null;
			InputStream in = null;
			HttpClient httpClient = new DefaultHttpClient();
			HttpGet httpGet = new HttpGet(url);
			try {
				HttpResponse httpResponse = httpClient.execute(httpGet);
				if (HttpStatus.SC_OK == httpResponse.getStatusLine().getStatusCode()) {
					HttpEntity entity = httpResponse.getEntity();
					in = entity.getContent();
					long total = entity.getContentLength();  
					ByteArrayOutputStream baos = new ByteArrayOutputStream();  
                    byte[] buf = new byte[1024];  
                    int count = 0;  
                    int length = -1; 
                    while ((length = in.read(buf)) != -1) { 
                        baos.write(buf, 0, length);  
                        count += length;  
                        //调用publishProgress公布进度,最后onProgressUpdate方法将被执行  
                        publishProgress((int) ((count / (float) total) * 100));  
                        //为了演示进度,休眠100毫秒  
						Thread.sleep(100);
                    }
                    /**
                     * @time 2015-9-22 - 下午1:48:59
                     * @bug 这是android 1.6的一个bug,解决方案如下
                     * */
                    //bitmap = BitmapFactory.decodeStream(in);//不清楚为什么这样写获取不到图片,改用如下方式就能了,如果不为了演示进度条的功能,将while注掉,然后使用此方法,却能显示图片??????
                    //bitmap = BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.toByteArray().length);
                    
                    /**
                     * 最终解决办法
                     * */
                    if (in == null) {
                    	bitmap = BitmapFactory.decodeStream(in);
					}else {
						//为了防止加载大图片OOM 采用BitmapFactory.Options进行缩略图处理
						BitmapFactory.Options options = new BitmapFactory.Options();
						options.inJustDecodeBounds = true;//不返回实际的Bitmap
						options.inSampleSize = Util.computeSampleSize(options, -1, 128 * 128);
						
						//为什么加上options参数获取的Bitmap就为null呢????????????   心累。。。。。
						/**
						 * @time 2015-9-22 - 下午3:03:32
						 * @bug 这是因为options.inJustDecodeBounds = true后不会创建Bitmap空间只作为暂存,最后
						 * 还要设置回来显示Bitmap
						 * */
						options.inJustDecodeBounds = false;
						bitmap = BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.toByteArray().length, options);
					}
                    return bitmap;
				}
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				httpClient.getConnectionManager().shutdown();
			}
			return null;
		}
		
		@Override
		protected void onProgressUpdate(Integer... values) {
			seekBar.setProgress(values[0]);
		}
		
		@Override
		protected void onPostExecute(Bitmap result) {
			progressDialog.dismiss();
			/**
			 * 尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,
			 * 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。 ☆
			 * */
			mImageView.setImageBitmap(result);
			Toast.makeText(getApplicationContext(), "下载成功!", Toast.LENGTH_SHORT).show();
		}
		
	}
}


里面有用到一个缩放比的工具类:在这里
☆的说明参看博客:/article/1904195.html

最后别忘了添加网络访问权限

<uses-permission android:name="android.permission.INTERNET"/>


不了解HttpClient用法的参见这篇博客:http://blog.csdn.net/bhq2010/article/details/9210007

我把manifest也贴出来吧:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lzy.exploremessagedemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="21" />
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.Light" >
        <activity
            android:name=".AsyncTaskActivity" <!-- 换成上一篇博文的主文件".MainActivity"就可以启动上一个项目了 -->
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


至此关于AsyncTask的使用就是这么简单。

栗子源码下载:点我下载

—— lovey hy.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: