四.スレッド管理のAndroidでのマルチスレッド

21085 ワード

初心を忘れずに頑張って行こう、Tomorrow Is Another Day!
関連記事
  • 1.スレッド管理のThreadベース
  • 2.スレッド管理スレッドプール
  • 三.スレッド管理のThreadLocal
  • 4.スレッド管理Androidのマルチスレッド
  • 概要:
  • AsyncTask
  • HandlerThreadとIntentService
  • 本文を読む前に、Handlerメッセージメカニズムを深く理解する必要があります.Handlerの理解がまだ十分でない場合は、まずこの文章-AlndroidメッセージメカニズムHandlerを読むことができます.
    一.AsyncTask
    Androidはhandlerとスレッドプールをカプセル化し、UIの更新を簡素化する非同期クラスを提供している.
    1.1基本使用
    AsyncTaskは抽象的な汎用クラスであり、3つの汎用パラメータはそれぞれParams、Progress、Resultである.
  • Params:入力パラメータタイプ
  • Progress:タスクの実行の進捗
  • Result:結果を返すタイプ
  • public abstract class AsyncTask 
    

    4つのコアメソッドが用意されています.
  • onPreExecute:メインスレッドで実行、タスク開始前.
  • Result doInBackground(Params...params):サブスレッド実行、タスク実行時.
  • publishProgress(Progress...values)を呼び出してタスクの進捗状況を更新すると、onProgressUpdateメソッドがコールバックされます.
  • 計算結果ResultはonPostExecuteメソッドに返す.

  • onProgressUpdate(Progress...values):メインスレッドで実行、タスクの実行進捗更新時.
  • onPostExecute(Result result):メインスレッドで実行する、タスクの実行が完了した場合.

  • 使用例
    public class AsyncTaskActivity extends AppCompatActivity implements View.OnClickListener {
        //...      
    
        class MyAsyncTask extends AsyncTask {
    
    
            @Override
            protected void onPreExecute() {
                mProgressDialog = new ProgressDialog(AsyncTaskActivity.this);
                mProgressDialog.setMessage("    ");
                mProgressDialog.setMax(10);
                mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                mProgressDialog.show();
            }
    
            @Override
            protected Robot doInBackground(String... strings) {
    
                Robot robot = null;
                if (strings != null && strings.length > 0) {
                    for (int i = 0; i < 11; i++) {
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        publishProgress(i);
                        if (isCancelled()) {
                            break;
                        } else if (i == 10) {{
                            robot = new Robot("i", strings[0]);
                        }
                    }
                } else {
                    throw new IllegalArgumentException("please set the params");
                }
    
                return robot;
            }
    
            @Override
            protected void onProgressUpdate(Integer... values) {
                if (values != null && values.length > 0) {
                    mProgressDialog.setProgress(values[0]);
                }
            }
    
            @Override
            protected void onPostExecute(Robot robot) {
                mProgressDialog.dismiss();
                tvName.setText(robot == null ? "    " : robot.getName());
            }
    
    
            @Override
            protected void onCancelled() {
                tvName.setText("     ");
            }
        }
    
    }
    
    

    まずonPreExecuteでProgressDialogコントロールを初期化し、次にdoInBackgroundシミュレーションにより時間のかかるロボット構造フローを実行する.構築プロセスでpublishProgressを呼び出して実行タスクの進捗状況を更新する.最後に、タスクの実行が完了すると、onPostExecuteでマシン名が更新され、タスクのキャンセルをクリックすると、onCancelledが呼び出される.
    AsyncTaskの使用については比較的簡単でここまでである.具体的にはその実現原理を見てみましょう.
    1.2基本原理
    ソースバージョンはAndroid 8.0に基づく
    1.私たちはまずAsyncTaskの構造から始めて、具体的にどのような操作をしたかを見ます.
    対応ソース
    public AsyncTask() {
            this((Looper) null);
    }
    
    public AsyncTask(@Nullable Looper callbackLooper) {
            //1.   Handler
            mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
    
            //2.   WorkerRunnable,       Callable    .
            mWorker = new WorkerRunnable() {
                public Result call() throws Exception {
                    //...        ,     .
                    return result;
                }
            };
    
            //3.   FutureTask   WorkerRunnable.
            // run      mWorker Call  .
            mFuture = new FutureTask(mWorker) {
                @Override
                protected void done() {
                    //...        ,     .
                }
            };
        }
    

    ここでは主に3つの初期化作業を行った.
  • Handlerを初期化し、メインスレッドのLooperをバインドする.
  • はWorkerRunnableを初期化し、実際にはCallableインタフェースを実現するクラスである.
  • (Callableにまだ慣れていない場合は、前の記事を見て紹介してください.)

  • 初期化FutureTaskはWorkerRunnableをカプセル化した.
  • ここではFutureTaskを簡単に復習し、RunnableインタフェースとFutureTaskインタフェースを実現した.したがって、実行スレッドを実現するとともに、スレッドの実行後の戻り値を取得することができる.(FutureTaskにまだ慣れていない場合は、前の記事を見て紹介してください.)


  • 2.AsyncTaskをインスタンス化すると、executeメソッドを呼び出してタスクを実行します.ソースコードを見てください.
    対応ソース
    public final AsyncTask execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
    }
    
    public final AsyncTask 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();//  onPreExecute
    
            mWorker.mParams = params;//        WorkRunnable
            exec.execute(mFuture);//    .
            
            return this;
    }
    

    excuteメソッドでexecuteOnExecutorメソッドを呼び出し、このプロセスをまとめます.
  • 状態を検査する.
  • FutureTaskはWorkRunnableを持ち、WorkRunnableは入力パラメータを持つ.
  • 最後にスレッドプールを利用してfutureTaskタスクを実行するが、ここではFutureTaskをRunnableに相当する役割と見なすことができる.

  • 3.スレッドプールの処理フロー
    executeOnExecutor(sDefaultExecutor,params)メソッドでは、sDefaultExecutorスレッドプールが使用する.スレッドプールの実装を見てみましょう.
    対応ソース
       /**
         *            
         */
        private static class SerialExecutor implements Executor {
            final ArrayDeque mTasks = new ArrayDeque();
            Runnable mActive;
    
            public synchronized void execute(final Runnable r) {
                //1.        
                mTasks.offer(new Runnable() {
                    public void run() {
                        try {
                            r.run();//  futureTask run  
                        } finally {//      ,         .
                            scheduleNext();
                        }
                    }
                });
                if (mActive == null) {//   ,     .
                    scheduleNext();
                }
            }
    
            protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {//         
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            }
        }
        
         /**
         *         THREAD_POOL_EXECUTOR
         */
        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        private static final int KEEP_ALIVE_SECONDS = 30;
         private static final BlockingQueue sPoolWorkQueue =
                new LinkedBlockingQueue(128);
        static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory);
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
        }
        
    

    ここでは2つのスレッドプールに関し、AsyncTaskの核心でもある.
  • SerialExecutor:タスクのキューに使用します.
  • がタスクキューに挿入する.
  • タスクがないか、またはタスクの実行が完了すると、キューから新しいタスクを別のスレッドプールに繰り返し実行する.

  • threadPoolExecutor:実際にタスクを実行するために使用されます.
  • CORE_POOL_SIZE:コアスレッド数とCPUコアに関する
  • MAXIMUM_POOL_SIZE:最大スレッド数はCPUコアに関する
  • KEEP_ALIVE_SECONDS, TimeUnit.SECONDS:タイムアウトメカニズムは30秒
  • のタイムアウト機構もコアスレッドに作用する.

  • sPoolWorkQueue:ブロックキュー

  • SerialExecutorの存在だけに、3.0以降はシリアル実行であることがわかるので、同時問題(飽和ポリシーの実行)はない.
    4.Callメソッドによって最後に、タスクを実行した後、主スレッドにどのようにコールバックするかを見てみましょう.
    futureTaskのrunメソッドではWorkerRunnableのcallメソッドがコールバックされます.ここでは、前のFutureTaskのrunメソッドのソースコードを振り返ってみましょう.
    対応ソース
    public void run() {
            if (state != NEW ||
                !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
                return;
            try {
                Callable c = callable;
                if (c != null && state == NEW) {
                    V result;
                    boolean ran;
                    try {
                        //  callable Call  ,         .
                        result = c.call();
                        ran = true;
                    } catch (Throwable ex) {
                        result = null;
                        ran = false;
                        setException(ex);
                    }
                    if (ran)
                        set(result);
                }
            } finally {
                //...      
            }
        }
    

    ここでは、WorkerRunnableの初期化時のコードに戻る.
        
            mWorker = new WorkerRunnable() {
                public Result call() throws Exception {
        
                    mTaskInvoked.set(true);
                    Result result = null;
                    try {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        /**
                          * 1.  doInBackground   
                          */
                        result = doInBackground(mParams);
                        Binder.flushPendingCommands();
                    } catch (Throwable tr) {
                        mCancelled.set(true);
                        throw tr;
                    } finally {
                        
                        postResult(result);
                    }
                    return result;
                }
            };
    
          
         /**
          * 2.  handler result     
          */
         private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult(this, result));
            message.sendToTarget();
            return result;
        }
        
       
        /**
          * 3.     
          */
        private static class InternalHandler extends Handler {
            public InternalHandler(Looper looper) {
                super(looper);
            }
    
            @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
                        //  finish
                        result.mTask.finish(result.mData[0]);
                        break;
                    //...      
                }
            }
        }
        
        
        private void finish(Result result) {
            if (isCancelled()) {
                //     
                onCancelled(result);
            } else {
                //  onPostExecute
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
        
    

    Callメソッドフローのまとめ
  • 回調doInBackground方法.
  • は、handlerを介してresultを伝達するメッセージを送信する.
  • メッセージ、コールバックonCancelledまたはonPostExecute.

  • 最後に簡略版のAsyncTaskのワークフローチャートを示します
    asyntaskワークフロー簡略図
    ここまでAsyncTaskの実現原理の基本解析が完了するが、先に述べた3.0までは並列であり、3.0以降はシリアルであり、並列を実現するには以下のような方法を採用することができる.
    //   
    executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params);
    
    //   
    executeOnExecutor(       ,params);
    

    3.0以下が並列の効果であることを理解すればよい.ここではプレゼンテーションをしません.今のアプリは基本的に5.0以下が合わないので、カマキリを車に乗せて、貴重な時間を節約して未来のトレンドの知識を見る必要はありません.
    二.HandlerThreadとIntentService
    2.1 HandlerThread
    メッセージループのスレッドである、このスレッドでHandlerを使用することができる.
    対応ソース
    @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();//      
            synchronized (this) {
                mLooper = Looper.myLooper();
                //Looper     ,           
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();//      
            mTid = -1;
        }
        
        
    public Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            
            //      ,  looper     
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
            return mLooper;
        }
        
    

    通常のスレッドに比べて、その特徴は.
  • 通常スレッドはrunメソッドで実行する時間のかかるタスク
  • である.
  • このスレッドでhandlerを使用して時間のかかるタスクを実行することができ、無限ループであるため、使用しなくなった場合、quitまたはquitSafelyを呼び出してスレッドの実行を終了する必要がある.
  • 具体的な使用シーンはInentServiceを参照してください.

  • 対応ソース
    2.2 IntentService
    サービスです.handlerThreadとHandlerがパッケージされています.
    サービスだからこそ、システムに殺されにくい.以下の特徴を有する.
    特徴:
  • は、サービスに対して、時間のかかるタスクをサービスで実行することができる.
  • は、スレッドに対して単純なスレッドよりも優先度が高い.
  • の実行が完了すると自動的に停止する.

  • きほんしよう
    public class MyIntentService extends IntentService {
        private static final String TAG = "MyIntentService";
    
        public MyIntentService() {
            super(TAG);
        }
    
        @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            String taskName = intent.getStringExtra("taskName");
            Log.d(TAG, "taskName: " + taskName);
            SystemClock.sleep(2500);
            if ("org.jason.taskOne".equals(taskName)){
                Log.d(TAG, "do task: "+taskName);
            }
    
        }
    
        @Override
        public void onDestroy() {
            Log.d(TAG, "onDestroy: ");
            super.onDestroy();
        }
    }
    
    private void doIntentService() {
            //         
            Intent intent = new Intent(this, MyIntentService.class);
            intent.putExtra("taskName", "org.jason.taskOne");
            startService(intent);
            intent.putExtra("taskName", "org.jason.taskTw0");
            startService(intent);
            intent.putExtra("taskName", "org.jason.taskThree");
            startService(intent);
    }
    
    //    
    12-27 14:34:01.338 D/MyIntentService: taskName: org.jason.taskOne
    12-27 14:34:03.839 D/MyIntentService: do task: org.jason.taskOne
    12-27 14:34:03.840 D/MyIntentService: taskName: org.jason.taskTw0
    12-27 14:34:06.341 D/MyIntentService: taskName: org.jason.taskThree
    12-27 14:34:08.841 D/MyIntentService: onDestroy: 
    
    

    上のログから特徴がわかるほか、タスクが順番に実行されていることもわかります.これは、handlerのlooperがメッセージを順番に処理しているため、内部のhanlderがメッセージを処理することと関係があります.次に、どのように実現されているかを見てみましょう.
    きほんげんり
    InentServiceはサービスを継承する抽象クラスである.サービスである以上、サービスのライフサイクルに基づいて分析します.
    1.まずOnCreateメソッドの流れを見る
    対応ソース
    public abstract class IntentService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
            //1.     HandlerThread
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
            //2.     Handler,  HandlerThread Looper.      handler, HandlerThread      .
            //(             looper,               )
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
    }    
    

    2.OnStartメソッドフローに続く
    onStartCommandではonStartメソッドが呼び出されていますが、ここではこのメソッドを直接見ます.
    対応ソース
     @Override
        public void onStart(@Nullable Intent intent, int startId) {
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent;
            //1.  handler    ,           intent    id  .
            mServiceHandler.sendMessage(msg);
        }
        
            
     private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
            
            //2.    ,
            //  : handler   HandlerThread looper,           .
            @Override
            public void handleMessage(Message msg) {
                //a.  onHandleIntent
                onHandleIntent((Intent)msg.obj);
                //b.    
                stopSelf(msg.arg1);
            }
    }    
    

    3.最後にonDestroyの方法を見る
    @Override
        public void onDestroy() {
            //    looper,          ,        .            .
            mServiceLooper.quit();
        }
    

    前述HandlerThreadについても述べるが、使用しない場合はquitまたはquitSafelyを呼び出してスレッドの実行を終了する必要がある.システムのソースコードにもこのステップがあることがわかるので、メッセージループを持つスレッドをカスタマイズすると、必ず終了することを覚えています.これは良いプログラミング習慣です.
    HandlerThreadとその応用InentServiceについてここまで紹介した.
    私は技术が限られているので、间违ったところがあれば、皆さんに提出してください.私は感谢に堪えません.みんなで勉强して进歩します.
    参照リンク:
  • www.cnblogs.com/whoislcj/p/…
  • blog.csdn.net/singwhatiwa…
  • item.jd.com/12125491.ht…