這是 Android Performance Patterns 第五季的第三集欧漱,之前視頻中一直在介紹線程的重要性兜挨,以及 Android 中我們提供的一些類功能和使用場景膏孟,這一集老外會講一講內(nèi)存和線程的關系。
(老外四分鐘講了這么多拌汇,我的心在滴血...)
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.
我們想要盡可能專注于創(chuàng)建和管理線程以及代碼中的任務柒桑,而趨向于忽視線程和內(nèi)中最大,最難的問題噪舀。
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.
雖然線程和內(nèi)存在編程中有著在復雜而漫長的歷史,但 Android 中還有一些需要注意的細微的差別与倡。
You gotta remember that memory in computing isn't really thread safe.
你需要記住的是界逛,在內(nèi)存中計算并不是線程安全的(這么理解對么)。
When two threads are operating on the same block of memory, weird things happen.
當兩個線程操作同一個代碼塊時奇怪的事情就會發(fā)生了纺座。
I mean, you can get memory contention problem with read write access order, ABA problem, rips in the fabric of space time.
我是說息拜,讀寫訪問順序可能帶來,內(nèi)存爭用净响、ABA少欺、空間時間的結(jié)構(gòu)中破裂等問題,不知道這么理解正不正確馋贤。
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.
但無論如何仿滔,修復上面那些問題就意味著通過鎖的形式限制內(nèi)存訪問線程(就是加鎖唄)惠毁,后半句我對它的理解是,這是一套完整視頻系列堤撵,但是我們現(xiàn)在不會深入講解仁讨,不知道這么理解對不對。
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.
在大多數(shù)情況下洞豁,我們可以在這里使用同樣手法去處理同其他平臺上遇到問題,所以就是手法是相似的荒给,在別的地方遇到問題丈挟,在這里也遇到了,那么我們就可以使用同樣的手法去處理這些問題志电。
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 線程(主線程)被創(chuàng)建例朱、使用和銷毀,但不能保證在其他線程也都能正常運行鱼蝉。
Trying to modify or even reference them on other threads can throw exceptions, cause silent failures, crashed, and just general weirdness.
嘗試在其他線程修改或者甚至引用 UI 對象 可能會導致異常洒嗤、引發(fā)一些無聲的失敗、崩潰或者一些普通的奇怪現(xiàn)象魁亦。
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.
但是利术,在工作線程完成之前呈野,該視圖已經(jīng)從主線程的視圖層次結(jié)構(gòu)中被刪除了。
So what do you do here印叁?
這時候會發(fā)生啥呢际跪?
I mean, you can't trust any of the properties of that view since the data has changed.
這句話應該怎么理解呢?由于數(shù)據(jù)已經(jīng)修改了喉钢,所以不能相信視圖的任何屬性姆打,應該是承接上面那句,該視圖已經(jīng)從主線程的視圖層次結(jié)構(gòu)中被刪除了肠虽,所以就不要相信視圖的任何屬性幔戏,也不要試圖再去操作 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.
并且更新這些屬性不會真的意味著什么税课,因為它不再是層次結(jié)構(gòu)的一部分闲延,也不會被繪制到屏幕上痊剖,所以果然是已經(jīng)被從視圖層級中刪除了,所以再去更新他們的屬性并沒有什么卵用垒玲。
Now remember, views contain reference to their owning activity.
現(xiàn)在記住陆馁,視圖控件包含著他們自己所在的 Activity 的引用。
I mean, we did a whole video on how leaking those views can cause all sorts of memory problems.
我是說合愈,之前的視頻中(第三季)叮贩,說過這些視圖控件可能引起內(nèi)存泄漏等問題。
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 將不會被回收直到線程工作完畢捺萌。
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.
如果你開始一些工作(代碼中的一些任務)桃纯,并且用戶在任務完成之前旋轉(zhuǎn)了三次屏幕,那么就可能會在內(nèi)存中存在三個該 Activity 的實例披坏,畢竟旋轉(zhuǎn)屏幕正常情況下會銷毀并重建該 Activity态坦。
And to be clear, it's not just explicit references to UI objcets that you need to worry about.
需要明確的是,你擔心的不僅僅是明確引用的 UI 對象刮萌,應該還有很多驮配,如果使用不當娘扩,都會造成內(nèi)存問題着茸,比如內(nèi)存泄漏。
You also have to be cautious of implicit references, as well.
你也不得不小心點隱式引用琐旁,比如內(nèi)部類會隱式持有外部類的引用涮阔。
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 中聲明了一些作為內(nèi)部類的線程對象敬特。
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 是一個內(nèi)部類牺陶,那么它就會隱式的持有 Activity 對象的引用伟阔,并保留著該引用知道一異步工作的對象被銷毀,這里是內(nèi)部類的只是點掰伸,如果我們將 MyAsyncTask 聲明為嵌套類(靜態(tài)內(nèi)部類)那么就不會有再持有外圍類對象的引用了皱炉。
The result is the same problem.
這句話意思應是說,返回的結(jié)果也同樣是一個問題狮鸭。
Until this work completes. the activity stays around in memory.
直到異步任務完成合搅,Activity 還依然會留在內(nèi)存中多搀。
And by the way, this type of pattern also leads to common types of crashed seen in Android apps.
順便提一句,這種類型的的模式也是導致 Android 應用中常見的崩潰的原因之一灾部。
Some block of threading work has kicked off.
一些線程工作已經(jīng)開始了康铭。
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.
之后赌髓,當線程任務完成后从藤,它嘗試著更新一個不再有效的狀態(tài)。
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.
如果任務結(jié)束了春弥,結(jié)果需要是一個對話框通知用戶呛哟,那么那個已經(jīng)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逃呼。
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.
舉個例子,當你想要開始一些任務時抡笼,創(chuàng)建一個將視圖與一些功能匹配的工作記錄苏揣,臥槽,這句話這么翻譯好像不太好吧推姻。
When that block of work is finished, it submits the results back to activity using an intent or a run on UI thread call.
當任務代碼塊執(zhí)行結(jié)束后平匈,它會使用 Intent 或者在主線程中將結(jié)果返回給 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 可以使用新的信息來回調(diào)更新函數(shù)藏古,或者如果 View 對象已經(jīng)不存在了增炭,那么及應該將任務一起刪掉。
然后拧晕,活動可以使用新信息調(diào)用更新功能隙姿,或者如果視圖不在那里,則只需將工作完全刪除厂捞。
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.
沒有崩潰,沒有內(nèi)存泄漏臭墨,只是安靜的寫著代碼赔嚎,而不是寫著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 提供一個工具尽狠,它可以幫助我們更深入地了解線程和內(nèi)存如何協(xié)同工作衔憨,但這個工具我暫時還沒找到哪里打開。袄膏。践图。
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+ 和他們進行交流...
可能有些地方翻譯的不正確,我會盡力提升自己翻譯的準確度斥黑,并用自己的語言表達出來揖盘,捎帶手會提一些視頻的知識點,歡迎討論锌奴。