Android 線程互動

來源:互聯網
上載者:User

標籤:android開發   兩種   except   進度   com   i++   回收   內建   .exe   

 在Android開發過程中,耗時操作是不允許寫在主線程(UI線程)中的,以免由於等待時間過長而發生ANR。所以耗時操作需要建立子線程來完成,然而往往這些操作都需要與主線程進行通訊互動(例如更新主線程的UI),但android規定除了UI線程外,其他線程都不可以對UI控制項進行訪問或操控,所以我們需要通過一些方法來實現這些功能。

 

1. Handler:

 

handler是android中專門用來線上程之間傳遞資訊類的工具。API參考:https://developer.android.google.cn/reference/android/os/Handler

1、在B線程中調用Looper.prepare和Looper.loop。(主線程不需要)
2、編寫Handler類,重寫其中的handleMessage方法。
3、建立Handler類的執行個體,並綁定looper
4、調用handler的sentMessage方法發送訊息。

 

  • 子線程更新主線程(UI)

因為主線程內建Looper機制,所有我們不用建立Looper:

Handler mHandler = new Handler(){   @Override  public void handleMessage(Message msg) {      super.handleMessage(msg);      switch (msg.what) {          case 1:              //do something,refresh UI;              break;          default:              break;          }      }       };  

 

然後開啟一個子線程,在子線程裡直接使用Handler發送訊息:

new Thread() {    public void run() {    Message message = new Message();
    message.what = 1;    message.obj = "子線程發送的訊息Hi~Hi";    mHandler .sendMessage(message);
  };}.start();

 

  • 子線程之間互動

public class ThreadActivity extends AppCompatActivity {

  private final String TAG = "ThreadActivity";

  // 子線程Handler
  private Handler mSubHandler = null;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_thread1);
    new MyThread().start();
    createThread();
  }


  /**
   * 建立子線程,用於發送訊息
   */
  private void createThread() {
    new Thread() {
      @Override
      public void run() {
        int count = 0;
        while (count < 10) {
          Message msg = new Message();
          msg.obj = "子線程計時器:" + count;
          msg.what = 1;
          // 使用子線程Handler發送訊息
          mSubHandler.sendMessage(msg);
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          count++;
        }
      }
    }.start();
  }

  /**
   * 用於接收子線程發送過來的訊息
   */
  class MyThread extends Thread {

    @Override
    public void run() {
      Looper.prepare();
      mSubHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          switch (msg.what) {
            case 1:
            Log.i(TAG, (String) msg.obj);
            break;
          }
        }
      };
      Looper.loop();
    }
  }
}

 

2. HandlerThread:

HandlerThread是一個包含Looper的Thread,我們可以直接使用這個 Looper 建立 Handler。API參考:https://developer.android.google.cn/reference/android/os/HandlerThread

 

HandlerThread適用於單線程+非同步隊列模型情境,相對Handler + Thread簡潔。

// 也可實現run方法
HandlerThread mHandlerThread = new HandlerThread("HandlerThread_Test");mHandlerThread.start();Handler mThreadHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.i(TAG, "threadName--" + Thread.currentThread().getName() + (String) msg.obj); break; } }}; // 發送訊息至HanderThreadmThreadHandler.sendMessage(msg);

 

3. runOnUiThread

Activity 裡面的runOnUiThread( Runnable )方法便子線程更新UI更為簡潔。另外還有View.Post(Runnable)和View.PostDelayed(Runnabe,long)方法,用法與runOnUiThread基本相同。

new Thread(new Runnable() {    @Override    public void run() {        try {            Thread.sleep( 1000 );        } catch (InterruptedException e) {            e.printStackTrace();        }         runOnUiThread(new Runnable() {            @Override            public void run() {                // 執行UI操作                 Toast.makeText(MainActivity.this, "Test", Toast.LENGTH_SHORT).show();            }        });    }}).start();

 

4. AsyncTask

API參考:https://developer.android.google.cn/reference/android/os/AsyncTask

AsyncTask是一個抽象類別,它是由Android封裝的一個輕量級非同步類。它可以線上程池中執行背景工作,然後把執行的進度和最終結果傳遞給主線程並在主線程中更新UI。AsyncTasks應該用於短操作(最多幾秒)。如果您需要保持線程長時間運行,強烈建議您使用java.util.concurrent包提供的各種API,例如Executor,ThreadPoolExecutor和FutureTask。 非同步任務由3個泛型型別定義:Params,Progress和Result,以及4個步驟:onPreExecute,doInBackground,onProgressUpdate和onPostExecute。

使用AsyncTask時需要繼承AsyncTask類並必須實現doInBackground(params...)方法,大多數情況下學需要實現onPostExecute(Result)方法。

 

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {    protected Long doInBackground(URL... urls) {        int count = urls.length;        long totalSize = 0;        for (int i = 0; i < count; i++) {            totalSize += Downloader.downloadFile(urls[i]);            publishProgress((int) ((i / (float) count) * 100));            // Escape early if cancel() is called            if (isCancelled()) break;        }        return totalSize;    }    protected void onProgressUpdate(Integer... progress) {        setProgressPercent(progress[0]);    }    protected void onPostExecute(Long result) {        showDialog("Downloaded " + result + " bytes");    }}

 

建立成功後執行任務非常簡單:

new DownloadFilesTask().execute(url1, url2, url3); // Params

 

AsyncTask的泛型型別:

  • Params:在執行時傳遞給任務的參數類型;
  • Progress:非同步任務執行過程中,返回執行進度值的類型;
  • Result:背景工作執行結果類型。

並非所有類型都必須在非同步任務中使用,如果不需要使用,則用Void來代替。

rivate class MyTask extends AsyncTask<Void, Void, Void> { ... }

 

當執行一個非同步任務時,需要經曆4個步驟:

  • onPreExecute():在非同步任務執行前,在UI主線和中調用。此步驟通常用於設定任務,例如在用記介面中顯示進度條。
  • doInBackground(Params...):onPreExecute()執行完後,立即在後台線程中調用此方法。此步驟用於執行已耗用時間可能較長的背景工作,參數由execute(params...)傳入。通過此步驟得到結果並返回到最後一步。在計算過程中,可通過調用publishProgress(Progress...)方法,並通過onProgressUpdate(Progress...)更新UI顯示任務執行進度。
  • onProgressUpdate(Progress...):當publishProgress(Progress...)方法執行後,此步驟在UI主線程中被調用,並更新UI當前任務進度。
  • onPostExecute(Result):在後台線程計算完成後在UI線程上調用。 後台線程計算的結果作為參數傳遞給此步驟。

AsyncTask還提供了onCancelled()方法,它同樣在主線程中執行,當非同步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用,但是要注意的是,AsyncTask中的cancel()方法並不是真正去取消任務,只是設定這個任務為取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記為中斷,需要線上程內部進行標記判斷然後中斷線程。

使用AsyncTask還需要注意以下問題:

  • 非同步任務的執行個體必須在UI線程中建立,即AsyncTask對象必須在UI線程中建立。
  • execute(Params...) 必須在UI線程上執行。
  • 不要手動調用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)方法。
  • 該任務只能執行一次(如果嘗試執行第二次執行,則會引發異常)。
  • 不能在doInBackground(Params... params)中更改UI組件的資訊。
  • AsyncTask不與任何組件綁定生命週期,所以在Activity/或者Fragment中建立執行AsyncTask時,最好在Activity/Fragment的onDestory()調用 cancel(boolean);
  • 如果AsyncTask被聲明為Activity的非靜態內部類,那麼AsyncTask會保留一個對建立了AsyncTask的Activity的引用。如果Activity已經被銷毀,AsyncTask的後台線程還在執行,它將繼續在記憶體裡保留這個引用,導致Activity無法被回收,引起記憶體泄露。
  • 旋轉螢幕或Activity在後台被系統殺掉等情況會導致Activity的重新建立,之前啟動並執行AsyncTask(非靜態內部類)會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新介面將不再生效。

AsyncTask裡面有兩種線程池供我們調用,預設使用SERIAL_EXECUTOR。AsyncTask的核心線程是5,隊列容量是128,最大線程數是9。

  • THREAD_POOL_EXECUTOR, 非同步線程池。
  • SERIAL_EXECUTOR,同步線程池。

 

 一個簡單的AsyncTask例子:

自訂AsyncTask:

class TestAsyncTask extends AsyncTask<Integer, Integer, Integer> {    @Override    protected Integer doInBackground(Integer... integers) {        int count = integers[0];        int len = integers[1];        while (count < len) {            int pro = 100 * count / len;            Log.i(TAG, "----------" + pro + ":" + count + ":" + len);            publishProgress(pro);            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }            count++;        }        return count;    }    @Override    protected void onProgressUpdate(Integer... values) {        mText.setText("Progress:" + values[0] + "%");    }    @Override    protected void onPostExecute(Integer integer) {        mText.setText("Finished:" + integer + "%");    }}

 

建立TestAsyncTask執行個體:

mAsyncTask.execute(0, 100);

 

Android 線程互動

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.