線程的概念
手機客戶端有個概念,即UI主線程,我們能看到的按鈕控件等其實都是有一條UI的主線程在運行着。如果有一條UI主線程在運行,在UI的主線程中我們去訪問網絡資源(存數據或者取數據),我們會遇到阻塞的現象。學習線程的好處:UI主線程是用於負責展示UI控件的,我們要完成耗時任務的操作,比如提交數據或者存數據應該在另外一個線程完成。
如果我們讓不同的組件在這裏運行,我們應該讓他運行在獨立的線程中,比如說,UI主線程在這運行着,同時我又下文件,又上傳數據,又看電影,這些分別不同的操作應該放在獨立的線程操作,不應該放在一個線程中操作。
系統不會給我們創建一個獨立的線程給每一個組件,也就是說,當我們點擊一個按鈕,訪問網絡圖片時(這是在主線程中操作的),我們期望的是不改在主線程中操作,系統不會幫我們創建的。如果不從主線程中分離開,這樣就會有好多組件的操作在主線程中操作,會導致主線程任務繁忙,尤其是耗時操作的時候(導致阻塞),反應超過5s就會可能產生ANR(application not responding)。
所以,UI主線程不能當做工作線程,Android中的線程模式應該是單線程的模式。
①不要阻塞UI主線程(不在UI線程訪問網絡等)。
②不在UI以外的線程當中訪問UI(UI的概念就是在主線程中呈現的)。
異步和同步的區別:
看下圖(參考別人的)
處理複雜的工作線程兩種機制:Handler或者AsyncTask
AsyncTask(異步任務)允許我們在用戶UI上執行異步的工作,這個執行可能是個阻塞的操作,它可以把結果發佈給UI。舉個例子,當我們下載一個文件時,刻度條就是UI,我們一邊下載,一邊讓刻度條走動來達到更新UI的效果。
異步任務操作的方法:
聲明一個類MyTask繼承(extends)AsyncTask(抽象類)->實現doInBackground()方法->爲了更新UI實現onPostExecute()方法->把這些結果從doInBackground()方法中傳遞到UI上->執行這個異步任務,只需在UI線程中調用execute()方法。
AsyncTask異步任務分爲三個參數Params,Progress和Result,四個步驟調用onPreExecute,doInBackground,onProgressUpdate和onPostExecute。
三個參數:
①Params:這個參數類型表示我們執行異步任務要發送的參數,也就是說,這個異步任務要執行什麼東西,比如訪問網絡這裏就是一個網址。
②Progress:這個參數是一個進度單位的類型,這個進度單位是在後臺計算完成之後被髮布的單位。
③Result:表示後臺執行的返回結果。
並非所有的異步任務都要用到三個參數,如果不用就用Void類型代替。
四個步驟:
執行一個完整的異步任務,要依次經歷以下四個步驟(步驟是有順序的①②③④順序執行),其中,doInBackground()方法是必須要執行的,即繼承了AsyncTask方法就一定要實現doInBackground方法,也就是說你得做一件事情,否則異步任務沒有意義:
①onPreExecute(),異步任務被執行之前執行,UI展示的時候執行。這個步驟通常用來構建一個異步任務,比如,我們可以顯示一個進度條給用戶看。
②doInBackground(Params...),在onPreExecute()之後doInBackground()會立即執行,這個步驟通常用來執行後臺的操作(耗時操作)。Params...這個參數表示我們要執行的參數。參數的結果會被計算出來,並且通過這個步驟返回給最後一個步驟onPostExecute()。在這個步驟中間我們可以用publicProgress(Progress...)來發佈一個或多個的進度單位,在第三個步驟onProgressUpdate()被調用時,把這個值發佈給UI線程。
③onProgressUpdate(Progress...)
④onPostExecute(Result),後臺計算出結果後會執行在UI上面,UI上的結果通常會來自doInBackground(Params...)。
注意:假設我不使用提示框第①個步驟不需要調用,如果我不需要把這個結果更新給UI第④個步驟不需要調用,如果我在下載的時候不更新進度條,第③個步驟不需要,這就是爲什麼一個類繼承了AsyncTask方法的時候僅僅需要doInBackground方法。
下面舉個例子:點擊下載網絡圖片按鈕,實現下載網絡圖片功能
activity_main.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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.android_asynctask_downloadimage.MainActivity" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginLeft="60dp"
android:layout_marginTop="40dp"
/>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="下載網絡圖片" />
</RelativeLayout>
MainActivity.java
package com.example.android_asynctask_downloadimage;
import java.io.IOException;
import org.apache.http.HttpResponse;
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 org.apache.http.util.EntityUtils;
import android.support.v7.app.ActionBarActivity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends ActionBarActivity {
private Button btn1;
private ImageView imageView1;
private final String IMAGE_PATH="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png";
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1=(Button) this.findViewById(R.id.button1);
imageView1=(ImageView) this.findViewById(R.id.imageView1);
dialog=new ProgressDialog(MainActivity.this);
dialog.setTitle("提示");
dialog.setCancelable(false);
dialog.setMessage("正在下載圖片,請等待...");
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
new MyTask().execute(IMAGE_PATH);//執行異步任務
}
});//點擊這個按鈕下載圖片,需要訪問網絡,記得在清單文件AndroidManifest.xml中加授權android.permission.INTERNET
}
/**
* 異步任務執行網絡下載圖片
* @author liuya
*
*/
public class MyTask extends AsyncTask<String, Integer, byte[]>{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
dialog.show();
}
/**
* 後臺訪問網絡
*/
@Override
protected byte[] doInBackground(String... params) {
// TODO Auto-generated method stub
HttpClient httpClient=new DefaultHttpClient();
HttpGet httpGet=new HttpGet(params[0]);//可變參數,三個點代表可變參數
byte[] result=null;
try {
HttpResponse httpResponse=httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode()==200){
result=EntityUtils.toByteArray(httpResponse.getEntity());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
httpClient.getConnectionManager().shutdown();
}
return result;
}
/**
* 更新UI
*/
@Override
protected void onPostExecute(byte[] result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
Bitmap bitmap=BitmapFactory.decodeByteArray(result, 0, result.length);
imageView1.setImageBitmap(bitmap);
dialog.dismiss();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
效果圖如下