天天看點

Android 開發中利用異步來優化運作速度和性能

<b>本文講的是Android 開發中利用異步來優化運作速度和性能,</b>

<b></b>

Android 開發中利用異步來優化運作速度和性能

在大多數使用場景下,我們沒有必要産生多個背景線程,簡單的建立AsyncTasks或者使用基于任務隊列的IntentService就可以很好的滿足我們對異步處理的需求。然而當我們真的需要多個背景線程的時候,我們常常會使用下面的代碼簡單的建立多個線程。

該方法有幾個問題。一方面,作業系統限制了同一域下連接配接數(限制為4)。這意味着,你的代碼并沒有真的按照你的意願執行。建立的線程如果超過數量限制則需要等待舊線程執行完畢。 另外,每一個線程都被建立來執行一個任務,然後銷毀。這些線程也沒有被重用。

舉個例子,如果你想開發一個連拍應用能在1秒鐘連拍10張圖檔(或者更多)。應用該具備如下的子任務:

在一秒的時間内撲捉10張以byte[]形式儲存的照片,并且不能夠阻塞UI線程。

将byte[]儲存的資料格式從YUV轉換成RGB。

使用轉換後的資料建立Bitmap。

變換Bitmap的方向。

生成縮略圖大小的Bitmap。

将全尺寸的Bitmap以Jpeg壓縮檔案的格式寫入磁盤中。

使用上傳隊列将圖檔儲存到伺服器中。

很明顯,如果你将太多的子任務放在UI線程中,你的應用在性能上的表現将不會太好。在這種情況下,唯一的解決方案就是先将相機預覽的資料緩存起來,當UI線程閑置的時候再來利用緩存的資料執行剩下的任務。

另外一個可選的解決方案是建立一個長時間在背景運作的HandlerThread,它能夠接受相機預覽的資料,并處理完剩下的全部任務。當然這種做法的性能會好些,但是如果使用者想再連拍的話,将會面臨較大的延遲,因為他需要等待HandlerThread處理完前一次連拍。

看起來所有的任務都被背景的單一線程處理完畢了,我們性能提升主要得益于背景線程長期運作并不會被銷毀和重建。然而,我們背景的單一線程卻要和其他優先等級更高的任務共享,而且這些任務隻能夠順序執行。

我們也可以建立第二個HandlerThread來處理我們的圖像,然後建立第三個HandlerThread來将照片寫入磁盤,最後再建立第四個HandlerThread來将照片上傳到伺服器中。我們能夠加快拍照的速度,但是,這些線程互相之間還是遵循順序執行的規則,并不是真的并發。因為每張照片是順序處理的,而且處理每一張照片需要一定的時間,導緻使用者在點選拍照按鈕到顯示全部縮略圖的時候仍然能夠明顯的感覺到延遲。

我們可以根據需求建立多個線程,但是建立過多的線程會消耗CPU周期影響性能,并且線程的建立和銷毀也需要時間成本。是以我們不想建立多餘的線程,但是又想能夠充分的利用裝置的硬體資源。這個時候我們可以使用ThreadPool。

通過建立ThreadPool對象的單例來在你的應用中使用ThreadPool。

然後,在上面的代碼中,簡單的修改Handler的回調函數為:

優化已經完成!通過下面的視訊,我們觀察到加載縮略圖的速度提升是非常明顯的。

這種做法的優點是我們可以定義線程池的大小并且指定空餘線程保持活動的時間。我們也可以建立多個ThreadPools來處理多個任務或者使用單個ThreadPool來處理多個任務。但是在使用完後記得清理資源。

我們甚至可以為每一個功能建立一個獨立的ThreadPool。譬如說在這個例子中我們可以建立三個ThreadPool,第一個ThreadPool負責資料轉換成Bitmap,第二個ThreadPool負責寫資料到磁盤中去,第三個ThreadPool上傳Bitmap到伺服器中去。這樣做的話,如果我們的ThreadPool最大擁有4條線程,那麼我們就能夠同時的轉換,寫入,上傳四張相片。使用者将看到4張縮略圖是同時顯示而不是一個個的顯示出來的。

使用ThreadPool前: 如果可以,從頂部觀察計數器的變化來得知當底部縮略圖從開始顯示到全部顯示完成所耗費的時間。在程式中除了adapter中的notifyDataSetChanged()方法外,我已經将大部分的操作從主線程中剝離,是以計數器的運作是很流暢的。

使用ThreadPool後: 通過頂部的計數器,我們發現使用了ThreadPool後,照片的縮略圖加載速度明顯變快。

<b>原文釋出時間為:2016年04月20日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>

繼續閱讀