1. 程式人生 > >淺談android的執行緒安全和handler處理

淺談android的執行緒安全和handler處理

android的UI操作不是安全的,同時也只有主執行緒才能操作UI,同時主執行緒對UI操作有一定的時間限制(最長5秒)。為了能夠作一些比較好使的操作(比如下載、開啟大檔案等),android提供了一些列機制。

執行緒安全:如果你的程式碼所在的程序中又多個執行緒在同時執行,而這些執行緒可能會同時執行這段程式碼。如果每次執行結果和單執行緒執行的結果是一樣的,而且其他的變數的值也喝預期的是一樣的,就是執行緒安全的,或者說:一個類或者程式所提供的介面對於執行緒來說是原子操作或者多個執行緒之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。

當一個程式第一次啟動的時候,android會啟動一個LINUX程序和一個主執行緒。預設的情況下,所有該程式的元件都將在該程序和執行緒中執行。主執行緒(Main Thread)主要負責處理與UI相關的事件,如:使用者的按鍵事件

使用者接觸螢幕的事件以及螢幕繪圖事件並把相關的時間分發到對應的元件進行處理。所以主執行緒通常又被叫做UI執行緒。UI執行緒雨AndroidUI工具包中的元件進行互動,在考法Android應用時必須遵守單執行緒模型的原則--AndroidUI操作並不是執行緒安全的並且這些操作必須在UI執行緒中執行。

當主執行緒正在做一些比較耗時的操作的時候,如正從網路上下載一個大圖片或者訪問資料庫,由於主執行緒悲這些好事的操作阻塞住,無法及時的響應使用者的事件,從使用者的角度看會覺得程式已經死掉。如果程式長時間不響應,使用者還可能得重啟系統。為了避免這樣的情況,Android設定了一個5秒的超時時間,一旦使用者的時間由於主執行緒阻塞而超過5秒鐘沒有響應,Android會彈出一個應許程式沒有響應的對話方塊(ANR)。

Android的UI是單執行緒的,一些費時的操作交給獨立的執行緒來執行UI物件。

handler引入

在Android開發中,通常會遇到:在UI基面上進行某項操作後要執行一段很好使的程式碼,比如我們在介面上點選下載按鈕執行網路請求,這是一個好使操作,不知什麼時候才能完成。為了保證不影響UI執行緒,我們會建立一個新的執行緒去執行耗時的程式碼。當我們的耗時操作完成時,我們需要更新UI介面告知使用者操作完成了。簡單程式碼如下:

public class MainActivity extends Activity implements Button.OnClickListener {

    private
TextView statusTextView = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); statusTextView = (TextView)findViewById(R.id.statusTextView); Button btnDownload = (Button)findViewById(R.id.btnDownload); btnDownload.setOnClickListener(this); } @Override public void onClick(View v) { DownloadThread downloadThread = new DownloadThread(); downloadThread.start(); } class DownloadThread extends Thread{ @Override public void run() { try{ System.out.println("開始下載檔案"); //此處讓執行緒DownloadThread休眠5秒中,模擬檔案的耗時過程 Thread.sleep(5000); System.out.println("檔案下載完成"); //檔案下載完成後更新UI MainActivity.this.statusTextView.setText("檔案下載完成"); }catch (InterruptedException e){ e.printStackTrace(); } } } }

上面程式碼點選“下載”按鈕後會啟動一個新的執行緒去執行世紀的下載操作,執行完畢後更新UI介面。但是在實際程式碼執行到MainActivity.this.statusTextView.setText("檔案下載完成");時,報錯系統崩潰退出:報錯如下:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
錯誤的意思是隻有建立View的原始執行緒才能更新View。出現這樣錯誤的原因是Android中的View不是執行緒安全的,在Android應用啟動時,會自動建立一個執行緒,即程式的主執行緒,主執行緒負責UI的展示、UI時間訊息的處理等等,因此主執行緒也叫做UI執行緒,statusTextView是在UI執行緒中建立的,當我們在DownloadThread執行緒中去更新UI執行緒中建立的statusTextView時就會報錯。Android為了解決這種問題引入了Handler機制。

handler可以用來在多執行緒之間進行通訊,在另一個執行緒紅去更新UI控制元件只是Handler使用中的一種典型案例,除此之外,Handler可以做很多其他的事情。每個Handler都綁定了一個執行緒,假設存在兩個執行緒ThreadA和ThreadB,並且HandlerA綁定了ThreadA,在ThreadB中的程式碼執行到某處時猶豫某些原因,我們需要讓ThreadA執行某些程式碼,此時我們就可以使用Handler,我們可以在ThreadB中想HandlerA中加入某些資訊來告知ThreadA中發作某些處理。Handler是多執行緒之間通訊的橋樑,通過Handler,我們可以在一個執行緒中控制另一個執行緒去做某事。

使用post方法

public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView = null;

    //uiHandler在主執行緒中建立,所以自動繫結主執行緒
    private Handler uiHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = (TextView)findViewById(R.id.statusTextView);
        Button btnDownload = (Button)findViewById(R.id.btnDownload);
        btnDownload.setOnClickListener(this);
        System.out.println("Main thread id " + Thread.currentThread().getId());
    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread{
        @Override
        public void run() {
            try{
                System.out.println("DownloadThread id " + Thread.currentThread().getId());
                System.out.println("開始下載檔案");
                //此處讓執行緒DownloadThread休眠5秒中,模擬檔案的耗時過程
                Thread.sleep(5000);
                System.out.println("檔案下載完成");
                //檔案下載完成後更新UI
                Runnable runnable = new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Runnable thread id " + Thread.currentThread().getId());
                        MainActivity.this.statusTextView.setText("檔案下載完成");
                    }
                };
                uiHandler.post(runnable);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

我們在Activity中建立了一個Handler成員變數uiHandler,Handler有個特點,在執行new Handler()的時候,預設情況下Handler會綁定當前程式碼執行的執行緒,我們在主執行緒中例項化了uiHandler,所以uiHandler就自動綁定了主執行緒,即UI執行緒。當我們在DownloadThread中執行完耗時程式碼後,我們將一個Runnable物件通過post方法傳入到了Handler中,Handler會在合適的時候讓主執行緒執行Runnable中的程式碼,這樣Runnable就在主執行緒中執行了,從而正確更新了主執行緒中的UI。

使用sendMessage方法

public class MainActivity extends Activity implements Button.OnClickListener {

    private TextView statusTextView = null;

    //uiHandler在主執行緒中建立,所以自動繫結主執行緒
    private Handler uiHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    System.out.println("handleMessage thread id " + Thread.currentThread().getId());
                    System.out.println("msg.arg1:" + msg.arg1);
                    System.out.println("msg.arg2:" + msg.arg2);
                    MainActivity.this.statusTextView.setText("檔案下載完成");
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        statusTextView = (TextView)findViewById(R.id.statusTextView);
        Button btnDownload = (Button)findViewById(R.id.btnDownload);
        btnDownload.setOnClickListener(this);
        System.out.println("Main thread id " + Thread.currentThread().getId());
    }

    @Override
    public void onClick(View v) {
        DownloadThread downloadThread = new DownloadThread();
        downloadThread.start();
    }

    class DownloadThread extends Thread{
        @Override
        public void run() {
            try{
                System.out.println("DownloadThread id " + Thread.currentThread().getId());
                System.out.println("開始下載檔案");
                //此處讓執行緒DownloadThread休眠5秒中,模擬檔案的耗時過程
                Thread.sleep(5000);
                System.out.println("檔案下載完成");
                //檔案下載完成後更新UI
                Message msg = new Message();
                //雖然Message的建構函式式public的,我們也可以通過以下兩種方式通過迴圈物件獲取Message
                //msg = Message.obtain(uiHandler);
                //msg = uiHandler.obtainMessage();

                //what是我們自定義的一個Message的識別碼,以便於在Handler的handleMessage方法中根據what識別
                //出不同的Message,以便我們做出不同的處理操作
                msg.what = 1;

                //我們可以通過arg1和arg2給Message傳入簡單的資料
                msg.arg1 = 123;
                msg.arg2 = 321;
                //我們也可以通過給obj賦值Object型別傳遞向Message傳入任意資料
                //msg.obj = null;
                //我們還可以通過setData方法和getData方法向Message中寫入和讀取Bundle型別的資料
                //msg.setData(null);
                //Bundle data = msg.getData();

                //將該Message傳送給對應的Handler
                uiHandler.sendMessage(msg);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

需要說明的是,如果在handleMessage中 不需要判斷Message型別,那麼就無須設定Message的what值;而且讓Message攜帶資料也不是必須的,只有在需要的時候才需要讓其攜帶資料;如果確實需要讓Message攜帶資料,應該儘量使用arg1或arg2或兩者,能用arg1和arg2解決的話就不要用obj,因為用arg1和arg2更高效。

相關推薦

android執行安全handler處理

android的UI操作不是安全的,同時也只有主執行緒才能操作UI,同時主執行緒對UI操作有一定的時間限制(最長5秒)。為了能夠作一些比較好使的操作(比如下載、開啟大檔案等),android提供了一些列機制。 執行緒安全:如果你的程式碼所在的程序中又多個執行緒

執行安全問題

下面我們還是用看電影賣票的案例來談一談多執行緒安全的問題。 方案一:使用同步程式碼塊 //實現賣票案例 /* 賣票案例出現執行緒安全問題 解決方案一:使用同步程式碼塊 格式: synchronized(鎖物件){ 可能會出現執行緒安全問題的程式

java執行安全

在瞭解執行緒安全之前,有必要談談執行緒的工作原理,我本人的理解是這樣的: jvm中有主存,Java中所有變數都儲存在主存中,對於所有執行緒都是共享的.同時每個執行緒擁有自己獨有的工作記憶體,當一個執行緒對一個變數進行操作時,都要在自己的工作記憶體中建立該變數的一個副本,操作

android執行

執行緒池的基本思想還是一種物件池的思想,開闢一塊記憶體空間,裡面存放了眾多(未死亡)的執行緒,池中執行緒執行排程由池管理器來處理。當有執行緒任務時,從池中取一個,執行完成後執行緒物件歸池,這樣可以避免反覆建立執行緒物件所帶來的效能開銷,節省了系統的資源。 比如:一個應用

高併發程式設計:執行安全ThreadLocal

執行緒安全的概念:當多個執行緒訪問某一個類(物件或方法)時,這個類始終都能表現出正確的行為,那麼這個類(物件或方法)就是執行緒安全的。 執行緒安全 說的可能比較抽象,下面就以一個簡單的例子來看看什麼是執行緒安全問題。 public class MyThread impleme

java中執行安全執行安全的集合

  執行緒安全 非執行緒安全 Collection Vector ArrayList、LinkedList   H

java執行池(基於jdk1.8)

多執行緒讓程式世界豐富多彩,也讓其錯綜複雜。對於執行緒的建立和銷燬成了一筆不小的開銷,為了減少這些開銷,出現了執行緒池。執行緒池對執行緒進行管理,對於需要使用多執行緒的你來說,只需要把你的任務丟給執行緒池就可以了。當你把任務丟給執行緒池的時候,它是如何處理的呢?

Java併發程式設計:執行安全ThreadLocal

執行緒安全的概念:當多個執行緒訪問某一個類(物件或方法)時,這個類始終都能表現出正確的行為,那麼這個類(物件或方法)就是執行緒安全的。 執行緒安全 說的可能比較抽象,下面就以一個簡單的例子來看看什麼是執行緒安全問題。 public class MyThread

Java多執行程式設計-(1)-執行安全鎖Synchronized概念

一、程序與執行緒的概念 (1)在傳統的作業系統中,程式並不能獨立執行,作為資源分配和獨立執行的基本單位都是程序。 在未配置 OS 的系統中,程式的執行方式

深入理解Java虛擬機器學習筆記3-執行安全鎖優化

併發處理是壓榨計算機運算能力最有力的工具。 1.執行緒安全 當多個執行緒訪問一個物件時,如果不用考慮這些執行緒執行時環境下排程和交替執行,也不需要進行額外的同步,或者在呼叫方進行任何其他的協調操作,呼叫這個物件的行為都可以獲取正確的結果,那麼這個物件是執行緒安全的。 2

一道JAVA面試,執行安全靜態內部類

前言:4月1號去一家網際網路公司面試,做了一份筆試。考察的內容也非常基礎,但是裡面卻充滿著各種各樣的擴充套件。但是這份題我做得並不好,平時用框架什麼的用多了,反而基礎顯得非常不紮實。憑著記憶寫起最後一套題目。記一下,紮實一下自己的基礎。 程式碼 /*

理解JVM(六):執行安全鎖優化

執行緒安全的實現方法 互斥同步 互斥是因,同步是果;互斥是方法,同步是目的。 synchronized關鍵字 synchronized關鍵字是基本的互斥同步手段。它在編譯後會在同步程式碼塊前後加入2條位元組碼指令:monitorenter和mo

C++執行安全容器的設計

    最近看到一本書,《C++併發程式設計實戰》,[美] Anthony Williams 著,裡面有談及執行緒安全容器的設計及實現程式碼,本人覺得這樣的設計有點問題,問題還是比較明顯的,寫在這裡,供讀者自己思考吧。      關於程式碼,可以在這裡下載: https:/

對比執行安全可重入函式

1.什麼叫執行緒安全 如果你的程式所在的程序中有多個執行緒在同時執行,而這些執行緒可能同時執行一段程式碼或同時訪問一個物件,如果每次執行完這段程式碼或訪問完這個物件之後,所得到的結果和單執行緒執行的

執行池的理解

1、首先由幾個介面和類的關係是需要先說明的: extends   implementsextends Executor(介面)----------------->ExecutorService(介面)------------------->AbstractExe

執行之鎖的機制

Java中鎖的機制 synchronized–Java語言的關鍵字,當它用來修飾一個方法或者一個程式碼塊的時候,能夠保證在同一時刻最多隻有一個執行緒執行該段程式碼。 當兩個併發執行緒訪問同一個物件Object中的這個synchronized同步程式碼塊時,

執行安全鎖 Synchronized 概念

程序與執行緒的概念 在傳統的作業系統中,程式並不能獨立執行,作為資源分配和獨立執行的基本單位都是程序。 在未配置 OS 的系統中,程式的執行方式是順序執行,即必須在一個程式執行完後,才允許另一個程式執行;在多道程式環境下,則允許多個程式併發執行。程式的這兩種執行方式間有著顯

PHP版本中執行安全執行安全的區別

Windows版的PHP從版本5.2.1開始有Thread Safe(c之分,這兩者不同在於何處?到底應該用哪種?這裡做一個簡單的介紹。 從2000年10月20日釋出的第一個Windows版的PHP3.0.17開始的都是執行緒安全的版本,這是由於與Linux/Unix系統

深入理解執行安全可重入函式

執行緒安全 基本定義 執行緒安全:簡單來說執行緒安全就是多個執行緒併發同一段程式碼時,不會出現不同的結果,我們就可以說該執行緒是安全的; 執行緒不安全:說完了執行緒安全,執行緒不安全的問題就很好解

執行中的懶漢式單例

1.場景:我們都知道在應用開發中有時會使用到單例設計模式,那麼今天我們就淺談一下單例中的一種-懶漢式。 顧名思義懶漢式其實是一個很有意思的名字,懶漢在我們開發中常說的用到了才去建立物件。此種生成方式遇到多執行緒又會擦出怎樣的火花呢? public class Singlet