Android 線程交互

来源:https://www.cnblogs.com/elvischiu/archive/2018/05/01/8978066.html
-Advertisement-
Play Games

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


 在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; } } }; // 發送消息至HanderThread mThreadHandler.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還提供了cancelled(boolean)方法,它同樣在主線程中執行,當非同步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用,但是要註意的是,AsyncTask中的cancel(boolean)方法並不是真正去取消任務,只是設置這個任務為取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記為中斷,需要線上程內部進行標記判斷然後中斷線程。參數true表示立即取消任務(不一定成功),false則表示允許任務執行完成後再取消。

使用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);

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 概況 1.1 任務 口語理解(Spoken Language Understanding, SLU) 作為語音識別與自然語言處理之間的一個新興領域,其目的是為了讓電腦從用戶的講話中理解他們的意圖。SLU是口語對話系統( "Spoken Dialog Systems" )的一個非常關鍵的環節。 ...
  • Alias——使用一個列名別名AS 關鍵字: GROUP BY語句: 比如在合計(sum函數)的時候常需要用group by語句來進行分類求和 2018-05-01 23:21:52 ...
  • 原文鏈接:http://www.cnblogs.com/xrq730/p/8944539.html,轉載請註明出處,謝謝 本文目錄 上一篇文章以認識Redis為主,寫了Redis系列的第一篇,現在開啟第二部分的學習,在本文中,我們將看到以下內容: Redis數據結構String、Hash、List、 ...
  • 連接查詢:同時設計兩個及以上的表的查詢 連接條件或連接謂詞:用來連接兩個表的條件一般格式: [<表名1>]<列名1> <比較運算符> [<表名2>]<列名2> [<表名1>]<列名1> between [<表名2>]<列名2> and [<表名2>]<列名3> 等值連接: 連接運算符為= 查詢每個學 ...
  • linux上安裝redis,本地遠程訪問問題總結,redis超時連接問題 ...
  • MongoDB運行的兩種方式 檢查是否有MongoDB:which mongod 創建資料庫存儲目錄:mkdir -p /data/db 檢查磁碟目錄是否有空間(一般要大於4G):df -lh 啟動:a直接啟動:mongod --dbpath=/data/db --port=27017 b守護進程的 ...
  • 對oracle資料庫,PL/SQL用法的快速學習 ...
  • 一、超級管理員創建及開啟登錄驗證 如果MongoDB要開啟登錄驗證,必須在開啟登錄驗證之前先創建好超級管理員,否則無法登錄資料庫! 例如,創建一個超級管理員admin,關聯給admin資料庫,角色設置為root(超級管理員) 首先,進入到目標庫admin,use admin 然後,輸入指令 db.c ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...