AsyncTask(背景執行緒)


AsyncTask非同步任務,或稱異步任務,是一個相當常用的類別,是專門用來處理背景任務與UI的類別。

Android 4.0 之後,有明文規定所有的網路行為都不能在主執行緒(Main/UI Thread)執行 所以先放一個Button進去加入以下程式碼

button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                for(int i = 0 ; i < 10000000 ; i++){
                    Log.d("Tag = " , "HI");
                }
            }
        });

就會直接跳出錯誤訊息,因為主執行緒處理過多的問題,如果你不知道哪一條事主執行緒,可以加入這段程式碼

Thread.currentThread().getId()

然後試著印出來,你會發現他會寫1,1就是主執行緒的ID,也是UI Thread。 你可能會想,只是下載一張小圖片應該不會花費太久時間,那我寫在主執行緒就好了。

然後就在onClick裡面寫了以下的程式碼。

try {
   URL url = new URL("http://i.imgur.com/Uki7N9T.jpg");
    //取得圖片的URL
   Bitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
    //透過BitmapFactory來下載URL的圖片
   imageView.setImageBitmap(bitmap);
   //設置圖片到ImageView之中
} catch (IOException e) {
    e.printStackTrace();
}

透過BitmapFactory來下載圖片,在使用Try Catch來捕捉一些可能的例外,看起來滿正確的,但是實際執行會發現有一個例外。

android.os.NetworkOnMainThreadException

意思是,你不能在主執行緒做網路的事情

AsyncTask<Params, Progress, Result>

這是基本的架構,使用泛型來定義參數, 泛型意思是,你可以定義任意的資料型態給他。

Params : 參數,你要餵什麼樣的參數給它。

Progress : 進度條,進度條的資料型態要用哪種

Result : 結果,你希望這個背景任務最後會有什麼樣的結果回傳給你。

此外,AsyncTask會有四個步驟。

onPreExecute : 執行前,一些基本設定可以在這邊做。

doInBackground : 執行中,在背景做任務。

onProgressUpdate : 執行中,當你呼叫publishProgress的時候會到這邊,可以告知使用者進度。

onPostExecute : 執行後,最後的結果會在這邊。

參數說明 : 丟入網址(String),進度條用整數(Integer),拿到圖片(Bitmap)

private class GetImage extends AsyncTask<String , Integer , Bitmap>{

        @Override
        protected void onPreExecute() {
            //執行前 設定可以在這邊設定
            super.onPreExecute();
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            //執行中 在背景做事情
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            //執行中 可以在這邊告知使用者進度
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //執行後 完成背景任務
            super.onPostExecute(bitmap);
        }
    }

String...是什麼意思,這東西的意思是你可以傳單一一個String, 或者是一個String陣列都可以。

假如你丟一個String進去,你只要取得第一個元素即可。

String urlStr = params[0];

接著在把剛才下載圖片的程式改寫到doInBackground之中。

protected Bitmap doInBackground(String... params) {
            //執行中 在背景做事情
            String urlStr = params[0];
            try {
                URL url = new URL(urlStr);
                Bitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
                imageView.setImageBitmap(bitmap);
                return bitmap;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

這時候會跳出一個例外

Only the original thread that created a view hierarchy can touch its views.

意思是,你只能在UI Thread去修改UI,因為你現在是在背景,因此你必須回到UI Thread才能對UI做事情(這裡只有doInBackground不是由UI Thread處理)

最後做了一個Dialog去Loading圖片,以下為主程式碼

import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    Button button;
    ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.button);
        imageView = (ImageView) findViewById(R.id.imageView);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new GetImage().execute("http://i.imgur.com/Uki7N9T.jpg");

            }
        });


    }


        private class GetImage extends AsyncTask<String, Integer, Bitmap> {
            private ProgressDialog progressDialog;

            @Override
            protected void onPreExecute() {
                //執行前 設定可以在這邊設定
                super.onPreExecute();
                progressDialog=new ProgressDialog(MainActivity.this);
                progressDialog.setMessage("Loading");
                progressDialog.setTitle("Loading");
                progressDialog.setCancelable(false);
                progressDialog.setProgressStyle(progressDialog.STYLE_HORIZONTAL);
                progressDialog.show();
            }

            @Override
            protected Bitmap doInBackground(String... params) {
                //執行中 在背景做事情

                String urlStr = params[0];
                try {
                    URL url = new URL(urlStr);
                    return BitmapFactory.decodeStream(url.openConnection().getInputStream());
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                //執行中 可以在這邊告知使用者進度
                super.onProgressUpdate(values);
                progressDialog.setProgress(values[0]);

            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                //執行後 完成背景任務
                super.onPostExecute(bitmap);
                progressDialog.dismiss();
                imageView.setImageBitmap(bitmap);
            }
        }

    }

results matching ""

    No results matching ""