1. 程式人生 > 實用技巧 >Android程式設計師必會!Android黑科技保活實現原理揭祕,Android篇

Android程式設計師必會!Android黑科技保活實現原理揭祕,Android篇

前言

很多公司在招人這件事情上都會面臨一個問題;
“我們的招聘要求又不高,能做專案就行,但為什麼就是招不到人?”

很多公司還面臨一個問題,招聘的時候這人各方面都不錯,但上崗了就是不出活,績效平平。

要解決上面的這兩個問題,需要一個衡量人能力的標準,這個標準不僅適用於招聘,同樣也適用於考核、職等評定等,我叫這種標準為技能樹。

這裡所說的技能樹,不僅包含技術能力,還包括工作能力。我始終認為一個人的工作能力並不同等於他的技術能力,工作能力除技術本身外,還包括這個人的綜合素質(合作交流、工作態度、自我實現慾望等)。很多人技術能力不錯,但工作上僅僅是一個執行者,難當大任。

下面列出的Android應用開發人員的技能樹僅為本人結合自身工作經驗和感受的理解,無論對於一個團隊還是個人,它不一定是標準,但具有一定的參考價值。

1、MVVM架構模式概覽

這是使用MVVM架構模式+Kotlin協程+JetPack(ViewModel+LiveData)+Retrofit的架構,實現WanAndroid登入介面的小DEMO,後續會慢慢完善WanAndroid客戶端

1、ViewModel

為了從介面控制器Activity/Fragment邏輯中分離出檢視View資料所有權,架構元件為介面控制器提供了 ViewModel 輔助程式類,該類負責為介面準備資料。在配置更改期間會自動保留 ViewModel 物件,以便它們儲存的資料立即可供下一個 Activity 或 Fragment 例項使用。

2、LiveData

LiveData 是一種可觀察的資料儲存器類,具有生命週期感知能力,意指它遵循其他應用元件如 Activity、Fragment 或 Service 生命週期,可確保 LiveData 僅更新處於活躍生命週期狀態的應用元件觀察者。LiveData 物件通常儲存在 ViewModel 物件中,並可通過 getter 方法進行訪問。

3、Kotlin協程

協程依附線上程上,可以實現順序編寫非同步程式碼,自動進行執行緒切換。並且ViewModelScope為應用中的每個 ViewModel 定義了 ViewModelScope。如果 ViewModel 已清除,則在此範圍內啟動的協程都會自動取消。

4、Retrofit

將服務介面中的網路請求函式宣告為suspend掛起介面函式,以支援Kotlin執行緒,並將suspend函式結果作為 LiveData 物件傳送。

2、ViewModel

//獲取ViewModel
viewModel=ViewModelProvider(this).get(MainViewModel::class.java)` 

ViewModel 物件存在的時間範圍是獲取 ViewModel 時傳遞給 ViewModelProvider 的 Lifecycle。ViewModel 將一直留在記憶體中,直到限定其存在時間範圍的 Lifecycle 永久消失:對於 Activity,是在 Activity 完成時;而對於 Fragment,是在 Fragment 分離時。

3、LiveData

//對User資料進行觀察
viewModel.user.observe(this,Observer{
//展示登入結果
if(it.errorCode==0){
Toast.makeText(this,it.data?.nickname,Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(this,it.errorMsg,Toast.LENGTH_SHORT).show()
}
})

使用 LiveData 具有以下優勢:確保介面符合資料狀態

LiveData 遵循觀察者模式。當生命週期狀態發生變化時,LiveData 會通知 Observer 物件。您可以整合程式碼以在這些 Observer 物件中更新介面。觀察者可以在每次發生更改時更新介面,而不是在每次應用資料發生更改時更新介面。

不會發生記憶體洩漏

觀察者會繫結到 Lifecycle 物件,並在其關聯的生命週期遭到銷燬後進行自我清理。

不會因 Activity 停止而導致崩潰

如果觀察者的生命週期處於非活躍狀態(如返回棧中的 Activity),則它不會接收任何 LiveData 事件。
不再需要手動處理生命週期
介面元件只是觀察相關資料,不會停止或恢復觀察。LiveData 將自動管理所有這些操作,因為它在觀察時可以感知相關的生命週期狀態變化。

資料始終保持最新狀態

如果生命週期變為非活躍狀態,它會在再次變為活躍狀態時接收最新的資料。例如,曾經在後臺的 Activity 會在返回前臺後立即接收最新的資料。

適當的配置更改

如果由於配置更改(如裝置旋轉)而重新建立了 Activity 或 Fragment,它會立即接收最新的可用資料。

共享資源

可以使用單一例項模式擴充套件 LiveData 物件以封裝系統服務,以便在應用中共享它們。

4、Kotlin協程

4.1、非同步的本質

什麼是非同步?

非同步就是同時進行一個以上彼此目的不同的任務。

但是對於有前後依賴關係的任務,非同步該如何處理呢?

利用非同步中的回撥機制處理。

為什麼需要非同步回撥機制?

因為不同的任務之間存在前後的依賴關係。

非同步回撥機制有什麼缺點?

程式碼結構過分耦合,遇到多重函式回撥的巢狀耦合,也就是回撥地獄,程式碼會難以維護。

解決回撥地獄的方案有什麼?

鏈式呼叫結構。
常見方式就是使用RxJava,它是反應函數語言程式設計在Java中的實現。
但是RxJava中流的建立、轉化與消費都需要使用到各種類和豐富的操作符,加大了RxJava的學習成本。
減少在無封裝情況下使用RxJava,因為你無法保證團隊裡面的每一個成員都能看懂它,並且在修改時都能做出正確選擇。

在序列的執行中,雖然程式碼確實是順序執行的,但其實是在不同的執行緒上順序執行的。那為什麼在序列的執行中程式碼執行順序一致,卻還要使用回撥呢?

因為序列的執行中,執行是阻塞式的,主執行緒的阻塞會導致很嚴重的問題,所以所有的耗時操作不能在主執行緒中執行,所以就需要多執行緒並行來執行。

在並行的執行中,非同步回撥其實就是程式碼的多執行緒順序執行。那能不能既按照順序的方式編寫程式碼,又可以讓程式碼在不同的執行緒順序執行,自動完成執行緒的切換工作呢?

那就是Kotlin協程。
Kotlin 的協程是一種無棧協程的實現,它的控制流轉依靠對協程體本身編譯生成的狀態機的狀態流轉來實現,變數儲存也是通過閉包語法來實現的。

結論:

非同步回撥就是程式碼的多執行緒順序執行,而Kotlin協程可以實現順序編寫非同步程式碼,自動進行執行緒切換。

那麼協程自動進行執行緒切換的原理是什麼?

Yield:讓出CPU,放棄排程控制權,回到上一次Resume的地方
Resume:獲取排程控制權,繼續執行程式,到上一次Yield的地方

例子:

1.GlobalScope.launch發起了一個協程,並在IO執行緒上執行,
2\. 在協程裡,去呼叫介面獲取結果。
3.拿到結果,使用withContext(Dispatchers.Main)切換到主執行緒並更新介面

4.2、協程的型別

是協程範圍,指的是協程內的程式碼執行的時間週期範圍,如果超出了指定的協程範圍,協程會被取消執行。

GlobalScope

指的是與應用程序相同的協程範圍,也就是在程序沒有結束之前協程內的程式碼都可以執行。

JetPack中提供的生命週期感知型協程範圍:

ViewModelScope,為應用中的每個 ViewModel 定義了 ViewModelScope。如果 ViewModel 已清除,則在此範圍內啟動的協程都會自動取消。

LifecycleScope,為每個 Lifecycle 物件定義了 LifecycleScope。在此範圍內啟動的協程會在 Lifecycle 被銷燬時取消。

使用 LiveData 時,可能需要非同步計算值。可以使用 liveData 構建器函式呼叫suspend函式,並將結果作為 LiveData 物件傳送。

相關連結:https://developer.android.google.cn/topic/libraries/architecture/coroutines

4.3、協程的啟動

launch方法:

/**
*重要知識:ViewModel+協程
*/
funViewModel.launch(
block:suspendCoroutineScope.()->Unit,
onError:(e:Throwable)->Unit={},
onComplete:()->Unit={}
){
viewModelScope.launch(CoroutineExceptionHandler{_,e->onError(e)}){
try{
block.invoke(this)
}finally{
onComplete()
}
}
}

原始碼:

publicfunCoroutineScope.launch(
context:CoroutineContext=EmptyCoroutineContext,
start:CoroutineStart=CoroutineStart.DEFAULT,
block:suspendCoroutineScope.()->Unit
):Job{
valnewContext=newCoroutineContext(context)
valcoroutine=if(start.isLazy)
LazyStandaloneCoroutine(newContext,block)else
StandaloneCoroutine(newContext,active=true)
coroutine.start(start,coroutine,block)
returncoroutine
}

4.3.1、launch方法解釋

context

協程上下文,可以指定協程執行的執行緒。預設與指定的CoroutineScope中的coroutineContext保持一致,比如GlobalScope預設執行在一個後臺工作執行緒內。也可以通過顯示指定引數來更改協程執行的執行緒,Dispatchers提供了幾個值可以指定:Dispatchers.Default、Dispatchers.Main、Dispatchers.IO、Dispatchers.Unconfined。

start

協程的啟動模式。預設的CoroutineStart.DEFAULT是指協程立即執行,除此之外還有CoroutineStart.LAZY、CoroutineStart.ATOMIC、CoroutineStart.UNDISPATCHED。

block

協程主體。也就是要在協程內部執行的程式碼,可以通過lamda表示式的方式方便的編寫協程內執行的程式碼。

CoroutineExceptionHandler

指定CoroutineExceptionHandler來處理協程內部的異常。

Job

返回值,對當前建立的協程的引用。可以通過Job的start、cancel、join等方法來控制協程的啟動和取消。

4.4、suspend掛起函式

suspend關鍵字只起到了標誌這個函式是一個耗時操作,必須放在協程中執行的作用,而withContext方法則進行了執行緒的切換工作。

協程中的程式碼自動地切換到其他執行緒之後又自動地切換回了主執行緒!順序編寫保證了邏輯上的直觀性,協程的自動執行緒切換又保證了程式碼的非阻塞性。掛起函式必須在協程或者其他掛起函式中被呼叫,也就是掛起函式必須直接或者間接地在協程中執行。

那為什麼協程中的程式碼沒有在主執行緒中執行呢?而且執行完畢為什麼還會自動地切回主執行緒呢?

協程的掛起可以理解為協程中的程式碼離開協程所線上程的過程,協程的恢復可以理解為協程中的程式碼重新進入協程所線上程的過程。協程就是通過的這個掛起恢復機制進行執行緒的切換。

4.5、async await方法

用async方法包裹了suspend方法來執行併發請求,併發結果都返回之後,切換到主執行緒,接著再用await方法來獲取併發請求結果。

5、Retrofit

HTTP介面suspend掛起函式:

interfaceApiService{
@FormUrlEncoded
@POST("user/login")
suspendfunloginForm(@Field("username")username:String,@Field("password")password:String):BaseResponse<User>
}

kotlin泛型:

dataclassBaseResponse<T>(
valerrorCode:Int=0,
valerrorMsg:String?=null,
vardata:T?=null
)

這是使用MVVM架構模式+Kotlin協程+JetPack(ViewModel+LiveData)+Retrofit的架構,實現WanAndroid登入介面的小DEMO,後續會慢慢完善WanAndroid客戶端

總結

寫到這裡也結束了,在文章最後放上一個小小的福利,以下為小編自己在學習過程中整理出的一個關於Flutter的學習思路及方向,從事網際網路開發,最主要的是要學好技術,而學習技術是一條慢長而艱苦的道路,不能靠一時激情,也不是熬幾天幾夜就能學好的,必須養成平時努力學習的習慣,更加需要準確的學習方向達到有效的學習效果。
由於內容較多就只放上一個大概的大綱,需要更及詳細的學習思維導圖的點選我的GitHub免費獲取。
還有免費的高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)微信小程式、Flutter全方面的Android進階實踐技術資料,並且還有技術大牛一起討論交流解決問題。