android線程學習心得

來源:互聯網
上載者:User

標籤:

有一篇關於android線程講的非常好,大家可以參考下,其中有一句話講的非常好,就拿來做開篇之句:

當一個程式第一次啟動時,Android會同時啟動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如使用者的按鍵事件,使用者接觸螢幕的事件以及螢幕繪圖事件,並把相關的事件分發到對應的組件進行處理,所以主線程通常又被叫做UI線程。在開發Android應用時必須遵守單執行緒模式的原則: Android UI操作並不是安全執行緒的並且這些操作必須在UI線程中執行。

 

咱新手在第一次接觸android線程的背景是這樣的:

1:需求中要進行一次http互動

步驟大概是這樣

    1.1:點擊提交按鈕

    1.2:http同步擷取結果

    1.3:將結果寫入到TextView上。

這樣寫,執行時會報錯,不能在UI線程上發起同步的http網路請求。(耗時可能會導致ANR,application not respond)

 

2:OK,既然不能在UI線程上發起http請求,那咱新開一個子線程。

於是步驟變成了這樣:

   1.1:點擊提交按鈕

   1.2:開啟一個子線程

   1.3:http同步擷取結果

   1.4:將結果寫入到TextView上。

一執行,BOOM,報錯,大概意思是子線程裡不能直接操作UI元素,為什麼呢?請再看看本文開頭的句子(安全執行緒)。

3:好吧,那就在子線程裡通過Handler來操作UI的元素吧

最後,測試通過的代碼大概是這樣

   1.1:點擊提交按鈕

   1.2:開啟一個子線程

   1.3:http同步擷取結果

   1.4:將結果作為Message,傳遞給Handler

   1.5:在Handler裡將結果寫入到TextView上。

tips:如果你用android studio 的code inspect功能,就會發現,它提示你,這樣的做法可能會導致記憶體泄露,為什麼呢?因為Handler裡持有了UI裡面的元素的引用,當UI結束掉自己時(此時handler還在耐心等待http訪問結果),發現某個元素被Hnadler持有,就不能被GC回收了,這就會造成記憶體泄露。解決辦法很簡單,Handler改為static,消除內部匿名引用,同時,將對象的引用改為WeakReference<>即可。一篇詳細解釋原因的文章,具體代碼參考如下,來源於咱的通訊錄APP

static class ImageDoneHandler extends Handler {        WeakReference<ImageView> imageView;        WeakReference<Bitmap> bitmap;        WeakReference<String> url;        ZImage.CacheType cacheType;        ImageDoneHandler(Looper looper, ImageView _imageView, Bitmap _bitmap, String url, ZImage.CacheType cacheType) {            super(looper);            imageView = new WeakReference<>(_imageView);            bitmap = new WeakReference<>(_bitmap);            this.url = new WeakReference<>(url);            this.cacheType = cacheType;        }        @Override        public void handleMessage(Message msg) {            if (msg.what != MSG_IMAGE_LOAD_DONE)                return;            ImageView _imageView = imageView.get();            Bitmap _bitmap = bitmap.get();            String _url = url.get();            if (_imageView == null || _bitmap == null)                return;            if (_url.equals(_imageView.getTag().toString())) {                _imageView.setImageBitmap(_bitmap);                if (cacheType == ZImage.CacheType.DiskMemory)                    ZImage.getInstance().putToMemoryCache(_url, _bitmap);            }        }    }

 

寫到現在,咱還是不懂,為啥Handler裡面就可以改UI裡面的元素呢?

這時候就需要理解android非同步訊息處理的四大部分了( Message、 Handler、 MessageQueue 和Looper)。

咱才疏學淺,因此下面的知識來源於《第一行代碼》書籍的節選片段,非常的精彩,值得反覆閱讀,大家深呼吸下,系好安全帶,開始咯~

先來一張非同步訊息處理的整個流程圖解,大家對照著圖解看更直觀(圖片來源《第一行代碼》)

1. Message

Message 是線上程之間傳遞的訊息,它可以在內部攜帶少量的資訊,用於在不同線
程之間交換資料。上一小節中我們使用到了 Message 的 what 欄位,除此之外還可以使
用 arg1 和 arg2 欄位來攜帶一些整型資料,使用 obj 欄位攜帶一個 Object 對象。

2. Handler

Handler 顧名思義也就是處理者的意思,它主要是用於發送和處理訊息的。發送消
息一般是使用 Handler 的 sendMessage()方法,而發出的訊息經過一系列地輾轉處理後,
最終會傳遞到 Handler 的 handleMessage()方法中。


3. MessageQueue

MessageQueue 是訊息佇列的意思,它主要用於存放所有通過 Handler 發送的訊息。
這部分訊息會一直存在於訊息佇列中,等待被處理。每個線程中只會有一個 MessageQueue
對象。

4. Looper

Looper 是每個線程中的 MessageQueue 的管家,調用 Looper 的 loop()方法後,就會
進入到一個無限迴圈當中,然後每當發現 MessageQueue 中存在一條訊息,就會將它取
出,並傳遞到 Handler 的 handleMessage()方法中。每個線程中也只會有一個 Looper 對象。

 

瞭解了 Message、 Handler、 MessageQueue 以及 Looper 的基本概念後我們再來對非同步訊息處理的整個流程梳理一遍。

 

1:首先需要在主線程當中建立一個 Handler 對象,並重寫handleMessage()方法。

2:然後當子線程中需要進行 UI 操作時,就建立一個 Message 對象,並通過 Handler 將這條訊息發送出去。

3:之後這條訊息會被添加到 MessageQueue 的隊列中等待被處理,

4:而 Looper 則會一直嘗試從 MessageQueue 中取出待處理訊息,最後分發回 Handler的 handleMessage()方法中。

5:由於 Handler 是在主線程中建立的,所以此時 handleMessage()方法中的代碼也會在主線程中運行,於是我們在這裡就可以安心地進行 UI 操作了。

 

AsyncTask非同步任務類

幸運的是,在一些大部分的場合,android為我們提供了一個AsyncTask非同步任務抽象類別,通過實現他可以非常方便的執行各種耗時操作,而不必擔心UI線程被卡住,同時也避免了原生非同步線程與UI線程互動繁瑣的寫法。

在繼承時,我們可以指定三個泛型參數類型(都是參考型別哦,實值型別的記得也要改成參考型別,比如int ->Integer),它們分別是:

1:參數Param ,傳遞給子線程執行的

2:進度提示Progress,如果需要即時在介面更新非同步處理進度,就可以通過這個參數反饋

3:結果Result,在主線程裡,我們就擷取到了非同步執行的結果。

來個例子吧

    /**     * http請求使用者是否存在,穿入http的url地址,返回布爾類型是否存在     */    class QueryUserExistTask extends AsyncTask<String,Void,Boolean>    {        /**         * 在非同步請求處理之前         */        @Override        protected void onPreExecute() {            super.onPreExecute();        }        /**         * 非同步處理,這裡同樣不能互動UI元素哦         * @param params         * @return         */        @Override        protected Boolean doInBackground(String... params) {            return null;        }        /**         * 非同步處理完了,切回到主線程,返回處理結果         * @param aBoolean         */        @Override        protected void onPostExecute(Boolean aBoolean) {            super.onPostExecute(aBoolean);        }    }

調用例子:

  new QueryUserExistTask().execute("http://192.168.1.1/u/kimmy");

 

總結:

1:保持主線程流暢度很重要,耗費大量資源的工作盡量放到子線程完成。

2:大部分情況下AsyncTask都能勝任非同步重任。

3:高並發的非同步任務、或者非同步任務之間彼此需要調度的情況,需要自己編寫線程池來處理

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.