Memory & Threading. (Android Performance Patterns Season 5, Ep. 3)

這是 Android Performance Patterns 第五季的第三集,之前視頻中一直在介紹線程的重要性,以及 Android 中我們提供的一些類功能和使用場景,這一集老外會講一講內存和線程的關系。

本期視頻地址 https://www.youtube.com/watch?v=tBHPmQQNiS8

(老外四分鐘講了這么多,我的心在滴血...)

As much as we'd like focus on creating and managing thread and work packets, we tend to ignore the biggest, hardest problem in threading, memory.

我們想要盡可能專注于創建和管理線程以及代碼中的任務,而趨向于忽視線程和內中最大,最難的問題。

My name is Colt McAnils.

大家好,我叫Colt McAnils.

And while threading and memory have a long, complicated history in programming, there are some specific nuances on Android that you need to be aware of.

雖然線程和內存在編程中有著在復雜而漫長的歷史,但 Android 中還有一些需要注意的細微的差別。

You gotta remember that memory in computing isn't really thread safe.

你需要記住的是,在內存中計算并不是線程安全的(這么理解對么)。

When two threads are operating on the same block of memory, weird things happen.

當兩個線程操作同一個代碼塊時奇怪的事情就會發生了。

兩個線程同時操作一個區域會發生奇怪的事情.png

I mean, you can get memory contention problem with read write access order, ABA problem, rips in the fabric of space time.

我是說,讀寫訪問順序可能帶來,內存爭用、ABA、空間時間的結構中破裂等問題,不知道這么理解正不正確。

ABA 問題可以參考維基百科,地址如下:

https://zh.wikipedia.org/wiki/%E6%AF%94%E8%BE%83%E5%B9%B6%E4%BA%A4%E6%8D%A2

Ok, maybe not --- maybe not the last one.

好吧,也許不是最后一個(這句話太突兀了吧)。

But anyhow, fixing these means restricting memory access form threads using locking, which is a whole separate video series that we're not going to get into right now.

但無論如何,修復上面那些問題就意味著通過鎖的形式限制內存訪問線程(就是加鎖唄),后半句我對它的理解是,這是一套完整視頻系列,但是我們現在不會深入講解,不知道這么理解對不對。

But most important to understand is that Android isn't immune to these problems.

但是最重要的是理解其實 Android 中也不能避免這些問題。

For the most part, the same techniques you use to deal with these issues on other platforms can be applied here, too.

在大多數情況下,我們可以在這里使用同樣手法去處理同其他平臺上遇到問題,所以就是手法是相似的,在別的地方遇到問題,在這里也遇到了,那么我們就可以使用同樣的手法去處理這些問題。

But there a few specific cases that you need to be aware of.

但我們我也需要注意一些特殊的情況。

Let's start with the fact that by desgin, UI objects are not thread safe.

來讓我們看一個設計的事實,UI 對象不是線程安全的(這么理解對么)。

UI objects are expected to be created, used, and destroyed all on the UI thread, and not guaranteed to behave properly on any other threads.

UI 對象被預期在 UI 線程(主線程)被創建、使用和銷毀,但不能保證在其他線程也都能正常運行。

Trying to modify or even reference them on other threads can throw exceptions, cause silent failures, crashed, and just general weirdness.

嘗試在其他線程修改或者甚至引用 UI 對象 可能會導致異常、引發一些無聲的失敗、崩潰或者一些普通的奇怪現象。

嘗試在其他線程修改 UI 會有失敗,崩潰之類的問題.png

In fact, just holding a direct reference to a UI Objcet on the work thread can be a problem.

事實上,只是在工作線程直接持有一個 UI 對象的引用也可能會導致問題。

For example, your work object may contain a reference to a view.

例如,在工作對象可能包含對一個視圖的引用。

But before your work completes on the worker thread, the view is removed from the view hierarchy on the main thread.

但是,在工作線程完成之前,該視圖已經從主線程的視圖層次結構中被刪除了。

So 別在工作線程操作 UI.png

So what do you do here?

這時候會發生啥呢?

I mean, you can't trust any of the properties of that view since the data has changed.

這句話應該怎么理解呢?由于數據已經修改了,所以不能相信視圖的任何屬性,應該是承接上面那句,該視圖已經從主線程的視圖層次結構中被刪除了,所以就不要相信視圖的任何屬性,也不要試圖再去操作 View。

And updating those properties dosen't really mean anything, since it's not part of the hierarchy anymore and won't be drawn to the screen.

并且更新這些屬性不會真的意味著什么,因為它不再是層次結構的一部分,也不會被繪制到屏幕上,所以果然是已經被從視圖層級中刪除了,所以再去更新他們的屬性并沒有什么卵用。

Now remember, views contain reference to their owning activity.

現在記住,視圖控件包含著他們自己所在的 Activity 的引用。

I mean, we did a whole video on how leaking those views can cause all sorts of memory problems.

我是說,之前的視頻中(第三季),說過這些視圖控件可能引起內存泄漏等問題。

第三季第六集講的內存泄漏.png

But this gets even worse when threading is involved.、

但如果涉及到線程,可能問題會更糟。

If an activity is destoryed but there still exists a threaded block of work that reference it, the activity won't get collected util that work finishes.

如果 Activity 被銷毀了但是工作線程還引用著它,那么 Activity 將不會被回收直到線程工作完畢。

其他線程還持有著 Activity 的引用,導致 Activity 無法被回收,直到線程工作結束.png

So if you kick off some work and the user rotates the screen three times in a row before that work completes, you could end up with three instances of that activity object resident in memory.

如果你開始一些工作(代碼中的一些任務),并且用戶在任務完成之前旋轉了三次屏幕,那么就可能會在內存中存在三個該 Activity 的實例,畢竟旋轉屏幕正常情況下會銷毀并重建該 Activity。

And to be clear, it's not just explicit references to UI objcets that you need to worry about.

需要明確的是,你擔心的不僅僅是明確引用的 UI 對象,應該還有很多,如果使用不當,都會造成內存問題,比如內存泄漏。

You also have to be cautious of implicit references, as well.

你也不得不小心點隱式引用,比如內部類會隱式持有外部類的引用。

Check out this very common seen-all-the-time coding pattern in Android apps.

在 Android App 中查看這個非常常見的編碼模式,這句話翻譯的不是很好。

You're got some threading object that's declared as an inner class of an activity.

你在 Activity 中聲明了一些作為內部類的線程對象。

The problem here is that the async task object now has an implicit reference to the enclosing activity and will keep that reference until the work object has been destory.

這里的問題就是,異步的任務對象也就還下圖中的 MyAsyncTask 是一個內部類,那么它就會隱式的持有 Activity 對象的引用,并保留著該引用知道一異步工作的對象被銷毀,這里是內部類的只是點,如果我們將 MyAsyncTask 聲明為嵌套類(靜態內部類)那么就不會有再持有外圍類對象的引用了。

Activity 中的內部類會隱式持有 Activity 的引用.png

The result is the same problem.

這句話意思應是說,返回的結果也同樣是一個問題。

Until this work completes. the activity stays around in memory.

直到異步任務完成,Activity 還依然會留在內存中。

And by the way, this type of pattern also leads to common types of crashed seen in Android apps.

順便提一句,這種類型的的模式也是導致 Android 應用中常見的崩潰的原因之一。

而且還可能會導致 App 崩潰.png

Some block of threading work has kicked off.

一些線程工作已經開始了。

But the uesr hint Back or does something else to change the top-most activity.

但是用戶點了返回或者其他什么事情改變了最頂端的 Activity。

Later, when the threaded work completes, it tries to make updates to a state that's no logger valid.

之后,當線程任務完成后,它嘗試著更新一個不再有效的狀態。

The result is a dialog box notifying me that an app I haven't used for 10 minutes has crashed, which is kind of awkward for everybody.

如果任務結束了,結果需要是一個對話框通知用戶,那么那個已經10分鐘沒使用的應用就會崩潰,這對每個人來說都略顯尷尬。

The takeaway here is that you shouldn't hold references to any types of UI specific objects in any of your threading scenarios, which leads to the natural question, how are we suppsed to update the UI from threaded work.

所以你不應該在任何線程場景中保留著任何類型的 UI 對象的引用,這將導致一個自然問題,我們應該如何從線程工作中去更新 UI。

不要持有引用.png

The trick here is to force the top level activity or fragments to be the sole system responsible for updating the UI objects.

這里的訣竅是強制位于頂層的 Activity or Fragment 成為更新 UI 對象的唯一負責人。

For example, when you'd like to kick off some work, create a work record that pairs a view with some update function.

舉個例子,當你想要開始一些任務時,創建一個將視圖與一些功能匹配的工作記錄,臥槽,這句話這么翻譯好像不太好吧。

When that block of work is finished, it submits the results back to activity using an intent or a run on UI thread call.

當任務代碼塊執行結束后,它會使用 Intent 或者在主線程中將結果返回給 Activity。

The activity can then call the update function with the new information, or if the view isn't there, just drop the work altogether.

Activity 可以使用新的信息來回調更新函數,或者如果 View 對象已經不存在了,那么及應該將任務一起刪掉。

然后,活動可以使用新信息調用更新功能,或者如果視圖不在那里,則只需將工作完全刪除。

And if the activity that issued the work was destoryed, then the new activity won't have a reference to any of this and will just drop the work too.

應該是說如果 Activity 被銷毀了,那么基于這個 Activity 運行的任務也應該都停止掉,別影響別的 Activity 工作。

See?

看到了沒?

No crashed, no memory leaks, just pure clean fun.

沒有崩潰,沒有內存泄漏,只是安靜的寫著代碼,而不是寫著Bug,這才是純粹的編碼樂趣。

Now, if you ever want a deeper look at how threading and memory are working together, make sure to check out the powerful new tools inside of Android Studio, which just got an awesome revamp as a version 2.0.

AS 2.0 提供一個工具,它可以幫助我們更深入地了解線程和內存如何協同工作,但這個工具我暫時還沒找到哪里打開。。。

AS 2.0 提供的一個查看線程工具.png

The deeper you go into Android performance, the more you realize how important memory is on this platform, which is why should check out the rest of the memory videos on the Android Performance Patterns playlists.

打廣告,記得來看他們的 Android Performance Patterns 視頻。

....以下略,即可以去 Google+ 和他們進行交流...

可能有些地方翻譯的不正確,我會盡力提升自己翻譯的準確度,并用自己的語言表達出來,捎帶手會提一些視頻的知識點,歡迎討論。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容