1.1 請(qǐng)談?wù)勀銓?duì) MVC、MVP、MVVM、MVI的理解?
MVC
Model:主要用于網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)、業(yè)務(wù)邏輯處理等操作。
View:用于展示UI,一般采用XML文件進(jìn)行界面的描述。
Controller:控制層的重任落在了眾多Activity上,Activity需要交割業(yè)務(wù)邏輯至Model層處理。
事件從View層流向Controller層,經(jīng)過(guò)Model層處理,然后通知View層更新。
缺點(diǎn):
- Controller層,也就是activity承擔(dān)了部分View層的職責(zé),導(dǎo)致該層過(guò)于臃腫。
- 在MVC中,Model層處理完數(shù)據(jù)后,直接通知View層更新,因此MV耦合性強(qiáng)。
MVP
Model:數(shù)據(jù)處理層。
View:視圖顯示層。
Presenter:業(yè)務(wù)邏輯處理層。
通過(guò)接口,V和P間接相互持有引用,P和M間接相互持有引用,但是V和M完全解耦,而且把業(yè)務(wù)邏輯從activity中移到 P中,減輕了activity的壓力。
缺點(diǎn):
當(dāng)View層足夠復(fù)雜的時(shí)候,View接口會(huì)變得很龐大。
MVVM
Model:數(shù)據(jù)處理層。
View:視圖顯示層。
ViewModel:視圖模型,通過(guò)框架實(shí)現(xiàn)View層和Model層的雙向綁定。
優(yōu)點(diǎn):
- View和Model雙向綁定,一方的改變都會(huì)影響另一方,開(kāi)發(fā)者不用再去手動(dòng)修改UI的數(shù)據(jù)。
- 不需要findViewById也不需要butterknife,不需要拿到具體的View去設(shè)置數(shù)據(jù)綁定監(jiān)聽(tīng)器等等,這些都可以用DataBinding完成。
- View和Model的雙向綁定是支持生命周期檢測(cè)的,不會(huì)擔(dān)心頁(yè)面銷(xiāo)毀了還有回調(diào)發(fā)生,這個(gè)由Lifecycle完成。
- 不會(huì)像MVC一樣導(dǎo)致Activity中代碼量巨大,也不會(huì)像MVP一樣出現(xiàn)大量的View和Presenter接口,項(xiàng)目結(jié)構(gòu)更加低耦合。
缺點(diǎn):
由于數(shù)據(jù)和視圖的雙向綁定,導(dǎo)致出現(xiàn)問(wèn)題時(shí)不好定位來(lái)源,有可能數(shù)據(jù)問(wèn)題導(dǎo)致,也有可能業(yè)務(wù)邏輯中對(duì)視圖屬性的修改導(dǎo)致。
VMI
View: Activity 和 Layout XML 文件,與 MVVM 中 View 的概念相同;
Intent: 定義數(shù)據(jù)操作,是將數(shù)據(jù)傳到 Model 的唯一來(lái)源,相比 MVVM 是新的概念;
ViewModel: 存儲(chǔ)視圖狀態(tài),負(fù)責(zé)處理表現(xiàn)邏輯,并將 ViewState 設(shè)置給可觀察數(shù)據(jù)容器;
ViewState: 一個(gè)數(shù)據(jù)類(lèi),包含頁(yè)面狀態(tài)和對(duì)應(yīng)的數(shù)據(jù)。
唯一可信源: 數(shù)據(jù)只有一個(gè)來(lái)源(ViewModel),與 MVVM 的思想相同;
單數(shù)據(jù)流: View 和 ViewModel 之間只有一個(gè)數(shù)據(jù)流,只有一個(gè)地方可以修改數(shù)據(jù),確保數(shù)據(jù)是安全穩(wěn)定的。并且 View 只需要訂閱一個(gè) ViewState 就可以獲取所有狀態(tài)和數(shù)據(jù),相比 MVVM 是新的特性;
響應(yīng)式: ViewState 包含頁(yè)面當(dāng)前的狀態(tài)和數(shù)據(jù),View 通過(guò)訂閱 ViewState 就可以完成頁(yè)面刷新,相比于 MVVM 是新的特性。
優(yōu)點(diǎn):
Mvi模式采用單向流動(dòng)的設(shè)計(jì)思路,用戶意圖Intent通過(guò)View層傳輸?shù)組odel,數(shù)據(jù)處理完畢之后,再返回狀態(tài)State到View層顯示,由此一個(gè)循環(huán)結(jié)束.一個(gè)界面的所有東西,都是不斷循環(huán)在一個(gè)閉環(huán)內(nèi),所以數(shù)據(jù)的流向都能方便地監(jiān)測(cè)到,哪一環(huán)出現(xiàn)問(wèn)題就能快速地定位到問(wèn)題所在.
缺點(diǎn):
State 膨脹: 所有視圖變化都轉(zhuǎn)換為 ViewState,還需要管理不同狀態(tài)下對(duì)應(yīng)的數(shù)據(jù)。實(shí)踐中應(yīng)該根據(jù)狀態(tài)之間的關(guān)聯(lián)程度來(lái)決定使用單流還是多流;
內(nèi)存開(kāi)銷(xiāo): ViewState 是不可變類(lèi),狀態(tài)變更時(shí)需要?jiǎng)?chuàng)建新的對(duì)象,存在一定內(nèi)存開(kāi)銷(xiāo);
局部刷新: View 根據(jù) ViewState 響應(yīng),不易實(shí)現(xiàn)局部 Diff 刷新,可以使用 Flow#distinctUntilChanged() 來(lái)刷新來(lái)減少不必要的刷新。
選擇:
1、如果項(xiàng)目簡(jiǎn)單,沒(méi)什么復(fù)雜性,未來(lái)改動(dòng)也不大的話,那就不要用設(shè)計(jì)模式或者架構(gòu)方法,只需要將每個(gè)模塊封裝好,方便調(diào)用即可,不要為了使用設(shè)計(jì)模式或架構(gòu)方法而使用。
2、對(duì)于偏向展示型的app,絕大多數(shù)業(yè)務(wù)邏輯都在后端,app主要功能就是展示數(shù)據(jù),交互等,建議使用mvvm。
3、對(duì)于工具類(lèi)或者需要寫(xiě)很多業(yè)務(wù)邏輯app,使用mvp或者mvvm都可。
4、MVI 與前者的主要區(qū)別不在于強(qiáng)調(diào)嚴(yán)格的單向數(shù)據(jù)流,而在于從命令式的開(kāi)發(fā)模式,轉(zhuǎn)變?yōu)轫憫?yīng)式的開(kāi)發(fā)模式。
我們并不是說(shuō)越新潮,越復(fù)雜的架構(gòu)就是最好的,只有合適的架構(gòu)才是最好的。
1.2 分別介紹下你所知道Android的幾種存儲(chǔ)方式?
網(wǎng)絡(luò)存儲(chǔ) :一般就是http get或http post 從服務(wù)器獲取數(shù)據(jù),業(yè)務(wù)數(shù)據(jù)獲取的常用辦法。
SqlLite:將數(shù)據(jù)緩存到本地?cái)?shù)據(jù)庫(kù),可用于存儲(chǔ)大量不經(jīng)常改變的數(shù)據(jù),可配合ContentProvider使用。
文件存儲(chǔ):將一些不太敏感的數(shù)據(jù)保存到本地,SharePreference:用XML格式文件存儲(chǔ)數(shù)據(jù),在data/data/<pa'ka'geName>/shared_prefs下,不支持?jǐn)?shù)據(jù)頻繁讀寫(xiě),頻繁讀寫(xiě)會(huì)造成數(shù)據(jù)錯(cuò)亂。
ContentProvider:四大組件之一,一般配合SqlLite、SharePreference、文件存儲(chǔ)使用,支持?jǐn)?shù)據(jù)的并發(fā)讀取。
1.3 簡(jiǎn)述下熱修復(fù)的原理?
熱修復(fù)分為三個(gè)部分,分別是Java代碼部分熱修復(fù),Native代碼部分熱修復(fù),還有資源熱修復(fù)。
資源部分熱更新直接反射更改所有保存的AssetManager和Resources對(duì)象就行(可能需要重啟應(yīng)用)
Native代碼部分也很簡(jiǎn)單,系統(tǒng)找到一個(gè)so文件的路徑是根據(jù)ClassLoader找的,修改ClassLoader里保存的路徑就行(可能需要重啟應(yīng)用)
Java部分的話目前主流有兩種方式,一種是Java派,一種是Native派。
- java派:通過(guò)修改ClassLoader來(lái)讓系統(tǒng)優(yōu)先加載補(bǔ)丁包里的類(lèi)
代表作有騰訊的tinker,谷歌官方的Instant Run,包括multidex也是采用的這種方案優(yōu)點(diǎn)是穩(wěn)定性較好,缺點(diǎn)是可能需要重啟應(yīng)用 - native派:通過(guò)內(nèi)存操作實(shí)現(xiàn),比如方法替換等
代表作是阿里的SopHix,如果算上hook框架的話,還有dexposed,epic等等
優(yōu)點(diǎn)是即時(shí)生效無(wú)需重啟,缺點(diǎn)是穩(wěn)定性不好:如果采用方法替換方式實(shí)現(xiàn),假如這個(gè)方法被內(nèi)聯(lián)/Sharpening優(yōu)化了,那么就失效了;inline hook則無(wú)法修改超短方法。
熱修復(fù)后使用反射調(diào)用對(duì)應(yīng)方法時(shí)可能發(fā)生IllegalArgumentException。
1.4 談?wù)勅绾芜m配更多機(jī)型的?
- 一般來(lái)說(shuō)主要針對(duì)屏幕適配,最小寬度適配和今日頭條density適配
- 權(quán)限適配,安卓6.0的運(yùn)行時(shí)權(quán)限,這里有坑,6.0以前,Vivo有i管家進(jìn)行權(quán)限管理,魅族自帶有權(quán)限管理,還有其他第三方軟件進(jìn)行權(quán)限限制,導(dǎo)致權(quán)限不可用
- 異形屏幕適配,一般來(lái)說(shuō)都是劉海,水滴,挖孔部分不進(jìn)行使用或者就直接不管不顯示缺失部分,可以滿足大部分需求,小部分需求需要使用異形部分的需要按手機(jī)型號(hào)進(jìn)行特定適配,官網(wǎng)都有適配方法
- 安卓系統(tǒng)適配,及時(shí)關(guān)注新系統(tǒng)新特性,使情況修改 targetSdk
- 語(yǔ)言,Left Right和Start End,這些適配基本不需要太大關(guān)注
- “和ios一樣”,口才或者腦細(xì)胞適配,能說(shuō)服就下班,不能就加班
1.5 請(qǐng)談?wù)勀闶侨绾芜M(jìn)行多渠道打包的?
配置gradle實(shí)現(xiàn)多渠道打包
每當(dāng)應(yīng)用發(fā)布一個(gè)新的版本的時(shí)候,我們會(huì)分發(fā)到每一個(gè)應(yīng)用市場(chǎng)中去,比如,360手機(jī)助手,小米應(yīng)用市場(chǎng),華為應(yīng)用市場(chǎng)等。為了能夠統(tǒng)計(jì)每個(gè)應(yīng)用市場(chǎng)的下載量,活躍量我們必須用一個(gè)標(biāo)記來(lái)區(qū)分這些不同市場(chǎng)分發(fā)下去的應(yīng)用,渠道號(hào)也就應(yīng)運(yùn)而生。隨著渠道的不斷增加,需要生成的渠道包也就越來(lái)越多。 在打包的過(guò)程中,我們一般都是使用gradle來(lái)進(jìn)行的。gradle為我們的打包提高了很多的便利,多渠道打包也可以輕松實(shí)現(xiàn)。
- 首先在AndroidManifest.xml文件中定義一個(gè)metadata
<meta-data android:name="CHANNEL" android:value="${CHANNEL_VALUE}" />
2.然后在gradle文件中設(shè)置一下productFlavors
android {
productFlavors {
xiaomi {
manifestPlaceholders =[CHANNEL_VALUE: "xiaomi"]
}
_360 {
manifestPlaceholders =[CHANNEL_VALUE: "_360"]
}
baidu {
manifestPlaceholders =[CHANNEL_VALUE: "baidu"]
}
wandoujia {
manifestPlaceholders =[CHANNEL_VALUE: "wandoujia"]
}
}
}
productFlavors是為了在同一個(gè)項(xiàng)目中創(chuàng)建應(yīng)用的不同版本。具體的配置信息可以看官方說(shuō)明。
- 執(zhí)行g(shù)radle AS就可以將所有的渠道包輸出了。
gradle實(shí)現(xiàn)多渠道打包的缺點(diǎn)
雖然gradle配置多渠道打包很簡(jiǎn)單,也很方便,但是這種方式存在一個(gè)致命的缺陷,那就是費(fèi)時(shí)間。因?yàn)锳ndroidManifest.xml文件被修改過(guò)了,所以所有的包都必須重新編譯簽名。一般來(lái)說(shuō)100個(gè)渠道包就要至少一個(gè)小時(shí)的時(shí)間,這一個(gè)小時(shí)5杯咖啡都不夠等的。更要命的是萬(wàn)一哪里需要微調(diào)一下代碼或者文案,那么不好意思,一切又得重頭來(lái)。這就很麻煩了,所以有沒(méi)有什么方法可以快速完成打包呢?我們繼續(xù)往下看。
1.6多渠道快速打包
快速打包方案Version_1.0
如上所說(shuō),我們?nèi)サ叫畔⒅皇切薷牧艘幌耺anifest文件里面的一個(gè)meta-data的值而已,有沒(méi)有什么辦法可以不需要重新構(gòu)建代碼呢?答案是肯定的。我們可以使用apktool,反編譯我們的APK文件。
apktool d yourApkName build
經(jīng)過(guò)解碼后,我們會(huì)得到如下文件:
我們發(fā)現(xiàn)我們需要修改的manifest文件就在里面,所以通過(guò)命令可以修改下他的內(nèi)容,然后重新打包,就可以生成一個(gè)全新的渠道包了,省去了重新編譯構(gòu)建代碼的過(guò)程。使用一下Python腳本,將manifest文件里面channel信息進(jìn)行替換。
import re
def replace_channel(channel, manifest):
pattern = r'(<metadata\s+android:name="channel"\s+android:value=")(\S+)("\s+/>)'
replacement = r"\g<1>{channel}\g<3>".format(channel=channel)
return re.sub(pattern, replacement, manifest)
然后通過(guò)apktool重新將文件夾打包生成APK。
apktool b build your_unsigned_apk
最后,使用jarsigner重新簽名apk:
jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore your_keystore_path -storepass your_storepass -signedjar your_signed_apk, your_unsigned_apk, your_alias
通過(guò)這上面的一系列過(guò)程,我們可以實(shí)現(xiàn)不重新編譯構(gòu)建項(xiàng)目就生成不同的渠道包。這會(huì)節(jié)省很多的時(shí)間。但是隨著渠道包增加,重新簽名也會(huì)占用很大一部分時(shí)間,那能不能不重新簽名呢?
分析簽名的算法后發(fā)現(xiàn),在打包過(guò)程后的META-INF文件夾下面添加空白文件是不會(huì)對(duì)簽名的結(jié)果產(chǎn)生影響的。
所以我們只要像META_INF文件夾里面寫(xiě)入空白的文件來(lái)標(biāo)識(shí)渠道號(hào)就可以了。
通過(guò)Python腳本像APK文件中寫(xiě)入渠道:
import zipfile
zipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "METAINF/mtchannel_{channel}".format(channel=your_channel)
zipped.write(your_empty_file,empty_channel_file)
執(zhí)行后會(huì)在META-INF文件夾下面生成一個(gè)空白文件:
然后我們?cè)陧?xiàng)目中去讀取這個(gè)空白文件:
public static String getChannel(Context context) {
ApplicationInfo appinfo = context.getApplicationInfo();
String sourceDir = appinfo.sourceDir;
String ret = "";
ZipFile zipfile = null;
try {
zipfile = new ZipFile(sourceDir);
Enumeration<?> entries = zipfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.startsWith("mtchannel")) {
ret = entryName;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipfile != null) {
try {
zipfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
String[] split = ret.split("_");
if (split != null && split.length >= 2) {
return
ret.substring(split[0].length() + 1);
} else {
return "";
}
}
這樣每生成一個(gè)渠道包只需要復(fù)制一下APK,然后添加一個(gè)空態(tài)的文件到META-INF下面就可以了,這樣100個(gè)渠道包一分鐘之內(nèi)就可以搞定了。
快速打包方案Version_2.0
上面的方案基本上已經(jīng)比較完美的解決我們打包的問(wèn)題了,然而好景不長(zhǎng),Google在Android 7.0中更新了應(yīng)用的簽名算法-APK Signature Scheme v2,它是一個(gè)對(duì)全文件進(jìn)行簽名的方案,能提供更快的應(yīng)用安裝時(shí)間、對(duì)未授權(quán)APK文件的更改提供更多保護(hù),在默認(rèn)情況下,Android Gradle 2.2.0插件會(huì)使用APK Signature Scheme v2和傳統(tǒng)簽名方案來(lái)簽署你的應(yīng)用。因?yàn)槭菍?duì)全文件進(jìn)行簽名的,所以之前的添加空白文件的方案就沒(méi)有作用了。
不過(guò)目前這個(gè)方案還不是強(qiáng)制性的,我們可以選擇在gradle配置文件中將其關(guān)閉:
android {
defaultConfig { ...}
signingConfigs {
release {
storeFile file ("myreleasekey.keystore")
storePassword "password"
keyAlias "MyReleaseKey"
keyPassword "password"
v2SigningEnabled false
}
}
}
那么新的簽名方案對(duì)已有的渠道生成方案有什么影響呢?
下圖是新的應(yīng)用簽名方案和舊的簽名方案的一個(gè)對(duì)比:
新的簽名方案會(huì)在ZIP文件格式的 Central Directory 區(qū)塊所在文件位置的前面添加一個(gè)APK Signing Block區(qū)塊,下面按照Z(yǔ)IP文件的格式來(lái)分析新應(yīng)用簽名方案簽名后的APK包。 整個(gè)APK(ZIP文件格式)會(huì)被分為以下四個(gè)區(qū)塊:
- Contents of ZIP entries(from offset 0 until the start of APK Signing Block)
- APK Signing Block
- ZIP Central Directory
- ZIP End of Central Directory
新應(yīng)用簽名方案的簽名信息會(huì)被保存在區(qū)塊2(APK Signing Block)中, 而區(qū)塊1(Contents of ZIP entries)、區(qū)塊3(ZIP Central Directory)、區(qū)塊4(ZIP End of Central Directory)是受保護(hù)的,在簽名后任何對(duì)區(qū)塊1、3、4的修改都逃不過(guò)新的應(yīng)用簽名方案的檢查。
之前的渠道包生成方案是通過(guò)在META-INF目錄下添加空文件,用空文件的名稱來(lái)作為渠道的唯一標(biāo)識(shí),之前在META-INF下添加文件是不需要重新簽名應(yīng)用的,這樣會(huì)節(jié)省不少打包的時(shí)間,從而提高打渠道包的速度。但在新的應(yīng)用簽名方案下META-INF已經(jīng)被列入了保護(hù)區(qū)了,向META-INF添加空文件的方案會(huì)對(duì)區(qū)塊1、3、4都會(huì)有影響,新應(yīng)用簽名方案簽署的應(yīng)用經(jīng)過(guò)我們舊的生成渠道包方案處理后,在安裝時(shí)會(huì)報(bào)以下錯(cuò)誤:
Failure
[INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from base.apk:
META-INF/CERT.SF indicates base.apk is signed using APK Signature Scheme v2,
but no such signature was found. Signature stripped?]
區(qū)塊1,3,4是受保護(hù)的,任何的修改都會(huì)引起簽名的不一致,但是區(qū)塊2是不受保護(hù)的,所以能不能在區(qū)塊2上面找到解決辦法呢?首先看一下區(qū)塊2的文件結(jié)構(gòu):
區(qū)塊2中APK Signing Block是由這幾部分組成:2個(gè)用來(lái)標(biāo)示這個(gè)區(qū)塊長(zhǎng)度的8字節(jié) + 這個(gè)區(qū)塊的魔數(shù)(APK Sig Block 42)+ 這個(gè)區(qū)塊所承載的數(shù)據(jù)(ID-value)。
我們重點(diǎn)來(lái)看一下這個(gè)ID-value,它由一個(gè)8字節(jié)的長(zhǎng)度標(biāo)示+4字節(jié)的ID+它的負(fù)載組成。V2的簽名信息是以ID(0x7109871a)的ID-value來(lái)保存在這個(gè)區(qū)塊中,不知大家有沒(méi)有注意這是一組ID-value,也就是說(shuō)它是可以有若干個(gè)這樣的ID-value來(lái)組成,那我們是不是可以在這里做一些文章呢?
對(duì)于簽名的認(rèn)證過(guò)程是這樣的:
- 尋找APK Signing Block,如果能夠找到,則進(jìn)行驗(yàn)證,驗(yàn)證成功則繼續(xù)進(jìn)行安裝,如果失敗了則終止安裝
- 如果未找到APK Signing Block,則執(zhí)行原來(lái)的簽名驗(yàn)證機(jī)制,也是驗(yàn)證成功則繼續(xù)進(jìn)行安裝,如果失敗了則終止安裝
在校驗(yàn)的時(shí)候,檢驗(yàn)代碼如下:
public static ByteBuffer findApkSignatureSchemeV2Block(ByteBuffer apkSigningBlock, Result result)
throws SignatureNotFoundException {
checkByteOrderLittleEndian(apkSigningBlock);
// FORMAT:
// OFFSET DATA TYPE DESCRIPTION
// * @+0 bytes uint64: size in bytes (excluding this field)
// * @+8 bytes pairs
// * @-24 bytes uint64: size in bytes (same as the one above)
// * @-16 bytes uint128: magic
ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);
int entryCount = 0;
while (pairs.hasRemaining()) {
entryCount++;
if (pairs.remaining() < 8) {
throw new SignatureNotFoundException(
"Insufficient data to read size of APK Signing Block entry #" + entryCount);
}
long lenLong = pairs.getLong();
if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
throw new SignatureNotFoundException("APK Signing Block entry #" + entryCount +
" size out of range: " + lenLong);
}
int len = (int) lenLong;
int nextEntryPos = pairs.position() + len;
if (len > pairs.remaining()) {
throw new SignatureNotFoundException("APK Signing Block entry #" + entryCount +
" size out of range: " + len + ", available: " + pairs.remaining());
}
int id = pairs.getInt();
if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {
return getByteBuffer(pairs, len - 4);
}
result.addWarning(Issue.APK_SIG_BLOCK_UNKNOWN_ENTRY_ID, id);
pairs.position(nextEntryPos);
}
throw new SignatureNotFoundException(
"No APK Signature Scheme v2 block in APK Signing Block");
}
我們可以發(fā)現(xiàn),述代碼中關(guān)鍵的一個(gè)位置是 if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {return getByteBuffer(pairs, len - 4);},通過(guò)源代碼可以看出Android是通過(guò)查找ID為APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a的ID-value,來(lái)獲取APK Signature Scheme v2 Block,對(duì)這個(gè)區(qū)塊中其他的ID-value選擇了忽略。也就是說(shuō),在APKSignature Scheme v2中沒(méi)有看到對(duì)無(wú)法識(shí)別的ID,有相關(guān)處理的介紹。所以我們可以通過(guò)寫(xiě)入自定義的ID-Value來(lái)自定義渠道。
1.6 MVP中你是如何處理Presenter層以防止內(nèi)存泄漏的?
首先 MVP 會(huì)出現(xiàn)內(nèi)存泄漏是因?yàn)?Presenter 層持有 View對(duì)象,一般我們會(huì)把 Activity 做為 View 傳遞到Presenter,Presenter 持有 View對(duì)象,Activity 退出了但是沒(méi)有回收出現(xiàn)內(nèi)存泄漏。
解決辦法:
- Activity onDestroy() 方法中調(diào)用 Presenter 中的方法,把 View 置為 null
- 使用 Lifecycle
- 使用 MVVM
1.7 如何計(jì)算一張圖片所占的內(nèi)存空間大小?
通常情況下圖片占用內(nèi)存的大小:圖片分辨率X像素點(diǎn)大小。
api獲取方法為bitmap.getByteCount()。
如果放入res/drawable下,通過(guò)BitmapFactory.decodeResource()方法加載不同dpi文件下的同一張圖片到內(nèi)存的大小是不一樣(存在分辨率的轉(zhuǎn)換),同時(shí)也與設(shè)備的dpi大小有關(guān)。而放在sd卡、網(wǎng)絡(luò)或者assert里則是一樣的(即使是不通dpi的設(shè)備)。圖片占用內(nèi)存大小跟控件大小無(wú)關(guān)(glide是先拿到控件大小,再去對(duì)圖片加載做的處理)。
1.8 有沒(méi)有遇到64k問(wèn)題,應(yīng)該如何解決?
單個(gè)dex文件方法超過(guò)64k,基本上都是引用過(guò)多的依賴才導(dǎo)致的。
解決方案:
- 導(dǎo)入依賴
'com.android.support:multidex:1.0.1'
- defaultConfig增加這個(gè)設(shè)置
multiDexEnabled true
- android下面增加這個(gè)設(shè)置
dexOptions {
incremental true
javaMaxHeapSize "4g"
}
以上都是在app的buildl.gradle中設(shè)置的,編譯。
- 打開(kāi)自定義的Application,繼承MultiDexApplication,并重寫(xiě)attachBaseContext方法
1.9 如何優(yōu)化 Gradle 的構(gòu)建速度?
- 物理設(shè)備:16g+內(nèi)存、硬盤(pán)ssd、高配CPU,最好是超頻CPU。5.0GHz那種
- 配置:使用高版本AS
android.injected.testOnly=false
android.buildCacheDir=buildCacheDir
org.gradle.caching=true
# 開(kāi)啟緩存
android.enableBuildCache=true
android.enableAapt2=true
# 配置大內(nèi)存
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# 并行編譯
org.gradle.parallel=true
# 開(kāi)啟孵化模式
org.gradle.configureondemand=true
# 守護(hù)進(jìn)程
org.gradle.daemon=true
android.enableSeparateAnnotationProcessing=true
buildTypes{
debug{
crunchPngs false
aaptOptions.cruncherEnabled = false
}
}
dexOptions {
preDexLibraries true
}
如果使用了multiDexEnabled ,一定要依賴最新版本的1.0.3版本
依賴的庫(kù)不帶+號(hào),每次都會(huì)自動(dòng)檢查最新版
- AS設(shè)置,如果已下載好依賴,可以設(shè)置離線模式
以上配置可以加快構(gòu)建和編譯速度。
1.10 如何獲取Android設(shè)備唯一ID?
常用方法:
- IMEI
權(quán)限(讀手機(jī)權(quán)限)
可以手動(dòng)更改(模擬器上) /關(guān)閉權(quán)限報(bào)錯(cuò) - Mac地址
訪問(wèn)WIFI權(quán)限
有些設(shè)備沒(méi)有wifi(沒(méi)有上網(wǎng)功能)/6.0以上版本返回的都是固定的02:00:00 之類(lèi)地址 - ANDROID_ID
設(shè)備首次運(yùn)行,系統(tǒng)會(huì)隨機(jī)生成一個(gè)64位的數(shù)字,轉(zhuǎn)換成16進(jìn)制保存
手機(jī)恢復(fù)出廠設(shè)置后值有變化/ 有些國(guó)產(chǎn)設(shè)備不會(huì)返回值 - Serial Number (設(shè)備序列號(hào))
有些國(guó)產(chǎn)手機(jī)(紅米)會(huì)出現(xiàn)垃圾數(shù)據(jù)
實(shí)現(xiàn)思路:
- 獲取設(shè)備的IMEI -->設(shè)備的Mac地址-->隨機(jī)生成的UUID
- 拼接在一起,然后用MD5加密
- 存儲(chǔ)到本地的文件中,隱藏并且存儲(chǔ)在App中的SP中
- 使用的時(shí)候先從SP中讀取,沒(méi)有的再生成,然后把生成的唯一id保存到SP中
1.11 談一談Android P禁用http對(duì)我們開(kāi)發(fā)有什么影響?
- APP改用https請(qǐng)求
- targetSdkVersion 降到27以下
- res --> add : network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
AndroidManifest.xml--> application :
android:networkSecurityConfig="@xml/network_security_config"
1.12 什么是AOP?在Android中它有哪些應(yīng)用場(chǎng)景?
aop的實(shí)現(xiàn)有靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理有靜態(tài)代理模式,基于Ajc編譯器的AspectJ,動(dòng)態(tài)代理有JDK動(dòng)態(tài)代理,在Android的實(shí)現(xiàn)是InvicationHandler,需要實(shí)現(xiàn)接口,還有CGlib實(shí)現(xiàn)方式創(chuàng)建子類(lèi)來(lái)繼承源類(lèi)。
應(yīng)用場(chǎng)景有各種狀態(tài)監(jiān)測(cè)比如登錄、網(wǎng)絡(luò)、權(quán)限等。日志埋點(diǎn),性能分析。
1.13 什么是MVVM?你是如何將其應(yīng)用于具體項(xiàng)目中的?
MVVM架構(gòu)其實(shí)由來(lái)已久,在Android中MVVM指代的是Model-View-ViewModel。Model就是數(shù)據(jù)模型,View一般認(rèn)為是Activity/Fragment。那么這個(gè)ViewModel其實(shí)是Google為Android應(yīng)用程序搭建MVVM架構(gòu)而設(shè)計(jì)的一個(gè)組件(類(lèi))。
顧名思義,ViewModel可以認(rèn)為是View與Model之間的一個(gè)容器,在沒(méi)有MVVM之前,我們通常會(huì)將View和Model之間的業(yè)務(wù)處理直接放在Activity/Fragment中進(jìn)行處理,現(xiàn)在有了ViewModel,可以將相關(guān)邏輯放在其中處理,View(Activity/Fragment)只進(jìn)行數(shù)據(jù)的展示,而不進(jìn)行數(shù)據(jù)的加工處理。
那么數(shù)據(jù)在處理或者變化后,如何通知到View呢?這就需要通過(guò)Jetpack的另外一個(gè)組件LiveData。LiveData可以使得ViewModel中的數(shù)據(jù)變化后,通知到View,它起到一個(gè)橋梁的作用。本質(zhì)上這是一個(gè)觀察者模式。
在Android項(xiàng)目中應(yīng)用MVVM架構(gòu),Google官方建議的做法是使用Jetpack相關(guān)組件,這些組件設(shè)計(jì)的目的就是為了方便開(kāi)發(fā)者搭建MVVM規(guī)范的應(yīng)用程序。
1.14 請(qǐng)談?wù)勀闶侨绾螌?shí)現(xiàn)數(shù)據(jù)埋點(diǎn)的?
一、埋點(diǎn)技術(shù)
代碼埋點(diǎn):
所謂的代碼埋點(diǎn)就是在你需要統(tǒng)計(jì)數(shù)據(jù)的地方植入N行代碼,統(tǒng)計(jì)用戶的關(guān)鍵行為。比如你想統(tǒng)計(jì)首頁(yè)某個(gè)banner的點(diǎn)擊量,上報(bào)的數(shù)據(jù)可以采用KEY-VALUE形式,我們定義 KEY為「CLICK_ADD_BTN」,VALUE的值為點(diǎn)擊的次數(shù)。當(dāng)用戶點(diǎn)擊banner時(shí),banner詳情的代碼會(huì)通過(guò)按鈕的「回調(diào)」來(lái)觸發(fā)執(zhí)行,程序猿在業(yè)務(wù)代碼執(zhí)行完后,又加上了統(tǒng)計(jì)代碼,把「CLICK_ADD_BTN」對(duì)應(yīng)的VALUE加1,banner被統(tǒng)計(jì)到了一次使用。
代碼埋點(diǎn)的優(yōu)點(diǎn):
使用者控制精準(zhǔn),可以非常精確地選擇什么時(shí)候發(fā)送數(shù)據(jù)使用者可以比較方便地設(shè)置自定義屬性、自定義事件,傳遞比較豐富的數(shù)據(jù)到服務(wù)端。
代碼埋點(diǎn)的缺點(diǎn):
埋點(diǎn)代價(jià)比較大,每一個(gè)控件的埋點(diǎn)都需要添加相應(yīng)的代碼,不僅工作量大,而且限定了必須是技術(shù)人員才能完成;
更新代價(jià)比較大,每一次更新,都需要更新埋點(diǎn)方案,然后通過(guò)各個(gè)應(yīng)用市場(chǎng)進(jìn)行分發(fā),而且有的用戶還不一定更新,這樣你就獲取不到這批用戶數(shù)據(jù)。
可視化埋點(diǎn):
既然代碼埋點(diǎn)代價(jià)比較大,每一個(gè)埋點(diǎn)都需要寫(xiě)代碼,那就使用可視化交互手段代替寫(xiě)代碼;既然每次代碼埋點(diǎn)都需要更新,那就參照現(xiàn)在的很多手游做法,把核心代碼和配置、資源分開(kāi),每次用戶啟動(dòng)app的時(shí)候通過(guò)網(wǎng)絡(luò)更新配置和資源。
可視化埋點(diǎn)優(yōu)勢(shì):
可視化買(mǎi)點(diǎn)解決了代碼埋點(diǎn)埋點(diǎn)代價(jià)大和更新代價(jià)大兩個(gè)問(wèn)題。
可視化埋點(diǎn)劣勢(shì):
可視化埋點(diǎn)能夠覆蓋的功能有限,目前并不是所有的控件操作都可以通過(guò)這種方案進(jìn)行定制;
無(wú)埋點(diǎn):
可視化埋點(diǎn)先通過(guò)界面配置哪些控件的操作數(shù)據(jù)需要收集;“無(wú)埋點(diǎn)”則是先盡可能收集所有控件的操作數(shù)據(jù),然后再通過(guò)界面配置哪些數(shù)據(jù)需要在系統(tǒng)里面進(jìn)行分析,“無(wú)埋點(diǎn)”也就是“全埋點(diǎn)”的意思。
無(wú)埋點(diǎn)的優(yōu)點(diǎn):
可視化埋點(diǎn)只能收集到你埋點(diǎn)以后的數(shù)據(jù),如果你想對(duì)某個(gè)按鈕進(jìn)行點(diǎn)擊分析,則只能分析增加可視化埋點(diǎn)以后的數(shù)據(jù),之前的數(shù)據(jù)你收集不到,而無(wú)埋點(diǎn)在你部署SDK的時(shí)候數(shù)據(jù)就一直在收集。
因?yàn)闊o(wú)埋點(diǎn)對(duì)頁(yè)面所有元素進(jìn)行埋點(diǎn),那么這個(gè)頁(yè)面每個(gè)元素被點(diǎn)擊的概率你也就知道,對(duì)點(diǎn)擊概率比較大的元素可以進(jìn)行深入分析。
無(wú)埋點(diǎn)的缺點(diǎn):
由于無(wú)埋點(diǎn)方案所有的元素?cái)?shù)據(jù)都收集,會(huì)給數(shù)據(jù)傳輸和服務(wù)器帶來(lái)較大的壓力。
二、數(shù)據(jù)埋點(diǎn)方式
1、公司研發(fā)在自己的產(chǎn)品當(dāng)中注入統(tǒng)計(jì)代碼,搭建相應(yīng)的后臺(tái)查詢,這種代價(jià)比較大。
2、集成第三方統(tǒng)計(jì)的SDK,比如友盟、百度移動(dòng)統(tǒng)計(jì)、Sensors Data、GrowingIO、Talking Data等。
三、如何進(jìn)行數(shù)據(jù)埋點(diǎn)
1、明確目標(biāo)
經(jīng)常有人問(wèn)我說(shuō)我要獲取那些數(shù)據(jù)來(lái)進(jìn)行數(shù)據(jù)分析,其實(shí)這個(gè)問(wèn)題不應(yīng)該問(wèn)別人,應(yīng)該問(wèn)問(wèn)你自己,你是想用這個(gè)數(shù)據(jù)干什么,如果你想繪制基礎(chǔ)的人群畫(huà)像你就需要獲取用戶機(jī)型、網(wǎng)絡(luò)類(lèi)型、操作系統(tǒng),IP地域等數(shù)據(jù);如果你想分析每一個(gè)注冊(cè)轉(zhuǎn)化率,你就需要獲取每一個(gè)步驟的點(diǎn)擊次數(shù),然后制作成漏斗,看那一步轉(zhuǎn)化率出現(xiàn)了問(wèn)題;
目的不一樣,獲取的數(shù)據(jù)也不一樣,使用的埋點(diǎn)技術(shù)也不一樣,我們無(wú)論做什么事情都不能忘了我們的目的!
2、獲取相應(yīng)數(shù)據(jù)
業(yè)務(wù)不同,目的不同獲取的數(shù)據(jù)也不同,這里我只說(shuō)一些比較共性的數(shù)據(jù)。
2.1、產(chǎn)品各個(gè)渠道下載量
這個(gè)可以用第三方數(shù)據(jù)統(tǒng)計(jì)工具來(lái)進(jìn)行,這樣我們可以知道我們產(chǎn)品著重在那個(gè)渠道進(jìn)行推廣。
2.2、產(chǎn)品活躍狀態(tài)分析
產(chǎn)品活躍狀態(tài)監(jiān)控,留存分析、流失分析、新增變化等,次日留存率、七日留存率、月留存率,尤其對(duì)于處于成長(zhǎng)期的產(chǎn)品而已,這個(gè)指標(biāo)很重要,如果留存率比較低,說(shuō)明你的產(chǎn)品有問(wèn)題,這個(gè)時(shí)候你就需要進(jìn)行用戶調(diào)研,找到流失的問(wèn)題,否則大面積拉新,只能拉多少死多少,至于留存率、新增的變化這些數(shù)據(jù),我們也可以借助第三方統(tǒng)計(jì)工具來(lái)進(jìn)行。
2.3、事件分析
比如你想統(tǒng)計(jì)某個(gè)頁(yè)面的UV、PV、元素的點(diǎn)擊量、用戶停留時(shí)長(zhǎng)、頁(yè)面跳出率等數(shù)據(jù)指標(biāo),可以選擇代碼埋點(diǎn)和可視化埋點(diǎn)等前端埋點(diǎn)解決方案。當(dāng)某個(gè)頁(yè)面的UV很高,但是跳出率也很高,說(shuō)明頁(yè)面有問(wèn)題,你就要好好想想頁(yè)面的問(wèn)題出在什么地方。
2.4、基本信息獲取
基本信息獲取,例如機(jī)型、網(wǎng)絡(luò)類(lèi)型、操作系統(tǒng),IP地域等,繪制基礎(chǔ)用戶人群畫(huà)像,這種分析出來(lái)的用戶畫(huà)像顆粒度比較大,如果想更精準(zhǔn)的進(jìn)行用戶畫(huà)像可以結(jié)合推薦系統(tǒng),來(lái)獲取用戶的興趣指標(biāo),以及用戶操作行為等數(shù)據(jù)來(lái)進(jìn)行更精準(zhǔn)的用戶畫(huà)像,從而為產(chǎn)品運(yùn)營(yíng)和產(chǎn)品設(shè)計(jì)提供參考,可以借助第三方統(tǒng)計(jì)工具和自定義埋點(diǎn)的方式進(jìn)行數(shù)據(jù)的收集。
2.5、漏斗模型
對(duì)于產(chǎn)品的關(guān)鍵路徑一定要進(jìn)行漏斗模型分析,比如注冊(cè)路徑,從用戶輸入注冊(cè)手機(jī)號(hào)到注冊(cè)成功,中間可能會(huì)有幾個(gè)步驟,如果100個(gè)人注冊(cè),最后只有一個(gè)人注冊(cè)成功,那么求運(yùn)營(yíng)同學(xué)心里的陰影面積。還有電商的購(gòu)買(mǎi)下單路徑,從瀏覽商品到最后下單購(gòu)買(mǎi)成功,每一個(gè)步驟的轉(zhuǎn)化率是多少,對(duì)于漏的比較多的那個(gè)步驟我們肯定要著重關(guān)注,分析原因。這個(gè)可以技術(shù)研發(fā)進(jìn)行埋點(diǎn),獲取更精確的數(shù)據(jù),比如下圖的埋點(diǎn)表。
如何給app客戶端進(jìn)行埋點(diǎn)?
領(lǐng)導(dǎo)說(shuō),APP需要加一下統(tǒng)計(jì),你負(fù)責(zé)搞定
研發(fā)說(shuō),APP需要統(tǒng)計(jì)哪些地方,你列一下埋點(diǎn)需求
研發(fā)說(shuō),APP的數(shù)據(jù)統(tǒng)計(jì)SDK用哪家的?你選好了注冊(cè)一下、運(yùn)營(yíng)說(shuō),咱們的APP都能看哪些數(shù)據(jù)?平臺(tái)在哪?怎么查首頁(yè)的UV?
數(shù)據(jù)分析是一個(gè)很復(fù)雜的工作,很多人在談如何挖掘數(shù)據(jù),做用戶畫(huà)像,設(shè)計(jì)數(shù)據(jù)漏斗,如何負(fù)責(zé)用戶生命周期管理,但發(fā)現(xiàn)很多人卻卡在了數(shù)據(jù)分析的第一步,那就是如何做數(shù)據(jù)埋點(diǎn)。
首頁(yè)陳峰老師先明確下完成一個(gè)APP數(shù)據(jù)埋點(diǎn)的幾個(gè)步驟:
- 注冊(cè)一家統(tǒng)計(jì)網(wǎng)站
- 新建應(yīng)用
- 獲取KEY和SDK代碼包
- 將埋點(diǎn)需求和SDK包發(fā)給研發(fā)
- 自定義埋點(diǎn)需求完善
- 研發(fā)開(kāi)發(fā)并完成APP上線
- 在后臺(tái)查看數(shù)據(jù)
1、注冊(cè)賬號(hào)
建議用公司郵箱或者公用郵箱注冊(cè),別用自己的私人郵箱和手機(jī)號(hào)碼,后續(xù)一旦有交接和工作變動(dòng)時(shí)會(huì)比較麻煩。
2、新建應(yīng)用
登錄后一般都有“新建應(yīng)用”,可以選標(biāo)準(zhǔn)統(tǒng)計(jì),大部分APP都選這個(gè)。游戲的app另說(shuō)。
名稱寫(xiě)自己app的名稱,分類(lèi)自己選1個(gè)。選錯(cuò)了也不影響。
平臺(tái)根據(jù)情況自己選。后期我們看數(shù)據(jù)和埋點(diǎn)都是ios和安卓分開(kāi)的,所以你如果2個(gè)端都做,就一起都選上。
描述可選,不用填。
3、獲取KEY和SDK代碼包
完成后可以得到2個(gè)APPKEY。分別是ios和安卓的。
這里的appkey很重要,你可以下載了給研發(fā),也可以稍后讓研發(fā)自己登錄進(jìn)來(lái)自己下載。
ios和安卓是分開(kāi)2個(gè)獨(dú)立的,后續(xù)埋點(diǎn)和看數(shù)據(jù)都是分開(kāi)的。這個(gè)切記。
這時(shí)候,重點(diǎn)來(lái)了。
此時(shí),如果我們只想看 APP的活躍用戶,留存用戶,下載量。用戶地域分布,渠道分布,那么其實(shí)就夠了。
4、將埋點(diǎn)需求和SDK包發(fā)給研發(fā)
你這時(shí)候,就把剛才獲得的appkey和sdk包的下載地址,發(fā)給研發(fā)。或者直接把賬號(hào)和密碼發(fā)給研發(fā)。然后告訴研發(fā),集成下百度移動(dòng)統(tǒng)計(jì)的SDK包。這樣發(fā)版后,就可以看到大部分?jǐn)?shù)據(jù)了。
如下的數(shù)據(jù)都可以看到:
5、自定義事件完善
比如我們想看頁(yè)面里面 注冊(cè) 搜索按鈕,頂部banner,底部 首頁(yè)和 我的 2個(gè)導(dǎo)航條的點(diǎn)擊量。
一個(gè)埋點(diǎn)事件對(duì)應(yīng)1個(gè)按鈕或者一個(gè)頁(yè)面或者一個(gè)彈層。你來(lái)定義。
如果埋點(diǎn)比較多,你也可以批量添加。批量添加的時(shí)候,您需要下載excel模板,按照要求填寫(xiě)好,上傳進(jìn)來(lái)即可。具體一看便知。
添加完成后就可以把這個(gè)列表導(dǎo)出或者人肉復(fù)制出來(lái)一個(gè)表格。發(fā)給研發(fā)。并附上你的原型圖。做好對(duì)應(yīng)關(guān)系標(biāo)注。
6、研發(fā)開(kāi)發(fā)并完成APP上線
完成上面幾步后,研發(fā)哥哥就可以看懂進(jìn)入第7步研發(fā)階段了。
7、在后臺(tái)查看數(shù)據(jù)
上線后就可以看到數(shù)據(jù)了。大部分?jǐn)?shù)據(jù)一般隔天更新。
三、埋點(diǎn)后能看到什么數(shù)據(jù)
上面提到,按照步驟完成數(shù)據(jù)分析sdk集成和自定義事件后,就可以看到數(shù)據(jù)了。
不添加自定義事件,可以看到基礎(chǔ)數(shù)據(jù),添加后,可以看到更細(xì)節(jié)的按鈕,頁(yè)面等點(diǎn)擊數(shù)據(jù)。
查看自定義事件埋點(diǎn)數(shù)據(jù),還是進(jìn)入剛才的“事件分析”頁(yè)面,點(diǎn)擊對(duì)應(yīng)埋點(diǎn)即可看到數(shù)據(jù)。
可以篩選時(shí)間段。
除了這些外,如果你還想看 幾個(gè)頁(yè)面之間的轉(zhuǎn)化路徑和數(shù)據(jù)漏斗。那還需要添加“轉(zhuǎn)化分析”。
添加轉(zhuǎn)化分析后,可以看到例如: 進(jìn)入首頁(yè)-點(diǎn)擊注冊(cè)按鈕進(jìn)入注冊(cè)成功頁(yè) 這幾步的轉(zhuǎn)化率和流失率。會(huì)自動(dòng)生成一個(gè)轉(zhuǎn)化分析圖。當(dāng)然你也可以分別看這幾個(gè)頁(yè)面的數(shù)據(jù),自己去分析匯總。
進(jìn)階的方法還有把事件埋點(diǎn)配合轉(zhuǎn)化分析、訪問(wèn)路徑、轉(zhuǎn)化漏斗等工具使用,從點(diǎn)到面地了解用戶的使用行為、APP存在的問(wèn)題。
產(chǎn)品核心指標(biāo)一般包含:
- 產(chǎn)品規(guī)模
1.1 用戶數(shù)據(jù)。如新增用戶、用戶類(lèi)型分布、活躍用戶、沉默用戶、啟動(dòng)次數(shù)、版本分析等。
1.2 業(yè)務(wù)數(shù)據(jù)。這個(gè)與具體業(yè)務(wù)有關(guān),如問(wèn)答社區(qū)的問(wèn)題數(shù),回答數(shù),全網(wǎng)熱度,瀏覽量;如對(duì)含交易平臺(tái)的交易量,交易額,客單價(jià),轉(zhuǎn)化率,利潤(rùn)等。 - 產(chǎn)品運(yùn)營(yíng)
2.1 流量數(shù)據(jù)。pv,uv,dau,mau,留存分析(次日留存,7日留存, 用戶新鮮度), 流失分析(日周月、自然流失、回歸流失),
2.2 渠道數(shù)據(jù)。渠道流量,渠道轉(zhuǎn)換率,渠道評(píng)價(jià),渠道時(shí)段詳情,渠道質(zhì)量(渠道活躍用戶/渠道流量)等。
1.15 假如讓你實(shí)現(xiàn)斷點(diǎn)上傳功能,你認(rèn)為應(yīng)該怎樣去做?
分2種情況:
分塊上傳:多線程讀取本地文件指定區(qū)域流上傳,header帶有上傳位置標(biāo)記,服務(wù)器接收到多個(gè)上傳請(qǐng)求會(huì)生成多個(gè)上傳臨時(shí)文件接收,最后合并成完整文件。(續(xù)傳如下)
正常續(xù)傳:本地請(qǐng)求上傳,服務(wù)器響應(yīng)是否未完成的臨時(shí)文件和文件byte,本地收到就接著指定位置讀流上傳。
其中會(huì)涉及塊的數(shù)據(jù)校驗(yàn)等
1.16 webp和svg格式的圖片各自有什么特點(diǎn)?應(yīng)該如何在Android中使用?
一、
svg格式
svg是矢量圖,這意味著svg圖片由直線和曲線以及繪制它們的方法組成。當(dāng)你放大一個(gè)svg圖片的時(shí)候,你看到的還是線和曲線,而不會(huì)出現(xiàn)像素點(diǎn)。svg圖片在放大時(shí),不會(huì)失真,所以它非常適合用來(lái)繪制企業(yè)Logo、Icon
SVG格式特點(diǎn):
1、SVG 指可伸縮矢量圖形 (Scalable Vector Graphics)
2、SVG 用來(lái)定義用于網(wǎng)絡(luò)的基于矢量的圖形
3、SVG 使用 XML 格式定義圖形
4、SVG 圖像在放大或改變尺寸的情況下其圖形質(zhì)量不會(huì)有所損失
5、SVG 是萬(wàn)維網(wǎng)聯(lián)盟的標(biāo)準(zhǔn)
6、SVG 與諸如 DOM和 XSL 之類(lèi)的W3C標(biāo)準(zhǔn)是一個(gè)整體
二、
webp格式
webp是谷歌開(kāi)發(fā)的一種新圖片格式,是同時(shí)支持有損和無(wú)損壓縮的、使用直接色的、點(diǎn)陣圖。
1.17 說(shuō)說(shuō)你是如何進(jìn)行單元測(cè)試的?以及如何應(yīng)用在MVP和MVVM中?
單元測(cè)試庫(kù) junit mockito Rebolectric
說(shuō)下mvp工程中的測(cè)試方法
測(cè)試主要有 三大部分
- 普通工具類(lèi) 使用junit 直接測(cè)試
- mvp的p使用@mock標(biāo)注view的接口,初始化真正的p,直接調(diào)用p的方法看看verify view的某些方法是否按照預(yù)期被調(diào)用
- mvp的v用rebolectric去setup一個(gè)Activity,然后用這個(gè)庫(kù)找到界面上的按鈕,或者觸發(fā)生命周期(onStart),判斷一下當(dāng)前界面的某些view是否被顯示或者TextView的值或者Dialog是否顯示,Toast是否彈出錯(cuò)誤
- 還有網(wǎng)絡(luò)部分的測(cè)試,可以直接使用junit進(jìn)行測(cè)試,判斷下返回值是否符合預(yù)期
1.18 對(duì)于GIF圖片加載有什么思路和建議?
gif圖實(shí)際上就是多幀合并的圖
參考Fresco內(nèi)部實(shí)現(xiàn):
1,View層使用一個(gè)Drawable,包含bitmap,并依據(jù)gif的信息不斷的更新并繪制bitmap
2,C層提供api功能,例如:輸入gif數(shù)據(jù)流,提供解析gif信息、更新bitmap等功能
1.19 為什么要將項(xiàng)目遷移到AndroidX?如何進(jìn)行遷移?
現(xiàn)在Android官方支持的最低系統(tǒng)版本已經(jīng)是4.0.1,對(duì)應(yīng)的API版本號(hào)是15。support-v4、appcompat-v7庫(kù)也不再支持那么久遠(yuǎn)的系統(tǒng)了,但是它們的名字卻一直保留了下來(lái),雖然它們現(xiàn)在的實(shí)際作用已經(jīng)對(duì)不上當(dāng)初命名的原因了。
那么很明顯,Android團(tuán)隊(duì)也意識(shí)到這種命名已經(jīng)非常不合適了,于是對(duì)這些API的架構(gòu)進(jìn)行了一次重新的劃分,推出了AndroidX。因此,AndroidX本質(zhì)上其實(shí)就是對(duì)Android Support Library進(jìn)行的一次升級(jí),升級(jí)內(nèi)容主要在于以下兩個(gè)方面。
第一,包名。之前Android Support Library中的API,它們的包名都是在android.support.下面的,而AndroidX庫(kù)中所有API的包名都變成了在androidx.下面。這是一個(gè)很大的變化,意味著以后凡是android.包下面的API都是隨著Android操作系統(tǒng)發(fā)布的,而androidx.包下面的API都是隨著擴(kuò)展庫(kù)發(fā)布的,這些API基本不會(huì)依賴于操作系統(tǒng)的具體版本。
第二,命名規(guī)則。吸取了之前命名規(guī)則的弊端,AndroidX所有庫(kù)的命名規(guī)則里都不會(huì)再包含具體操作系統(tǒng)API的版本號(hào)了。比如,像appcompat-v7庫(kù),在AndroidX中就變成了appcompat庫(kù)。
一個(gè)AndroidX完整的依賴庫(kù)格式如下所示:
implementation 'androidx.appcompat:appcompat:1.0.2'
了解了AndroidX是什么之后,現(xiàn)在你應(yīng)該放輕松了吧?它其實(shí)并不是什么全新的東西,而是對(duì)Android SupportLibrary的一次升級(jí)。因此,AndroidX上手起來(lái)也沒(méi)有任何困難的地方,比如之前你經(jīng)常使用的RecyclerView、ViewPager等等庫(kù),在AndroidX中都會(huì)有一個(gè)對(duì)應(yīng)的版本,只要改一下包名就可以完全無(wú)縫使用,用法方面基本上都沒(méi)有任何的變化。
但是有一點(diǎn)需要注意,AndroidX和Android Support Library中的庫(kù)是非常不建議混合在一起使用的,因?yàn)樗鼈兛赡軙?huì)產(chǎn)生很多不兼容的問(wèn)題。最好的做法是,要么全部使用AndroidX中的庫(kù),要么全部使用Android Support Library中的庫(kù)。
而現(xiàn)在Android團(tuán)隊(duì)官方的態(tài)度也很明確,未來(lái)都會(huì)為AndroidX為主,Android Support Library已經(jīng)不再建議使用,并會(huì)慢慢停止維護(hù)。另外,從Android Studio 3.4.2開(kāi)始,我發(fā)現(xiàn)新建的項(xiàng)目已經(jīng)強(qiáng)制勾選使用AndroidX架構(gòu)了。