天天看點

大佬分享開發經驗!年末阿裡百度等大廠技術面試題彙總,移動架構師成長路線開頭1、MVVM架構模式概覽2、ViewModel3、LiveData4、Kotlin協程5、Retrofit

開頭

在大廠,寫得一手好文檔是一個非常吃香的技能。這可不隻是一個錦上添花的東西,而是很多工程師晉升,打造自己話語權的武器。 我這兩年在組内的深刻體會就是,大部分厲害的進階工程師(不包括那些純混日子靠資曆晉升的人),寫文檔的能力一點也不含糊,很能抓住上級和項目的G點。

大佬分享開發經驗!年末阿裡百度等大廠技術面試題彙總,移動架構師成長路線開頭1、MVVM架構模式概覽2、ViewModel3、LiveData4、Kotlin協程5、Retrofit

可能有人會覺得,我技術牛逼就行了,為啥還要提高寫文檔的能力,有這功夫我還不如多看看源碼分析?這是一些初級或者剛入門的工程師的普遍的困惑。這是因為大部分剛剛入行的朋友有一個很深的誤區,就是他們以為做軟體工程是一個和計算機打交道的工作,其實不然。軟體工程不隻是和代碼打交道,更重要的是和人打交道,是一份社會性質很強的工作。在大部分公司裡面,尤其是大廠,牽涉到的人,組,都是非常非常多的。在小廠,人與人之間交流意見和設計可以口口相傳,心領神會,但是一旦人開始多了,就隻能靠文檔了。除非你可以厲害到一個人把所有代碼撸完,不然還是最好老老實實的夯實自己寫文檔的能力。

如果你有寫技術部落格的習慣,那麼恭喜你,相信你已經對如何抓住文檔閱聽人的技巧有所了解了。這對你在大廠生存有很大的幫助。如果沒有也不要傷心,這篇文章就是為你精心設計的。

在這篇文章裡,我會大緻的把一份安卓的項目設計文檔的骨架,和一些我工作中實際遇到的正反例都列出來,友善大家以後在工作中實踐。

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 對象傳送。

大佬分享開發經驗!年末阿裡百度等大廠技術面試題彙總,移動架構師成長路線開頭1、MVVM架構模式概覽2、ViewModel3、LiveData4、Kotlin協程5、Retrofit

2、ViewModel

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

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

大佬分享開發經驗!年末阿裡百度等大廠技術面試題彙總,移動架構師成長路線開頭1、MVVM架構模式概覽2、ViewModel3、LiveData4、Kotlin協程5、Retrofit

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 對象以封裝系統服務,以便在應用中共享它們。
           
大佬分享開發經驗!年末阿裡百度等大廠技術面試題彙總,移動架構師成長路線開頭1、MVVM架構模式概覽2、ViewModel3、LiveData4、Kotlin協程5、Retrofit

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+協程
 */
fun ViewModel.launch(
    block: suspend CoroutineScope.() -> Unit,
    onError: (e: Throwable) -> Unit = {},
    onComplete: () -> Unit = {}
) {
    viewModelScope.launch(CoroutineExceptionHandler { _, e -> onError(e) }) {
        try {
            block.invoke(this)
        } finally {
            onComplete()
        }
    }
}
           

源碼:

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
           

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挂起函數:

interface ApiService {
    @FormUrlEncoded
    @POST("user/login")
    suspend fun loginForm(@Field("username")  username: String,@Field("password")  password: String): BaseResponse<User>
}
           

kotlin泛型:

data class BaseResponse<T>(
    val errorCode: Int=0,
    val errorMsg:String? = null,
    var data: T? = null
)
           

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

最後,如果大夥有什麼好的學習方法或建議歡迎大家在評論中積極留言哈,希望大家能夠共同學習、共同努力、共同進步。

小編在這裡祝小夥伴們在未來的日子裡都可以 升職加薪,當上總經理,出任CEO,迎娶白富美,走上人生巅峰!!

不論遇到什麼困難,都不應該成為我們放棄的理由!

很多人在剛接觸這個行業的時候或者是在遇到瓶頸期的時候,總會遇到一些問題,比如學了一段時間感覺沒有方向感,不知道該從那裡入手去學習,需要一份小編整理出來的學習資料的**關注我首頁或者點選我的GitHub免費領取~**

這裡是關于我自己的Android 學習,面試文檔,視訊收集大整理,有興趣的夥伴們可以看看~

覺沒有方向感,不知道該從那裡入手去學習,需要一份小編整理出來的學習資料的**關注我首頁或者點選我的GitHub免費領取~**

這裡是關于我自己的Android 學習,面試文檔,視訊收集大整理,有興趣的夥伴們可以看看~

如果你看到了這裡,覺得文章寫得不錯就給個贊呗?如果你覺得那裡值得改進的,請給我留言,一定會認真查詢,修正不足,謝謝。