日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

【建议收藏】2020年中高级Android大厂面试秘籍,为你保驾护航金三银四,直通大厂(Android高级篇上)...

發(fā)布時(shí)間:2023/12/8 Android 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【建议收藏】2020年中高级Android大厂面试秘籍,为你保驾护航金三银四,直通大厂(Android高级篇上)... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

成為一名優(yōu)秀的Android開發(fā),需要一份完備的知識(shí)體系,在這里,讓我們一起成長(zhǎng)為自己所想的那樣~。

A awesome android expert interview questions and answers(continuous updating ...)

從幾十份頂級(jí)面試倉(cāng)庫(kù)和300多篇高質(zhì)量面經(jīng)中總結(jié)出一份全面成體系化的Android高級(jí)面試題集。

歡迎來(lái)到2020年中高級(jí)Android大廠面試秘籍,為你保駕護(hù)航金三銀四,直通大廠的Android高級(jí)篇(上)。

Android高級(jí)面試題 (???)


一、性能優(yōu)化

1、App穩(wěn)定性優(yōu)化

1、你們做了哪些穩(wěn)定性方面的優(yōu)化?

隨著項(xiàng)目的逐漸成熟,用戶基數(shù)逐漸增多,DAU持續(xù)升高,我們遇到了很多穩(wěn)定性方面的問(wèn)題,對(duì)于我們技術(shù)同學(xué)遇到了很多的挑戰(zhàn),用戶經(jīng)常使用我們的App卡頓或者是功能不可用,因此我們就針對(duì)穩(wěn)定性開啟了專項(xiàng)的優(yōu)化,我們主要優(yōu)化了三項(xiàng):

  • Crash專項(xiàng)優(yōu)化(=>2)
  • 性能穩(wěn)定性優(yōu)化(=>2)
  • 業(yè)務(wù)穩(wěn)定性優(yōu)化(=>3)

通過(guò)這三方面的優(yōu)化我們搭建了移動(dòng)端的高可用平臺(tái)。同時(shí),也做了很多的措施來(lái)讓App真正地實(shí)現(xiàn)了高可用。

2、性能穩(wěn)定性是怎么做的?

  • 全面的性能優(yōu)化:啟動(dòng)速度、內(nèi)存優(yōu)化、繪制優(yōu)化
  • 線下發(fā)現(xiàn)問(wèn)題、優(yōu)化為主
  • 線上監(jiān)控為主
  • Crash專項(xiàng)優(yōu)化

我們針對(duì)啟動(dòng)速度,內(nèi)存、布局加載、卡頓、瘦身、流量、電量等多個(gè)方面做了多維的優(yōu)化。

我們的優(yōu)化主要分為了兩個(gè)層次,即線上和線下,針對(duì)于線下呢,我們側(cè)重于發(fā)現(xiàn)問(wèn)題,直接解決,將問(wèn)題盡可能在上線之前解決為目的。而真正到了線上呢,我們最主要的目的就是為了監(jiān)控,對(duì)于各個(gè)性能緯度的監(jiān)控呢,可以讓我們盡可能早地獲取到異常情況的報(bào)警。

同時(shí)呢,對(duì)于線上最嚴(yán)重的性能問(wèn)題性問(wèn)題:Crash,我們做了專項(xiàng)的優(yōu)化,不僅優(yōu)化了Crash的具體指標(biāo),而且也盡可能地獲取了Crash發(fā)生時(shí)的詳細(xì)信息,結(jié)合后端的聚合、報(bào)警等功能,便于我們快速地定位問(wèn)題。

3、業(yè)務(wù)穩(wěn)定性如何保障?

  • 數(shù)據(jù)采集 + 報(bào)警
  • 需要對(duì)項(xiàng)目的主流程與核心路徑進(jìn)行埋點(diǎn)監(jiān)控,
  • 同時(shí)還需知道每一步發(fā)生了多少異常,這樣,我們就知道了所有業(yè)務(wù)流程的轉(zhuǎn)換率以及相應(yīng)界面的轉(zhuǎn)換率
  • 結(jié)合大盤,如果轉(zhuǎn)換率低于某個(gè)值,進(jìn)行報(bào)警
  • 異常監(jiān)控 + 單點(diǎn)追查
  • 兜底策略

移動(dòng)端業(yè)務(wù)高可用它側(cè)重于用戶功能完整可用,主要是為了解決一些線上一些異常情況導(dǎo)致用戶他雖然沒(méi)有崩潰,也沒(méi)有性能問(wèn)題,但是呢,只是單純的功能不可用的情況,我們需要對(duì)項(xiàng)目的主流程、核心路徑進(jìn)行埋點(diǎn)監(jiān)控,來(lái)計(jì)算每一步它真實(shí)的轉(zhuǎn)換率是多少,同時(shí)呢,還需要知道在每一步到底發(fā)生了多少異常。這樣我們就知道了所有業(yè)務(wù)流程的轉(zhuǎn)換率以及相應(yīng)界面的轉(zhuǎn)換率,有了大盤的數(shù)據(jù)呢,我們就知道了,如果轉(zhuǎn)換率或者是某些監(jiān)控的成功率低于某個(gè)值,那很有可能就是出現(xiàn)了線上異常,結(jié)合了相應(yīng)的報(bào)警功能,我們就不需要等用戶來(lái)反饋了,這個(gè)就是業(yè)務(wù)穩(wěn)定性保障的基礎(chǔ)。

同時(shí)呢,對(duì)于一些特殊情況,比如說(shuō),開發(fā)過(guò)程當(dāng)中或代碼中出現(xiàn)了一些catch代碼塊,捕獲住了異常,讓程序不崩潰,這其實(shí)是不合理的,程序雖然沒(méi)有崩潰,當(dāng)時(shí)程序的功能已經(jīng)變得不可用,所以呢,這些被catch的異常我們也需要上報(bào)上來(lái),這樣我們才能知道用戶到底出現(xiàn)了什么問(wèn)題而導(dǎo)致的異常。此外,線上還有一些單點(diǎn)問(wèn)題,比如說(shuō)用戶點(diǎn)擊登錄一直進(jìn)不去,這種就屬于單點(diǎn)問(wèn)題,其實(shí)我們是無(wú)法找出其和其它問(wèn)題的共性之處的,所以呢,我們就必須要找到它對(duì)應(yīng)的詳細(xì)信息。

最后,如果發(fā)生了異常情況,我們還采取了一系列措施進(jìn)行快速止損。(=>4)

4、如果發(fā)生了異常情況,怎么快速止損?

  • 功能開關(guān)
  • 統(tǒng)跳中心
  • 動(dòng)態(tài)修復(fù):熱修復(fù)、資源包更新
  • 自主修復(fù):安全模式

首先,需要讓App具備一些高級(jí)的能力,我們對(duì)于任何要上線的新功能,要加上一個(gè)功能的開關(guān),通過(guò)配置中心下發(fā)的開關(guān)呢,來(lái)決定是否要顯示新功能的入口。如果有異常情況,可以緊急關(guān)閉新功能的入口,那就可以讓這個(gè)App處于可控的狀態(tài)了。

然后,我們需要給App設(shè)立路由跳轉(zhuǎn),所有的界面跳轉(zhuǎn)都需要通過(guò)路由來(lái)分發(fā),如果我們匹配到需要跳轉(zhuǎn)到有bug的這樣一個(gè)新功能時(shí),那我們就不跳轉(zhuǎn)了,或者是跳轉(zhuǎn)到統(tǒng)一的異常正處理中的界面。如果這兩種方式都不可以,那就可以考慮通過(guò)熱修復(fù)的方式來(lái)動(dòng)態(tài)修復(fù),目前熱修復(fù)的方案其實(shí)已經(jīng)比較成熟了,我們完全可以低成本地在我們的項(xiàng)目中添加熱修復(fù)的能力,當(dāng)然,如果有些功能是由RN或WeeX來(lái)實(shí)現(xiàn)就更好了,那就可以通過(guò)更新資源包的方式來(lái)實(shí)現(xiàn)動(dòng)態(tài)更新。而這些如果都不可以的話呢,那就可以考慮自己去給應(yīng)用加上一個(gè)自主修復(fù)的能力,如果App啟動(dòng)多次的話,那就可以考慮清空所有的緩存數(shù)據(jù),將App重置到安裝的狀態(tài),到了最嚴(yán)重的等級(jí)呢,可以阻塞主線程,此時(shí)一定要等App熱修復(fù)成功之后才允許用戶進(jìn)入。

需要更全面更深入的理解請(qǐng)查看深入探索Android穩(wěn)定性優(yōu)化

2、App啟動(dòng)速度優(yōu)化

1、啟動(dòng)優(yōu)化是怎么做的?

  • 分析現(xiàn)狀、確認(rèn)問(wèn)題
  • 針對(duì)性優(yōu)化(先概括,引導(dǎo)其深入)
  • 長(zhǎng)期保持優(yōu)化效果

在某一個(gè)版本之后呢,我們會(huì)發(fā)現(xiàn)這個(gè)啟動(dòng)速度變得特別慢,同時(shí)用戶給我們的反饋也越來(lái)越多,所以,我們開始考慮對(duì)應(yīng)用的啟動(dòng)速度來(lái)進(jìn)行優(yōu)化。然后,我們就對(duì)啟動(dòng)的代碼進(jìn)行了代碼層面的梳理,我們發(fā)現(xiàn)應(yīng)用的啟動(dòng)流程已經(jīng)非常復(fù)雜,接著,我們通過(guò)一系列的工具來(lái)確認(rèn)是否在主線程中執(zhí)行了太多的耗時(shí)操作。

我們經(jīng)過(guò)了細(xì)查代碼之后,發(fā)現(xiàn)應(yīng)用主線程中的任務(wù)太多,我們就想了一個(gè)方案去針對(duì)性地解決,也就是進(jìn)行異步初始化。(引導(dǎo)=>第2題) 然后,我們還發(fā)現(xiàn)了另外一個(gè)問(wèn)題,也可以進(jìn)行針對(duì)性的優(yōu)化,就是在我們的初始化代碼當(dāng)中有些的優(yōu)先級(jí)并不是那么高,它可以不放在Application的onCreate中執(zhí)行,而完全可以放在之后延遲執(zhí)行的,因?yàn)槲覀儗?duì)這些代碼進(jìn)行了延遲初始化,最后,我們還結(jié)合了idealHandler做了一個(gè)更優(yōu)的延遲初始化的方案,利用它可以在主線程的空閑時(shí)間進(jìn)行初始化,以減少啟動(dòng)耗時(shí)導(dǎo)致的卡頓現(xiàn)象。做完這些之后,我們的啟動(dòng)速度就變得很快了。

最后,我簡(jiǎn)單說(shuō)下我們是怎么長(zhǎng)期來(lái)保持啟動(dòng)優(yōu)化的效果的。首先,我們做了我們的啟動(dòng)器,并且結(jié)合了我們的CI,在線上加上了很多方面的監(jiān)控。(引導(dǎo)=> 第4題)

2、是怎么異步的,異步遇到問(wèn)題沒(méi)有?

  • 體現(xiàn)演進(jìn)過(guò)程
  • 詳細(xì)介紹啟動(dòng)器

我們最初是采用的普通的一個(gè)異步的方案,即new Thread + 設(shè)置線程優(yōu)先級(jí)為后臺(tái)線程的方式在Application的onCreate方法中進(jìn)行異步初始化,后來(lái),我們使用了線程池、IntentService的方式,但是,在我們應(yīng)用的演進(jìn)過(guò)程當(dāng)中,發(fā)現(xiàn)代碼會(huì)變得不夠優(yōu)雅,并且有些場(chǎng)景非常不好處理,比如說(shuō)多個(gè)初始化任務(wù)直接的依賴關(guān)系,比如說(shuō)某一個(gè)初始化任務(wù)需要在某一個(gè)特定的生命周期中初始化完成,這些都是使用線程池、IntentService無(wú)法實(shí)現(xiàn)的。所以說(shuō),我們就開始思考一個(gè)新的解決方案,它能夠完美地解決我們剛剛所遇到的這些問(wèn)題。

這個(gè)方案就是我們目前所使用的啟動(dòng)器,在啟動(dòng)器的概念中,我們將每一個(gè)初始化代碼抽象成了一個(gè)Task,然后,對(duì)它們進(jìn)行了一個(gè)排序,根據(jù)它們之間的依賴關(guān)系排了一個(gè)有向無(wú)環(huán)圖,接著,使用一個(gè)異步隊(duì)列進(jìn)行執(zhí)行,并且這個(gè)異步隊(duì)列它和CPU的核心數(shù)是強(qiáng)烈相關(guān)的,它能夠最大程度地保證我們的主線程和別的線程都能夠執(zhí)行我們的任務(wù),也就是大家?guī)缀醵伎梢酝瑫r(shí)完成。

3、啟動(dòng)優(yōu)化有哪些容易忽略的注意點(diǎn)?

  • cpu time與wall time
  • 注意延遲初始化的優(yōu)化
  • 介紹下黑科技

首先,在CPU Profiler和Systrace中有兩個(gè)很重要的指標(biāo),即cpu time與wall time,我們必須清楚cpu time與wall time之間的區(qū)別,wall time指的是代碼執(zhí)行的時(shí)間,而cpu time指的是代碼消耗CPU的時(shí)間,鎖沖突會(huì)造成兩者時(shí)間差距過(guò)大。我們需要以cpu time來(lái)作為我們優(yōu)化的一個(gè)方向。

其次,我們不僅只追求啟動(dòng)速度上的一個(gè)提升,也需要注意延遲初始化的一個(gè)優(yōu)化,對(duì)于延遲初始化,通常的做法是在界面顯示之后才去進(jìn)行加載,但是如果此時(shí)界面需要進(jìn)行滑動(dòng)等與用戶交互的一系列操作,就會(huì)有很嚴(yán)重的卡頓現(xiàn)象,因此我們使用了idealHandler來(lái)實(shí)現(xiàn)cpu空閑時(shí)間來(lái)執(zhí)行耗時(shí)任務(wù),這極大地提升了用戶的體驗(yàn),避免了因啟動(dòng)耗時(shí)任務(wù)而導(dǎo)致的頁(yè)面卡頓現(xiàn)象。

最后,對(duì)于啟動(dòng)優(yōu)化,還有一些黑科技,首先,就是我們采用了類預(yù)先加載的方式,我們?cè)贛ultiDex.install方法之后起了一個(gè)線程,然后用Class.forName的方式來(lái)預(yù)先觸發(fā)類的加載,然后當(dāng)我們這個(gè)類真正被使用的時(shí)候,就不用再進(jìn)行類加載的過(guò)程了。同時(shí),我們?cè)倏碨ystrace圖的時(shí)候,有一部分手機(jī)其實(shí)并沒(méi)有給我們應(yīng)用去跑滿cpu,比如說(shuō)它有8核,但是卻只給了我們4核等這些情況,然后,有些應(yīng)用對(duì)此做了一些黑科技,它會(huì)將cpu的核心數(shù)以及cpu的頻率在啟動(dòng)的時(shí)候去進(jìn)行一個(gè)暴力的提升。

4、版本迭代導(dǎo)致的啟動(dòng)變慢有好的解決方式嗎?

  • 啟動(dòng)器
  • 結(jié)合CI
  • 監(jiān)控完善

這種問(wèn)題其實(shí)我們之前也遇到過(guò),這的確非常難以解決。但是,我們后面對(duì)此進(jìn)行了反復(fù)的思考與嘗試,終于找到了一個(gè)比較好的解決方式。

首先,我們使用了啟動(dòng)器去管理每一個(gè)初始化任務(wù),并且啟動(dòng)器中每一個(gè)任務(wù)的執(zhí)行都是被其自動(dòng)進(jìn)行分配的,也就是說(shuō)這些自動(dòng)分配的task我們會(huì)盡量保證它會(huì)平均分配在我們每一個(gè)線程當(dāng)中的,這和我們普通的異步是不一樣的,它可以很好地緩解我們應(yīng)用的啟動(dòng)變慢。

其次,我們還結(jié)合了CI,比如說(shuō),我們現(xiàn)在限制了一些類,如Application,如果有人修改了它,我們不會(huì)讓這部分代碼合并到主干分支或者是修改之后會(huì)有一些內(nèi)部的工具如郵件的形式發(fā)送到我,然后,我就會(huì)和他確認(rèn)他加的這些代碼到底是耗時(shí)多少,能否異步初始化,不能異步的話就考慮延遲初始化,如果初始化時(shí)間太長(zhǎng),則可以考慮是否能進(jìn)行懶加載,等用到的時(shí)候再去使用等等。

然后,我們會(huì)將問(wèn)題盡可能地暴露在上線之前。同時(shí),我們真正已經(jīng)到了線上的一個(gè)環(huán)境下時(shí),我們進(jìn)行了監(jiān)控的一個(gè)完善,我們不僅是監(jiān)控了App的整個(gè)的啟動(dòng)時(shí)間,同時(shí)呢,我們也將每一個(gè)生命周期都進(jìn)行了一個(gè)監(jiān)控。比如說(shuō)Application的onCreate與onAttachBaseContext方法的耗時(shí),以及這兩個(gè)生命周期之間間隔的時(shí)間,我們都進(jìn)行了一個(gè)監(jiān)控,如果說(shuō)下一次我們發(fā)現(xiàn)了這個(gè)啟動(dòng)速度變慢了,我們就可以去查找到底是哪一個(gè)環(huán)節(jié)變慢了,我們會(huì)和以前的版本進(jìn)行對(duì)比,對(duì)比完成之后呢,我們就可以來(lái)找這一段新加的代碼。

5、開放問(wèn)題:如果提高啟動(dòng)速度,設(shè)計(jì)一個(gè)延遲加載框架或者sdk的方法和注意的問(wèn)題

需要更全面更深入的理解請(qǐng)查看深入探索Android啟動(dòng)速度優(yōu)化

3、App內(nèi)存優(yōu)化

1、你們內(nèi)存優(yōu)化項(xiàng)目的過(guò)程是怎么做的?

1、分析現(xiàn)狀、確認(rèn)問(wèn)題

我們發(fā)現(xiàn)我們的APP在內(nèi)存方面可能存在很大的問(wèn)題,第一方面的原因是我們的線上的OOM率比較高。第二點(diǎn)呢,我們經(jīng)常會(huì)看到在我們的Android Studio的Profiler工具中內(nèi)存的抖動(dòng)比較頻繁。這是我一個(gè)初步的現(xiàn)狀,然后在我們知道了這個(gè)初步的現(xiàn)狀之后,進(jìn)行了問(wèn)題的確認(rèn),我們經(jīng)過(guò)一系列的調(diào)研以及深入研究,我們最終發(fā)現(xiàn)我們的項(xiàng)目中存在以下幾點(diǎn)大問(wèn)題,比如說(shuō):內(nèi)存抖動(dòng)、內(nèi)存溢出、內(nèi)存泄漏,還有我們的Bitmap使用非常粗獷。

2、針對(duì)性優(yōu)化

比如內(nèi)存抖動(dòng)的解決 -> Memory Profiler工具的使用(呈現(xiàn)了鋸齒張圖形) -> 分析到具體代碼存在的問(wèn)題(頻繁被調(diào)用的方法中出現(xiàn)了日志字符串的拼接),也可以說(shuō)說(shuō)內(nèi)存泄漏或內(nèi)存溢出的解決。

3、效率提升

為了不增加業(yè)務(wù)同學(xué)的工作量,我們使用了一些工具類或ARTHook這樣的大圖檢測(cè)方案,沒(méi)有任何的侵入性,同時(shí),我們將這些技術(shù)教給了大家,然后讓大家一起進(jìn)行工作效率上的提升。

我們對(duì)內(nèi)存優(yōu)化工具M(jìn)emory Profiler、MAT的使用比較熟悉,因此針對(duì)一系列不同問(wèn)題的情況,我們寫了一系列解決方案的文檔,分享給大家。這樣,我們整個(gè)團(tuán)隊(duì)成員的內(nèi)存優(yōu)化意識(shí)就變強(qiáng)了。

2、你做了內(nèi)存優(yōu)化最大的感受是什么?

1、磨刀不誤砍柴工

我們一開始并沒(méi)有直接去分析項(xiàng)目中代碼哪些地方存在內(nèi)存問(wèn)題,而是先去學(xué)習(xí)了Google官方的一些文檔,比如說(shuō)學(xué)習(xí)了Memory Profiler工具的使用、學(xué)習(xí)了MAT工具的使用,在我們將這些工具學(xué)習(xí)熟練之后,當(dāng)在我們的項(xiàng)目中遇到內(nèi)存問(wèn)題時(shí),我們就能夠很快地進(jìn)行排查定位問(wèn)題進(jìn)行解決。

2、技術(shù)優(yōu)化必須結(jié)合業(yè)務(wù)代碼

一開始,我們做了整體APP運(yùn)行階段的一個(gè)內(nèi)存上報(bào),然后,我們?cè)谝恍┲攸c(diǎn)的內(nèi)存消耗模塊進(jìn)行了一些監(jiān)控,但是后面發(fā)現(xiàn)這些監(jiān)控并沒(méi)有緊密地結(jié)合我們的業(yè)務(wù)代碼,比如說(shuō)在梳理完項(xiàng)目之后,發(fā)現(xiàn)我們項(xiàng)目中存在使用多個(gè)圖片庫(kù)的情況,多個(gè)圖片庫(kù)的內(nèi)存緩存肯定是不公用的,所以導(dǎo)致我們整個(gè)項(xiàng)目的內(nèi)存使用量非常高。所以進(jìn)行技術(shù)優(yōu)化時(shí)必須結(jié)合我們的業(yè)務(wù)代碼。

3、系統(tǒng)化完善解決方案

我們?cè)谧鰞?nèi)存優(yōu)化的過(guò)程中,不僅做了Android端的優(yōu)化工作,還將我們Android端一些數(shù)據(jù)的采集上報(bào)到了我們的服務(wù)器,然后傳到我們的后臺(tái),這樣,方便我們的無(wú)論是Bug跟蹤人員或者是Crash跟蹤人員進(jìn)行一系列問(wèn)題的解決。

3、如何檢測(cè)所有不合理的地方?

比如說(shuō)大圖片的檢測(cè),我們最初的一個(gè)方案是通過(guò)繼承ImageView,重寫它的onDraw方法來(lái)實(shí)現(xiàn)。但是,我們?cè)谕茝V它的過(guò)程中,發(fā)現(xiàn)很多開發(fā)人員并不接受,因?yàn)楹芏郔mageView之前已經(jīng)寫過(guò)了,你現(xiàn)在讓他去替換,工作成本是比較高的。所以說(shuō),后來(lái)我們就想,有沒(méi)有一種方案可以免替換,最終我們就找到了ARTHook這樣一個(gè)Hook的方案。

如何避免內(nèi)存抖動(dòng)?(代碼注意事項(xiàng))

內(nèi)存抖動(dòng)是由于短時(shí)間內(nèi)有大量對(duì)象進(jìn)出新生區(qū)導(dǎo)致的,它伴隨著頻繁的GC,gc會(huì)大量占用ui線程和cpu資源,會(huì)導(dǎo)致app整體卡頓。

避免發(fā)生內(nèi)存抖動(dòng)的幾點(diǎn)建議:

  • 盡量避免在循環(huán)體內(nèi)創(chuàng)建對(duì)象,應(yīng)該把對(duì)象創(chuàng)建移到循環(huán)體外。
  • 注意自定義View的onDraw()方法會(huì)被頻繁調(diào)用,所以在這里面不應(yīng)該頻繁的創(chuàng)建對(duì)象。
  • 當(dāng)需要大量使用Bitmap的時(shí)候,試著把它們緩存在數(shù)組或容器中實(shí)現(xiàn)復(fù)用。
  • 對(duì)于能夠復(fù)用的對(duì)象,同理可以使用對(duì)象池將它們緩存起來(lái)。

需要更全面更深入的理解請(qǐng)查看Android性能優(yōu)化之內(nèi)存優(yōu)化、深入探索Android內(nèi)存優(yōu)化

4、App繪制優(yōu)化

1、你在做布局優(yōu)化的過(guò)程中用到了哪些工具?

我在做布局優(yōu)化的過(guò)程中,用到了很多的工具,但是每一個(gè)工具都有它不同的使用場(chǎng)景,不同的場(chǎng)景應(yīng)該使用不同的工具。下面我從線上和線下兩個(gè)角度來(lái)進(jìn)行分析。

比如說(shuō),我要統(tǒng)計(jì)線上的FPS,我使用的就是Choreographer這個(gè)類,它具有以下特性:

  • 1、能夠獲取整體的幀率。
  • 2、能夠帶到線上使用。
  • 3、它獲取的幀率幾乎是實(shí)時(shí)的,能夠滿足我們的需求。

同時(shí),在線下,如果要去優(yōu)化布局加載帶來(lái)的時(shí)間消耗,那就需要檢測(cè)每一個(gè)布局的耗時(shí),對(duì)此我使用的是AOP的方式,它沒(méi)有侵入性,同時(shí)也不需要?jiǎng)e的開發(fā)同學(xué)進(jìn)行接入,就可以方便地獲取每一個(gè)布局加載的耗時(shí)。如果還要更細(xì)粒度地去檢測(cè)每一個(gè)控件的加載耗時(shí),那么就需要使用LayoutInflaterCompat.setFactory2這個(gè)方法去進(jìn)行Hook。

此外,我還使用了LayoutInspector和Systrace這兩個(gè)工具,Systrace可以很方便地看到每幀的具體耗時(shí)以及這一幀在布局當(dāng)中它真正做了什么。而LayoutInspector可以很方便地看到每一個(gè)界面的布局層級(jí),幫助我們對(duì)層級(jí)進(jìn)行優(yōu)化。

2、布局為什么會(huì)導(dǎo)致卡頓,你又是如何優(yōu)化的?

分析完布局的加載流程之后,我們發(fā)現(xiàn)有如下四點(diǎn)可能會(huì)導(dǎo)致布局卡頓:

  • 1、首先,系統(tǒng)會(huì)將我們的Xml文件通過(guò)IO的方式映射的方式加載到我們的內(nèi)存當(dāng)中,而IO的過(guò)程可能會(huì)導(dǎo)致卡頓。
  • 2、其次,布局加載的過(guò)程是一個(gè)反射的過(guò)程,而反射的過(guò)程也會(huì)可能會(huì)導(dǎo)致卡頓。
  • 3、同時(shí),這個(gè)布局的層級(jí)如果比較深,那么進(jìn)行布局遍歷的過(guò)程就會(huì)比較耗時(shí)。
  • 4、最后,不合理的嵌套R(shí)elativeLayout布局也會(huì)導(dǎo)致重繪的次數(shù)過(guò)多。

對(duì)此,我們的優(yōu)化方式有如下幾種:

  • 1、針對(duì)布局加載Xml文件的優(yōu)化,我們使用了異步Inflate的方式,即AsyncLayoutInflater。它的核心原理是在子線程中對(duì)我們的Layout進(jìn)行加載,而加載完成之后會(huì)將View通過(guò)Handler發(fā)送到主線程來(lái)使用。所以不會(huì)阻塞我們的主線程,加載的時(shí)間全部是在異步線程中進(jìn)行消耗的。而這僅僅是一個(gè)從側(cè)面緩解的思路。
  • 2、后面,我們發(fā)現(xiàn)了一個(gè)從根源解決上述痛點(diǎn)的方式,即使用X2C框架。它的一個(gè)核心原理就是在開發(fā)過(guò)程我們還是使用的XML進(jìn)行編寫布局,但是在編譯的時(shí)候它會(huì)使用APT的方式將XML布局轉(zhuǎn)換為Java的方式進(jìn)行布局,通過(guò)這樣的方式去寫布局,它有以下優(yōu)點(diǎn):1、它省去了使用IO的方式去加載XML布局的耗時(shí)過(guò)程。2、它是采用Java代碼直接new的方式去創(chuàng)建控件對(duì)象,所以它也沒(méi)有反射帶來(lái)的性能損耗。這樣就從根本上解決了布局加載過(guò)程中帶來(lái)的問(wèn)題。
  • 3、然后,我們可以使用ConstraintLayout去減少我們界面布局的嵌套層級(jí),如果原始布局層級(jí)越深,它能減少的層級(jí)就越多。而使用它也能避免嵌套R(shí)elativeLayout布局導(dǎo)致的重繪次數(shù)過(guò)多。
  • 4、最后,我們可以使用AspectJ框架(即AOP)和LayoutInflaterCompat.setFactory2的方式分別去建立線下全局的布局加載速度和控件加載速度的監(jiān)控體系。

3、做完布局優(yōu)化有哪些成果產(chǎn)出?

  • 1、首先,我們建立了一個(gè)體系化的監(jiān)控手段,這里的體系還指的是線上加線下的一個(gè)綜合方案,針對(duì)線下,我們使用AOP或者ARTHook,可以很方便地獲取到每一個(gè)布局的加載耗時(shí)以及每一個(gè)控件的加載耗時(shí)。針對(duì)線上,我們通過(guò)Choreographer.getInstance().postFrameCallback的方式收集到了FPS,這樣我們可以知道用戶在哪些界面出現(xiàn)了丟幀的情況。
  • 2、然后,對(duì)于布局監(jiān)控方面,我們?cè)O(shè)立了FPS、布局加載時(shí)間、布局層級(jí)等一系列指標(biāo)。
  • 3、最后,在每一個(gè)版本上線之前,我們都會(huì)對(duì)我們的核心路徑進(jìn)行一次Review,確保我們的FPS、布局加載時(shí)間、布局層級(jí)等達(dá)到一個(gè)合理的狀態(tài)。

4、你是怎么做卡頓優(yōu)化的?

從項(xiàng)目的初期到壯大期,最后再到成熟期,每一個(gè)階段都針對(duì)卡頓優(yōu)化做了不同的處理。各個(gè)階段所做的事情如下所示:

  • 1、系統(tǒng)工具定位、解決
  • 2、自動(dòng)化卡頓方案及優(yōu)化
  • 3、線上監(jiān)控及線下監(jiān)測(cè)工具的建設(shè)

我做卡頓優(yōu)化也是經(jīng)歷了一些階段,最初我們的項(xiàng)目當(dāng)中的一些模塊出現(xiàn)了卡頓之后,我是通過(guò)系統(tǒng)工具進(jìn)行了定位,我使用了Systrace,然后看了卡頓周期內(nèi)的CPU狀況,同時(shí)結(jié)合代碼,對(duì)這個(gè)模塊進(jìn)行了重構(gòu),將部分代碼進(jìn)行了異步和延遲,在項(xiàng)目初期就是這樣解決了問(wèn)題。但是呢,隨著我們項(xiàng)目的擴(kuò)大,線下卡頓的問(wèn)題也越來(lái)越多,同時(shí),在線上,也有卡頓的反饋,但是線上的反饋卡頓,我們?cè)诰€下難以復(fù)現(xiàn),于是我們開始尋找自動(dòng)化的卡頓監(jiān)測(cè)方案,其思路是來(lái)自于Android的消息處理機(jī)制,主線程執(zhí)行任何代碼都會(huì)回到Looper.loop方法當(dāng)中,而這個(gè)方法中有一個(gè)mLogging對(duì)象,它會(huì)在每個(gè)message的執(zhí)行前后都會(huì)被調(diào)用,我們就是利用這個(gè)前后處理的時(shí)機(jī)來(lái)做到的自動(dòng)化監(jiān)測(cè)方案的。同時(shí),在這個(gè)階段,我們也完善了線上ANR的上報(bào),我們采取的方式就是監(jiān)控ANR的信息,同時(shí)結(jié)合了ANR-WatchDog,作為高版本沒(méi)有文件權(quán)限的一個(gè)補(bǔ)充方案。在做完這個(gè)卡頓檢測(cè)方案之后呢,我們還做了線上監(jiān)控及線下檢測(cè)工具的建設(shè),最終實(shí)現(xiàn)了一整套完善,多維度的解決方案。

5、你是怎么樣自動(dòng)化的獲取卡頓信息?

我們的思路是來(lái)自于Android的消息處理機(jī)制,主線程執(zhí)行任何代碼它都會(huì)走到Looper.loop方法當(dāng)中,而這個(gè)函數(shù)當(dāng)中有一個(gè)mLogging對(duì)象,它會(huì)在每個(gè)message處理前后都會(huì)被調(diào)用,而主線程發(fā)生了卡頓,那就一定會(huì)在dispatchMessage方法中執(zhí)行了耗時(shí)的代碼,那我們?cè)谶@個(gè)message執(zhí)行之前呢,我們可以在子線程當(dāng)中去postDelayed一個(gè)任務(wù),這個(gè)Delayed的時(shí)間就是我們?cè)O(shè)定的閾值,如果主線程的messaege在這個(gè)閾值之內(nèi)完成了,那就取消掉這個(gè)子線程當(dāng)中的任務(wù),如果主線程的message在閾值之內(nèi)沒(méi)有被完成,那子線程當(dāng)中的任務(wù)就會(huì)被執(zhí)行,它會(huì)獲取到當(dāng)前主線程執(zhí)行的一個(gè)堆棧,那我們就可以知道哪里發(fā)生了卡頓。

經(jīng)過(guò)實(shí)踐,我們發(fā)現(xiàn)這種方案獲取的堆棧信息它不一定是準(zhǔn)確的,因?yàn)楂@取到的堆棧信息它很可能是主線程最終執(zhí)行的一個(gè)位置,而真正耗時(shí)的地方其實(shí)已經(jīng)執(zhí)行完成了,于是呢,我們就對(duì)這個(gè)方案做了一些優(yōu)化,我們采取了高頻采集的方案,也就是在一個(gè)周期內(nèi)我們會(huì)多次采集主線程的堆棧信息,如果發(fā)生了卡頓,那我們就將這些卡頓信息壓縮之后上報(bào)給APM后臺(tái),然后找出重復(fù)的堆棧信息,這些重復(fù)發(fā)生的堆棧大概率就是卡頓發(fā)生的一個(gè)位置,這樣就提高了獲取卡頓信息的一個(gè)準(zhǔn)確性。

6、卡頓的一整套解決方案是怎么做的?

首先,針對(duì)卡頓,我們采用了線上、線下工具相結(jié)合的方式,線下工具我們冊(cè)中醫(yī)藥盡可能早地去暴露問(wèn)題,而針對(duì)于線上工具呢,我們側(cè)重于監(jiān)控的全面性、自動(dòng)化以及異常感知的靈敏度。

同時(shí)呢,卡頓問(wèn)題還有很多的難題。比如說(shuō)有的代碼呢,它不到你卡頓的一個(gè)閾值,但是執(zhí)行過(guò)多,或者它錯(cuò)誤地執(zhí)行了很多次,它也會(huì)導(dǎo)致用戶感官上的一個(gè)卡頓,所以我們?cè)诰€下通過(guò)AOP的方式對(duì)常見的耗時(shí)代碼進(jìn)行了Hook,然后對(duì)一段時(shí)間內(nèi)獲取到的數(shù)據(jù)進(jìn)行分析,我們就可以知道這些耗時(shí)的代碼發(fā)生的時(shí)機(jī)和次數(shù)以及耗時(shí)情況。然后,看它是不是滿足我們的一個(gè)預(yù)期,不滿足預(yù)期的話,我們就可以直接到線下進(jìn)行修改。同時(shí),卡頓監(jiān)控它還有很多容易被忽略的一個(gè)盲區(qū),比如說(shuō)生命周期的一個(gè)間隔,那對(duì)于這種特定的問(wèn)題呢,我們就采用了編譯時(shí)注解的方式修改了項(xiàng)目當(dāng)中所有Handler的父類,對(duì)于其中的兩個(gè)方法進(jìn)行了監(jiān)控,我們就可以知道主線程message的執(zhí)行時(shí)間以及它們的調(diào)用堆棧。

對(duì)于線上卡頓,我們除了計(jì)算App的卡頓率、ANR率等常規(guī)指標(biāo)之外呢,我們還計(jì)算了頁(yè)面的秒開率、生命周期的執(zhí)行時(shí)間等等。而且,在卡頓發(fā)生的時(shí)刻,我們也盡可能多地保存下來(lái)了當(dāng)前的一個(gè)場(chǎng)景信息,這為我們之后解決或者復(fù)現(xiàn)這個(gè)卡頓留下了依據(jù)。

7、TextView setText耗時(shí)的原因,對(duì)TextView繪制層源碼的理解?

8、開放問(wèn)題:優(yōu)化一個(gè)列表頁(yè)面的打開速度和流暢性。

需要更全面更深入的理解請(qǐng)查看Android性能優(yōu)化之繪制優(yōu)化、深入探索Android布局優(yōu)化(上)、深入探索Android布局優(yōu)化(下)、深入探索Android卡頓優(yōu)化(上)、深入探索Android卡頓優(yōu)化(下)

5、App瘦身

6、網(wǎng)絡(luò)優(yōu)化

1、移動(dòng)端獲取網(wǎng)絡(luò)數(shù)據(jù)優(yōu)化的幾個(gè)點(diǎn)

  • 1、連接復(fù)用:節(jié)省連接建立時(shí)間,如開啟 keep-alive。于Android來(lái)說(shuō)默認(rèn)情況下HttpURLConnection和HttpClient都開啟了keep-alive。只是2.2之前HttpURLConnection存在影響連接池的Bug。

  • 2、請(qǐng)求合并:即將多個(gè)請(qǐng)求合并為一個(gè)進(jìn)行請(qǐng)求,比較常見的就是網(wǎng)頁(yè)中的CSS Image Sprites。如果某個(gè)頁(yè)面內(nèi)請(qǐng)求過(guò)多,也可以考慮做一定的請(qǐng)求合并。

  • 3、減少請(qǐng)求數(shù)據(jù)的大小:對(duì)于post請(qǐng)求,body可以做gzip壓縮的,header也可以做數(shù)據(jù)壓縮(不過(guò)只支持http 2.0)。 返回?cái)?shù)據(jù)的body也可以做gzip壓縮,body數(shù)據(jù)體積可以縮小到原來(lái)的30%左右(也可以考慮壓縮返回的json數(shù)據(jù)的key數(shù)據(jù)的體積,尤其是針對(duì)返回?cái)?shù)據(jù)格式變化不大的情況,支付寶聊天返回的數(shù)據(jù)用到了)。

  • 4、根據(jù)用戶的當(dāng)前的網(wǎng)絡(luò)質(zhì)量來(lái)判斷下載什么質(zhì)量的圖片(電商用的比較多)。

  • 5、使用HttpDNS優(yōu)化DNS:DNS存在解析慢和DNS劫持等問(wèn)題,DNS 不僅支持 UDP,它還支持 TCP,但是大部分標(biāo)準(zhǔn)的 DNS 都是基于 UDP 與 DNS 服務(wù)器的 53 端口進(jìn)行交互。HTTPDNS 則不同,顧名思義它是利用 HTTP 協(xié)議與 DNS 服務(wù)器的 80 端口進(jìn)行交互。不走傳統(tǒng)的 DNS 解析,從而繞過(guò)運(yùn)營(yíng)商的 LocalDNS 服務(wù)器,有效的防止了域名劫持,提高域名解析的效率。

參考文章

2、客戶端網(wǎng)絡(luò)安全實(shí)現(xiàn)

3、設(shè)計(jì)一個(gè)網(wǎng)絡(luò)優(yōu)化方案,針對(duì)移動(dòng)端弱網(wǎng)環(huán)境。

7、App電量?jī)?yōu)化

8、安卓的安全優(yōu)化

1、提高app安全性的方法?

2、安卓的app加固如何做?

3、安卓的混淆原理是什么?

4、談?wù)勀銓?duì)安卓簽名的理解。

9、為什么WebView加載會(huì)慢呢?

這是因?yàn)樵诳蛻舳酥?#xff0c;加載H5頁(yè)面之前,需要先初始化WebView,在WebView完全初始化完成之前,后續(xù)的界面加載過(guò)程都是被阻塞的。

優(yōu)化手段圍繞著以下兩個(gè)點(diǎn)進(jìn)行:

  • 預(yù)加載WebView。
  • 加載WebView的同時(shí),請(qǐng)求H5頁(yè)面數(shù)據(jù)。

因此常見的方法是:

  • 全局WebView。
  • 客戶端代理頁(yè)面請(qǐng)求。WebView初始化完成后向客戶端請(qǐng)求數(shù)據(jù)。
  • asset存放離線包。

除此之外還有一些其他的優(yōu)化手段:

  • 腳本執(zhí)行慢,可以讓腳本最后運(yùn)行,不阻塞頁(yè)面解析。
  • DNS鏈接慢,可以讓客戶端復(fù)用使用的域名與鏈接。
  • React框架代碼執(zhí)行慢,可以將這部分代碼拆分出來(lái),提前進(jìn)行解析。

10、如何優(yōu)化自定義View

為了加速你的view,對(duì)于頻繁調(diào)用的方法,需要盡量減少不必要的代碼。先從onDraw開始,需要特別注意不應(yīng)該在這里做內(nèi)存分配的事情,因?yàn)樗鼤?huì)導(dǎo)致GC,從而導(dǎo)致卡頓。在初始化或者動(dòng)畫間隙期間做分配內(nèi)存的動(dòng)作。不要在動(dòng)畫正在執(zhí)行的時(shí)候做內(nèi)存分配的事情。

你還需要盡可能的減少onDraw被調(diào)用的次數(shù),大多數(shù)時(shí)候?qū)е耾nDraw都是因?yàn)檎{(diào)用了invalidate().因此請(qǐng)盡量減少調(diào)用invaildate()的次數(shù)。如果可能的話,盡量調(diào)用含有4個(gè)參數(shù)的invalidate()方法而不是沒(méi)有參數(shù)的invalidate()。沒(méi)有參數(shù)的invalidate會(huì)強(qiáng)制重繪整個(gè)view。

另外一個(gè)非常耗時(shí)的操作是請(qǐng)求layout。任何時(shí)候執(zhí)行requestLayout(),會(huì)使得Android UI系統(tǒng)去遍歷整個(gè)View的層級(jí)來(lái)計(jì)算出每一個(gè)view的大小。如果找到有沖突的值,它會(huì)需要重新計(jì)算好幾次。另外需要盡量保持View的層級(jí)是扁平化的,這樣對(duì)提高效率很有幫助。

如果你有一個(gè)復(fù)雜的UI,你應(yīng)該考慮寫一個(gè)自定義的ViewGroup來(lái)執(zhí)行他的layout操作。與內(nèi)置的view不同,自定義的view可以使得程序僅僅測(cè)量這一部分,這避免了遍歷整個(gè)view的層級(jí)結(jié)構(gòu)來(lái)計(jì)算大小。

11、FC(Force Close)什么時(shí)候會(huì)出現(xiàn)?

Error、OOM,StackOverFlowError、Runtime,比如說(shuō)空指針異常

解決的辦法:

  • 注意內(nèi)存的使用和管理
  • 使用Thread.UncaughtExceptionHandler接口

12、Java多線程引發(fā)的性能問(wèn)題,怎么解決?

13、TraceView的實(shí)現(xiàn)原理,分析數(shù)據(jù)誤差來(lái)源。

14、是否使用過(guò)SysTrace,原理的了解?

15、mmap + native 日志優(yōu)化?

傳統(tǒng)日志打印有兩個(gè)性能問(wèn)題,一個(gè)是反復(fù)操作文件描述符表,一個(gè)是反復(fù)進(jìn)入內(nèi)核態(tài)。所以需要使用mmap的方式去直接讀寫內(nèi)存。

二、Android Framework相關(guān)

1、Android系統(tǒng)架構(gòu)

Android 是一種基于 Linux 的開放源代碼軟件棧,為廣泛的設(shè)備和機(jī)型而創(chuàng)建。下圖所示為 Android 平臺(tái)的五大組件:

1.應(yīng)用程序

Android 隨附一套用于電子郵件、短信、日歷、互聯(lián)網(wǎng)瀏覽和聯(lián)系人等的核心應(yīng)用。平臺(tái)隨附的應(yīng)用與用戶可以選擇安裝的應(yīng)用一樣,沒(méi)有特殊狀態(tài)。因此第三方應(yīng)用可成為用戶的默認(rèn)網(wǎng)絡(luò)瀏覽器、短信 Messenger 甚至默認(rèn)鍵盤(有一些例外,例如系統(tǒng)的“設(shè)置”應(yīng)用)。

系統(tǒng)應(yīng)用可用作用戶的應(yīng)用,以及提供開發(fā)者可從其自己的應(yīng)用訪問(wèn)的主要功能。例如,如果您的應(yīng)用要發(fā)短信,您無(wú)需自己構(gòu)建該功能,可以改為調(diào)用已安裝的短信應(yīng)用向您指定的接收者發(fā)送消息。

2、Java API 框架

您可通過(guò)以 Java 語(yǔ)言編寫的 API 使用 Android OS 的整個(gè)功能集。這些 API 形成創(chuàng)建 Android 應(yīng)用所需的構(gòu)建塊,它們可簡(jiǎn)化核心模塊化系統(tǒng)組件和服務(wù)的重復(fù)使用,包括以下組件和服務(wù):

  • 豐富、可擴(kuò)展的視圖系統(tǒng),可用以構(gòu)建應(yīng)用的 UI,包括列表、網(wǎng)格、文本框、按鈕甚至可嵌入的網(wǎng)絡(luò)瀏覽器
  • 資源管理器,用于訪問(wèn)非代碼資源,例如本地化的字符串、圖形和布局文件
  • 通知管理器,可讓所有應(yīng)用在狀態(tài)欄中顯示自定義提醒
  • Activity 管理器,用于管理應(yīng)用的生命周期,提供常見的導(dǎo)航返回棧
  • 內(nèi)容提供程序,可讓應(yīng)用訪問(wèn)其他應(yīng)用(例如“聯(lián)系人”應(yīng)用)中的數(shù)據(jù)或者共享其自己的數(shù)據(jù)

開發(fā)者可以完全訪問(wèn) Android 系統(tǒng)應(yīng)用使用的框架 API。

3、系統(tǒng)運(yùn)行庫(kù)

1)原生 C/C++ 庫(kù)

許多核心 Android 系統(tǒng)組件和服務(wù)(例如 ART 和 HAL)構(gòu)建自原生代碼,需要以 C 和 C++ 編寫的原生庫(kù)。Android 平臺(tái)提供 Java 框架 API 以向應(yīng)用顯示其中部分原生庫(kù)的功能。例如,您可以通過(guò) Android 框架的 Java OpenGL API 訪問(wèn) OpenGL ES,以支持在應(yīng)用中繪制和操作 2D 和 3D 圖形。如果開發(fā)的是需要 C 或 C++ 代碼的應(yīng)用,可以使用 Android NDK 直接從原生代碼訪問(wèn)某些原生平臺(tái)庫(kù)。

2)Android Runtime

對(duì)于運(yùn)行 Android 5.0(API 級(jí)別 21)或更高版本的設(shè)備,每個(gè)應(yīng)用都在其自己的進(jìn)程中運(yùn)行,并且有其自己的 Android Runtime (ART) 實(shí)例。ART 編寫為通過(guò)執(zhí)行 DEX 文件在低內(nèi)存設(shè)備上運(yùn)行多個(gè)虛擬機(jī),DEX 文件是一種專為 Android 設(shè)計(jì)的字節(jié)碼格式,經(jīng)過(guò)優(yōu)化,使用的內(nèi)存很少。編譯工具鏈(例如 Jack)將 Java 源代碼編譯為 DEX 字節(jié)碼,使其可在 Android 平臺(tái)上運(yùn)行。

ART 的部分主要功能包括:

  • 預(yù)先 (AOT) 和即時(shí) (JIT) 編譯
  • 優(yōu)化的垃圾回收 (GC)
  • 更好的調(diào)試支持,包括專用采樣分析器、詳細(xì)的診斷異常和崩潰報(bào)告,并且能夠設(shè)置監(jiān)視點(diǎn)以監(jiān)控特定字段

在 Android 版本 5.0(API 級(jí)別 21)之前,Dalvik 是 Android Runtime。如果您的應(yīng)用在 ART 上運(yùn)行效果很好,那么它應(yīng)該也可在 Dalvik 上運(yùn)行,但反過(guò)來(lái)不一定。

Android 還包含一套核心運(yùn)行時(shí)庫(kù),可提供 Java API 框架使用的 Java 編程語(yǔ)言大部分功能,包括一些 Java 8 語(yǔ)言功能。

4、硬件抽象層 (HAL)

硬件抽象層 (HAL) 提供標(biāo)準(zhǔn)界面,向更高級(jí)別的 Java API 框架顯示設(shè)備硬件功能。HAL 包含多個(gè)庫(kù)模塊,其中每個(gè)模塊都為特定類型的硬件組件實(shí)現(xiàn)一個(gè)界面,例如相機(jī)或藍(lán)牙模塊。當(dāng)框架 API 要求訪問(wèn)設(shè)備硬件時(shí),Android 系統(tǒng)將為該硬件組件加載庫(kù)模塊。

5、Linux 內(nèi)核

Android 平臺(tái)的基礎(chǔ)是 Linux 內(nèi)核。例如,Android Runtime (ART) 依靠 Linux 內(nèi)核來(lái)執(zhí)行底層功能,例如線程和低層內(nèi)存管理。使用 Linux 內(nèi)核可讓 Android 利用主要安全功能,并且允許設(shè)備制造商為著名的內(nèi)核開發(fā)硬件驅(qū)動(dòng)程序。

對(duì)于Android應(yīng)用開發(fā)來(lái)說(shuō),最好能手繪下面的系統(tǒng)架構(gòu)圖:

2、View的事件分發(fā)機(jī)制?滑動(dòng)沖突怎么解決?

了解Activity的構(gòu)成

一個(gè)Activity包含了一個(gè)Window對(duì)象,這個(gè)對(duì)象是由PhoneWindow來(lái)實(shí)現(xiàn)的。PhoneWindow將DecorView作為整個(gè)應(yīng)用窗口的根View,而這個(gè)DecorView又將屏幕劃分為兩個(gè)區(qū)域:一個(gè)是TitleView,另一個(gè)是ContentView,而我們平時(shí)所寫的就是展示在ContentView中的。

觸摸事件的類型

觸摸事件對(duì)應(yīng)的是MotionEvent類,事件的類型主要有如下三種:

  • ACTION_DOWN
  • ACTION_MOVE(移動(dòng)的距離超過(guò)一定的閾值會(huì)被判定為ACTION_MOVE操作)
  • ACTION_UP

View事件分發(fā)本質(zhì)就是對(duì)MotionEvent事件分發(fā)的過(guò)程。即當(dāng)一個(gè)MotionEvent發(fā)生后,系統(tǒng)將這個(gè)點(diǎn)擊事件傳遞到一個(gè)具體的View上。

事件分發(fā)流程

事件分發(fā)過(guò)程由三個(gè)方法共同完成:

dispatchTouchEvent:方法返回值為true表示事件被當(dāng)前視圖消費(fèi)掉;返回為super.dispatchTouchEvent表示繼續(xù)分發(fā)該事件,返回為false表示交給父類的onTouchEvent處理。

onInterceptTouchEvent:方法返回值為true表示攔截這個(gè)事件并交由自身的onTouchEvent方法進(jìn)行消費(fèi);返回false表示不攔截,需要繼續(xù)傳遞給子視圖。如果return super.onInterceptTouchEvent(ev), 事件攔截分兩種情況: ?

  • 1.如果該View存在子View且點(diǎn)擊到了該子View, 則不攔截, 繼續(xù)分發(fā) 給子View 處理, 此時(shí)相當(dāng)于return false。
  • 2.如果該View沒(méi)有子View或者有子View但是沒(méi)有點(diǎn)擊中子View(此時(shí)ViewGroup 相當(dāng)于普通View), 則交由該View的onTouchEvent響應(yīng),此時(shí)相當(dāng)于return true。

注意:一般的LinearLayout、 RelativeLayout、FrameLayout等ViewGroup默認(rèn)不攔截, 而 ScrollView、ListView等ViewGroup則可能攔截,得看具體情況。

onTouchEvent:方法返回值為true表示當(dāng)前視圖可以處理對(duì)應(yīng)的事件;返回值為false表示當(dāng)前視圖不處理這個(gè)事件,它會(huì)被傳遞給父視圖的onTouchEvent方法進(jìn)行處理。如果return super.onTouchEvent(ev),事件處理分為兩種情況:

  • 1.如果該View是clickable或者longclickable的,則會(huì)返回true, 表示消費(fèi) 了該事件, 與返回true一樣;
  • 2.如果該View不是clickable或者longclickable的,則會(huì)返回false, 表示不 消費(fèi)該事件,將會(huì)向上傳遞,與返回false一樣。

注意:在Android系統(tǒng)中,擁有事件傳遞處理能力的類有以下三種:

  • Activity:擁有分發(fā)和消費(fèi)兩個(gè)方法。
  • ViewGroup:擁有分發(fā)、攔截和消費(fèi)三個(gè)方法。
  • View:擁有分發(fā)、消費(fèi)兩個(gè)方法。

三個(gè)方法的關(guān)系用偽代碼表示如下:

public boolean dispatchTouchEvent(MotionEvent ev) {boolean consume = false;if (onInterceptTouchEvent(ev)) {consume = onTouchEvent(ev);} else {coonsume = child.dispatchTouchEvent(ev);}return consume; } 復(fù)制代碼

通過(guò)上面的偽代碼,我們可以大致了解點(diǎn)擊事件的傳遞規(guī)則:對(duì)應(yīng)一個(gè)根ViewGroup來(lái)說(shuō),點(diǎn)擊事件產(chǎn)生后,首先會(huì)傳遞給它,這是它的dispatchTouchEvent就會(huì)被調(diào)用,如果這個(gè)ViewGroup的onInterceptTouchEvent方法返回true就表示它要攔截當(dāng)前事件,接著事件就會(huì)交給這個(gè)ViewGroup處理,這時(shí)如果它的mOnTouchListener被設(shè)置,則onTouch會(huì)被調(diào)用,否則onTouchEvent會(huì)被調(diào)用。在onTouchEvent中,如果設(shè)置了mOnCLickListener,則onClick會(huì)被調(diào)用。只要View的CLICKABLE和LONG_CLICKABLE有一個(gè)為true,onTouchEvent()就會(huì)返回true消耗這個(gè)事件。如果這個(gè)ViewGroup的onInterceptTouchEvent方法返回false就表示它不攔截當(dāng)前事件,這時(shí)當(dāng)前事件就會(huì)繼續(xù)傳遞給它的子元素,接著子元素的dispatchTouchEvent方法就會(huì)被調(diào)用,如此反復(fù)直到事件被最終處理。

一些重要的結(jié)論:

1、事件傳遞優(yōu)先級(jí):onTouchListener.onTouch > onTouchEvent > onClickListener.onClick。

2、正常情況下,一個(gè)時(shí)間序列只能被一個(gè)View攔截且消耗。因?yàn)橐坏┮粋€(gè)元素?cái)r截了此事件,那么同一個(gè)事件序列內(nèi)的所有事件都會(huì)直接交給它處理(即不會(huì)再調(diào)用這個(gè)View的攔截方法去詢問(wèn)它是否要攔截了,而是把剩余的ACTION_MOVE、ACTION_DOWN等事件直接交給它來(lái)處理)。特例:通過(guò)將重寫View的onTouchEvent返回false可強(qiáng)行將事件轉(zhuǎn)交給其他View處理。

3、如果View不消耗除ACTION_DOWN以外的其他事件,那么這個(gè)點(diǎn)擊事件會(huì)消失,此時(shí)父元素的onTouchEvent并不會(huì)被調(diào)用,并且當(dāng)前View可以持續(xù)收到后續(xù)的事件,最終這些消失的點(diǎn)擊事件會(huì)傳遞給Activity處理。

4、ViewGroup默認(rèn)不攔截任何事件(返回false)。

5、View的onTouchEvent默認(rèn)都會(huì)消耗事件(返回true),除非它是不可點(diǎn)擊的(clickable和longClickable同時(shí)為false)。View的longClickable屬性默認(rèn)都為false,clickable屬性要分情況,比如Button的clickable屬性默認(rèn)為true,而TextView的clickable默認(rèn)為false。

6、View的enable屬性不影響onTouchEvent的默認(rèn)返回值。

7、通過(guò)requestDisallowInterceptTouchEvent方法可以在子元素中干預(yù)父元素的事件分發(fā)過(guò)程,但是ACTION_DOWN事件除外。

記住這個(gè)圖的傳遞順序,面試的時(shí)候能夠畫出來(lái),就很詳細(xì)了:

ACTION_CANCEL什么時(shí)候觸發(fā),觸摸button然后滑動(dòng)到外部抬起會(huì)觸發(fā)點(diǎn)擊事件嗎,再滑動(dòng)回去抬起會(huì)么?

  • 一般ACTION_CANCEL和ACTION_UP都作為View一段事件處理的結(jié)束。如果在父View中攔截ACTION_UP或ACTION_MOVE,在第一次父視圖攔截消息的瞬間,父視圖指定子視圖不接受后續(xù)消息了,同時(shí)子視圖會(huì)收到ACTION_CANCEL事件。
  • 如果觸摸某個(gè)控件,但是又不是在這個(gè)控件的區(qū)域上抬起(移動(dòng)到別的地方了),就會(huì)出現(xiàn)action_cancel。
點(diǎn)擊事件被攔截,但是想傳到下面的View,如何操作?

重寫子類的requestDisallowInterceptTouchEvent()方法返回true就不會(huì)執(zhí)行父類的onInterceptTouchEvent(),即可將點(diǎn)擊事件傳到下面的View。

如何解決View的事件沖突?舉個(gè)開發(fā)中遇到的例子?

常見開發(fā)中事件沖突的有ScrollView與RecyclerView的滑動(dòng)沖突、RecyclerView內(nèi)嵌同時(shí)滑動(dòng)同一方向。

滑動(dòng)沖突的處理規(guī)則:

  • 對(duì)于由于外部滑動(dòng)和內(nèi)部滑動(dòng)方向不一致導(dǎo)致的滑動(dòng)沖突,可以根據(jù)滑動(dòng)的方向判斷誰(shuí)來(lái)攔截事件。
  • 對(duì)于由于外部滑動(dòng)方向和內(nèi)部滑動(dòng)方向一致導(dǎo)致的滑動(dòng)沖突,可以根據(jù)業(yè)務(wù)需求,規(guī)定何時(shí)讓外部View攔截事件,何時(shí)由內(nèi)部View攔截事件。
  • 對(duì)于上面兩種情況的嵌套,相對(duì)復(fù)雜,可同樣根據(jù)需求在業(yè)務(wù)上找到突破點(diǎn)。

滑動(dòng)沖突的實(shí)現(xiàn)方法:

  • 外部攔截法:指點(diǎn)擊事件都先經(jīng)過(guò)父容器的攔截處理,如果父容器需要此事件就攔截,否則就不攔截。具體方法:需要重寫父容器的onInterceptTouchEvent方法,在內(nèi)部做出相應(yīng)的攔截。
  • 內(nèi)部攔截法:指父容器不攔截任何事件,而將所有的事件都傳遞給子容器,如果子容器需要此事件就直接消耗,否則就交由父容器進(jìn)行處理。具體方法:需要配合requestDisallowInterceptTouchEvent方法。

加深理解,GOGOGO

3、View的繪制流程?

DecorView被加載到Window中

  • 從Activity的startActivity開始,最終調(diào)用到ActivityThread的handleLaunchActivity方法來(lái)創(chuàng)建Activity,首先,會(huì)調(diào)用performLaunchActivity方法,內(nèi)部會(huì)執(zhí)行Activity的onCreate方法,從而完成DecorView和Activity的創(chuàng)建。然后,會(huì)調(diào)用handleResumeActivity,里面首先會(huì)調(diào)用performResumeActivity去執(zhí)行Activity的onResume()方法,執(zhí)行完后會(huì)得到一個(gè)ActivityClientRecord對(duì)象,然后通過(guò)r.window.getDecorView()的方式得到DecorView,然后會(huì)通過(guò)a.getWindowManager()得到WindowManager,最終調(diào)用其addView()方法將DecorView加進(jìn)去。
  • WindowManager的實(shí)現(xiàn)類是WindowManagerImpl,它內(nèi)部會(huì)將addView的邏輯委托給WindowManagerGlobal,可見這里使用了接口隔離和委托模式將實(shí)現(xiàn)和抽象充分解耦。在WindowManagerGlobal的addView()方法中不僅會(huì)將DecorView添加到Window中,同時(shí)會(huì)創(chuàng)建ViewRootImpl對(duì)象,并將ViewRootImpl對(duì)象和DecorView通過(guò)root.setView()把DecorView加載到Window中。這里的ViewRootImpl是ViewRoot的實(shí)現(xiàn)類,是連接WindowManager和DecorView的紐帶。View的三大流程均是通過(guò)ViewRoot來(lái)完成的。

了解繪制的整體流程

繪制會(huì)從根視圖ViewRoot的performTraversals()方法開始,從上到下遍歷整個(gè)視圖樹,每個(gè)View控件負(fù)責(zé)繪制自己,而ViewGroup還需要負(fù)責(zé)通知自己的子View進(jìn)行繪制操作。

理解MeasureSpec

MeasureSpec表示的是一個(gè)32位的整形值,它的高2位表示測(cè)量模式SpecMode,低30位表示某種測(cè)量模式下的規(guī)格大小SpecSize。MeasureSpec是View類的一個(gè)靜態(tài)內(nèi)部類,用來(lái)說(shuō)明應(yīng)該如何測(cè)量這個(gè)View。它由三種測(cè)量模式,如下:

  • EXACTLY:精確測(cè)量模式,視圖寬高指定為match_parent或具體數(shù)值時(shí)生效,表示父視圖已經(jīng)決定了子視圖的精確大小,這種模式下View的測(cè)量值就是SpecSize的值。
  • AT_MOST:最大值測(cè)量模式,當(dāng)視圖的寬高指定為wrap_content時(shí)生效,此時(shí)子視圖的尺寸可以是不超過(guò)父視圖允許的最大尺寸的任何尺寸。
  • UNSPECIFIED:不指定測(cè)量模式, 父視圖沒(méi)有限制子視圖的大小,子視圖可以是想要的任何尺寸,通常用于系統(tǒng)內(nèi)部,應(yīng)用開發(fā)中很少用到。

MeasureSpec通過(guò)將SpecMode和SpecSize打包成一個(gè)int值來(lái)避免過(guò)多的對(duì)象內(nèi)存分配,為了方便操作,其提供了打包和解包的方法,打包方法為makeMeasureSpec,解包方法為getMode和getSize。

普通View的MeasureSpec的創(chuàng)建規(guī)則如下:

對(duì)于DecorView而言,它的MeasureSpec由窗口尺寸和其自身的LayoutParams共同決定;對(duì)于普通的View,它的MeasureSpec由父視圖的MeasureSpec和其自身的LayoutParams共同決定。

如何根據(jù)MeasureSpec去實(shí)現(xiàn)一個(gè)瀑布流的自定義ViewGroup?

View繪制流程之Measure

  • 首先,在ViewGroup中的measureChildren()方法中會(huì)遍歷測(cè)量ViewGroup中所有的View,當(dāng)View的可見性處于GONE狀態(tài)時(shí),不對(duì)其進(jìn)行測(cè)量。
  • 然后,測(cè)量某個(gè)指定的View時(shí),根據(jù)父容器的MeasureSpec和子View的LayoutParams等信息計(jì)算子View的MeasureSpec。
  • 最后,將計(jì)算出的MeasureSpec傳入View的measure方法,這里ViewGroup沒(méi)有定義測(cè)量的具體過(guò)程,因?yàn)閂iewGroup是一個(gè)抽象類,其測(cè)量過(guò)程的onMeasure方法需要各個(gè)子類去實(shí)現(xiàn)。不同的ViewGroup子類有不同的布局特性,這導(dǎo)致它們的測(cè)量細(xì)節(jié)各不相同,如果需要自定義測(cè)量過(guò)程,則子類可以重寫這個(gè)方法。(setMeasureDimension方法用于設(shè)置View的測(cè)量寬高,如果View沒(méi)有重寫onMeasure方法,則會(huì)默認(rèn)調(diào)用getDefaultSize來(lái)獲得View的寬高)
getSuggestMinimumWidth分析

如果View沒(méi)有設(shè)置背景,那么返回android:minWidth這個(gè)屬性所指定的值,這個(gè)值可以為0;如果View設(shè)置了背景,則返回android:minWidth和背景的最小寬度這兩者中的最大值。

自定義View時(shí)手動(dòng)處理wrap_content時(shí)的情形

直接繼承View的控件需要重寫onMeasure方法并設(shè)置wrap_content時(shí)的自身大小,否則在布局中使用wrap_content就相當(dāng)于使用match_parent。此時(shí),可以在wrap_content的情況下(對(duì)應(yīng)MeasureSpec.AT_MOST)指定內(nèi)部寬/高(mWidth和mHeight)。

LinearLayout的onMeasure方法實(shí)現(xiàn)解析(這里僅分析measureVertical核心源碼)

系統(tǒng)會(huì)遍歷子元素并對(duì)每個(gè)子元素執(zhí)行measureChildBeforeLayout方法,這個(gè)方法內(nèi)部會(huì)調(diào)用子元素的measure方法,這樣各個(gè)子元素就開始依次進(jìn)入measure過(guò)程,并且系統(tǒng)會(huì)通過(guò)mTotalLength這個(gè)變量來(lái)存儲(chǔ)LinearLayout在豎直方向的初步高度。每測(cè)量一個(gè)子元素,mTotalLength就會(huì)增加,增加的部分主要包括了子元素的高度以及子元素在豎直方向上的margin等。

在Activity中獲取某個(gè)View的寬高

由于View的measure過(guò)程和Activity的生命周期方法不是同步執(zhí)行的,如果View還沒(méi)有測(cè)量完畢,那么獲得的寬/高就是0。所以在onCreate、onStart、onResume中均無(wú)法正確得到某個(gè)View的寬高信息。解決方式如下:

  • Activity/View#onWindowFocusChanged:此時(shí)View已經(jīng)初始化完畢,當(dāng)Activity的窗口得到焦點(diǎn)和失去焦點(diǎn)時(shí)均會(huì)被調(diào)用一次,如果頻繁地進(jìn)行onResume和onPause,那么onWindowFocusChanged也會(huì)被頻繁地調(diào)用。
  • view.post(runnable): 通過(guò)post可以將一個(gè)runnable投遞到消息隊(duì)列的尾部,始化好了然后等待Looper調(diào)用次runnable的時(shí)候,View也已經(jīng)初始化好了。
  • ViewTreeObserver#addOnGlobalLayoutListener:當(dāng)View樹的狀態(tài)發(fā)生改變或者View樹內(nèi)部的View的可見性發(fā)生改變時(shí),onGlobalLayout方法將被回調(diào)。
  • View.measure(int widthMeasureSpec, int heightMeasureSpec):match_parent時(shí)不知道parentSize的大小,測(cè)不出;具體數(shù)值時(shí),直接makeMeasureSpec固定值,然后調(diào)用view..measure就可以了;wrap_content時(shí),在最大化模式下,用View理論上能支持的最大值去構(gòu)造MeasureSpec是合理的。

View的繪制流程之Layout

首先,會(huì)通過(guò)setFrame方法來(lái)設(shè)定View的四個(gè)頂點(diǎn)的位置,即View在父容器中的位置。然后,會(huì)執(zhí)行到onLayout空方法,子類如果是ViewGroup類型,則重寫這個(gè)方法,實(shí)現(xiàn)ViewGroup中所有View控件布局流程。

LinearLayout的onLayout方法實(shí)現(xiàn)解析(layoutVertical核心源碼)

其中會(huì)遍歷調(diào)用每個(gè)子View的setChildFrame方法為子元素確定對(duì)應(yīng)的位置。其中的childTop會(huì)逐漸增大,意味著后面的子元素會(huì)被放置在靠下的位置。

注意:在View的默認(rèn)實(shí)現(xiàn)中,View的測(cè)量寬/高和最終寬/高是相等的,只不過(guò)測(cè)量寬/高形成于View的measure過(guò)程,而最終寬/高形成于View的layout過(guò)程,即兩者的賦值時(shí)機(jī)不同,測(cè)量寬/高的賦值時(shí)機(jī)稍微早一些。在一些特殊的情況下則兩者不相等:

  • 重寫View的layout方法,使最終寬度總是比測(cè)量寬/高大100px。
  • View需要多次measure才能確定自己的測(cè)量寬/高,在前幾次測(cè)量的過(guò)程中,其得出的測(cè)量寬/高有可能和最終寬/高不一致,但最終來(lái)說(shuō),測(cè)量寬/高還是和最終寬/高相同。

View的繪制流程之Draw

Draw的基本流程

繪制基本上可以分為六個(gè)步驟:

  • 首先繪制View的背景;
  • 如果需要的話,保持canvas的圖層,為fading做準(zhǔn)備;
  • 然后,繪制View的內(nèi)容;
  • 接著,繪制View的子View;
  • 如果需要的話,繪制View的fading邊緣并恢復(fù)圖層;
  • 最后,繪制View的裝飾(例如滾動(dòng)條等等)。
setWillNotDraw的作用

如果一個(gè)View不需要繪制任何內(nèi)容,那么設(shè)置這個(gè)標(biāo)記位為true以后,系統(tǒng)會(huì)進(jìn)行相應(yīng)的優(yōu)化。

  • 默認(rèn)情況下,View沒(méi)有啟用這個(gè)優(yōu)化標(biāo)記位,但是ViewGroup會(huì)默認(rèn)啟用這個(gè)優(yōu)化標(biāo)記位。
  • 當(dāng)我們的自定義控件繼承于ViewGroup并且本身不具備繪制功能時(shí),就可以開啟這個(gè)標(biāo)記位從而便于系統(tǒng)進(jìn)行后續(xù)的優(yōu)化。
  • 當(dāng)明確知道一個(gè)ViewGroup需要通過(guò)onDraw來(lái)繪制內(nèi)容時(shí),我們需要顯示地關(guān)閉WILL_NOT_DRAW這個(gè)標(biāo)記位。

Requestlayout,onlayout,onDraw,DrawChild區(qū)別與聯(lián)系?

requestLayout()方法 :會(huì)導(dǎo)致調(diào)用 measure()過(guò)程 和 layout()過(guò)程,將會(huì)根據(jù)標(biāo)志位判斷是否需要ondraw。

onLayout()方法:如果該View是ViewGroup對(duì)象,需要實(shí)現(xiàn)該方法,對(duì)每個(gè)子視圖進(jìn)行布局。

onDraw()方法:繪制視圖本身 (每個(gè)View都需要重載該方法,ViewGroup不需要實(shí)現(xiàn)該方法)。

drawChild():去重新回調(diào)每個(gè)子視圖的draw()方法。

invalidate() 和 postInvalidate()的區(qū)別 ?

invalidate()與postInvalidate()都用于刷新View,主要區(qū)別是invalidate()在主線程中調(diào)用,若在子線程中使用需要配合handler;而postInvalidate()可在子線程中直接調(diào)用。

更詳細(xì)的內(nèi)容請(qǐng)點(diǎn)擊這里

4、跨進(jìn)程通信。

Android中進(jìn)程和線程的關(guān)系?區(qū)別?

  • 線程是CPU調(diào)度的最小單元,同時(shí)線程是一種有限的系統(tǒng)資源;而進(jìn)程一般指一個(gè)執(zhí)行單元,在PC和移動(dòng)設(shè)備上指一個(gè)程序或者一個(gè)應(yīng)用。
  • 一般來(lái)說(shuō),一個(gè)App程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程(包含與被包含的關(guān)系),通俗來(lái)講就是,在App這個(gè)工廠里面有一個(gè)進(jìn)程,線程就是里面的生產(chǎn)線,但主線程(即主生產(chǎn)線)只有一條,而子線程(即副生產(chǎn)線)可以有多個(gè)。
  • 進(jìn)程有自己獨(dú)立的地址空間,而進(jìn)程中的線程共享此地址空間,都可以并發(fā)執(zhí)行。

如何開啟多進(jìn)程?應(yīng)用是否可以開啟N個(gè)進(jìn)程?

在AndroidManifest中給四大組件指定屬性android:process開啟多進(jìn)程模式,在內(nèi)存允許的條件下可以開啟N個(gè)進(jìn)程。

為何需要IPC?多進(jìn)程通信可能會(huì)出現(xiàn)的問(wèn)題?

所有運(yùn)行在不同進(jìn)程的四大組件(Activity、Service、Receiver、ContentProvider)共享數(shù)據(jù)都會(huì)失敗,這是由于Android為每個(gè)應(yīng)用分配了獨(dú)立的虛擬機(jī),不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間,這會(huì)導(dǎo)致在不同的虛擬機(jī)中訪問(wèn)同一個(gè)類的對(duì)象會(huì)產(chǎn)生多份副本。比如常用例子(通過(guò)開啟多進(jìn)程獲取更大內(nèi)存空間、兩個(gè)或者多個(gè)應(yīng)用之間共享數(shù)據(jù)、微信全家桶)。

一般來(lái)說(shuō),使用多進(jìn)程通信會(huì)造成如下幾方面的問(wèn)題:

  • 靜態(tài)成員和單例模式完全失效:獨(dú)立的虛擬機(jī)造成。
  • 線程同步機(jī)制完全失效:獨(dú)立的虛擬機(jī)造成。
  • SharedPreferences的可靠性下降:這是因?yàn)镾p不支持兩個(gè)進(jìn)程并發(fā)進(jìn)行讀寫,有一定幾率導(dǎo)致數(shù)據(jù)丟失。
  • Application會(huì)多次創(chuàng)建:Android系統(tǒng)在創(chuàng)建新的進(jìn)程時(shí)會(huì)分配獨(dú)立的虛擬機(jī),所以這個(gè)過(guò)程其實(shí)就是啟動(dòng)一個(gè)應(yīng)用的過(guò)程,自然也會(huì)創(chuàng)建新的Application。

Android中IPC方式、各種方式優(yōu)缺點(diǎn)?

講講AIDL?如何優(yōu)化多模塊都使用AIDL的情況?

AIDL(Android Interface Definition Language,Android接口定義語(yǔ)言):如果在一個(gè)進(jìn)程中要調(diào)用另一個(gè)進(jìn)程中對(duì)象的方法,可使用AIDL生成可序列化的參數(shù),AIDL會(huì)生成一個(gè)服務(wù)端對(duì)象的代理類,通過(guò)它客戶端可以實(shí)現(xiàn)間接調(diào)用服務(wù)端對(duì)象的方法。

AIDL的本質(zhì)是系統(tǒng)提供了一套可快速實(shí)現(xiàn)Binder的工具。關(guān)鍵類和方法:

  • AIDL接口:繼承IInterface。
  • Stub類:Binder的實(shí)現(xiàn)類,服務(wù)端通過(guò)這個(gè)類來(lái)提供服務(wù)。
  • Proxy類:服務(wù)端的本地代理,客戶端通過(guò)這個(gè)類調(diào)用服務(wù)端的方法。
  • asInterface():客戶端調(diào)用,將服務(wù)端返回的Binder對(duì)象,轉(zhuǎn)換成客戶端所需要的AIDL接口類型的對(duì)象。如果客戶端和服務(wù)端位于同一進(jìn)程,則直接返回Stub對(duì)象本身,否則返回系統(tǒng)封裝后的Stub.proxy對(duì)象。
  • asBinder():根據(jù)當(dāng)前調(diào)用情況返回代理Proxy的Binder對(duì)象。
  • onTransact():運(yùn)行在服務(wù)端的Binder線程池中,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求時(shí),遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層封裝后交由此方法來(lái)處理。
  • transact():運(yùn)行在客戶端,當(dāng)客戶端發(fā)起遠(yuǎn)程請(qǐng)求的同時(shí)將當(dāng)前線程掛起。之后調(diào)用服務(wù)端的onTransact()直到遠(yuǎn)程請(qǐng)求返回,當(dāng)前線程才繼續(xù)執(zhí)行。

當(dāng)有多個(gè)業(yè)務(wù)模塊都需要AIDL來(lái)進(jìn)行IPC,此時(shí)需要為每個(gè)模塊創(chuàng)建特定的aidl文件,那么相應(yīng)的Service就會(huì)很多。必然會(huì)出現(xiàn)系統(tǒng)資源耗費(fèi)嚴(yán)重、應(yīng)用過(guò)度重量級(jí)的問(wèn)題。解決辦法是建立Binder連接池,即將每個(gè)業(yè)務(wù)模塊的Binder請(qǐng)求統(tǒng)一轉(zhuǎn)發(fā)到一個(gè)遠(yuǎn)程Service中去執(zhí)行,從而避免重復(fù)創(chuàng)建Service。

工作原理:每個(gè)業(yè)務(wù)模塊創(chuàng)建自己的AIDL接口并實(shí)現(xiàn)此接口,然后向服務(wù)端提供自己的唯一標(biāo)識(shí)和其對(duì)應(yīng)的Binder對(duì)象。服務(wù)端只需要一個(gè)Service并提供一個(gè)queryBinder接口,它會(huì)根據(jù)業(yè)務(wù)模塊的特征來(lái)返回相應(yīng)的Binder對(duì)象,不同的業(yè)務(wù)模塊拿到所需的Binder對(duì)象后就可以進(jìn)行遠(yuǎn)程方法的調(diào)用了。

為什么選擇Binder?

為什么選用Binder,在討論這個(gè)問(wèn)題之前,我們知道Android也是基于Linux內(nèi)核,Linux現(xiàn)有的進(jìn)程通信手段有以下幾種:

  • 管道:在創(chuàng)建時(shí)分配一個(gè)page大小的內(nèi)存,緩存區(qū)大小比較有限;
  • 消息隊(duì)列:信息復(fù)制兩次,額外的CPU消耗;不合適頻繁或信息量大的通信;
  • 共享內(nèi)存:無(wú)須復(fù)制,共享緩沖區(qū)直接附加到進(jìn)程虛擬地址空間,速度快;但進(jìn)程間的同步問(wèn)題操作系統(tǒng)無(wú)法實(shí)現(xiàn),必須各進(jìn)程利用同步工具解決;
  • 套接字:作為更通用的接口,傳輸效率低,主要用于不同機(jī)器或跨網(wǎng)絡(luò)的通信;
  • 信號(hào)量:常作為一種鎖機(jī)制,防止某進(jìn)程正在訪問(wèn)共享資源時(shí),其他進(jìn)程也訪問(wèn)該資源。因此,主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段。 不適用于信息交換,更適用于進(jìn)程中斷控制,比如非法內(nèi)存訪問(wèn),殺死某個(gè)進(jìn)程等;

既然有現(xiàn)有的IPC方式,為什么重新設(shè)計(jì)一套Binder機(jī)制呢。主要是出于以上三個(gè)方面的考量:

  • 1、效率:傳輸效率主要影響因素是內(nèi)存拷貝的次數(shù),拷貝次數(shù)越少,傳輸速率越高。從Android進(jìn)程架構(gòu)角度分析:對(duì)于消息隊(duì)列、Socket和管道來(lái)說(shuō),數(shù)據(jù)先從發(fā)送方的緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,再?gòu)膬?nèi)核緩存區(qū)拷貝到接收方的緩存區(qū),一共兩次拷貝,如圖:

而對(duì)于Binder來(lái)說(shuō),數(shù)據(jù)從發(fā)送方的緩存區(qū)拷貝到內(nèi)核的緩存區(qū),而接收方的緩存區(qū)與內(nèi)核的緩存區(qū)是映射到同一塊物理地址的,節(jié)省了一次數(shù)據(jù)拷貝的過(guò)程,如圖:

共享內(nèi)存不需要拷貝,Binder的性能僅次于共享內(nèi)存。

  • 2、穩(wěn)定性:上面說(shuō)到共享內(nèi)存的性能優(yōu)于Binder,那為什么不采用共享內(nèi)存呢,因?yàn)楣蚕韮?nèi)存需要處理并發(fā)同步問(wèn)題,容易出現(xiàn)死鎖和資源競(jìng)爭(zhēng),穩(wěn)定性較差。Socket雖然是基于C/S架構(gòu)的,但是它主要是用于網(wǎng)絡(luò)間的通信且傳輸效率較低。Binder基于C/S架構(gòu) ,Server端與Client端相對(duì)獨(dú)立,穩(wěn)定性較好。
  • 3、安全性:傳統(tǒng)Linux IPC的接收方無(wú)法獲得對(duì)方進(jìn)程可靠的UID/PID,從而無(wú)法鑒別對(duì)方身份;而Binder機(jī)制為每個(gè)進(jìn)程分配了UID/PID,且在Binder通信時(shí)會(huì)根據(jù)UID/PID進(jìn)行有效性檢測(cè)。

Binder機(jī)制的作用和原理?

Linux系統(tǒng)將一個(gè)進(jìn)程分為用戶空間和內(nèi)核空間。對(duì)于進(jìn)程之間來(lái)說(shuō),用戶空間的數(shù)據(jù)不可共享,內(nèi)核空間的數(shù)據(jù)可共享,為了保證安全性和獨(dú)立性,一個(gè)進(jìn)程不能直接操作或者訪問(wèn)另一個(gè)進(jìn)程,即Android的進(jìn)程是相互獨(dú)立、隔離的,這就需要跨進(jìn)程之間的數(shù)據(jù)通信方式。普通的跨進(jìn)程通信方式一般需要2次內(nèi)存拷貝,如下圖所示:

一次完整的 Binder IPC 通信過(guò)程通常是這樣:

  • 首先 Binder 驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū)。
  • 接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū),建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系。
  • 發(fā)送方進(jìn)程通過(guò)系統(tǒng)調(diào)用 copyfromuser() 將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū),由于內(nèi)核緩存區(qū)和接收進(jìn)程的用戶空間存在內(nèi)存映射,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信。

Binder框架中ServiceManager的作用?

Binder框架 是基于 C/S 架構(gòu)的。由一系列的組件組成,包括 Client、Server、ServiceManager、Binder驅(qū)動(dòng),其中 Client、Server、Service Manager 運(yùn)行在用戶空間,Binder 驅(qū)動(dòng)運(yùn)行在內(nèi)核空間。如下圖所示:

  • Server&Client:服務(wù)器&客戶端。在Binder驅(qū)動(dòng)和Service Manager提供的基礎(chǔ)設(shè)施上,進(jìn)行Client-Server之間的通信。
  • ServiceManager(如同DNS域名服務(wù)器)服務(wù)的管理者,將Binder名字轉(zhuǎn)換為Client中對(duì)該Binder的引用,使得Client可以通過(guò)Binder名字獲得Server中Binder實(shí)體的引用。
  • Binder驅(qū)動(dòng)(如同路由器):負(fù)責(zé)進(jìn)程之間binder通信的建立,計(jì)數(shù)管理以及數(shù)據(jù)的傳遞交互等底層支持。

最后,結(jié)合Android跨進(jìn)程通信:圖文詳解 Binder機(jī)制 的總結(jié)圖來(lái)綜合理解一下:

Binder 的完整定義

  • 從進(jìn)程間通信的角度看,Binder 是一種進(jìn)程間通信的機(jī)制;
  • 從 Server 進(jìn)程的角度看,Binder 指的是 Server 中的 Binder 實(shí)體對(duì)象;
  • 從 Client 進(jìn)程的角度看,Binder 指的是 Binder 代理對(duì)象,是 Binder 實(shí)體對(duì)象的一個(gè)遠(yuǎn)程代理;
  • 從傳輸過(guò)程的角度看,Binder 是一個(gè)可以跨進(jìn)程傳輸?shù)膶?duì)象;Binder 驅(qū)動(dòng)會(huì)對(duì)這個(gè)跨越進(jìn)程邊界的對(duì)象對(duì)一點(diǎn)點(diǎn)特殊處理,自動(dòng)完成代理對(duì)象和本地對(duì)象之間的轉(zhuǎn)換。

手寫實(shí)現(xiàn)簡(jiǎn)化版AMS(AIDL實(shí)現(xiàn))

與Binder相關(guān)的幾個(gè)類的職責(zé):

  • IBinder:跨進(jìn)程通信的Base接口,它聲明了跨進(jìn)程通信需要實(shí)現(xiàn)的一系列抽象方法,實(shí)現(xiàn)了這個(gè)接口就說(shuō)明可以進(jìn)行跨進(jìn)程通信,Client和Server都要實(shí)現(xiàn)此接口。
  • IInterface:這也是一個(gè)Base接口,用來(lái)表示Server提供了哪些能力,是Client和Server通信的協(xié)議。
  • Binder:提供Binder服務(wù)的本地對(duì)象的基類,它實(shí)現(xiàn)了IBinder接口,所有本地對(duì)象都要繼承這個(gè)類。
  • BinderProxy:在Binder.java這個(gè)文件中還定義了一個(gè)BinderProxy類,這個(gè)類表示Binder代理對(duì)象它同樣實(shí)現(xiàn)了IBinder接口,不過(guò)它的很多實(shí)現(xiàn)都交由native層處理。Client中拿到的實(shí)際上是這個(gè)代理對(duì)象。
  • Stub:這個(gè)類在編譯aidl文件后自動(dòng)生成,它繼承自Binder,表示它是一個(gè)Binder本地對(duì)象;它是一個(gè)抽象類,實(shí)現(xiàn)了IInterface接口,表明它的子類需要實(shí)現(xiàn)Server將要提供的具體能力(即aidl文件中聲明的方法)。
  • Proxy:它實(shí)現(xiàn)了IInterface接口,說(shuō)明它是Binder通信過(guò)程的一部分;它實(shí)現(xiàn)了aidl中聲明的方法,但最終還是交由其中的mRemote成員來(lái)處理,說(shuō)明它是一個(gè)代理對(duì)象,mRemote成員實(shí)際上就是BinderProxy。

aidl文件只是用來(lái)定義C/S交互的接口,Android在編譯時(shí)會(huì)自動(dòng)生成相應(yīng)的Java類,生成的類中包含了Stub和Proxy靜態(tài)內(nèi)部類,用來(lái)封裝數(shù)據(jù)轉(zhuǎn)換的過(guò)程,實(shí)際使用時(shí)只關(guān)心具體的Java接口類即可。為什么Stub和Proxy是靜態(tài)內(nèi)部類呢?這其實(shí)只是為了將三個(gè)類放在一個(gè)文件中,提高代碼的聚合性。通過(guò)上面的分析,我們其實(shí)完全可以不通過(guò)aidl,手動(dòng)編碼來(lái)實(shí)現(xiàn)Binder的通信,下面我們通過(guò)編碼來(lái)實(shí)現(xiàn)ActivityManagerService:

1、首先定義IActivityManager接口:

public interface IActivityManager extends IInterface {//binder描述符String DESCRIPTOR = "android.app.IActivityManager";//方法編號(hào)int TRANSACTION_startActivity = IBinder.FIRST_CALL_TRANSACTION + 0;//聲明一個(gè)啟動(dòng)activity的方法,為了簡(jiǎn)化,這里只傳入intent參數(shù)int startActivity(Intent intent) throws RemoteException; } 復(fù)制代碼

2、然后,實(shí)現(xiàn)ActivityManagerService側(cè)的本地Binder對(duì)象基類:

// 名稱隨意,不一定叫Stub public abstract class ActivityManagerNative extends Binder implements IActivityManager {public static IActivityManager asInterface(IBinder obj) {if (obj == null) {return null;}IActivityManager in = (IActivityManager) obj.queryLocalInterface(IActivityManager.DESCRIPTOR);if (in != null) {return in;}//代理對(duì)象,見下面的代碼return new ActivityManagerProxy(obj);}@Overridepublic IBinder asBinder() {return this;}@Overrideprotected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {switch (code) {// 獲取binder描述符case INTERFACE_TRANSACTION:reply.writeString(IActivityManager.DESCRIPTOR);return true;// 啟動(dòng)activity,從data中反序列化出intent參數(shù)后,直接調(diào)用子類startActivity方法啟動(dòng)activity。case IActivityManager.TRANSACTION_startActivity:data.enforceInterface(IActivityManager.DESCRIPTOR);Intent intent = Intent.CREATOR.createFromParcel(data);int result = this.startActivity(intent);reply.writeNoException();reply.writeInt(result);return true;}return super.onTransact(code, data, reply, flags);} } 復(fù)制代碼

3、接著,實(shí)現(xiàn)Client側(cè)的代理對(duì)象:

public class ActivityManagerProxy implements IActivityManager {private IBinder mRemote;public ActivityManagerProxy(IBinder remote) {mRemote = remote;}@Overridepublic IBinder asBinder() {return mRemote;}@Overridepublic int startActivity(Intent intent) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();int result;try {// 將intent參數(shù)序列化,寫入data中intent.writeToParcel(data, 0);// 調(diào)用BinderProxy對(duì)象的transact方法,交由Binder驅(qū)動(dòng)處理。mRemote.transact(IActivityManager.TRANSACTION_startActivity, data, reply, 0);reply.readException();// 等待server執(zhí)行結(jié)束后,讀取執(zhí)行結(jié)果result = reply.readInt();} finally {data.recycle();reply.recycle();}return result;} } 復(fù)制代碼

4、最后,實(shí)現(xiàn)Binder本地對(duì)象(IActivityManager接口):

public class ActivityManagerService extends ActivityManagerNative {@Overridepublic int startActivity(Intent intent) throws RemoteException {// 啟動(dòng)activityreturn 0;} } 復(fù)制代碼

簡(jiǎn)化版的ActivityManagerService到這里就已經(jīng)實(shí)現(xiàn)了,剩下就是Client只需要獲取到AMS的代理對(duì)象IActivityManager就可以通信了。

簡(jiǎn)單講講 binder 驅(qū)動(dòng)吧?

從 Java 層來(lái)看就像訪問(wèn)本地接口一樣,客戶端基于 BinderProxy 服務(wù)端基于 IBinder 對(duì)象,從 native 層來(lái)看來(lái)看客戶端基于 BpBinder 到 ICPThreadState 到 binder 驅(qū)動(dòng),服務(wù)端由 binder 驅(qū)動(dòng)喚醒 IPCThreadSate 到 BbBinder ??邕M(jìn)程通信的原理最終是要基于內(nèi)核的,所以最會(huì)會(huì)涉及到 binder_open 、binder_mmap 和 binder_ioctl這三種系統(tǒng)調(diào)用。

跨進(jìn)程傳遞大內(nèi)存數(shù)據(jù)如何做?

binder 肯定是不行的,因?yàn)橛成涞淖畲髢?nèi)存只有 1M-8K,可以采用 binder + 匿名共享內(nèi)存的形式,像跨進(jìn)程傳遞大的 bitmap 需要打開系統(tǒng)底層的 ashmem 機(jī)制。

請(qǐng)按順序仔細(xì)閱讀下列文章提升對(duì)Binder機(jī)制的理解程度:

寫給 Android 應(yīng)用工程師的 Binder 原理剖析

Binder學(xué)習(xí)指南

Binder設(shè)計(jì)與實(shí)現(xiàn)

老羅Binder機(jī)制分析系列或Android系統(tǒng)源代碼情景分析Binder章節(jié)

5、Android系統(tǒng)啟動(dòng)流程是什么?(提示:init進(jìn)程 -> Zygote進(jìn)程 –> SystemServer進(jìn)程 –> 各種系統(tǒng)服務(wù) –> 應(yīng)用進(jìn)程)

Android系統(tǒng)啟動(dòng)的核心流程如下:

  • 1、啟動(dòng)電源以及系統(tǒng)啟動(dòng):當(dāng)電源按下時(shí)引導(dǎo)芯片從預(yù)定義的地方(固化在ROM)開始執(zhí)行,加載引導(dǎo)程序BootLoader到RAM,然后執(zhí)行。
  • 2、引導(dǎo)程序BootLoader:BootLoader是在Android系統(tǒng)開始運(yùn)行前的一個(gè)小程序,主要用于把系統(tǒng)OS拉起來(lái)并運(yùn)行。
  • 3、Linux內(nèi)核啟動(dòng):當(dāng)內(nèi)核啟動(dòng)時(shí),設(shè)置緩存、被保護(hù)存儲(chǔ)器、計(jì)劃列表、加載驅(qū)動(dòng)。當(dāng)其完成系統(tǒng)設(shè)置時(shí),會(huì)先在系統(tǒng)文件中尋找init.rc文件,并啟動(dòng)init進(jìn)程。
  • 4、init進(jìn)程啟動(dòng):初始化和啟動(dòng)屬性服務(wù),并且啟動(dòng)Zygote進(jìn)程。
  • 5、Zygote進(jìn)程啟動(dòng):創(chuàng)建JVM并為其注冊(cè)JNI方法,創(chuàng)建服務(wù)器端Socket,啟動(dòng)SystemServer進(jìn)程。
  • 6、SystemServer進(jìn)程啟動(dòng):啟動(dòng)Binder線程池和SystemServiceManager,并且啟動(dòng)各種系統(tǒng)服務(wù)。
  • 7、Launcher啟動(dòng):被SystemServer進(jìn)程啟動(dòng)的AMS會(huì)啟動(dòng)Launcher,Launcher啟動(dòng)后會(huì)將已安裝應(yīng)用的快捷圖標(biāo)顯示到系統(tǒng)桌面上。

需要更詳細(xì)的分析請(qǐng)查看以下系列文章:

Android系統(tǒng)啟動(dòng)流程之init進(jìn)程啟動(dòng)

Android系統(tǒng)啟動(dòng)流程之Zygote進(jìn)程啟動(dòng)

Android系統(tǒng)啟動(dòng)流程之SystemServer進(jìn)程啟動(dòng)

Android系統(tǒng)啟動(dòng)流程之Launcher進(jìn)程啟動(dòng)

系統(tǒng)是怎么幫我們啟動(dòng)找到桌面應(yīng)用的?

通過(guò)意圖,PMS 會(huì)解析所有 apk 的 AndroidManifest.xml ,如果解析過(guò)會(huì)存到 package.xml 中不會(huì)反復(fù)解析,PMS 有了它就能找到了。

6、啟動(dòng)一個(gè)程序,可以主界面點(diǎn)擊圖標(biāo)進(jìn)入,也可以從一個(gè)程序中跳轉(zhuǎn)過(guò)去,二者有什么區(qū)別?

是因?yàn)閱?dòng)程序(主界面也是一個(gè)app),發(fā)現(xiàn)了在這個(gè)程序中存在一個(gè)設(shè)置為的activity, 所以這個(gè)launcher會(huì)把icon提出來(lái),放在主界面上。當(dāng)用戶點(diǎn)擊icon的時(shí)候,發(fā)出一個(gè)Intent:

Intent intent = mActivity.getPackageManager().getLaunchIntentForPackage(packageName); mActivity.startActivity(intent); 復(fù)制代碼

跳過(guò)去可以跳到任意允許的頁(yè)面,如一個(gè)程序可以下載,那么真正下載的頁(yè)面可能不是首頁(yè)(也有可能是首頁(yè)),這時(shí)還是構(gòu)造一個(gè)Intent,startActivity。這個(gè)intent中的action可能有多種view,download都有可能。系統(tǒng)會(huì)根據(jù)第三方程序向系統(tǒng)注冊(cè)的功能,為你的Intent選擇可以打開的程序或者頁(yè)面。所以唯一的一點(diǎn) 不同的是從icon的點(diǎn)擊啟動(dòng)的intent的action是相對(duì)單一的,從程序中跳轉(zhuǎn)或者啟動(dòng)可能樣式更多一些。本質(zhì)是相同的。

7、AMS家族重要術(shù)語(yǔ)解釋。

1.ActivityManagerServices,簡(jiǎn)稱AMS,服務(wù)端對(duì)象,負(fù)責(zé)系統(tǒng)中所有Activity的生命周期。

2.ActivityThread,App的真正入口。當(dāng)開啟App之后,調(diào)用main()開始運(yùn)行,開啟消息循環(huán)隊(duì)列,這就是傳說(shuō)的UI線程或者叫主線程。與ActivityManagerService一起完成Activity的管理工作。

3.ApplicationThread,用來(lái)實(shí)現(xiàn)ActivityManagerServie與ActivityThread之間的交互。在ActivityManagerSevice需要管理相關(guān)Application中的Activity的生命周期時(shí),通過(guò)ApplicationThread的代理對(duì)象與ActivityThread通信。

4.ApplicationThreadProxy,是ApplicationThread在服務(wù)器端的代理,負(fù)責(zé)和客戶端的ApplicationThread通信。AMS就是通過(guò)該代理與ActivityThread進(jìn)行通信的。

5.Instrumentation,每一個(gè)應(yīng)用程序只有一個(gè)Instrumetation對(duì)象,每個(gè)Activity內(nèi)都有一個(gè)對(duì)該對(duì)象的引用,Instrumentation可以理解為應(yīng)用進(jìn)程的管家,ActivityThread要?jiǎng)?chuàng)建或暫停某個(gè)Activity時(shí),都需要通過(guò)Instrumentation來(lái)進(jìn)行具體的操作。

6.ActivityStack,Activity在AMS的棧管理,用來(lái)記錄經(jīng)啟動(dòng)的Activity的先后關(guān)系,狀態(tài)信息等。通過(guò)ActivtyStack決定是否需要啟動(dòng)新的進(jìn)程。

7.ActivityRecord,ActivityStack的管理對(duì)象,每個(gè)Acivity在AMS對(duì)應(yīng)一個(gè)ActivityRecord,來(lái)記錄Activity狀態(tài)以及其他的管理信息。其實(shí)就是服務(wù)器端的Activit對(duì)象的映像。

8.TaskRecord,AMS抽象出來(lái)的一個(gè)“任務(wù)”的概念,是記錄ActivityRecord的棧,一個(gè)“Task”包含若干個(gè)ActivityRecord。AMS用TaskRecord確保Activity啟動(dòng)和退出的順序。如果你清楚Activity的4種launchMode,那么對(duì)這概念應(yīng)該不陌生。

8、App啟動(dòng)流程(Activity的冷啟動(dòng)流程)。

點(diǎn)擊應(yīng)用圖標(biāo)后會(huì)去啟動(dòng)應(yīng)用的Launcher Activity,如果Launcer Activity所在的進(jìn)程沒(méi)有創(chuàng)建,還會(huì)創(chuàng)建新進(jìn)程,整體的流程就是一個(gè)Activity的啟動(dòng)流程。

Activity的啟動(dòng)流程圖(放大可查看)如下所示:

整個(gè)流程涉及的主要角色有:

  • Instrumentation: 監(jiān)控應(yīng)用與系統(tǒng)相關(guān)的交互行為。
  • AMS:組件管理調(diào)度中心,什么都不干,但是什么都管。
  • ActivityStarter:Activity啟動(dòng)的控制器,處理Intent與Flag對(duì)Activity啟動(dòng)的影響,具體說(shuō)來(lái)有:1 尋找符合啟動(dòng)條件的Activity,如果有多個(gè),讓用戶選擇;2 校驗(yàn)啟動(dòng)參數(shù)的合法性;3 返回int參數(shù),代表Activity是否啟動(dòng)成功。
  • ActivityStackSupervisior:這個(gè)類的作用你從它的名字就可以看出來(lái),它用來(lái)管理任務(wù)棧。
  • ActivityStack:用來(lái)管理任務(wù)棧里的Activity。
  • ActivityThread:最終干活的人,Activity、Service、BroadcastReceiver的啟動(dòng)、切換、調(diào)度等各種操作都在這個(gè)類里完成。

注:這里單獨(dú)提一下ActivityStackSupervisior,這是高版本才有的類,它用來(lái)管理多個(gè)ActivityStack,早期的版本只有一個(gè)ActivityStack對(duì)應(yīng)著手機(jī)屏幕,后來(lái)高版本支持多屏以后,就有了多個(gè)ActivityStack,于是就引入了ActivityStackSupervisior用來(lái)管理多個(gè)ActivityStack。

整個(gè)流程主要涉及四個(gè)進(jìn)程:

  • 調(diào)用者進(jìn)程,如果是在桌面啟動(dòng)應(yīng)用就是Launcher應(yīng)用進(jìn)程。
  • ActivityManagerService等待所在的System Server進(jìn)程,該進(jìn)程主要運(yùn)行著系統(tǒng)服務(wù)組件。
  • Zygote進(jìn)程,該進(jìn)程主要用來(lái)fork新進(jìn)程。
  • 新啟動(dòng)的應(yīng)用進(jìn)程,該進(jìn)程就是用來(lái)承載應(yīng)用運(yùn)行的進(jìn)程了,它也是應(yīng)用的主線程(新創(chuàng)建的進(jìn)程就是主線程),處理組件生命周期、界面繪制等相關(guān)事情。

有了以上的理解,整個(gè)流程可以概括如下:

  • 1、點(diǎn)擊桌面應(yīng)用圖標(biāo),Launcher進(jìn)程將啟動(dòng)Activity(MainActivity)的請(qǐng)求以Binder的方式發(fā)送給了AMS。
  • 2、AMS接收到啟動(dòng)請(qǐng)求后,交付ActivityStarter處理Intent和Flag等信息,然后再交給ActivityStackSupervisior/ActivityStack 處理Activity進(jìn)棧相關(guān)流程。同時(shí)以Socket方式請(qǐng)求Zygote進(jìn)程fork新進(jìn)程。
  • 3、Zygote接收到新進(jìn)程創(chuàng)建請(qǐng)求后fork出新進(jìn)程。
  • 4、在新進(jìn)程里創(chuàng)建ActivityThread對(duì)象,新創(chuàng)建的進(jìn)程就是應(yīng)用的主線程,在主線程里開啟Looper消息循環(huán),開始處理創(chuàng)建Activity。
  • 5、ActivityThread利用ClassLoader去加載Activity、創(chuàng)建Activity實(shí)例,并回調(diào)Activity的onCreate()方法,這樣便完成了Activity的啟動(dòng)。

最后,再看看另一幅啟動(dòng)流程圖來(lái)加深理解:

9、ActivityThread工作原理。

10、說(shuō)下四大組件的啟動(dòng)過(guò)程,四大組件的啟動(dòng)與銷毀的方式。

廣播發(fā)送和接收的原理了解嗎?

  • 繼承BroadcastReceiver,重寫onReceive()方法。
  • 通過(guò)Binder機(jī)制向ActivityManagerService注冊(cè)廣播。
  • 通過(guò)Binder機(jī)制向ActivityMangerService發(fā)送廣播。
  • ActivityManagerService查找符合相應(yīng)條件的廣播(IntentFilter/Permission)的BroadcastReceiver,將廣播發(fā)送到BroadcastReceiver所在的消息隊(duì)列中。
  • BroadcastReceiver所在消息隊(duì)列拿到此廣播后,回調(diào)它的onReceive()方法。

11、AMS是如何管理Activity的?

12、理解Window和WindowManager。

1.Window用于顯示View和接收各種事件,Window有三種型:應(yīng)用Window(每個(gè)Activity對(duì)應(yīng)一個(gè)Window)、子Widow(不能單獨(dú)存在,附屬于特定Window)、系統(tǒng)window(toast和狀態(tài)欄)

2.Window分層級(jí),應(yīng)用Window在1-99、子Window在1000-1999、系統(tǒng)Window在2000-2999.WindowManager提供了增改View的三個(gè)功能。

3.Window是個(gè)抽象概念:每一個(gè)Window對(duì)應(yīng)著一個(gè)ViewRootImpl,Window通過(guò)ViewRootImpl來(lái)和View建立聯(lián)系,View是Window存在的實(shí)體,只能通過(guò)WindowManager來(lái)訪問(wèn)Window。

4.WindowManager的實(shí)現(xiàn)是WindowManagerImpl,其再委托WindowManagerGlobal來(lái)對(duì)Window進(jìn)行操作,其中有四種List分別儲(chǔ)存對(duì)應(yīng)的View、ViewRootImpl、WindowManger.LayoutParams和正在被刪除的View。

5.Window的實(shí)體是存在于遠(yuǎn)端的WindowMangerService,所以增刪改Window在本端是修改上面的幾個(gè)List然后通過(guò)ViewRootImpl重繪View,通過(guò)WindowSession(每Window個(gè)對(duì)應(yīng)一個(gè))在遠(yuǎn)端修改Window。

6.Activity創(chuàng)建Window:Activity會(huì)在attach()中創(chuàng)建Window并設(shè)置其回調(diào)(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy類創(chuàng)建PhoneWindow實(shí)現(xiàn)的。然后通過(guò)Activity#setContentView()調(diào)用PhoneWindow的setContentView。

13、WMS是如何管理Window的?

14、大體說(shuō)清一個(gè)應(yīng)用程序安裝到手機(jī)上時(shí)發(fā)生了什么?

APK的安裝流程如下所示:

復(fù)制APK到/data/app目錄下,解壓并掃描安裝包。

資源管理器解析APK里的資源文件。

解析AndroidManifest文件,并在/data/data/目錄下創(chuàng)建對(duì)應(yīng)的應(yīng)用數(shù)據(jù)目錄。

然后對(duì)dex文件進(jìn)行優(yōu)化,并保存在dalvik-cache目錄下。

將AndroidManifest文件解析出的四大組件信息注冊(cè)到PackageManagerService中。

安裝完成后,發(fā)送廣播。

15、Android的打包流程?(即描述清點(diǎn)擊 Android Studio 的 build 按鈕后發(fā)生了什么?)apk里有哪些東西?簽名算法的原理?

apk打包流程

Android的包文件APK分為兩個(gè)部分:代碼和資源,所以打包方面也分為資源打包和代碼打包兩個(gè)方面,下面就來(lái)分析資源和代碼的編譯打包原理。

APK整體的的打包流程如下圖所示:

具體說(shuō)來(lái):

  • 通過(guò)AAPT工具進(jìn)行資源文件(包括AndroidManifest.xml、布局文件、各種xml資源等)的打包,生成R.java文件。
  • 通過(guò)AIDL工具處理AIDL文件,生成相應(yīng)的Java文件。
  • 通過(guò)Java Compiler編譯R.java、Java接口文件、Java源文件,生成.class文件。
  • 通過(guò)dex命令,將.class文件和第三方庫(kù)中的.class文件處理生成classes.dex,該過(guò)程主要完成Java字節(jié)碼轉(zhuǎn)換成Dalvik字節(jié)碼,壓縮常量池以及清除冗余信息等工作。
  • 通過(guò)ApkBuilder工具將資源文件、DEX文件打包生成APK文件。
  • 通過(guò)Jarsigner工具,利用KeyStore對(duì)生成的APK文件進(jìn)行簽名。
  • 如果是正式版的APK,還會(huì)利用ZipAlign工具進(jìn)行對(duì)齊處理,對(duì)齊的過(guò)程就是將APK文件中所有的資源文件距離文件的起始距位置都偏移4字節(jié)的整數(shù)倍,這樣通過(guò)內(nèi)存映射訪問(wèn)APK文件的速度會(huì)更快,并且會(huì)減少其在設(shè)備上運(yùn)行時(shí)的內(nèi)存占用。

apk組成

  • dex:最終生成的Dalvik字節(jié)碼。
  • res:存放資源文件的目錄。
  • asserts:額外建立的資源文件夾。
  • lib:如果存在的話,存放的是ndk編出來(lái)的so庫(kù)。
  • META-INF:存放簽名信息

MANIFEST.MF(清單文件):其中每一個(gè)資源文件都有一個(gè)SHA-256-Digest簽名,MANIFEST.MF文件的SHA256(SHA1)并base64編碼的結(jié)果即為CERT.SF中的SHA256-Digest-Manifest值。

CERT.SF(待簽名文件):除了開頭處定義的SHA256(SHA1)-Digest-Manifest值,后面幾項(xiàng)的值是對(duì)MANIFEST.MF文件中的每項(xiàng)再次SHA256并base64編碼后的值。

CERT.RSA(簽名結(jié)果文件):其中包含了公鑰、加密算法等信息。首先對(duì)前一步生成的MANIFEST.MF使用了SHA256(SHA1)-RSA算法,用開發(fā)者私鑰簽名,然后在安裝時(shí)使用公鑰解密。最后,將其與未加密的摘要信息(MANIFEST.MF文件)進(jìn)行對(duì)比,如果相符,則表明內(nèi)容沒(méi)有被修改。

  • androidManifest:程序的全局清單配置文件。
  • resources.arsc:編譯后的二進(jìn)制資源文件。

簽名算法的原理

為什么要簽名?
  • 確保Apk來(lái)源的真實(shí)性。
  • 確保Apk沒(méi)有被第三方篡改。
什么是簽名?

在Apk中寫入一個(gè)“指紋”。指紋寫入以后,Apk中有任何修改,都會(huì)導(dǎo)致這個(gè)指紋無(wú)效,Android系統(tǒng)在安裝Apk進(jìn)行簽名校驗(yàn)時(shí)就會(huì)不通過(guò),從而保證了安全性。

數(shù)字摘要

對(duì)一個(gè)任意長(zhǎng)度的數(shù)據(jù),通過(guò)一個(gè)Hash算法計(jì)算后,都可以得到一個(gè)固定長(zhǎng)度的二進(jìn)制數(shù)據(jù),這個(gè)數(shù)據(jù)就稱為“摘要”。

補(bǔ)充:

  • 散列算法的基礎(chǔ)原理:將數(shù)據(jù)(如一段文字)運(yùn)算變?yōu)榱硪还潭ㄩL(zhǎng)度值。
  • SHA-1:在密碼學(xué)中,SHA-1(安全散列算法1)是一種加密散列函數(shù),它接受輸入并產(chǎn)生一個(gè)160 位(20 字節(jié))散列值,稱為消息摘要 。
  • MD5:MD5消息摘要算法(英語(yǔ):MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數(shù),可以產(chǎn)生出一個(gè)128位(16字節(jié))的散列值(hash value),用于確保信息傳輸完整一致。
  • SHA-2:名稱來(lái)自于安全散列算法2(英語(yǔ):Secure Hash Algorithm 2)的縮寫,一種密碼散列函數(shù)算法標(biāo)準(zhǔn),其下又可再分為六個(gè)不同的算法標(biāo)準(zhǔn),包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。

特征:

  • 唯一性
  • 固定長(zhǎng)度:比較常用的Hash算法有MD5和SHA1,MD5的長(zhǎng)度是128拉,SHA1的長(zhǎng)度是160位。
  • 不可逆性
簽名和校驗(yàn)的主要過(guò)程

簽名就是在摘要的基礎(chǔ)上再進(jìn)行一次加密,對(duì)摘要加密后的數(shù)據(jù)就可以當(dāng)作數(shù)字簽名。

簽名過(guò)程:
  • 1、計(jì)算摘要:通過(guò)Hash算法提取出原始數(shù)據(jù)的摘要。
  • 2、計(jì)算簽名:再通過(guò)基于密鑰(私鑰)的非對(duì)稱加密算法對(duì)提取出的摘要進(jìn)行加密,加密后的數(shù)據(jù)就是簽名信息。
  • 3、寫入簽名:將簽名信息寫入原始數(shù)據(jù)的簽名區(qū)塊內(nèi)。
校驗(yàn)過(guò)程:
  • 1、首先用同樣的Hash算法從接收到的數(shù)據(jù)中提取出摘要。
  • 2、解密簽名:使用發(fā)送方的公鑰對(duì)數(shù)字簽名進(jìn)行解密,解密出原始摘要。
  • 3、比較摘要:如果解密后的數(shù)據(jù)和提取的摘要一致,則校驗(yàn)通過(guò);如果數(shù)據(jù)被第三方篡改過(guò),解密后的數(shù)據(jù)和摘要將會(huì)不一致,則校驗(yàn)不通過(guò)。
數(shù)字證書

如何保證公鑰的可靠性呢?答案是數(shù)字證書,數(shù)字證書是身份認(rèn)證機(jī)構(gòu)(Certificate Authority)頒發(fā)的,包含了以下信息:

  • 證書頒發(fā)機(jī)構(gòu)
  • 證書頒發(fā)機(jī)構(gòu)簽名
  • 證書綁定的服務(wù)器域名
  • 證書版本、有效期
  • 簽名使用的加密算法(非對(duì)稱算法,如RSA)
  • 公鑰等

接收方收到消息后,先向CA驗(yàn)證證書的合法性,再進(jìn)行簽名校驗(yàn)。

注意:Apk的證書通常是自簽名的,也就是由開發(fā)者自己制作,沒(méi)有向CA機(jī)構(gòu)申請(qǐng)。Android在安裝Apk時(shí)并沒(méi)有校驗(yàn)證書本身的合法性,只是從證書中提取公鑰和加密算法,這也正是對(duì)第三方Apk重新簽名后,還能夠繼續(xù)在沒(méi)有安裝這個(gè)Apk的系統(tǒng)中繼續(xù)安裝的原因。

keystore和證書格式

keystore文件中包含了私鑰、公鑰和數(shù)字證書。根據(jù)編碼不同,keystore文件分為很多種,Android使用的是Java標(biāo)準(zhǔn)keystore格式JKS(Java Key Storage),所以通過(guò)Android Studio導(dǎo)出的keystore文件是以.jks結(jié)尾的。

keystore使用的證書標(biāo)準(zhǔn)是X.509,X.509標(biāo)準(zhǔn)也有多種編碼格式,常用的有兩種:pem(Privacy Enhanced Mail)和der(Distinguished Encoding Rules)。jks使用的是der格式,Android也支持直接使用pem格式的證書進(jìn)行簽名。

兩種證書編碼格式的區(qū)別:

  • DER(Distinguished Encoding Rules)

二進(jìn)制格式,所有類型的證書和私鑰都可以存儲(chǔ)為der格式。

  • PEM(Privacy Enhanced Mail)

base64編碼,內(nèi)容以-----BEGIN xxx----- 開頭,以-----END xxx----- 結(jié)尾。

jarsigner和apksigner的區(qū)別

Android提供了兩種對(duì)Apk的簽名方式,一種是基于JAR的簽名方式,另一種是基于Apk的簽名方式,它們的主要區(qū)別在于使用的簽名文件不一樣:jarsigner使用keystore文件進(jìn)行簽名;apksigner除了支持使用keystore文件進(jìn)行簽名外,還支持直接指定pem證書文件和私鑰進(jìn)行簽名。

在簽名時(shí),除了要指定keystore文件和密碼外,也要指定alias和key的密碼,這是為什么呢?

keystore是一個(gè)密鑰庫(kù),也就是說(shuō)它可以存儲(chǔ)多對(duì)密鑰和證書,keystore的密碼是用于保護(hù)keystore本身的,一對(duì)密鑰和證書是通過(guò)alias來(lái)區(qū)分的。所以jarsigner是支持使用多個(gè)證書對(duì)Apk進(jìn)行簽名的,apksigner也同樣支持。

Android Apk V1 簽名原理
  • 1、解析出 CERT.RSA 文件中的證書、公鑰,解密 CERT.RSA 中的加密數(shù)據(jù)。
  • 2、解密結(jié)果和 CERT.SF 的指紋進(jìn)行對(duì)比,保證 CERT.SF 沒(méi)有被篡改。
  • 3、而 CERT.SF 中的內(nèi)容再和 MANIFEST.MF 指紋對(duì)比,保證 MANIFEST.MF 文件沒(méi)有被篡改。
  • 4、MANIFEST.MF 中的內(nèi)容和 APK 所有文件指紋逐一對(duì)比,保證 APK 沒(méi)有被篡改。

16、說(shuō)下安卓虛擬機(jī)和java虛擬機(jī)的原理和不同點(diǎn)?(JVM、Davilk、ART三者的原理和區(qū)別)

JVM 和Dalvik虛擬機(jī)的區(qū)別

JVM:.java -> javac -> .class -> jar -> .jar

架構(gòu): 堆和棧的架構(gòu).

DVM:.java -> javac -> .class -> dx.bat -> .dex

架構(gòu): 寄存器(cpu上的一塊高速緩存)

Android2個(gè)虛擬機(jī)的區(qū)別(一個(gè)5.0之前,一個(gè)5.0之后)

什么是Dalvik:Dalvik是Google公司自己設(shè)計(jì)用于Android平臺(tái)的Java虛擬機(jī)。Dalvik虛擬機(jī)是Google等廠商合作開發(fā)的Android移動(dòng)設(shè)備平臺(tái)的核心組成部分之一,它可以支持已轉(zhuǎn)換為.dex(即Dalvik Executable)格式的Java應(yīng)用程序的運(yùn)行,.dex格式是專為Dalvik應(yīng)用設(shè)計(jì)的一種壓縮格式,適合內(nèi)存和處理器速度有限的系統(tǒng)。Dalvik經(jīng)過(guò)優(yōu)化,允許在有限的內(nèi)存中同時(shí)運(yùn)行多個(gè)虛擬機(jī)的實(shí)例,并且每一個(gè)Dalvik應(yīng)用作為獨(dú)立的Linux進(jìn)程執(zhí)行。獨(dú)立的進(jìn)程可以防止在虛擬機(jī)崩潰的時(shí)候所有程序都被關(guān)閉。

什么是ART:Android操作系統(tǒng)已經(jīng)成熟,Google的Android團(tuán)隊(duì)開始將注意力轉(zhuǎn)向一些底層組件,其中之一是負(fù)責(zé)應(yīng)用程序運(yùn)行的Dalvik運(yùn)行時(shí)。Google開發(fā)者已經(jīng)花了兩年時(shí)間開發(fā)更快執(zhí)行效率更高更省電的替代ART運(yùn)行時(shí)。ART代表Android Runtime,其處理應(yīng)用程序執(zhí)行的方式完全不同于Dalvik,Dalvik是依靠一個(gè)Just-In-Time(JIT)編譯器去解釋字節(jié)碼。開發(fā)者編譯后的應(yīng)用代碼需要通過(guò)一個(gè)解釋器在用戶的設(shè)備上運(yùn)行,這一機(jī)制并不高效,但讓應(yīng)用能更容易在不同硬件和架構(gòu)上運(yùn)行。ART則完全改變了這套做法,在應(yīng)用安裝的時(shí)候就預(yù)編譯字節(jié)碼為機(jī)器語(yǔ)言,這一機(jī)制叫Ahead-Of-Time(AOT)編譯。在移除解釋代碼這一過(guò)程后,應(yīng)用程序執(zhí)行將更有效率,啟動(dòng)更快。

ART優(yōu)點(diǎn):

  • 系統(tǒng)性能的顯著提升。
  • 應(yīng)用啟動(dòng)更快、運(yùn)行更快、體驗(yàn)更流暢、觸感反饋更及時(shí)。
  • 更長(zhǎng)的電池續(xù)航能力。
  • 支持更低的硬件。

ART缺點(diǎn):

  • 更大的存儲(chǔ)空間占用,可能會(huì)增加10%-20%。
  • 更長(zhǎng)的應(yīng)用安裝時(shí)間。

ART和Davlik中垃圾回收的區(qū)別?

17、安卓采用自動(dòng)垃圾回收機(jī)制,請(qǐng)說(shuō)下安卓?jī)?nèi)存管理的原理?

開放性問(wèn)題:如何設(shè)計(jì)垃圾回收算法?

18、Android中App是如何沙箱化的,為何要這么做?

19、一個(gè)圖片在app中調(diào)用R.id后是如何找到的?

20、JNI

Java調(diào)用C++

  • 在Java中聲明Native方法(即需要調(diào)用的本地方法)
  • 編譯上述 Java源文件javac(得到 .class文件) 3。 通過(guò) javah 命令導(dǎo)出JNI的頭文件(.h文件)
  • 使用 Java需要交互的本地代碼 實(shí)現(xiàn)在 Java中聲明的Native方法
  • 編譯.so庫(kù)文件
  • 通過(guò)Java命令執(zhí)行 Java程序,最終實(shí)現(xiàn)Java調(diào)用本地代碼

C++調(diào)用Java

  • 從classpath路徑下搜索ClassMethod這個(gè)類,并返回該類的Class對(duì)象。

  • 獲取類的默認(rèn)構(gòu)造方法ID。

  • 查找實(shí)例方法的ID。

  • 創(chuàng)建該類的實(shí)例。

  • 調(diào)用對(duì)象的實(shí)例方法。

    JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessMethod_callJavaInstaceMethod (JNIEnv *env, jclass cls) { jclass clazz = NULL; jobject jobj = NULL; jmethodID mid_construct = NULL; jmethodID mid_instance = NULL; jstring str_arg = NULL; // 1、從classpath路徑下搜索ClassMethod這個(gè)類,并返回該類的Class對(duì)象 clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassMethod"); if (clazz == NULL) { printf("找不到'com.study.jnilearn.ClassMethod'這個(gè)類"); return; } // 2、獲取類的默認(rèn)構(gòu)造方法ID mid_construct = (*env)->GetMethodID(env,clazz, "<init>","()V"); if (mid_construct == NULL) { printf("找不到默認(rèn)的構(gòu)造方法"); return; } // 3、查找實(shí)例方法的ID mid_instance = (*env)->GetMethodID(env, clazz, "callInstanceMethod", "(Ljava/lang/String;I)V"); if (mid_instance == NULL) { return; } // 4、創(chuàng)建該類的實(shí)例 jobj = (*env)->NewObject(env,clazz,mid_construct); if (jobj == NULL) { printf("在com.study.jnilearn.ClassMethod類中找不到callInstanceMethod方法"); return; } // 5、調(diào)用對(duì)象的實(shí)例方法 str_arg = (*env)->NewStringUTF(env,"我是實(shí)例方法"); (*env)->CallVoidMethod(env,jobj,mid_instance,str_arg,200); // 刪除局部引用 (*env)->DeleteLocalRef(env,clazz); (*env)->DeleteLocalRef(env,jobj); (*env)->DeleteLocalRef(env,str_arg); } 復(fù)制代碼

如何在jni中注冊(cè)native函數(shù),有幾種注冊(cè)方式?

so 的加載流程是怎樣的,生命周期是怎樣的?

這個(gè)要從 java 層去看源碼分析,是從 ClassLoader 的 PathList 中去找到目標(biāo)路徑加載的,同時(shí) so 是通過(guò) mmap 加載映射到虛擬空間的。生命周期加載庫(kù)和卸載庫(kù)時(shí)分別調(diào)用 JNI_OnLoad 和 JNI_OnUnload() 方法。

21、請(qǐng)介紹一下NDK?

公眾號(hào)

我的公眾號(hào) JsonChao 開通啦,如果您想第一時(shí)間獲取最新文章和最新動(dòng)態(tài),歡迎掃描關(guān)注~

贊賞

如果這個(gè)庫(kù)對(duì)您有很大幫助,您愿意支持這個(gè)項(xiàng)目的進(jìn)一步開發(fā)和這個(gè)項(xiàng)目的持續(xù)維護(hù)。你可以掃描下面的二維碼,讓我喝一杯咖啡或啤酒。非常感謝您的捐贈(zèng)。謝謝!


Contanct Me

● 微信 && 微信群:

歡迎關(guān)注我的微信:bcce5360。由于微信群人數(shù)太多無(wú)法生成群邀二維碼,所以麻煩大家想進(jìn)微信群的朋友們,加我微信拉你進(jìn)群(PS:微信群的學(xué)習(xí)氛圍與各項(xiàng)福利將會(huì)超乎你的想象)。

● QQ群:

2千人QQ群,Awesome-Android學(xué)習(xí)交流群,QQ群號(hào):959936182, 歡迎大家加入~

About me

  • Email: chao.qu521@gmail.com

  • Blog: jsonchao.github.io/

  • 掘金: juejin.im/user/5a3ba9…

很感謝您閱讀這篇文章,希望您能將它分享給您的朋友或技術(shù)群,這對(duì)我意義重大。

希望我們能成為朋友,在 Github、掘金上一起分享知識(shí)。

總結(jié)

以上是生活随笔為你收集整理的【建议收藏】2020年中高级Android大厂面试秘籍,为你保驾护航金三银四,直通大厂(Android高级篇上)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

在线看一区二区 | 狠狠狠色丁香综合久久天下网 | 色在线视频 | 国产一区av在线 | 天天摸天天舔 | 国产中文字幕一区 | 亚洲精品在线资源 | 免费看短 | 99亚洲天堂 | 日韩欧美一区二区三区黑寡妇 | 99理论片 | 精品一区电影 | 色综合天天狠狠 | 国产免费av一区二区三区 | 成年人在线电影 | 激情六月婷婷久久 | 日韩欧美综合视频 | 99国产精品久久久久老师 | 国产中文字幕视频在线观看 | 欧美在线视频不卡 | 天天看天天操 | 国产精品无av码在线观看 | 国产日韩精品一区二区三区在线 | 人人射| 91免费视频网站在线观看 | 成人毛片在线视频 | 成人黄色小说视频 | 日韩xxx视频 | 成人午夜电影在线观看 | 日躁夜躁狠狠躁2001 | 亚洲区视频在线观看 | 久久九九久久精品 | 中文字幕在线色 | 久久视频二区 | 国产美女在线免费观看 | 在线国产一区二区三区 | 日韩免费观看一区二区 | 日本久久不卡视频 | 欧美一区在线观看视频 | 韩日精品在线 | 黄色成人毛片 | 午夜av免费观看 | 亚洲精品欧美成人 | 天天天色| 精品国产福利在线 | 天天射狠狠干 | 五月开心婷婷网 | 日韩av电影免费观看 | 久久久久高清 | 九九热.com| 在线观看成人网 | 婷婷视频在线播放 | 国产 日韩 中文字幕 | 中文字幕在线观看完整 | 91亚洲网站 | 五月天色综合 | 免费一级片视频 | 久草在线视频中文 | 午夜精品久久久久久久99水蜜桃 | 国产第一页福利影院 | 激情五月综合 | 免费看黄色小说的网站 | 少妇bbb好爽 | 日本性视频| 狠狠色噜噜狠狠狠狠 | a电影在线观看 | freejavvideo日本免费 | 色av色av色av | 亚洲永久精品在线观看 | 久久久久久久久久网站 | 亚洲精品国产精品国自 | 国内精品久久久久久久久 | 免费视频你懂得 | 亚洲在线精品视频 | 中文在线a天堂 | 超碰在线99 | 九九av | 制服丝袜天堂 | 免费观看一级一片 | 国产精品久久99综合免费观看尤物 | 日日夜夜网| 久久久影片 | 国产午夜在线 | 射综合网 | 成人精品一区二区三区电影免费 | 五月婷婷在线观看视频 | 免费h在线观看 | 天天舔天天射天天操 | 亚洲高清在线视频 | 天天干天天干天天干 | 久久精品79国产精品 | av爱干| 日本黄区免费视频观看 | 国产精品入口麻豆www | 中文字幕欧美日韩va免费视频 | 99视频免费看 | 精品国产激情 | 在线观看一 | 999久久国精品免费观看网站 | 色福利网站 | 免费午夜网站 | www.色国产 | 午夜aaaa| 香蕉久久久久久久 | 中文字幕欲求不满 | 久久私人影院 | 日本最新一区二区三区 | 国产精品久久久久亚洲影视 | 久久久久久影视 | 久久成人视屏 | 日韩女同一区二区三区在线观看 | 国产精品男女啪啪 | 99热最新地址 | 日韩一级片观看 | 久久久久久久久综合 | 99这里只有久久精品视频 | 四虎8848免费高清在线观看 | 深夜福利视频在线观看 | 成人免费视频网站 | 亚洲欧洲中文日韩久久av乱码 | 久久影视一区 | 成人欧美一区二区三区黑人麻豆 | 在线成人av | 国产欧美综合在线观看 | 天天天天天天干 | 久久夜色精品亚洲噜噜国4 午夜视频在线观看欧美 | 欧美韩日在线 | 国产综合精品一区二区三区 | 91精品国产高清自在线观看 | 天天躁日日躁狠狠 | 国产精品免费观看视频 | 97av视频| 91视频 - 114av | 国产一区麻豆 | 成片视频免费观看 | 欧美天堂影院 | 人人干狠狠干 | 中文字幕中文字幕在线中文字幕三区 | 欧美国产高清 | 夜夜操天天干 | 久久免费观看视频 | 亚洲日本中文字幕在线观看 | 一区二区欧美日韩 | 国产美女精品久久久 | 亚洲精品在线网站 | 天天综合导航 | 久久超级碰视频 | 精品久久1 | 成人va视频| 色中色亚洲 | 狠狠网站 | 亚洲精品美女在线观看播放 | 欧美精品第一 | www.天天射| 天天干,天天射,天天操,天天摸 | 国产又粗又猛又爽 | 99精品视频免费全部在线 | 国产精品一区二区中文字幕 | 久久免费一级片 | 久久亚洲私人国产精品va | 亚洲国产综合在线 | 亚洲精品在线一区二区三区 | 国产又粗又猛又爽又黄的视频免费 | 国产专区精品视频 | 久久国产高清视频 | 久久影视精品 | 日韩精品电影在线播放 | 日日干av | 免费久久久久久久 | 天天操天天操天天操天天 | 91丨九色丨蝌蚪丨对白 | 免费91在线观看 | 国产人成在线观看 | 在线成人中文字幕 | 久久成人国产精品一区二区 | 国产成人在线一区 | 国产精品美女久久久免费 | 久久综合之合合综合久久 | 一级片免费在线 | 81精品国产乱码久久久久久 | 亚洲欧美国产日韩在线观看 | 激情伊人| 久久亚洲综合国产精品99麻豆的功能介绍 | 在线观看日韩av | 91精品在线视频 | 婷婷综合亚洲 | 五月婷婷六月丁香在线观看 | 久久精品第一页 | 天堂在线视频免费观看 | 亚洲精品看片 | 91漂亮少妇露脸在线播放 | 日韩av成人在线观看 | 国产一二三四在线观看视频 | 欧美成人性网 | 天天草视频 | 成人免费一区二区三区在线观看 | 国产一二三四在线视频 | 97精品国产97久久久久久春色 | 免费观看黄色12片一级视频 | 麻豆传媒视频在线播放 | www免费| 欧美成人精品在线 | 国产又粗又硬又爽视频 | 菠萝菠萝在线精品视频 | 91在线porny国产在线看 | 精品亚洲午夜久久久久91 | 成 人 a v天堂 | 在线国产欧美 | 免费观看一级视频 | 国产成人一区二区啪在线观看 | 久久久久亚洲精品中文字幕 | 狠狠地操 | 色黄久久久久久 | 狠狠色香婷婷久久亚洲精品 | 去干成人网 | 国产精品久久久久久婷婷天堂 | 欧美激情视频三区 | 免费国产亚洲视频 | 九九精品无码 | 欧美影院久久 | 中文在线中文资源 | 九九热在线视频免费观看 | 日日草av | 国产精品少妇 | 日韩av影视在线 | 日韩成人免费电影 | 成年人网站免费在线观看 | 日韩视频一区二区三区 | 人人添人人澡人人澡人人人爽 | 亚洲激情av | 久久在线视频在线 | 精品久久久成人 | 久久免费视频精品 | 2020天天干夜夜爽 | 日本久久久亚洲精品 | 国产一级片观看 | 久久国产片 | 亚洲另类xxxx| 99久久婷婷国产精品综合 | 久久国产色 | 国产成人一区二 | 成年人在线观看视频免费 | 亚洲免费精品一区二区 | 天天操夜夜干 | 国产精品18久久久久久不卡孕妇 | 免费高清在线一区 | 美女av在线免费 | 国产无遮挡又黄又爽在线观看 | 夜色成人网 | 国产麻豆视频在线观看 | 欧美日韩一区二区三区在线免费观看 | 色婷婷av一区 | 亚洲免费在线看 | 天天天干天天天操 | 一区二区三区免费在线观看视频 | 丁香高清视频在线看看 | 狠狠色丁香婷婷综合久小说久 | 黄色三级久久 | 中文字幕网站视频在线 | www.天天色 | 久久久久亚洲精品男人的天堂 | 免费日韩av电影 | 日本精品一区二区在线观看 | 三级av中文字幕 | 蜜臀久久99静品久久久久久 | 色婷婷久久久综合中文字幕 | 国产午夜视频在线观看 | 国产精品国产三级国产aⅴ9色 | 制服丝袜欧美 | 欧美在线一级片 | 狠日日| 亚洲欧美国内爽妇网 | 久久免费观看少妇a级毛片 久久久久成人免费 | 五月天亚洲综合 | 国产视频一区二区在线观看 | 亚洲成人黄色在线观看 | 国产韩国日本高清视频 | www.香蕉视频在线观看 | 日本黄色免费在线 | 在线观看国产亚洲 | 国产欧美久久久精品影院 | 精品亚洲一区二区三区 | 色偷偷88欧美精品久久久 | 一级黄毛片| 久久精品高清视频 | av在线免费播放 | 久久大香线蕉app | 91精品在线免费观看 | 欧美日韩69 | 国产精品免费不卡 | 一区二区三区电影 | 婷婷亚洲激情 | 天天爱天天射 | 91精品视频免费在线观看 | 免费黄色在线网址 | 99草在线视频 | 成人综合日日夜夜 | 在线观看岛国 | 中文字幕色婷婷在线视频 | 欧美a级片网站 | 97精品电影院 | www.久久色.com| 久久手机免费视频 | 狠狠的干 | 欧美最猛性xxxxx(亚洲精品) | 91成人网在线观看 | 成年人视频在线免费播放 | 成人在线播放网站 | 亚洲欧美日韩一区二区三区在线观看 | 日韩欧美一区二区三区黑寡妇 | 91精品视频在线观看免费 | 精品 一区 在线 | 亚洲精品66 | 国产精品乱码久久久久 | 天天艹天天干天天 | 又黄又爽又色无遮挡免费 | 97碰碰视频 | 精品国产a | 日本在线视频网址 | 一区在线观看 | 久久天天躁| 国产精品99久久久久久大便 | 久久综合操 | 久久免费av | 国产精品久久三 | 国产精品入口传媒 | 国产精品v欧美精品v日韩 | 亚洲精品视频免费 | 6080yy精品一区二区三区 | 91在线观看高清 | 日日夜夜精品免费观看 | 精品国产成人av在线免 | 国产不卡免费av | av一级片 | 五月婷婷六月丁香在线观看 | 人人干人人上 | 日本在线中文 | 日韩激情中文字幕 | 日韩一区二区三区高清在线观看 | 中文国产成人精品久久一 | 中国精品少妇 | 国产精品嫩草69影院 | 日韩va欧美va亚洲va久久 | 国产一级做a | 天天狠狠 | 国产精品影音先锋 | 国产一级视频在线免费观看 | 黄色三级网站 | 国产婷婷久久 | avwww在线观看 | 亚洲精品在线观看免费 | 丁香婷婷久久久综合精品国产 | 久久精品99精品国产香蕉 | 在线观看网站av | 中文字幕一区二区三区四区视频 | 精品国产伦一区二区三区 | 狠狠干干 | 欧美日韩国产亚洲乱码字幕 | 亚洲综合在线观看视频 | 不卡av电影在线 | 久久私人影院 | 美女视频黄是免费的 | 久久久久久亚洲精品 | 国产一级久久久 | 在线观看完整版 | 99在线视频免费观看 | 日韩在线免费高清视频 | 91女神的呻吟细腰翘臀美女 | 日本在线中文在线 | 日批在线观看 | 亚洲日本成人网 | 免费看的av片 | 97精品伊人 | 日韩在线一二三区 | 久草在线免费看视频 | 啪啪小视频网站 | 黄色av网站在线免费观看 | 天天爱天天射 | 国产精品一区二区精品视频免费看 | www.91国产 | 中文字幕视频播放 | 在线国产一区二区 | 久草五月 | av超碰在线 | 亚洲久在线 | 狠狠干在线 | 人人看人人草 | 午夜久久影视 | 人人狠狠综合久久亚洲婷 | 国产精品成 | 欧美另类高潮 | 天天干天天玩天天操 | 毛片网免费 | 久久成人免费 | 亚洲欧美日韩在线看 | 狠狠干综合 | 五月婷婷播播 | 国产午夜麻豆影院在线观看 | 99精品国产亚洲 | 国产成人av免费在线观看 | 五月丁香| 在线亚洲午夜片av大片 | 九色91视频| 久久久久久久电影 | 黄色毛片在线 | 又色又爽又黄 | 久久成人在线视频 | 久久综合五月婷婷 | 97在线资源 | 中文字幕色在线 | 国产精品精品久久久 | 91av网站在线观看 | 久久9999久久| 成人精品电影 | 97夜夜澡人人双人人人喊 | 成人午夜黄色影院 | 伊人欧美| 91麻豆精品国产91久久久久久久久 | 国产成本人视频在线观看 | 99精品国产一区二区三区麻豆 | 在线久热| 国产伦理久久 | 九九九在线 | 亚洲2019精品| 色婷婷久久 | 国产黄色一级大片 | 国产黄网在线 | 91av成人| 久草在线中文888 | 波多野结衣在线中文字幕 | 中文字幕91在线 | av在线日韩| 国产精品入口传媒 | 91网站免费观看 | 国产精品va| 亚洲精品在线视频 | 国产成人精品免费在线观看 | 亚洲视频在线看 | 欧美性色综合网站 | 337p日本欧洲亚洲大胆裸体艺术 | 国产视频一区在线免费观看 | www.久久精品视频 | 亚洲精品综合一区二区 | 香蕉91视频| 黄色大全免费网站 | 精品久久久久免费极品大片 | 天天操天天拍 | 久草网免费 | 久久免费a | 久久天天躁狠狠躁夜夜不卡公司 | avove黑丝| 日韩中文在线观看 | 黄色免费在线看 | 亚洲精品一区二区三区高潮 | 国产亚洲精品久久久久久电影 | 九九免费精品 | 天天撸夜夜操 | av资源在线看 | 久久午夜电影 | 91成人精品一区在线播放69 | 国产一区二区综合 | 欧美aaa级片| 国产精品原创 | 亚洲一区二区三区毛片 | 亚洲国产片色 | 97视频精品 | 四虎成人精品永久免费av九九 | 久久你懂的 | 国偷自产视频一区二区久 | 69国产盗摄一区二区三区五区 | 国产亚洲精品久久久久秋 | 日韩欧美在线视频一区二区 | 国产在线污 | 久久综合九色欧美综合狠狠 | 久久99精品久久久久久清纯直播 | 国产在线视频一区二区 | 激情在线免费视频 | 日本午夜在线亚洲.国产 | 成人免费在线播放 | 成人av电影在线播放 | 色www. | 在线超碰av | 中文字幕第一页在线vr | 波多野结衣视频一区二区三区 | 麻豆国产在线播放 | 日韩免费在线视频 | 激情在线网 | 91亚洲欧美 | 天堂av在线| 91伊人久久大香线蕉蜜芽人口 | 91色偷偷| 国产一区成人 | av片无限看 | 久久久久久久av | 欧美成人久久 | 亚洲成年人在线播放 | 亚洲女在线 | 在线观看视频中文字幕 | 日韩99热| 免费在线观看av不卡 | 国产免费久久精品 | www.天堂av | 国产精品国产亚洲精品看不卡 | 黄a网| 特级片免费看 | 天天操综合网站 | 免费毛片一区二区三区久久久 | 国产一级免费在线观看 | 超级碰碰碰视频 | 精品在线观看一区二区 | 韩日精品在线观看 | 天天操天天操天天操天天操天天操天天操 | 日韩电影中文,亚洲精品乱码 | 免费色视频网站 | 91在线播放综合 | 91精选 | 91精品夜夜 | 久久免费的精品国产v∧ | 亚洲国产中文在线观看 | 欧美在线观看视频免费 | 最近日本中文字幕a | 亚洲 欧洲 国产 精品 | 在线免费观看视频一区二区三区 | 久久亚洲婷婷 | 狠狠干在线播放 | 最近中文字幕免费 | 美女网站视频免费都是黄 | 国产精品初高中精品久久 | 免费毛片一区二区三区久久久 | 色综合天天射 | 欧美三级高清 | 久久久综合电影 | 高清精品视频 | 国产综合精品一区二区三区 | 久久综合成人 | 欧美日韩一区二区在线 | 日韩精品免费一区二区 | 亚洲精品456在线播放乱码 | 欧美久久久一区二区三区 | 久久久久免费 | 在线观看国产中文字幕 | 久久久久久久久久久久久久av | 人人爱人人射 | 人人爽人人爽人人爽人人爽 | 国产成人精品一区二区三区在线观看 | 91人人人| 日韩精品免费在线播放 | 亚洲三级黄| 91香蕉视频720p | 久久99精品久久久久久清纯直播 | 最新超碰 | 久久久免费在线观看 | 日韩成人免费在线观看 | 久久99电影 | 亚洲精品美女久久久久网站 | 色婷婷激婷婷情综天天 | 亚洲精品午夜久久久久久久 | 天天操天天干天天摸 | 久久久久免费精品 | 久草视频在线新免费 | 天天舔夜夜操 | 免费黄色在线播放 | 狠狠亚洲 | 免费色av | 日韩国产精品久久 | 2019中文字幕第一页 | 亚洲视频大全 | 中文字幕一区2区3区 | 亚洲精品免费在线观看视频 | 国内少妇自拍视频一区 | 欧美日韩一区二区视频在线观看 | 国产精品久久久久久婷婷天堂 | 久草在线免费在线观看 | 91成人免费| 午夜视频福利 | 天操夜夜操 | 成人黄色在线 | 92中文资源在线 | 麻豆va一区二区三区久久浪 | 亚洲精品乱码久久久久久9色 | 国产理论在线 | 国产情侣一区 | 在线国产小视频 | 亚洲精品动漫成人3d无尽在线 | av在线不卡观看 | 成人免费影院 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 欧美一级片免费观看 | 欧美-第1页-屁屁影院 | www.69xx| 久久国产精品一区二区三区四区 | 久久亚洲二区 | 日韩动漫免费观看高清完整版在线观看 | 国产 视频 久久 | 香蕉视频久久 | av网站在线观看播放 | 欧美日韩在线电影 | 91精品伦理 | 精品久久毛片 | 黄色av网站在线观看免费 | 欧美一级黄色视屏 | 中文字幕影片免费在线观看 | 久久av电影 | 在线免费视频你懂的 | 国产精品久久久久久久av电影 | 在线看日韩av | 一区二区三区四区五区六区 | 久久99亚洲精品 | 美女网站一区 | 男女激情麻豆 | 夜色资源站国产www在线视频 | 在线免费av观看 | 国产精品久久久久久久久久久久午夜 | 久久久www成人免费精品张筱雨 | 国产喷水在线 | 国产理伦在线 | 日韩午夜视频在线观看 | 日韩专区在线 | 视频一区在线免费观看 | 在线播放 日韩专区 | 中文字幕在线视频国产 | 亚洲理论电影 | 香蕉视频国产在线 | www激情久久 | 天天干天天爽 | 午夜精品视频福利 | 色五丁香 | 深夜成人av | 国产日韩欧美精品在线观看 | 国产亚洲午夜高清国产拍精品 | 久久久久国产精品视频 | 在线观看成人av | 天天色天天草天天射 | 九色激情网 | 婷婷色中文网 | 九九免费精品 | 国产91学生粉嫩喷水 | 日本性动态图 | 99免费精品视频 | 91久久国产精品 | 五月天伊人 | 色干综合 | 青春草视频 | 我要色综合天天 | 色综合中文综合网 | 国产理论免费 | 久久久99精品免费观看app | 色先锋av资源中文字幕 | 精品一区二区三区久久 | 日韩高清三区 | 精品久久久久久久久久久久 | 亚洲三级精品 | 久久试看| 久久久久久久久久久久99 | 亚洲第一香蕉视频 | 97久久精品午夜一区二区 | 久久综合桃花 | 日本一区二区不卡高清 | 五月开心六月伊人色婷婷 | 亚洲综合在线视频 | 狠狠地操 | 国内亚洲精品 | 蜜桃麻豆www久久囤产精品 | 亚洲欧美国内爽妇网 | 久久精品99国产精品亚洲最刺激 | 国产福利91精品一区二区三区 | 国产一在线精品一区在线观看 | 91一区啪爱嗯打偷拍欧美 | 亚洲在线观看av | 又爽又黄又无遮挡网站动态图 | 在线色亚洲 | 国产91成人 | 中文字幕av电影下载 | 午夜在线观看 | 亚州激情视频 | 亚洲精品永久免费视频 | 精品一区电影国产 | 91伊人久久大香线蕉蜜芽人口 | 中文字幕乱码电影 | 91麻豆网| 最近日本mv字幕免费观看 | 中文字幕视频一区 | 色婷婷激情电影 | 免费国产ww| 欧美精品天堂 | 久久综合久久综合久久综合 | 久草网站在线观看 | 国产午夜精品理论片在线 | 嫩草伊人久久精品少妇av | 久久综合久久综合久久 | 天天做日日爱夜夜爽 | 久久久网页 | 色资源在线 | 狠狠干天天射 | 九九九热精品免费视频观看网站 | 片网址 | 日日躁夜夜躁aaaaxxxx | 香蕉视频最新网址 | 天天爱综合 | 人人超碰人人 | 亚洲国产美女精品久久久久∴ | 国产精品久久久久久一区二区三区 | 91精品国产入口 | 91九色最新地址 | 亚洲天天综合 | 国产精品久久久一区二区三区网站 | 国产欧美精品一区二区三区四区 | 久久精品国产一区二区 | 久草97| 精品福利视频在线观看 | 日韩欧美69| 婷婷六月天综合 | 91视频88av| 国产不卡在线视频 | 亚洲一区动漫 | av高清影院 | 精品国产精品久久一区免费式 | 国内精品久久久久久久97牛牛 | 中中文字幕av在线 | 国产精品一区二区白浆 | 99视频在线观看一区三区 | 欧美性受极品xxxx喷水 | 五月激情丁香图片 | 免费又黄又爽视频 | 99人成在线观看视频 | 久久久精品影视 | 欧美在线视频一区二区三区 | 中文字幕在线成人 | 97视频免费在线观看 | 五月激情片 | 九色精品免费永久在线 | 人人狠狠 | 一本之道乱码区 | 成人免费色 | 日韩av网站在线播放 | 狠狠久久伊人 | 精品久久久久久电影 | 亚洲天天做 | 国产h在线播放 | 久久理论视频 | 久久国产热视频 | 91精品久久久久久久91蜜桃 | 成人动图 | 国产一卡在线 | 超碰999 | 亚洲婷婷免费 | 欧美精品久久久 | 久久视频在线看 | 久久久久国产视频 | 久久天堂精品视频 | 中文字幕在线观看你懂的 | 91精品国产麻豆国产自产影视 | 久久成人亚洲欧美电影 | 精品久久一 | 日韩视频免费看 | 91在线看黄 | 色婷婷88av视频一二三区 | 欧美日韩中文字幕视频 | 亚洲激情视频在线观看 | 精品一区二区在线免费观看 | 4438全国亚洲精品在线观看视频 | 国产中文字幕一区二区 | 久久再线视频 | 色停停五月天 | 国产精品99久久久久久久久久久久 | 日日夜夜天天综合 | 久久99亚洲精品久久 | 狠狠狠狠狠干 | 久久精品99国产精品酒店日本 | 亚洲婷婷伊人 | 97在线免费视频观看 | 久久影院午夜论 | 少妇性aaaaaaaaa视频 | 99这里只有精品99 | 久久精品小视频 | 亚洲一区视频免费观看 | 精品视频资源站 | 久久影院中文字幕 | 免费大片av| 日日噜噜噜噜夜夜爽亚洲精品 | 在线观看一级视频 | 最新高清无码专区 | 在线观看日韩av | 午夜精品久久久久久久99 | 亚洲欧洲xxxx | 久久国语露脸国产精品电影 | 91精品国产三级a在线观看 | 黄网站色视频免费观看 | 亚洲国内精品在线 | 久久久久久久久久影院 | 日本精品久久久久中文字幕 | 在线观看日韩免费视频 | 丁香六月网| 丁香影院在线 | 国产精品美女久久久久久久久久久 | 激情视频区 | 日韩av电影国产 | 最新中文字幕在线资源 | 国产麻豆果冻传媒在线观看 | 91福利影院在线观看 | 久久精品美女视频 | 欧美综合色在线图区 | 91精品国产欧美一区二区成人 | 免费网站看av片 | 最近的中文字幕大全免费版 | 黄在线免费观看 | 91视频成人免费 | 国产高清视频在线观看 | 欧美日韩另类在线观看 | 日韩日韩日韩日韩 | 亚洲粉嫩av | 国产啊v在线 | 五月天婷婷在线播放 | 六月婷婷久香在线视频 | av亚洲产国偷v产偷v自拍小说 | 日日操夜| 综合久久一本 | 人人藻人人澡人人爽 | 成人a视频在线观看 | 一本一道久久a久久综合蜜桃 | 狠狠狠狠狠狠天天爱 | 91超国产| 69成人在线| 狠狠色狠狠色终合网 | 69久久久 | 久久这里只有精品久久 | 国产黄色免费观看 | 亚洲日本三级 | 国产玖玖视频 | 91精品视频在线免费观看 | 激情综合中文娱乐网 | 91高清在线 | 在线之家免费在线观看电影 | 最近中文字幕高清字幕免费mv | 久久a国产 | 精品嫩模福利一区二区蜜臀 | 国产精品乱码久久 | 亚洲精品66 | 久久久久综合网 | 成人影视免费 | av 一区二区三区 | 蜜桃av久久久亚洲精品 | 国产精品美女久久久久久 | 欧美日韩在线网站 | 国产精品一区二区久久精品爱微奶 | 在线看成人 | 一色屋精品视频在线观看 | 欧美日韩高清一区二区 国产亚洲免费看 | 免费视频区 | 不卡av免费在线观看 | 国产精品自产拍在线观看网站 | 国产麻豆剧传媒免费观看 | 亚洲色视频 | 婷婷在线播放 | 开心婷婷色 | 亚洲激精日韩激精欧美精品 | 在线观看成人国产 | 久久免费视频观看 | 亚洲午夜精品一区二区三区电影院 | 黄色电影网站在线观看 | 涩涩爱夜夜爱 | 国产精品精品 | 成人av一二三区 | 国产中文字幕在线观看 | 久久综合毛片 | 91精品国产91久久久久久三级 | 久久高清国产视频 | 六月婷婷久香在线视频 | 日韩免费在线观看视频 | 亚洲精品视频在线 | 视频在线观看亚洲 | 91亚色视频在线观看 | 黄色毛片在线观看 | 三级av网| 日本婷婷色 | 视频一区在线播放 | 天天干天天操人体 | 中文字幕刺激在线 | 国产亚洲成av人片在线观看桃 | 天天射日 | 亚洲精品国产精品乱码在线观看 | 国产一区二区电影在线观看 | www.超碰97.com | 深爱婷婷激情 | 成人黄色影片在线 | 婷婷伊人综合亚洲综合网 | 99精品在线看 | www.亚洲精品在线 | 免费三级网 | 国产录像在线观看 | 日韩av一卡二卡三卡 | 婷婷av在线| 最近高清中文在线字幕在线观看 | 国产精品短视频 | 91视频免费国产 | 亚洲jizzjizz日本少妇 | 91视频下载 | 欧美日韩免费网站 | 久久艹艹 | 成人午夜影院 | 久久丁香网 | 久久激五月天综合精品 | 国产无遮挡猛进猛出免费软件 | 午夜视频在线观看一区二区 | 在线黄色国产电影 | 999精品| a天堂最新版中文在线地址 久久99久久精品国产 | 成人黄色在线 | 人人要人人澡人人爽人人dvd | 黄色软件在线看 | 国产日韩一区在线 | 国产免费大片 | 亚洲精品一区二区三区高潮 | 九九久久成人 | 在线观看91av | 麻豆传媒视频观看 | 精品影院| 国产精品美乳一区二区免费 | 九九久久免费视频 | av电影中文字幕在线观看 | 深夜精品福利 | 欧美精品一二三 | 婷婷电影在线观看 | 国产自制av | 国产精品99久久久久久宅男 | 国产在线观看高清视频 | 麻豆视频在线免费 | 蜜臀av性久久久久av蜜臀三区 | 日本丶国产丶欧美色综合 | 欧美一二三区在线播放 | 国产黄色大全 | 狠狠干夜夜爱 | 福利视频一二区 | 国产无遮挡又黄又爽馒头漫画 | 久久永久免费视频 | 黄色一级免费 | 丁香婷婷网 | 日韩欧美高清视频在线观看 | 国内外激情视频 | 91精品国产乱码久久 | 99久久精品久久亚洲精品 | 黄色看片 | 亚洲国产无 | 日日激情 | 美女性爽视频国产免费app | 福利av影院 | 最近中文字幕视频网 | 91视频久久久久 | 中文字幕亚洲综合久久五月天色无吗'' | 99久久久久久国产精品 | 久久国产麻豆 | 亚洲欧美在线观看视频 | 久久综合九色综合网站 | 二区三区在线 | 四虎成人精品 | 色综合久久五月 | 亚洲成年片 | 69热国产视频 | 成人欧美一区二区三区在线观看 | 丰满少妇高潮在线观看 | 亚洲激情 欧美激情 | 国产色一区 | 97麻豆视频| 精品免费一区二区三区 | 亚洲一区网 | 91香蕉视频在线 | 午夜精品福利影院 | 天天干天天操天天入 | 福利片视频区 | 久久九九国产精品 | 丁香资源影视免费观看 | 免费视频区 | 国产九九九精品视频 | 久久女同性恋中文字幕 | 国产视频一二三 | 97网| 国产成人一区二区三区在线观看 | 中文字幕日本电影 | 午夜影视剧场 | 久久久五月婷婷 | 深夜福利视频在线观看 | 久久影视一区 | 日韩视频一区二区 | 中文字幕av最新更新 | 九七人人干 | 亚洲黄色av一区 | 国产精品小视频网站 | 欧美激情第八页 | 一级片视频免费观看 | 亚洲精品乱码久久久久v最新版 | 久久精品一二三区白丝高潮 | 国产精品日韩久久久久 | 国产一区 在线播放 | 亚州国产精品视频 | 日本久久久久久久久 | 99热国产在线中文 | 久久精品视频在线看 | 丝袜制服天堂 |