1. 程式人生 > >Android執行緒與執行緒池

Android執行緒與執行緒池

一.特殊的執行緒

1.AsynTask

底層用到了執行緒池,封裝了執行緒池和Handler,主要是為了方便開發者在子執行緒中更新UI

2.IntentService

內部採用HandlerThread來執行任務,當任務執行完畢後IntentService會自動退出,底層直接使用了執行緒(從任務執行的角度來看,IntentService的作用很像一個後臺執行緒,但是IntentService是一種服務,他不容易被系統殺死從而可以儘量保證任務的執行)

3.HandlerThread

是一種具有訊息迴圈的執行緒,在它的內部可以使用Handler,底層直接使用了執行緒

二.Android中的執行緒形態

 1.1 引數及方法介紹

1.AsyncTask是一個抽象的泛型類,它提供了Params,ProgressResult這三個泛型引數,其中Params表示引數的型別Progress表示後臺任務的執行進度的型別,而Result則表示後臺任務的返回結果的型別

如果AsyncTask確實不需要傳遞具體的引數,那麼這三個泛型引數可以用Void來代替,AsyncTask這個類的宣告如下所示。

public abstract class AsyncTask<Params ,Progress ,Result>

(1)onPreExecute

在主執行緒執行,在非同步任務執行之前,此方法會被呼叫,一般可用於做一些準備工作

(2)doInBackground(Params...params)

線上程池中執行,此方法用於執行非同步任務,params引數表示非同步任務的的輸入引數,在此方法中可以通publishProgress方法來更新任務的進度publicProgress方法會呼叫onProgressUpdate方法。另外此方法需要返回計算結果給onPostExecute

(3)onProgressUpdate(Progress...values)

在主執行緒中執行,當後臺任務的執行進度發生改變時此方法會被呼叫。

(4)onPostExecute(Result result)

在主執行緒中執行,非同步任務執行之後,此方法會被呼叫,其中Result

引數是後臺任務的返回值,即doInBackgroud的返回值

(5)onCancelled

在主執行緒中執行 非同步任務取消時呼叫,這個時候onPostExecute則不會呼叫

1.2  AsyncTask在具體的使用過程中也是有一些條件限制

(1)AsyncTask的類必須在主執行緒中載入,這就意味著第一個訪問AsncTask必須發生在主執行緒,當然這個過程在Android4.1及以上版本中已經被系統已經自動完成。Android5.0的原始碼中,可以檢視ActivityThreadmain方法,他會呼叫AsyncTaskinit方法,這就滿足了AsyncTask的類必須在主執行緒中進行載入這個條件(為什麼要滿足這個條件,因為要切回主執行緒的Handler必須是在主執行緒中建立,然後這個Handler是AsyncTask的內部靜態類,所以這個類必須在主執行緒載入

(2)AsyncTask的物件必須在主執行緒中建立

(3)Execute方法必須在UI執行緒呼叫

(4)一個AsyncTask物件只能執行一次,即只能呼叫一次execute方法,否則會報執行異常

(5)在Android1.6之前,AsyncTask是序列執行任務的,Android1.6的時候AsyncTask開始採用執行緒池裡處理並行任務,但是Android3.0開始,為了避免AsyncTask所帶來的併發錯誤,AsyncTask又採用一個執行緒來序列執行任,儘管如此,在Android3.0以及後續的版本中,我們仍然可以通過AsynTaskexecuteOnExecutor方法並行執行任務

1.3原始碼分析

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;


@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

sDefaultExecutor序列的執行緒池

//executeOnExecutor    //1

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}


//2

mFuture = new FutureTask<Result>(mWorker) {
    @Override
    protected void done() {
        try {
            postResultIfNotInvoked(get());
        } catch (InterruptedException e) {
            android.util.Log.w(LOG_TAG, e);
        } catch (ExecutionException e) {
            throw new RuntimeException("An error occurred while executing doInBackground()",
                    e.getCause());
        } catch (CancellationException e) {
            postResultIfNotInvoked(null);
        }
    }
};

可以看到把Params封裝成FutureTask(這個事併發類在這充當Runnable)物件

最上面寫了public static final Executor SERIAL_EXECUTOR = new SerialExecutor()
預設用的就是這個執行緒池

//3

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

這時候就是把任務佇列mTasks

scheduleNext()一個AsyncTask任務執行完後,呼叫這個來執行下一個AsyncTask


//4

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        return postResult(result);
    }
};

由於FutureTaskrun方法會呼叫mWorkercall方法最終會線上程池中執行,在這個方法中先將mTaskInvoked設為true,表示當前任務已經被呼叫過了,然後執行AsyncTaskdoInBackgroud方法,接著傳遞給PostResult

AsyncTaks中有兩個執行緒池

1.SerialExecutor

用於任務的佇列

2.THREAD_POOL_EXECUTOR(也叫InternalHandler)

用於將執行環境從執行緒池切換到主執行緒


//5

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}


//6

private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}


//7

private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

可以看出sHandler是一個靜態的Handler物件,為了能夠將執行環境切換到主執行緒

這就要求1.sHandler這個物件必須在主執行緒中建立(在哪建立,handler裡面的各種函式才能在哪執行,這裡需要在主執行緒)----------->2.由於靜態成員會在載入類的時候進行初始化,因此這就變相要求AsyncTask的類必須在主執行緒建立(因為sHandlerAsyncTask的靜態成員變數),否則同一個程序中的AsyncTask都將無法正常工作


//8

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

1.4簡單實驗(證明AsyncTaks即可序列也是並行)

public class MainActivity extends Activity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.bt_start_task).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        //序列
        new MyAsyncTask("我是序列task1").execute();
//        new MyAsyncTask("我是序列task2").execute();
//        new MyAsyncTask("我是序列task3").execute();
//        new MyAsyncTask("我是序列task4").execute();
//        new MyAsyncTask("我是序列task5").execute();
//        public final AsyncTask<Params, Progress, Result> execute(Params... params) {
//            return executeOnExecutor(sDefaultExecutor, params);
//        }


        //並行
        new MyAsyncTask("我是並行task1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
        new MyAsyncTask("我是並行task2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
        new MyAsyncTask("我是並行task3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
        new MyAsyncTask("我是並行task4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
        new MyAsyncTask("我是並行task5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");

    }

    private static class MyAsyncTask extends AsyncTask<String ,Integer,String>{

        private String mName = "AsyncTask";

        public MyAsyncTask(String mName) {
            super();
            this.mName = mName;
        }

        @Override
        protected String doInBackground(String... params) {
            SystemClock.sleep(3000);
            return mName;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("xcqw   "+mName+"  execute finish at"+df.format(new Date()));
        }
    }

}


可以通過看列印得出既可以序列也可以並行,但是為什麼呢?

序列呼叫execute和並行呼叫executeOnExecutor 最後都是呼叫 executeOnExecutor,只是傳的引數不一樣

//序列傳的引數(預設引數)

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
從佇列中依次取出任務然後呼叫THREAD_POOL_EXECUTOR.execute(mActive);

//並行傳的引數THREAD_POOL_EXECUTOR

每次都直接呼叫這個THREAD_POOL_EXECUTOR.execute,所以當然並行了

1.5簡單實踐

//版本更新功能用AsyncTask實現

//updateProgressDialogTask.java

package com.weixin.updateprogressdialog.task;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;

import com.weixin.updateprogressdialog.dialog.UpdateProcessDialog;
import com.weixin.updateprogressdialog.utils.ToastUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by xiongchao on 2015/10/29.
 */
public class UpdateTask extends AsyncTask<Object, Integer, File> {
    //下載的路徑
    private String urlpath;
    private UpdateProcessDialog mydialog;
    private Handler threadhandler;
    private Activity activity;
    private FileOutputStream fos;
    private BufferedInputStream bis;
    private InputStream is;
    //下載成功後重命名的檔案
    private  String  updateSuccessFilePath;
    //下載過程中存放apk的地址
    private String  updatingStorePath;
    //當前執行的執行緒
    private UpdateTask mCurrentTaks;
    public  static boolean isStopDownload = false;

//    public boolean isStopDownload() {
//        return isStopDownload;
//    }
//
//    public void setIsStopDownload(boolean isStopDownload) {
//        this.isStopDownload = isStopDownload;
//    }


    public void setUpdateComponents(UpdateTask Task,String serverversion,String updatingStorePath) {
        this.mCurrentTaks = Task;
        this.updateSuccessFilePath = Environment.getExternalStorageDirectory()+"/MyWeiXinUpDate/"+"update1"+serverversion+".apk";
        this.updatingStorePath = updatingStorePath;
    }



    public UpdateTask(Activity activity,String urlpath, UpdateProcessDialog dialog,Handler threadhandler) {

        this.activity = activity;
        this.urlpath = urlpath;
        this.mydialog = dialog;
        this.threadhandler = threadhandler;
    }

    // 主執行緒執行, 預載入
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    // 子執行緒執行, 非同步載入邏輯在此方法中處理
    @Override
    protected File doInBackground(Object... params) {
        System.out.println("xcq  " + "任務開始");
        urlpath = (String) params[0];
        //如果相等的話表示當前的sdcard掛載在手機上並且是可用的
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            try {
                URL url = new URL(urlpath);
                System.out.println("xcq  " + "去請求連線");
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                //設定連線超時
                conn.setConnectTimeout(5000);
                //設定下載超時
                conn.setReadTimeout(60000);
                //是否連線成功//如果沒有網路會在這裡直接跳到異常
                System.out.println("xcq  " + "去請求響應碼");
                //響應碼這阻塞時間較長,所以先開啟dialog
                threadhandler.post(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("xcq  mydialog   3"+mydialog);
                        if(mydialog != null) {
                            mydialog.show();
                        }
                    }
                });
                int code = conn.getResponseCode();
                if( 200 == code)
                {
                    File file = new File(updatingStorePath);
                    File suceessFile = new File(updateSuccessFilePath);
                    //如果已經下載過直接去安裝
                    if (suceessFile.exists()) {
                        return suceessFile;
                    }
                    //獲取到檔案的大小
                    long length = conn.getContentLength();
                    is = conn.getInputStream();
                    fos = new FileOutputStream(file);
                    bis = new BufferedInputStream(is);
                    byte[] buffer = new byte[1024];
                    int len;
                    int total = 0;
                    boolean renameflag = false;
                    while ((len = bis.read(buffer)) != -1) {
                        fos.write(buffer, 0, len);
                        total += len;
                        if (isStopDownload) {
                            stopDownLoad();
                            break;
                        }
                        int percent = (int) (total * 100 / length);

                        if(percent == 100){
                            System.out.println("xcq  " + "下載成功");
                            //如果下載成功,下載檔案重新命名
                            renameflag = file.renameTo(suceessFile);
                            if(renameflag == false){
                                throw new Exception("檔案重新命名失敗");
                            }
                        }
                        //獲取當前下載量//進入progressupdate更新
                        publishProgress(percent);

                    }
                    fos.close();
                    bis.close();
                    is.close();
                    myDialogDismiss(mydialog);
                    if(mCurrentTaks != null && renameflag){
                        System.out.println("xcq  " +"is"+isStopDownload);
                        return suceessFile;
                    }else{
                        //如果到這就是中斷更新了//還有可能是下載成功後文件重新命名失敗
                        System.out.println("xcq  " +"is"+isStopDownload);
                        return null;
                    }
                }
                else{
                    //連線伺服器失敗//伺服器返回引數不是200(OK)
                    ToastUtils.show(activity, "連線伺服器異常,請檢查網路");
                }

            } catch (Exception e) {
                System.out.println("xcq  異常" + e.toString());
                try {if (fos != null){fos.close();}} catch (IOException e1) {e1.printStackTrace();}
                try {if (bis != null) fos.close();} catch (IOException e1) {e1.printStackTrace();}
                try {if (fos != null) is.close();} catch (IOException e1) {e1.printStackTrace();}
                ToastUtils.show(activity,"下載失敗,請檢查網路");
                stopDownLoad();
                e.printStackTrace();
            }
        } else {
            ToastUtils.show(activity,"請檢查sd卡是否安裝正確");
            stopDownLoad();
            return null;
        }

        return null;
    }

    // 主執行緒執行, 更新進度
    @Override
    protected void onProgressUpdate(Integer... values) {
        int i = (int) values[0];
        System.out.println("xcq  Update進度" + i+"isStopDownload"+isStopDownload);
        if (mydialog != null) {
            mydialog.show();
            mydialog.updateProgressAndTitle(i);
        }
        super.onProgressUpdate(values);
    }

    // 主執行緒執行, 更新主介面
    @Override
    protected void onPostExecute(File result) {
        if (mCurrentTaks != null) {
            if (result != null && !mCurrentTaks.isCancelled()) {
                stopDownLoad();
                Intent intent = new Intent();
                //執行動作
                intent.setAction(Intent.ACTION_VIEW);
                //執行的資料型別
                intent.setDataAndType(Uri.fromFile(result), "application/vnd.android.package-archive");
                activity.startActivity(intent);
                System.out.println("xcq  onPostExecute");

            }
        }
        stopDownLoad();
    }

    private void myDialogDismiss(AlertDialog dialog) {
        if (dialog != null) {
            dialog.dismiss();
            dialog = null;
            System.out.println("xcq   dialog置null");
        }
    }

    private void myTaskCancel() {
        if (mCurrentTaks != null) {
            mCurrentTaks.cancel(true);
            mCurrentTaks = null;
        }
    }
    private void stopDownLoad() {
        myTaskCancel();
        myDialogDismiss(mydialog);
        isStopDownload = false;
    }
}


//FirstActivity.java

public class FirstActivity extends Activity {
    private UpdateProcessDialog mydialog;
    private String serverVersion;
    private UpdateTask task;
    private String result = "自行新增下載地址";
    private String updatestorepath = Environment.getExternalStorageDirectory()+"/MyWeiXinUpDate/update.apk";
    private Handler threadhandler= null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);
        if(threadhandler == null){
            threadhandler = new Handler();
        }

    }



    public void update(View view) {
        if (!UpdateTask.isStopDownload) {
            isVersionsUpdate("1.1");
        } else {
            Toast.makeText(FirstActivity.this, "小點即可,切勿狂按", Toast.LENGTH_SHORT).show();
        }
    }


    private void isVersionsUpdate(final String versions) {

        try {
            final JSONObject json = new JSONObject(result);
            JSONObject requstResult = json.getJSONObject("RequstResult");
            serverVersion = json.get("Versions").toString();
            final String apkurl = json.getString("Download");
            //這個state是從維信理財直接抄過來的
            int state = requstResult.getInt("State");
            if (state == 0) {
                if (versions.compareTo(json.get("Versions").toString()) >= 0) {
                    Toast.makeText(FirstActivity.this, "已是最新版本", Toast.LENGTH_SHORT).show();
                } else {
                    AlertDialog.Builder builder = new AlertDialog.Builder(FirstActivity.this);
                    builder.setTitle("有新版本" + json.get("Versions")
                            + ",是否現在更新?");
                    builder.setPositiveButton("真的確定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //開啟asynctask下載
                            getApkFromServer(apkurl);
                            //download(apkurl);
                        }
                    });
                    builder.setNegativeButton("取消", null);
                    builder.create().show();
                }
            }
        } catch (Exception e) {
            System.out.println("xcq  異常11" + e.toString());
            e.printStackTrace();
        }
    }


    //   方法一
    public void getApkFromServer(String url) {
        mydialog = UpdateProcessDialog.showDeleteDialog(FirstActivity.this, 100, new UpdateProcessDialog.OnDeleteCancelListener() {

            @Override
            public void onCancel() {
                UpdateTask.isStopDownload = true;
            }
        }, false);
        //dialog一建立就show(為了初始化),所以這裡就關閉顯示
        mydialog.dismiss();
        task = new UpdateTask(FirstActivity.this,url, mydialog,threadhandler);
        task.setUpdateComponents(task,serverVersion,updatestorepath);
        task.execute(url);

    }


2.HandlerThread

Thread(普通) run方法執行了一個耗時任務

HandlerThread內部建立了訊息佇列,外部需要通過Hanlder類的訊息方式來通知他執行一個具體任務,由於HandlerThreadrun方法是一個無限迴圈,因此當明確不需要再使用HandlerThread時,quit或者quitSafely方法來終止執行緒的執行。

3.IntentService

 1.在內部建立了一個執行緒執行耗時操,會在onHandlerIntent的回撥方法中執行

 2.執行完了會自動結束(onDestory),不需要手動操作

 3. 開啟多個會建立過個工作執行緒,但是每次只會執行一個工作執行緒,執行完第一個再執行第二個,然後才會結束(onDestory)

 例子:

//MyIntentService.java

public class MyIntentService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    //注意下這裡!!!
    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    public void onCreate() {
        System.out.println("xcqw onCreate");
        super.onCreate();
    }

    @Override
    public void setIntentRedelivery(boolean enabled) {
        System.out.println("xcqw setIntentRedelivery");
        super.setIntentRedelivery(enabled);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        System.out.println("xcqw onStart");
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("xcqw onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        System.out.println("xcqw onHandleIntent");
        //Intent是從Activity發過來的,攜帶識別引數,根據引數不同執行不同的任務
        String action = intent.getExtras().getString("param");
        if (action.equals("oper1")) {
            System.out.println("Operation1");
        }else if (action.equals("oper2")) {
            System.out.println("Operation2");
        }

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("xcqw onHandleIntent 結束了");
    }

    @Override
    public void onDestroy() {
        System.out.println("xcqw onDestroy");
        super.onDestroy();
    }
}

//MainActivity.java(開啟兩個不同的Service)
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tvNotice = (TextView) findViewById(R.id.tv_notice);
        //可以啟動多次,每啟動一次,就會新建一個work thread,但IntentService的例項始終只有一個
        //Operation 1
        Intent startServiceIntent = new Intent(this, MyIntentService.class);
        Bundle bundle = new Bundle();
        bundle.putString("param", "oper1");
        startServiceIntent.putExtras(bundle);
        startService(startServiceIntent);

        //Operation 2
        Intent startServiceIntent2 = new Intent(this, MyIntentService.class);
        Bundle bundle2 = new Bundle();
        bundle2.putString("param", "oper2");
        startServiceIntent2.putExtras(bundle2);
        startService(startServiceIntent2);
    }

}

//MainActivity.java(同一個Service開啟兩次)

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tvNotice = (TextView) findViewById(R.id.tv_notice);
        //可以啟動多次,每啟動一次,就會新建一個work thread,但IntentService的例項始終只有一個
        //Operation 1
        Intent startServiceIntent = new Intent(this, MyIntentService.class);
        Bundle bundle = new Bundle();
        bundle.putString("param", "oper1");
        startServiceIntent.putExtras(bundle);
        System.out.println("xcqw 開啟service");
        startService(startServiceIntent);
        System.out.println("xcqw 又開啟了同一個service");
        startService(startServiceIntent);
//        //Operation 2
//        Intent startServiceIntent2 = new Intent(this, MyIntentService.class);
//        Bundle bundle2 = new Bundle();
//        bundle2.putString("param", "oper2");
//        startServiceIntent2.putExtras(bundle2);
//        startService(startServiceIntent2);
    }


}


結果

//開啟不同的兩個

xcqw 開啟service one
xcqw 開啟service two
xcqw onCreate
xcqw onStartCommand
xcqw onStart
xcqw onStartCommand
xcqw onStart
xcqw onHandleIntent
xcqw Operation1
xcqw onHandleIntent 結束了
xcqw onHandleIntent
xcqw Operation2
xcqw onHandleIntent 結束了
xcqw onDestroy

//開啟同一個兩次

xcqw 開啟service
xcqw 又開啟了同一個service
xcqw onCreate
xcqw onStartCommand
xcqw onStart
xcqw onHandleIntent
xcqw onStartCommand
xcqw onStart
xcqw Operation1
xcqw onHandleIntent 結束了
xcqw onHandleIntent
xcqw Operation1
xcqw onHandleIntent 結束了
xcqw onDestroy

三: Android執行緒池

執行緒池的優點

(1)重用執行緒池的執行緒,避免執行緒的建立和銷燬所帶來的效能開銷

(2)可以控制執行緒池的最大併發數,避免大量的執行緒之間相互搶佔系統資源而導致阻塞

(3)能夠對執行緒進行簡單的管理,並提供定時執行以及指定間隔迴圈執行

Android執行緒池的概念源於Java中的ExecutorExecutor是一個介面,Android中真正的執行緒池的實現為ThreadPoolExecutor,Android中的執行緒池都是直接或間接通過配置ThreadPoolExecutor來實現的.

1.5.1ThreadPoolExecutor

   public ThreadPoolExecutor(int corePoolSize,

                              int maximumPoolSize,

                              long keepAliveTime,

                              TimeUnit unit,

                              BlockingQueue<Runnable> workQueue,

                              ThreadFactory threadFactory) {

        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,

             threadFactory, defaultHandler);

    }

corePoolSize :核心執行緒數,預設情況下,即使他們處於閒置狀態(IDLE),核心執行緒也會線上程池中一直存活.

除非將ThreadPoolExecutorallowCoreThreadTimeOut屬性設定為True,那麼閒置的核心執行緒在等待新任務到來時會有超時策略,這個時間間隔由KeepAliveTime所指定,超過這個時間核心執行緒就會被停止

maximumPoolSize:執行緒池所能容納的最大執行緒數,當活動執行緒數達到這個數值後,後續的新任務將會被阻塞。

keepAliveTime

非核心執行緒:超過這個時長,非核心執行緒就會被回收

核心執行緒(ThreadPoolExecutorallowThreadTimeOut屬性設定為True才會受影響):影響上面有說

Unit:用於指定KeepAliveTime引數的時間單位

workQueue

執行緒池中的任務佇列,通過執行緒池的execute方法提交的Runnable物件會儲存在這個引數

threadFactory

執行緒工廠,為執行緒池提供建立新執行緒的功能。ThreadFactory是一個介面,他只有一個方法:Thread newThread(Runnable r)

defaultHandler

當執行緒池無法執行新任務時,這可能是由於任務佇列已滿或者無法成功執行任務,有幾個策略,預設是abortPolicy,用了這個就會直接拋異常

1.5.2ThreadpoolExecutor執行任務時大致遵循如下規則:

(1).如果執行緒池的執行緒數量未達到核心執行緒的數量,那麼會直接啟動一個核心執行緒來執行任務

(2)如果執行緒池中的執行緒數量已經達到或者超過核心執行緒的數量,那麼任務會被插入任務佇列中等待執行

(3)如果步驟2插入失敗,這往往是任務佇列滿了,這個時候如果執行緒數量位達到執行緒池規定的最大值,那麼會立即啟動一個非核心執行緒來執行任務

(4)如果步驟3的執行緒數量已經達到執行緒池規定的最大值,那麼拒絕執行此任務,就會呼叫defaultHandler

哪些引數的配置在AsyncTask中可以清晰的看出來


private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);


AsynTaskTHREAD_POOL_EXECUTOR這個執行緒池進行了配置,配置後的執行緒池規格如下:

核心執行緒數等於:CPU核心數+1

執行緒池的最大執行緒數為CPU核心數的2+1

核心執行緒無超時機制,非核心執行緒在閒置時的超時時間為1

任務佇列的容量為128

執行緒池的分類

這裡指介紹Android中最常見的四類具有不同功能特性的執行緒池,它們都直接或間接地通過配置ThreadPoolExecutor來實現自己的功能特性,這四類執行緒池分別是FixedThreadPoolCachedThreadPool,ScheduledThreadPool以及SingleThreadExecutor.

1.FixedThreadPool

通過ExecutorsnewFixedThreadPool方法來建立,它是一種執行緒數量固定的執行緒池,

當執行緒處於空閒狀態時,它們並不會被回收,除非執行緒池被關閉了。

當所有的執行緒都處於活動狀態時,新任務都會處於等待狀態,直到有執行緒空閒出來。

由於FixedThreadPool只有核心執行緒並且這些核心執行緒不會被回收,這就意味著它能夠更加快速地相應外界的請求。

newFixedThreadPool方法的實現如下,可以發現FixedThreadPool中只有核心執行緒並且這些核心執行緒沒有超時機制,另外任務佇列沒有大小限制

核心執行緒超時機制:不會被回收

非核心執行緒在閒置時的超時時間:只有核心執行緒

任務佇列的容量:無限制


public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads,nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<Runnable>());
}

2.CachedThreadPool

通過Executornew CachedThreadPool方法來建立

它是一種執行緒數量不定的執行緒池,只有非核心執行緒,並且其最大執行緒數為Interger.MAX_VALUE.由於Integer.MAX_VALUE是一個很大的數,實際上就相當於最大執行緒數可以任意大。

當執行緒池中的執行緒都處於活動狀態時,執行緒池會建立新的執行緒來處理新任務,否則就會利用空閒的執行緒來處理新任務。

執行緒中的空閒執行緒都有超時機制,這個超時時長為60秒,超過60秒閒置執行緒就會被回收

FixedThreadPool不同的是CachedThreadPool的任務佇列其實相當於一個空集合,這就導致任何任務都會立即被執行。

因為在這種場景下SynchronousQueue是無法插入任務的,SynchronousQueue是一個非常特殊的佇列,在很多情況下可以把它簡單理解為一個無法儲存元素的佇列,由於它在實際中較少使用,這裡就不深入探討它了。

CachedThreadPool的特性來看,這類執行緒池比較適合執行大量的耗時較少的任務,當整個執行緒池都處於閒置狀態時,執行緒池中的執行緒都會超時而被停止,這個時候CacheThreadPool之中實際上是沒有任何執行緒,它幾乎是不佔用任何系統資源的。

核心執行緒超時機制:只有非核心執行緒

執行緒池的最大執行緒數:Integer.MaxValue(也就相當於任意大)

非核心執行緒在閒置時的超時時間:60秒

任務佇列的容量:無限制


public static ExecutorService newCachedThreadPool(int nThreads){
    return new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}

3.ScheduledThreadPool

通過Executors newScheduledThreadPool方法來建立,它的核心執行緒數量是固定的,而非核心執行緒數是沒有限制的,並且當非核心執行緒閒置時會被立即回收。ScheduledThreadPool這類執行緒池主要用於執行定時任務和具有固定週期的重複任務newScheduledThreadPool方法的實現如下所示。

核心執行緒數:一個固定的值

執行緒池的最大執行緒數:Integer.MaxValue(也就相當於任意大)

非核心執行緒在閒置時的超時時間:立即回收

public static ExecutorService ScheduledThreadPool(int nThreads){
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
    super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());
}

4.singleThreadExecutor

通過ExecutorsnewSingleThreadExecutor方法來建立。這類執行緒池內部只有一個核心執行緒,它確保所有任務都在同一個執行緒中按順序執行。SingleThreadExecutor的意義在於統一所有的外界任務到一個執行緒中,這使得在這些任務之間不需要處理執行緒同步的問題。實現方法如下

核心執行緒數:執行緒池中只有一個核心執行緒


public static ExecutorService newSingleThreadExecutor(int nThreads){
    return new FinalizableDeleGatedExecutorService(new ThreadPoolExecutor(1,1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<Runnable>()));
}