使用AsyncTask非同步更新UI介面(載入網路圖片)
阿新 • • 發佈:2018-12-31
寫這個部落格時抽抽了,順便又用了一些WebView的東西,更多webview參見這裡
1.簡單介紹下AsyncTask
AsyncTask,是android提供的輕量級的非同步類,可以直接繼承AsyncTask,在類中實現非同步操作,並提供介面反饋當前非同步執行的程度(可以通過介面實現UI進度更新),最後反饋執行的結果給UI主執行緒.比Handler更輕量級,但是在使用多個非同步操作和並需要進行Ui變更時,就變得複雜起來.程式碼量上,如果只開一個後臺程序AsyncTask是絕對首選,因為handler處理結構相對複雜。
2.使用
要使用AsyncTask工作我們要提供三個泛型引數,並重載幾個方法(至少過載一個doInBackground )。
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() 使用者呼叫取消時,要做的操作
- Task的例項必須在UI thread中建立;
- execute方法必須在UI thread中呼叫;
- 不要手動的呼叫onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法;
- 該task只能被執行一次,否則多次呼叫時將會出現異常;
建立在上一個部落格專案基礎上,建立了新的佈局處理。
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();
}
}
}
裡面有用到一個縮放比的工具類:在這裡 最後別忘了新增網路訪問許可權
<uses-permission android:name="android.permission.INTERNET"/>
我把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.
【歡迎上碼】
【微信公眾號搜尋 h2o2s2】