2019年4月5日 星期五

Android 當螢幕旋轉時保持 UI 狀態

markdown Android 的 activity 在螢幕旋轉時會被 destroy 然後再重新 create,這樣設計的用意是讓 activity 可以為不同螢幕方向套用不同的 layout,不過這樣會造成一個問題,就是 activity 重新 create 了,UI 也回復到初始狀態,想像一下如果在 UI 中有一個可以動態增加的 list,使用者只不過螢幕轉向了 list 就被清空,這應該是無法接受的。 在面對螢幕轉向的問題時,要嘛就是在 app 中限制螢幕方向不讓轉向,要嘛就是想辦法在螢幕轉向時保存 UI 資料。 螢幕轉向這種情況在 Android 中有專門的術語叫做 configuration change,螢幕轉向只是 configuration change 的其中一種,還有其他不同狀況的 configuration change 例如系統語言切換,Android 已經有提供兩個 callback function ,讓我們在 configuration change 時保存與回復資料。 * [onSaveInstanceState](https://developer.android.com/reference/android/app/Activity.html?hl=zh-tw#onSaveInstanceState(android.os.Bundle)) * [onRestoreInstanceState](https://developer.android.com/reference/android/app/Activity.html?hl=zh-tw#onRestoreInstanceState(android.os.Bundle)) 這兩個 callback function 是 activity life cycle 的其中一環,在 activity 因螢幕轉向被 destroy 和 recreate 的過程中會呼叫這兩個 callback function,開發者趁此機會把一些該存的資料保存起來。 不過這個方式有些問題,就是被保存的資料是寫入到 disk 中的,而這兩個 callback function 又是在 main thread 中執行的,若是小量單純的資料還好,但若是複雜的資料就不適合了,可能會讓 app 感覺 lag,使用者體驗變差。而且如果每次螢幕轉向資料都要寫入到 disk 一次,想想也覺得挺蠢的。 對這個問題,網上有人提出用一個 fragment 來保存這些資料,並且呼叫 Fragment.setRetainInstance(true) 讓 fragment 可以在螢幕轉向時不要被 recreate。這個方法不錯,而後在 2017 Google IO 大會上,Google 提出了一系列新的組件,其中有一個叫做 ViewModel 的組件,就是用來解決這個問題,其背後的原理,就是在內部用了一個 Fragment 並且設定 RetainInstance 為 true。 下面的圖很清楚的表達了 ViewModel 的 life cycle,除非 activity 被 finish 了,不然不管你怎麼重建,ViewModel 都還是在記憶體中活得好好的。至此,ViewModel 成為了處理螢幕轉向問題的最佳解法。 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0UrVMhkC_dQPGvD_0DiCoQtQwOQ7YWDWzLOG8FVPI8TsQ1zw8q9d3e_brMrGgSPr_Mnc0SimTw8vvVpdhmJPvwHuv3QG-cb4ymkps2fxq6vrugZZYm5USO_emhW8VBRiUpCbQza8yctJq/s1600/1+3Kr2-5HE0TLZ4eqq8UQCkQ.png) 但 ViewModel 的出現並不是要取代之前的 onSaveInstanceState、onRestoreInstanceState callback function,onSaveInstanceState 會在系統因一些資源匱乏的原因殺死你的 app 前呼叫,不只是在螢幕轉向時呼叫而已,將一些重要的資料保存在 disk 還是有必要的。所以還是應該視需求來決定用何種方式來保存你的 UI 資料。 Reference: * [The Real Best Practices to Save/Restore Activity's and Fragment's state. (StatedFragment is now deprecated)](https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en) * [ViewModels : A Simple Example](https://medium.com/androiddevelopers/viewmodels-a-simple-example-ed5ac416317e) * [ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders](https://medium.com/androiddevelopers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090)

沒有留言:

張貼留言