Android Service-サブスレッドでUIを更新


AndroidのUIはスレッドが安全ではありません.つまり、アプリケーションのUI要素を更新するには、メインスレッドで行わなければなりません.そうしないと、異常が発生します.ここではこの問題を解決するための2つの方法を紹介する.
解析非同期処理メカニズム
Androidにおける非同期メッセージ処理は主に4つの部分に分けられ,Message,Handler,MessageQueue,Looperである.1.Messageはスレッド間で伝達されるメッセージであり、異なるスレッド間でデータを交換するために内部で少量のメッセージを携帯することができる.2.Handlerは、その名の通り、主にメッセージを送信および処理するために使用される処理者の意味である.3.MessageQueueはメッセージキューの意味で、主にHandlerを通じて送信されたすべてのメッセージを格納するために使用されます.このメッセージはメッセージキューに常に存在し、処理を待つ.各スレッドにはメッセージキューオブジェクトが1つしかありません.4.Looperは各スレッドのメッセージキューの執事であり、そのloop()メソッドを呼び出すと無限ループに入り、メッセージキューにメッセージが1つ発見されるたびに取り出し、HandlerのhandleMessage()メソッドに渡され、各スレッドにもLooperオブジェクトが1つしかありません.クリックイベントを定義し、クリック時にUIの内容を変更できるようにします.
package com.example.administrator.myhandlerapplication;

import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    public static final int UPDATE_TIME=1;
    private int count=60;
    private Button mButton;
    private Button mButtonMainsend;
    private TextView mTextView;
  //       Handle
    private Handler handler=new Handler(){
        public void handleMessage(Message msg){
            switch (msg.what){
                case UPDATE_TIME                   

                    //     UI  
                   String time= (String) msg.obj;
                    mButton.setText(time);
                    if (count>0){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //          , UI       ,              ,                ,      UI        。  count--  handler 。
                        //handler.sendEmptyMessage(UPDATE_TIME);
                    }
                    break;
                default:
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton= (Button) findViewById(R.id.button_time);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new MyThread().start();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        while (count>0){
                            count--;
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            Message message=handler.obtainMessage();   //new Message();
                            message.obj=count+" ";
                            message.what=UPDATE_TIME;
            //  Message  
                            handler.sendMessage(message);
                        }
                    }
                }).start();
            }
        });
    }
}

UIの内容を更新する操作を表す整数定数を定義してからHandlerオブジェクトを新規作成し、親のhandleMessageメソッドを書き換え、Messageを処理します.ここではサブスレッドからメッセージを送信し、メインスレッドからメッセージを送信し、サブスレッドから対応する処理を行うこともできますが、一般的ではありません.ここでは例を挙げて説明するだけです.
package com.example.administrator.myhandlerapplication;

import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    public static final int UPDATE_TIME=1;
    private int count=60;
    private Button mButton;
    private Button mButtonMainsend;
    private TextView mTextView;
    //private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton= (Button) findViewById(R.id.button_time);
        mButtonMainsend= (Button) findViewById(R.id.button_mainsend);
        mButtonMainsend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            //                 ,         ,         
                handler.sendEmptyMessage(UPDATE_TIME);
            }
        });
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//               。
                new MyThread().start();

    }
    //     ,            ,  log     。
    class MyThread extends Thread{
        @Override
        public void run() {
            //super.run();
            Looper.prepare();
           handler=new Handler(){
               @Override
               public void handleMessage(Message msg) {
                   Log.d("11111111111111", "           ");
               }
           };
            Looper.loop();
        }
    }

}

メインスレッドでLooperはすべてカプセル化されているので定義する必要はありません.一方,サブスレッドではLooperのprepare()メソッドとloop()の間に受信メッセージを書く必要がある.
AsyncTaskの使用
Androidはまた、AsyncTask抽象クラスを提供し、サブクラスがUIを操作することを実現する.実はこのクラスの実現原理もメッセージ処理メカニズムに基づいており,パッケージ化されただけで,いくつかの方法で実現されている.AsyncTaskは抽象クラスなので、継承するためにサブクラスを作成し、継承するには3つの汎用値パラメータを作成する必要があります.1番目のパラメータParamsは、AsyncTaskを実行する際に入力する必要があるパラメータで、バックグラウンドタスクで2番目のパラメータProgressを使用し、バックグラウンドでタスクを実行する際に、インタフェースに現在の進捗を表示する必要がある場合は、ここで作成した汎用型を進捗単位として3番目のパラメータResultとして使用し、タスクの実行が完了した後に結果を返す必要がある場合は、ここで指定した汎用型を戻り値タイプとして使用します.
AsyncTaskから継承するクラスはまたその方法を書き換える必要があり、その中でよく書き換える必要があるのは4つの方法1である.onPreExecute()は、バックグラウンドタスクの実行を開始する前に呼び出され、いくつかのインタフェース上の初期化操作を行うために使用されます.2.doInBackgroundこのメソッドのコードはサブスレッドで実行され、ここですべての時間のかかる操作タスクを処理する必要があります.タスクが完了するとreturn文でタスクの実行結果が返され、AsyncTaskの3番目の汎用型がVoidである場合、タスクの実行結果が返されなくてもよい.なお、この方法ではUI操作を行うことができず、UI要素を更新する必要がある場合はpublishProgress()メソッドを呼び出して3を完了することができる.onProgressUpdate(Integer...values)バックグラウンドタスクがpublishProgress()メソッドを呼び出すと、このメソッドはすぐに呼び出され、メソッドに含まれるパラメータはバックグラウンドタスクで渡されます.この方法ではUIを操作することができ,パラメータの数値を用いてインタフェース要素を相応に更新することができる.4.onPostExecute(String s)バックグラウンドタスクが実行され、return文によって返されるとこのメソッドが呼び出され、返されたデータがパラメータとしてこのメソッドに渡され、返されたデータを用いてUI操作が可能になる.
まず、ボタンと進捗バーを定義し、ボタンのクリックイベントでバックグラウンドサービスを起動し、バックグラウンドサービスは進捗バーに情報を送信して表示します.
 "@+id/progressBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
    

MainActivityでクラスがAsyncTaskから継承され、メソッドが書き換えられることを宣言し、ボタンのクリックイベントでクラスのオブジェクトを取得し、バックグラウンドサービスにパラメータを送信します.
package com.example.administrator.masynctask;

import android.app.Activity;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends Activity {
    private int count=0;
    private Button mButtonStart;
    private ProgressBar mProgressBar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButtonStart= (Button) findViewById(R.id.button_sync);
        mProgressBar= (ProgressBar) findViewById(R.id.progressBar);
        mButtonStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            //    AsyncTask   ,       ,    
                MyAsyncTask myAsyncTask=new MyAsyncTask();
                myAsyncTask.execute();
            }
        });
    }
    class MyAsyncTask extends AsyncTask{

        @Override
        protected String doInBackground(Void... params) {
        //      
            while (count<101){
                count++;
                publishProgress(count);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "    ";
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            //  doInBackground(Void... params)         UI     
            mButtonStart.setText(s);
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            //  doInBackground(Void... params) publishProgress(count)      UI         
            mProgressBar.setProgress(values[0]);
        }
    }
}