類加載機(jī)制
如下圖所示蔗草,JVM類加載機(jī)制分為五個(gè)部分:加載咒彤,驗(yàn)證,準(zhǔn)備咒精,解析镶柱,初始化,下面我們就分別來(lái)看一下這五個(gè)過(guò)程模叙。
加載
加載是類加載過(guò)程中的一個(gè)階段歇拆,這個(gè)階段會(huì)在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的入口。注意這里不一定非得要從一個(gè)Class文件獲取故觅,這里既可以從ZIP包中讀瘸П印(比如從jar包和war包中讀取)输吏,也可以在運(yùn)行時(shí)計(jì)算生成(動(dòng)態(tài)代理)权旷,也可以由其它文件生成(比如將JSP文件轉(zhuǎn)換成對(duì)應(yīng)的Class類)。
驗(yàn)證
這一階段的主要目的是為了確保Class文件的字節(jié)流中包含的信息是否符合當(dāng)前虛擬機(jī)的要求贯溅,并且不會(huì)危害虛擬機(jī)自身的安全拄氯。
準(zhǔn)備
準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量的初始值階段,即在方法區(qū)中分配這些變量所使用的內(nèi)存空間它浅。注意這里所說(shuō)的初始值概念译柏,比如一個(gè)類變量定義為:
public static int v = 8080;
實(shí)際上變量v在準(zhǔn)備階段過(guò)后的初始值為0而不是8080,將v賦值為8080的putstatic指令是程序被編譯后姐霍,存放于類構(gòu)造器<client>方法之中鄙麦,這里我們后面會(huì)解釋。 但是注意如果聲明為:
public static final int v = 8080;
在編譯階段會(huì)為v生成ConstantValue屬性邮弹,在準(zhǔn)備階段虛擬機(jī)會(huì)根據(jù)ConstantValue屬性將v賦值為8080黔衡。
解析
解析階段是指虛擬機(jī)將常量池中的符號(hào)引用替換為直接引用的過(guò)程。符號(hào)引用就是class文件中的:
· CONSTANT_Class_info
· CONSTANT_Field_info
· CONSTANT_Method_info
等類型的常量腌乡。
下面我們解釋一下符號(hào)引用和直接引用的概念:
· 符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的布局無(wú)關(guān)盟劫,引用的目標(biāo)并不一定要已經(jīng)加載到內(nèi)存中。各種虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局可以各不相同与纽,但是它們能接受的符號(hào)引用必須是一致的侣签,因?yàn)榉?hào)引用的字面量形式明確定義在Java虛擬機(jī)規(guī)范的Class文件格式中。
· 直接引用可以是指向目標(biāo)的指針急迂,相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄影所。如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在僚碎。
初始化
初始化階段是類加載最后一個(gè)階段猴娩,前面的類加載階段之后,除了在加載階段可以自定義類加載器以外勺阐,其它操作都由JVM主導(dǎo)卷中。到了初始階段,才開始真正執(zhí)行類中定義的Java程序代碼渊抽。
初始化階段是執(zhí)行類構(gòu)造器<client>方法的過(guò)程蟆豫。<client>方法是由編譯器自動(dòng)收集類中的類變量的賦值操作和靜態(tài)語(yǔ)句塊中的語(yǔ)句合并而成的。虛擬機(jī)會(huì)保證<client>方法執(zhí)行之前懒闷,父類的<client>方法已經(jīng)執(zhí)行完畢十减。p.s: 如果一個(gè)類中沒(méi)有對(duì)靜態(tài)變量賦值也沒(méi)有靜態(tài)語(yǔ)句塊栈幸,那么編譯器可以不為這個(gè)類生成<client>()方法。
注意以下幾種情況不會(huì)執(zhí)行類初始化:
· 通過(guò)子類引用父類的靜態(tài)字段帮辟,只會(huì)觸發(fā)父類的初始化速址,而不會(huì)觸發(fā)子類的初始化。
· 定義對(duì)象數(shù)組织阅,不會(huì)觸發(fā)該類的初始化壳繁。
· 常量在編譯期間會(huì)存入調(diào)用類的常量池中,本質(zhì)上并沒(méi)有直接引用定義常量的類荔棉,不會(huì)觸發(fā)定義常量所在的類闹炉。
· 通過(guò)類名獲取Class對(duì)象,不會(huì)觸發(fā)類的初始化润樱。
· 通過(guò)Class.forName加載指定類時(shí)渣触,如果指定參數(shù)initialize為false時(shí),也不會(huì)觸發(fā)類初始化壹若,其實(shí)這個(gè)參數(shù)是告訴虛擬機(jī)嗅钻,是否要對(duì)類進(jìn)行初始化。
· 通過(guò)ClassLoader默認(rèn)的loadClass方法店展,也不會(huì)觸發(fā)初始化動(dòng)作养篓。
類加載器
虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)把加載動(dòng)作放到JVM外部實(shí)現(xiàn),以便讓應(yīng)用程序決定如何獲取所需的類赂蕴,JVM提供了3種類加載器:
· 啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)加載 JAVA_HOME\lib 目錄中的柳弄,或通過(guò)-Xbootclasspath參數(shù)指定路徑中的,且被虛擬機(jī)認(rèn)可(按文件名識(shí)別概说,如rt.jar)的類碧注。
· 擴(kuò)展類加載器(Extension ClassLoader):負(fù)責(zé)加載 JAVA_HOME\lib\ext 目錄中的,或通過(guò)java.ext.dirs系統(tǒng)變量指定路徑中的類庫(kù)糖赔。
· 應(yīng)用程序類加載器(Application ClassLoader):負(fù)責(zé)加載用戶路徑(classpath)上的類庫(kù)萍丐。
JVM通過(guò)雙親委派模型進(jìn)行類的加載,當(dāng)然我們也可以通過(guò)繼承java.lang.ClassLoader實(shí)現(xiàn)自定義的類加載器放典。
當(dāng)一個(gè)類加載器收到類加載任務(wù)逝变,會(huì)先交給其父類加載器去完成,因此最終加載任務(wù)都會(huì)傳遞到頂層的啟動(dòng)類加載器奋构,只有當(dāng)父類加載器無(wú)法完成加載任務(wù)時(shí)壳影,才會(huì)嘗試執(zhí)行加載任務(wù)。
采用雙親委派的一個(gè)好處是比如加載位于rt.jar包中的類java.lang.Object声怔,不管是哪個(gè)加載器加載這個(gè)類,最終都是委托給頂層的啟動(dòng)類加載器進(jìn)行加載舱呻,這樣就保證了使用不同的類加載器最終得到的都是同樣一個(gè)Object對(duì)象醋火。
在有些情境中可能會(huì)出現(xiàn)要我們自己來(lái)實(shí)現(xiàn)一個(gè)類加載器的需求悠汽,由于這里涉及的內(nèi)容比較廣泛,我想以后單獨(dú)寫一篇文章來(lái)講述芥驳,不過(guò)這里我們還是稍微來(lái)看一下柿冲。我們直接看一下jdk中的ClassLoader的源碼實(shí)現(xiàn):
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
|
· 首先通過(guò)Class c = findLoadedClass(name);判斷一個(gè)類是否已經(jīng)被加載過(guò)。
· 如果沒(méi)有被加載過(guò)執(zhí)行if (c == null)中的程序兆旬,遵循雙親委派的模型假抄,首先會(huì)通過(guò)遞歸從父加載器開始找,直到父類加載器是Bootstrap ClassLoader為止丽猬。
· 最后根據(jù)resolve的值宿饱,判斷這個(gè)class是否需要解析。
而上面的findClass()的實(shí)現(xiàn)如下脚祟,直接拋出一個(gè)異常谬以,并且方法是protected,很明顯這是留給我們開發(fā)者自己去實(shí)現(xiàn)的由桌,這里我們以后我們單獨(dú)寫一篇文章來(lái)講一下如何重寫findClass方法來(lái)實(shí)現(xiàn)我們自己的類加載器为黎。
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
|
Activity生命周期
一個(gè)Activity可以基本上存在三種狀態(tài):
恢復(fù)
這項(xiàng)Activity是在屏幕前的,并有用戶取得其焦點(diǎn)。(此狀態(tài),有時(shí)也簡(jiǎn)稱為“運(yùn)行”行您。)
暫停
另一個(gè)Activity在屏幕前,取得焦點(diǎn),但原來(lái)的Activity仍然可見铭乾。也就是說(shuō),另一個(gè)Activity是這一個(gè)頂部可見,或者是部分透明的或不覆蓋整個(gè)屏幕。暫停的Activity完全是存在的( Activity 對(duì)象保留在內(nèi)存中,它維護(hù)所有狀態(tài)和成員信息,并保持窗口管理器的聯(lián)系),但在極低的內(nèi)存的情況下,可以被系統(tǒng)終止娃循。
停止
Activity完全被另一個(gè)Activity遮住了(現(xiàn)在Activity是在“background”)炕檩。停止Activity也仍然存在( Activity 對(duì)象保留在內(nèi)存中,它保持狀態(tài)和成員信息,但不和窗口管理器有關(guān)聯(lián))。然而,它已不再是對(duì)用戶可見,其他地方需要內(nèi)存時(shí),它可以被系統(tǒng)終止淮野。
如果一項(xiàng)Activity被暫团跏椋或停止,該系統(tǒng)可以從內(nèi)存中刪除它,要求它結(jié)束(調(diào)用它的finish() 方法),或者干脆殺死它的進(jìn)程。Activity再次打開時(shí)(在被結(jié)束或被殺死),它必須創(chuàng)造所有的一切骤星。
實(shí)現(xiàn)生命周期回調(diào)函數(shù)
當(dāng)Activity按照如上所述在不同狀態(tài)轉(zhuǎn)進(jìn),出的時(shí),是通過(guò)各種回調(diào)方法進(jìn)行通知的经瓷。所有的回調(diào)方法是鉤子,當(dāng)?shù)腁ctivity狀態(tài)變化時(shí)您可以覆蓋他們來(lái)做適當(dāng)?shù)墓ぷ鲿r(shí)。以下的Activity,包括基本生命周期的每一個(gè)方法
:
1.啟動(dòng)onCreate()->onStart()->onResume()
2.按back鍵onPause()->onStop()->onDestroy()
再次啟動(dòng):onCreate()->onStart()->onResume()
3.按home鍵onPause()->onStop() 再次啟動(dòng)時(shí):onRestart()->onStart()->onResume()
4.切換到SecondActivity:FirstActivity.onPause()->FirstActivity.onStop()
再次啟動(dòng)時(shí): FirstActivity.onRestart()->FirstActivity.onStart()->FirstActivity.onResume()
5.切換SecondActivity (FirstActivity.finish()):
FirstActivity.onPause()->FirstActivity.onStop()->FirstActivity.onDestroy()
再次啟動(dòng)時(shí): FirstActivity.onCreate ()->FirstActivity.onStart()->FirstActivity.onResume()
6.切換到SecondActivity (SecondActivity主題為Dialog): FirstActivity.onPause()
再次啟動(dòng)時(shí): FirstActivity.onRestart()->FirstActivity.onStart()->FirstActivity.onResume()
7.配置改變Activity生命周期
某些設(shè)備配置在運(yùn)行時(shí)可以改變(如屏幕方向,鍵盤的可用性,和語(yǔ)言)洞难。當(dāng)這種變化發(fā)生時(shí),Android重新運(yùn)行Activity(系統(tǒng)調(diào)用的 onDestroy() ,然后立即調(diào)用的 onCreate()) 舆吮。這種行為旨在幫助您用您所提供的(如不同的屏幕方向和大小不同的布局)的替代資源進(jìn)行應(yīng)用程序自動(dòng)重載,以適應(yīng)新的配置。
AndroidManifest.xml
<activity android:name=”…” android:configChanges="keyboardHidden|orientation|screenSize"/>
回調(diào)方法的作用队贱,就是通知我們Activity生命周期的改變色冀,然后我們可以處理這種改變,以便程序不會(huì)崩潰或者數(shù)據(jù)丟失等等柱嫌,也就是擁有更好的用戶體檢锋恬,那么這么多回調(diào)方法里到底應(yīng)該怎么做呢?
1编丘、onCreate
最重要是在里面調(diào)用setContentView与学,還可以在里面初始化各控件彤悔、設(shè)置監(jiān)聽、并初始化一些全局的變量索守。
因?yàn)樵贏ctivity的一次生命周期中晕窑,onCreate方法只會(huì)執(zhí)行一次。在Paused和Stopped狀態(tài)下恢復(fù)或重啟的下卵佛,這些控件杨赤、監(jiān)聽和全局變量也不會(huì)丟失。即便是內(nèi)存不足截汪,被回收了疾牲,再次Recreate的話,又是一次新的生命周期的開始挫鸽,又會(huì)執(zhí)行onCreate方法说敏。
還可以在onCreate執(zhí)行數(shù)據(jù)操作,比如從Cursor中檢索數(shù)據(jù)等等丢郊,但是如果你每次進(jìn)入這個(gè)Activity都可能需要更新數(shù)據(jù)盔沫,那么最好放在onStart里面。(這個(gè)需要根據(jù)實(shí)際情況來(lái)確定)
2枫匾、onDestory
確定某些資源是否沒(méi)有被釋放架诞,做一些最終的清理工作,比如在這個(gè)Activity的onCreate中開啟的某個(gè)線程干茉,那么就要在onDestory中確定它是否結(jié)束了谴忧,如果沒(méi)有,就結(jié)束它角虫。
3沾谓、onStart和onRestart、onStop
Activity進(jìn)入到Stopped狀態(tài)之后戳鹅,它極有可能被系統(tǒng)所回收均驶,在某些極端情況下,系統(tǒng)可能是直接殺死應(yīng)用程序的進(jìn)程枫虏,而不是調(diào)用onDestory方法妇穴,所以我們需要在onStop方法中盡可能的釋放那些用戶暫時(shí)不需要使用的資源,防止內(nèi)存泄露隶债。
盡管onPause在onStop之前執(zhí)行腾它,但是onPause只適合做一些輕量級(jí)的操作,更多的耗時(shí)耗資源的操作還是要放在onStop里面死讹,比如說(shuō)對(duì)數(shù)據(jù)保存瞒滴,需要用到的數(shù)據(jù)庫(kù)操作。
因?yàn)閺腟topped狀態(tài)重啟之后赞警, onStart和onRestart方法都會(huì)被執(zhí)行妓忍,所以我們要判斷哪些操作分別要放在哪個(gè)方法里面 稀并。因?yàn)榭赡茉趏nStop方法里面釋放了一些資源,那么我們必須要重啟他們单默,這個(gè)時(shí)候這些重啟的操作放在onStart方法里面就比較好(因?yàn)閛nCreate之后也需要開啟這些資源)。那些因?yàn)镾topped之后引發(fā)的需要單獨(dú)操作的代碼忘瓦,就可以放在onRestart里面搁廓。
4、onResume和onPause
onPause和onResume中做的操作耕皮,其實(shí)意義上和onStart和inStop差不多境蜕,只不過(guò)是要更輕量級(jí)的,因?yàn)閛nPause不能阻塞轉(zhuǎn)變到下一個(gè)Activity凌停。
比如:停止動(dòng)畫粱年、取消broadcast receivers。當(dāng)然相應(yīng)的需要在onResume中重啟或初始化等等罚拟。
有時(shí)候也需要在onPause判斷用戶是調(diào)用finish結(jié)束這個(gè)Activity台诗,還是暫時(shí)離開,以便區(qū)分處理赐俗。這時(shí)候可以調(diào)用isFinishing()方法來(lái)判斷拉队。如果是用戶finish這個(gè)Activity,那么返回為true阻逮,如果只是暫時(shí)離開或者被系統(tǒng)回收的話粱快,就返回false。
最后加上
Android****里面保存數(shù)據(jù)狀態(tài)有哪幾種方式叔扼?
onPause() 用來(lái)持久化數(shù)據(jù)狀態(tài)
onsavedInstanceState() 保存數(shù)據(jù)狀態(tài)
Handler機(jī)制
Message類
Message 實(shí)現(xiàn)了Parcelable 接口事哭,也就是說(shuō)實(shí)現(xiàn)了序列化,這就說(shuō)明Message可以在不同進(jìn)程之間傳遞瓜富。
包含一個(gè)名為target的Handler 對(duì)象
包含一個(gè)名為callback的Runnable 對(duì)象
使用obtain 方法可以從消息池中獲取Message的實(shí)例鳍咱,也是推薦大家使用的方法,而不是直接調(diào)用構(gòu)造方法食呻。
非UI線程真的不能更新UI嗎流炕?不一定,之所以子線程不能更新界面仅胞,是因?yàn)锳ndroid在線程的方法里面采用checkThread進(jìn)行判斷是否是主線程每辟,而這個(gè)方法是在ViewRootImpl中的,這個(gè)類是在onResume里面才生成的干旧,因此渠欺,如果這個(gè)時(shí)候子線程在onCreate方法里面生成更新UI,而且沒(méi)有做阻塞椎眯,就是耗時(shí)多的操作挠将,還是可以更新UI的胳岂。
MessageQueue的中文翻譯是消息隊(duì)列,顧名思義舔稀,它的內(nèi)部存儲(chǔ)了一組消息乳丰,以隊(duì)列的形式對(duì)外提供插入和刪除的工作。雖然叫消息隊(duì)列内贮,但是它的內(nèi)部存儲(chǔ)結(jié)構(gòu)并不是真正的隊(duì)列产园,而是采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)消息列表。
handler的原理是什么夜郁?
答:1什燕、handler封裝消息的發(fā)送(主要包括消息發(fā)送給誰(shuí))
2、Looper——消息封裝的載體竞端。(1)內(nèi)部包含一個(gè)MessageQueue屎即,所有的Handler發(fā)送的消息都走向這個(gè)消息隊(duì)列;(2)Looper.Looper方法事富,就是一個(gè)死循環(huán)技俐,不斷地從MessageQueue取消息,如果有消息就處理消息统台,沒(méi)有消息就阻塞虽另。
3、MessageQueue饺谬,一個(gè)消息隊(duì)列捂刺,添加消息,處理消息
4募寨、handler內(nèi)部與Looper關(guān)聯(lián)族展,handler->Looper->MessageQueue
,handler發(fā)送消息就是向MessageQueue隊(duì)列發(fā)送消息。
總結(jié):handler負(fù)責(zé)發(fā)送消息拔鹰,Looper負(fù)責(zé)接收handler發(fā)送的消息仪缸,并把消息回傳給handler自己。
Android更新UI的方式列肢?
答:1恰画、runOnUIThread
2、handler post
3瓷马、handler sendMessage
4拴还、view post
樂(lè)觀鎖與悲觀鎖
https://blog.csdn.net/truelove12358/article/details/54963791
樂(lè)觀鎖。更加寬松的加鎖機(jī)制欧聘,在基于數(shù)據(jù)庫(kù)表的版本解決方案中片林,一般是通過(guò)為數(shù)據(jù)庫(kù)表增加一個(gè) “version” 字段來(lái)實(shí)現(xiàn)。讀取出數(shù)據(jù)時(shí),將此版本號(hào)一同讀出费封,之后更新時(shí)焕妙,對(duì)此版本號(hào)加一。此時(shí)弓摘,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對(duì)焚鹊,如果提交的數(shù)據(jù)版本號(hào)大于數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào),則予以更新韧献,否則認(rèn)為是過(guò)期數(shù)據(jù)寺旺。
悲觀鎖。正如其名势决,具有強(qiáng)烈的獨(dú)占和排他特性。它指的是對(duì)數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù)蓝撇,以及來(lái)自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度果复,因此,在整個(gè)數(shù)據(jù)處理過(guò)程中渤昌,將數(shù)據(jù)處于鎖定狀態(tài)虽抄。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫(kù)提供的鎖機(jī)制(也只有數(shù)據(jù)庫(kù)層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問(wèn)的排他性独柑,否則迈窟,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無(wú)法保證外部系統(tǒng)不會(huì)修改數(shù)據(jù))忌栅。但隨之而來(lái)的就是數(shù)據(jù)庫(kù)性能的大量開銷车酣,特別是對(duì)長(zhǎng)事務(wù)而言,這樣的開銷往往無(wú)法承受索绪。
CAS操作是在樂(lè)觀鎖上面執(zhí)行
JDK1.5中引入了底層的支持湖员,在int、long和對(duì)象的引用等類型上都公開了CAS的操作瑞驱,并且JVM把它們編譯為底層硬件提供的最有效的方法娘摔,在運(yùn)行CAS的平臺(tái)上,運(yùn)行時(shí)把它們編譯為相應(yīng)的機(jī)器指令唤反。在java.util.concurrent.atomic包下面的所有的原子變量類型中凳寺,比如AtomicInteger,都使用了這些底層的JVM支持為數(shù)字類型的引用類型提供一種高效的CAS操作彤侍。
在CAS操作中肠缨,會(huì)出現(xiàn)ABA問(wèn)題。就是如果V的值先由A變成B盏阶,再由B變成A怜瞒,那么仍然認(rèn)為是發(fā)生了變化,并需要重新執(zhí)行算法中的步驟。有簡(jiǎn)單的解決方案:不是更新某個(gè)引用的值吴汪,而是更新兩個(gè)值惠窄,包括一個(gè)引用和一個(gè)版本號(hào),即使這個(gè)值由A變?yōu)锽漾橙,然后為變?yōu)锳杆融,版本號(hào)也是不同的。
Android 7.0 8.0 p 兼容性問(wèn)題
Android 7.0
低電耗模式
當(dāng)設(shè)備處于充電狀態(tài)且屏幕已關(guān)閉一定時(shí)間后霜运,設(shè)備會(huì)進(jìn)入低電耗模式并應(yīng)用第一部分限制:關(guān)閉應(yīng)用網(wǎng)絡(luò)訪問(wèn)脾歇、推遲作業(yè)和同步。如果進(jìn)入低電耗模式后設(shè)備處于靜止?fàn)顟B(tài)達(dá)到一定時(shí)間淘捡,系統(tǒng)則會(huì)對(duì) PowerManager.WakeLock藕各、AlarmManager 鬧鈴、GPS 和 WLAN 掃描應(yīng)用余下的低電耗模式限制焦除。在此窗口期間激况,應(yīng)用程序可以訪問(wèn)網(wǎng)絡(luò)并執(zhí)行任何被推遲的作業(yè)/同步。
后臺(tái)優(yōu)化
移除了三項(xiàng)隱式廣播膘魄,以幫助優(yōu)化內(nèi)存使用和電量消耗
Android 框架提供多個(gè)解決方案來(lái)緩解對(duì)這些隱式廣播的需求乌逐。例如,JobSchedulerAPI 提供了一個(gè)穩(wěn)健可靠的機(jī)制來(lái)安排滿足指定條件(例如連入無(wú)限流量網(wǎng)絡(luò))時(shí)所執(zhí)行的網(wǎng)絡(luò)操作创葡。您甚至可以使用 JobScheduler 來(lái)適應(yīng)內(nèi)容提供程序變化浙踢。
權(quán)限更改
傳遞軟件包網(wǎng)域外的 file:// URI 可能給接收器留下無(wú)法訪問(wèn)的路徑,分享私有文件內(nèi)容的推薦方法是使用 FileProvider灿渴。
在應(yīng)用間共享文件洛波,同上
Android 8.0
針對(duì)所有 API 級(jí)別的應(yīng)用
為提高設(shè)備性能,系統(tǒng)會(huì)限制未在前臺(tái)運(yùn)行的應(yīng)用的某些行為骚露。具體而言:
現(xiàn)在奋岁,在后臺(tái)運(yùn)行的應(yīng)用對(duì)后臺(tái)服務(wù)的訪問(wèn)受到限制。
應(yīng)用無(wú)法使用其清單注冊(cè)大部分隱式廣播(即荸百,并非專門針對(duì)此應(yīng)用的廣播)闻伶。
后臺(tái)位置限制
為節(jié)約電池電量、保持良好的用戶體驗(yàn)和確保系統(tǒng)健康運(yùn)行够话,在運(yùn)行 Android 8.0 的設(shè)備上使用后臺(tái)應(yīng)用時(shí)因惭,降低了后臺(tái)應(yīng)用接收位置更新的頻率媳搪。此行為變更會(huì)影響包括 Google Play 服務(wù)在內(nèi)的所有接收位置更新的應(yīng)用选浑。
此類變更會(huì)影響以下 API:
Fused Location Provider (FLP)
Geofencing
GNSS Measurements
Location Manager
Wi-Fi Manager
應(yīng)用快捷鍵
com.android.launcher.action.INSTALL_SHORTCUT 廣播不再會(huì)對(duì)您的應(yīng)用有任何影響办素,因?yàn)樗F(xiàn)在是私有的隱式廣播。相反欣尼,您應(yīng)使用 ShortcutManager 類中的 requestPinShortcut() 函數(shù)創(chuàng)建應(yīng)用快捷方式爆雹。
現(xiàn)在停蕉,ACTION_CREATE_SHORTCUT Intent 可以創(chuàng)建可使用 ShortcutManager 類進(jìn)行管理的應(yīng)用快捷方式。此 Intent 還可以創(chuàng)建不與 ShortcutManager 交互的舊版啟動(dòng)器快捷方式钙态。在以前慧起,此 Intent 只能創(chuàng)建舊版啟動(dòng)器快捷方式。
現(xiàn)在册倒,使用 requestPinShortcut() 創(chuàng)建的快捷方式和在處理 ACTION_CREATE_SHORTCUT Intent 的操作組件中創(chuàng)建的快捷方式均已轉(zhuǎn)換為功能齊全的應(yīng)用快捷方式蚓挤。因此,應(yīng)用現(xiàn)在可以使用 ShortcutManager 中的函數(shù)來(lái)更新這些快捷方式驻子。
舊版快捷方式仍然保留了它們?cè)谂f版 Android 中的功能灿意,但您必須在應(yīng)用中手動(dòng)將它們轉(zhuǎn)換成應(yīng)用快捷方式。
語(yǔ)言區(qū)域和國(guó)際化
Android 7.0(API 級(jí)別 24)引入能指定默認(rèn)類別語(yǔ)言區(qū)域的概念崇呵,但是某些 API 在本應(yīng)使用默認(rèn) DISPLAY 類別語(yǔ)言區(qū)域時(shí)缤剧,仍然使用不帶參數(shù)的通用 Locale.getDefault() 函數(shù)。現(xiàn)在域慷,在 Android 8.0 中荒辕,以下函數(shù)使用 Locale.getDefault(Category.DISPLAY) 來(lái)代替 Locale.getDefault():
提醒窗口
輸入和導(dǎo)航
網(wǎng)頁(yè)表單自動(dòng)填充 Android 自動(dòng)填充框架提供對(duì)自動(dòng)填充功能的內(nèi)置支持
針對(duì) Android 8.0 的應(yīng)用
提醒窗口
使用 SYSTEM_ALERT_WINDOW 權(quán)限的應(yīng)用無(wú)法再使用以下窗口類型來(lái)在其他應(yīng)用和系統(tǒng)窗口上方顯示提醒窗口:
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
相反,應(yīng)用必須使用名為 TYPE_APPLICATION_OVERLAY 的新窗口類型芒粹。
使用 TYPE_APPLICATION_OVERLAY 窗口類型顯示應(yīng)用的提醒窗口時(shí),請(qǐng)記住新窗口類型的以下特性:
應(yīng)用的提醒窗口始終顯示在狀態(tài)欄和輸入法等關(guān)鍵系統(tǒng)窗口的下面大溜。
系統(tǒng)可以移動(dòng)使用 TYPE_APPLICATION_OVERLAY 窗口類型的窗口或調(diào)整其大小化漆,以改善屏幕顯示效果。
通過(guò)打開通知欄钦奋,用戶可以訪問(wèn)設(shè)置來(lái)阻止應(yīng)用顯示使用 TYPE_APPLICATION_OVERLAY 窗口類型顯示的提醒窗口座云。
媒體
框架會(huì)執(zhí)行音頻閃避。進(jìn)行 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 時(shí)付材,應(yīng)用不會(huì)失去焦點(diǎn)朦拖。新的 API 適用于需要暫停而不是閃避的應(yīng)用。請(qǐng)注意厌衔,此行為無(wú)法在 Android 8.0 1 版本中實(shí)現(xiàn)璧帝。
當(dāng)用戶打電話時(shí),活動(dòng)的媒體流將在通話期間靜音富寿。
集合的處理
在 Android 8.0 中睬隶,Collections.sort() 是在 List.sort() 的基礎(chǔ)上實(shí)現(xiàn)的。在 Android 7.x(API 級(jí)別 24 和 25)中页徐,則恰恰相反苏潜。在過(guò)去,List.sort() 的默認(rèn)實(shí)現(xiàn)會(huì)調(diào)用 Collections.sort()变勇。
類加載行為
平臺(tái)將檢查類加載器返回的類描述符是否與預(yù)期的描述符一致恤左。如果返回的描述符與預(yù)期不符,平臺(tái)會(huì)引發(fā) NoClassDefFoundError 錯(cuò)誤,并在異常日志中存儲(chǔ)一條注明不一致之處的詳細(xì)錯(cuò)誤消息飞袋。
平臺(tái)還檢查請(qǐng)求的類描述符是否有效戳气。此檢查捕獲間接加載諸如 GetFieldID() 等類的 JNI 調(diào)用,向這些類傳遞無(wú)效的描述符授嘀。例如物咳,找不到包含 java/lang/String 簽名的字段,是因?yàn)榇撕灻麩o(wú)效蹄皱;它應(yīng)為 Ljava/lang/String;览闰。
這與 JNI 對(duì) FindClass() 的調(diào)用不同,其中 java/lang/String 是一個(gè)有效的完全限定名稱巷折。
Android 8.0 不支持多個(gè)類加載器同時(shí)嘗試使用相同的 DexFile 對(duì)象來(lái)定義類压鉴。嘗試進(jìn)行此操作,會(huì)導(dǎo)致 Android 運(yùn)行時(shí)引發(fā) InternalError 錯(cuò)誤锻拘,同時(shí)顯示消息“Attempt to register dex file <filename> with multiple class loaders”油吭。
Android P
屏幕缺口支持
您可以按如下方法在任何運(yùn)行 Android P 的設(shè)備或模擬器上模擬屏幕缺口:
啟用開發(fā)者選項(xiàng)。
在 Developer options 屏幕中署拟,向下滾動(dòng)至 Drawing 部分并選擇 Simulate a display with a cutout婉宰。
選擇凹口屏幕的大小。
通知
短信 Android P 可在手機(jī)的“短信通知”中顯示圖像推穷。SmartReply:Android P 支持您的短信應(yīng)用中提供的建議回復(fù)心包。
多攝像頭支持和攝像頭更新
攝像頭方面的其他改進(jìn)還包括新的會(huì)話參數(shù)和 Surface 共享,前者有助于降低首次拍照期間的延遲馒铃,而后者則讓攝像頭客戶端能夠處理各種用例蟹腾,而無(wú)需停止并啟動(dòng)攝像頭視頻流。 我們還針對(duì)基于顯示屏的 flash 支持和 OIS 時(shí)間戳訪問(wèn)新增了一些 API区宇,用以實(shí)現(xiàn)應(yīng)用級(jí)的圖像穩(wěn)定化和特效娃殖。
適用于位圖和可繪制對(duì)象的 ImageDecoder
您應(yīng)使用 ImageDecoder 來(lái)解碼圖像,而不是使用 BitmapFactory 和 BitmapFactory.Options API议谷。
ImageDecoder 讓您可以從字節(jié)緩沖區(qū)炉爆、文件或 URI 來(lái)創(chuàng)建 Drawable 或 Bitmap。
內(nèi)存泄漏
1卧晓、單例造成的內(nèi)存泄漏
由于單例的靜態(tài)特性使得其生命周期和應(yīng)用的生命周期一樣長(zhǎng)
2叶洞、非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實(shí)例造成的內(nèi)存泄漏
因?yàn)榉庆o態(tài)內(nèi)部類默認(rèn)會(huì)持有外部類的引用,而該非靜態(tài)內(nèi)部類又創(chuàng)建了一個(gè)靜態(tài)的實(shí)例禀崖,該實(shí)例的生命周期和應(yīng)用的一樣長(zhǎng)衩辟,這就導(dǎo)致了該靜態(tài)實(shí)例一直會(huì)持有該Activity的引用,解決方法:將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類或?qū)⒃搩?nèi)部類抽取出來(lái)封裝成一個(gè)單例波附,如果需要使用Context艺晴,就使用Application的Context昼钻。
3、Handler造成的內(nèi)存泄漏
1封寞、從Android的角度
當(dāng)Android應(yīng)用程序啟動(dòng)時(shí)然评,該應(yīng)用程序的主線程會(huì)自動(dòng)創(chuàng)建一個(gè)Looper對(duì)象和與之關(guān)聯(lián)的MessageQueue。當(dāng)主線程中實(shí)例化一個(gè)Handler對(duì)象后狈究,它就會(huì)自動(dòng)與主線程Looper的MessageQueue關(guān)聯(lián)起來(lái)碗淌。所有發(fā)送到MessageQueue的Messag都會(huì)持有Handler的引用,所以Looper會(huì)據(jù)此回調(diào)Handle的handleMessage()方法來(lái)處理消息抖锥。只要MessageQueue中有未處理的Message亿眠,Looper就會(huì)不斷的從中取出并交給Handler處理。另外磅废,主線程的Looper對(duì)象會(huì)伴隨該應(yīng)用程序的整個(gè)生命周期纳像。
2、 Java角度
在Java中拯勉,非靜態(tài)內(nèi)部類和匿名類內(nèi)部類都會(huì)潛在持有它們所屬的外部類的引用竟趾,但是靜態(tài)內(nèi)部類卻不會(huì)。
4宫峦、線程造成的內(nèi)存泄漏
示例:AsyncTask和Runnable
AsyncTask和Runnable都使用了匿名內(nèi)部類岔帽,那么它們將持有其所在Activity的隱式引用。如果任務(wù)在Activity銷毀之前還未完成导绷,那么將導(dǎo)致Activity的內(nèi)存資源無(wú)法被回收犀勒,從而造成內(nèi)存泄漏。
解決方法:將AsyncTask和Runnable類獨(dú)立出來(lái)或者使用靜態(tài)內(nèi)部類诵次,這樣便可以避免內(nèi)存泄漏账蓉。
5枚碗、資源未關(guān)閉造成的內(nèi)存泄漏
1)比如在Activity中register了一個(gè)BraodcastReceiver逾一,但在Activity結(jié)束后沒(méi)有unregister該BraodcastReceiver。
2)資源性對(duì)象比如Cursor肮雨,Stream遵堵、File文件等往往都用了一些緩沖,我們?cè)诓皇褂玫臅r(shí)候怨规,應(yīng)該及時(shí)關(guān)閉它們陌宿,以便它們的緩沖及時(shí)回收內(nèi)存。它們的緩沖不僅存在于 java虛擬機(jī)內(nèi)波丰,還存在于java虛擬機(jī)外壳坪。如果我們僅僅是把它的引用設(shè)置為null,而不關(guān)閉它們掰烟,往往會(huì)造成內(nèi)存泄漏爽蝴。
3)對(duì)于資源性對(duì)象在不使用的時(shí)候沐批,應(yīng)該調(diào)用它的close()函數(shù)將其關(guān)閉掉,然后再設(shè)置為null蝎亚。在我們的程序退出時(shí)一定要確保我們的資源性對(duì)象已經(jīng)關(guān)閉九孩。
4)Bitmap對(duì)象不在使用時(shí)調(diào)用recycle()釋放內(nèi)存。2.3以后的bitmap應(yīng)該是不需要手動(dòng)recycle了发框,內(nèi)存已經(jīng)在java層了躺彬。
6、使用ListView時(shí)造成的內(nèi)存泄漏
構(gòu)造Adapter時(shí)梅惯,沒(méi)有使用緩存的convertView宪拥。
解決方法:在構(gòu)造Adapter時(shí),使用緩存的convertView个唧。
7江解、集合容器中的內(nèi)存泄露
如果這個(gè)集合是static的話,那情況就更嚴(yán)重了徙歼。
解決方法:在退出程序之前犁河,將集合里的東西clear,然后置為null魄梯,再退出程序桨螺。
8、WebView造成的泄露
當(dāng)我們不要使用WebView對(duì)象時(shí)酿秸,應(yīng)該調(diào)用它的destory()函數(shù)來(lái)銷毀它灭翔,并釋放其占用的內(nèi)存,否則其長(zhǎng)期占用的內(nèi)存也不能被回收辣苏,從而造成內(nèi)存泄露肝箱。
解決方法:為WebView另外開啟一個(gè)進(jìn)程,通過(guò)AIDL與主線程進(jìn)行通信稀蟋,WebView所在的進(jìn)程可以根據(jù)業(yè)務(wù)的需要選擇合適的時(shí)機(jī)進(jìn)行銷毀煌张,從而達(dá)到內(nèi)存的完整釋放。
大圖加載的緩存
按比例壓縮到跟控件大小退客,計(jì)算壓縮值骏融,壓縮后,可以根據(jù)圖片的色彩存儲(chǔ)模式選擇顯示萌狂,減輕內(nèi)存負(fù)擔(dān)档玻。
Bitmap優(yōu)化
LruCache 和diskLruCache 優(yōu)化緩存。 當(dāng)向 ImageView 中加載一張圖片時(shí),首先會(huì)在 LruCache 的緩存中進(jìn)行檢查茫藏。如果找到了相應(yīng)的鍵值误趴,則會(huì)立刻更新ImageView ,否則開啟一個(gè)后臺(tái)線程來(lái)加載這張圖片务傲。
子線程能不能創(chuàng)建Handler
創(chuàng)建主線程的Looper凉当、主線程的消息隊(duì)列...就連我們使用的handler也是主線程的碧囊。
不一定,之所以子線程不能更新界面纤怒,是因?yàn)锳ndroid在線程的方法里面采用checkThread進(jìn)行判斷是否是主線程糯而,而這個(gè)方法是在ViewRootImpl中的,這個(gè)類是在onResume里面才生成的泊窘,因此熄驼,如果這個(gè)時(shí)候子線程在onCreate方法里面生成更新UI,而且沒(méi)有做阻塞烘豹,就是耗時(shí)多的操作瓜贾,還是可以更新UI的。
所以以后要想創(chuàng)建非主線程的Handler時(shí)携悯,我們用HandlerThread類提供的Looper對(duì)象即可祭芦。創(chuàng)建HandlerThread對(duì)象的時(shí)候,有個(gè)參數(shù)憔鬼,是指定線程名字的龟劲。
線程間通信其他方式
一:Handler實(shí)現(xiàn)線程間的通信,消息是通過(guò)綁定在主線程的Handler來(lái)傳遞的轴或。
二昌跌、解決線程間通信的問(wèn)題:使用AsyncTask:
三、runOnUiThread()照雁,子線程中持有當(dāng)前Activity引用(假如為Activity mActivity;)蚕愤,即可以調(diào)用mActivity的runOnUiThread(Runnable r)方法。
四饺蚊、post()和postDelay()
子線程如果持有某個(gè)View的引用萍诱,要對(duì)該View進(jìn)行更新,則可調(diào)用該View對(duì)象的post(Runnable r)或postDelay(Runnable r)方法
volatile原理
通俗點(diǎn)講就是說(shuō)一個(gè)變量如果用volatile修飾了污呼,則Java可以確保所有線程看到這個(gè)變量的值是一致的裕坊,如果某個(gè)線程對(duì)volatile修飾的共享變量進(jìn)行更新,那么其他線程可以立馬看到這個(gè)更新曙求,這就是所謂的線程可見性碍庵。
volatile的使用
防重排序
并發(fā)環(huán)境下的單例實(shí)現(xiàn)方式映企,我們通澄蛴可以采用雙重檢查加鎖(DCL)的方式來(lái)實(shí)現(xiàn)
實(shí)現(xiàn)可見性
可見性問(wèn)題主要指一個(gè)線程修改了共享變量值,而另一個(gè)線程卻看不到堰氓。引起可見性問(wèn)題的主要原因是每個(gè)線程擁有自己的一個(gè)高速緩存區(qū)——線程工作內(nèi)存挤渐。volatile關(guān)鍵字能有效的解決這個(gè)問(wèn)題
保證原子性
volatile只能保證對(duì)單次讀/寫的原子性
因?yàn)閘ong和double兩種數(shù)據(jù)類型的操作可分為高32位和低32位兩部分,因此普通的long或double類型讀/寫可能不是原子的双絮。因此浴麻,鼓勵(lì)大家將共享的long和double變量設(shè)置為volatile類型得问,這樣能保證任何情況下對(duì)long和double的單次讀/寫操作都具有原子性。
volatile的原理
可見性實(shí)現(xiàn):(1)修改volatile變量時(shí)會(huì)強(qiáng)制將修改后的值刷新的主內(nèi)存中软免。
(2)修改volatile變量后會(huì)導(dǎo)致其他線程工作內(nèi)存中對(duì)應(yīng)的變量值失效宫纬。因此,再讀取該變量值的時(shí)候就需要重新從讀取主內(nèi)存中的值膏萧。
有序性實(shí)現(xiàn):通俗一點(diǎn)說(shuō)就是如果a happen-before b漓骚,則a所做的任何操作對(duì)b是可見的。
同一個(gè)線程中的榛泛,前面的操作 happen-before 后續(xù)的操作蝌蹂。(即單線程內(nèi)按代碼順序執(zhí)行。但是曹锨,在不影響在單線程環(huán)境執(zhí)行結(jié)果的前提下孤个,編譯器和處理器可以進(jìn)行重排序,這是合法的沛简。換句話說(shuō)齐鲤,這一是規(guī)則無(wú)法保證編譯重排和指令重排)。
volatile的使用優(yōu)化 用一種追加字節(jié)的方式來(lái)優(yōu)化隊(duì)列出隊(duì)和入隊(duì)的性能
那么是不是在使用volatile變量時(shí)都應(yīng)該追加到64字節(jié)呢椒楣?不是的佳遂。在兩種場(chǎng)景下不應(yīng)該使用這種方式。
緩存行非64字節(jié)寬的處理器撒顿。如P6系列和奔騰處理器丑罪,它們的L1和L2高速緩存行是32個(gè)字節(jié)寬。
共享變量不會(huì)被頻繁地寫凤壁。因?yàn)槭褂米芳幼止?jié)的方式需要處理器讀取更多的字節(jié)到高速緩沖區(qū)吩屹,這本身就會(huì)帶來(lái)一定的性能消耗,如果共享變量不被頻繁寫的話拧抖,鎖的幾率也非常小煤搜,就沒(méi)必要通過(guò)追加字節(jié)的方式來(lái)避免相互鎖定。
讀寫鎖的應(yīng)用
比如說(shuō)有兩個(gè)線程唧席,他們之間共享一塊數(shù)據(jù)擦盾,在一個(gè)線程寫數(shù)據(jù)和一個(gè)線程讀數(shù)據(jù)的時(shí)候,怎么樣保證數(shù)據(jù)和邏輯的正確性淌哟。是否需要同步和加鎖呢迹卢?還是完全沒(méi)有必要?實(shí)際上徒仓,在前面關(guān)于volatile的介紹里已經(jīng)基本上解決了腐碱。對(duì)于有多個(gè)讀取數(shù)據(jù)的線程和單個(gè)寫數(shù)據(jù)線程的場(chǎng)景,需要讀寫鎖掉弛。
RecyclerView與ListView的區(qū)別
RecyclerView症见,它主要的特點(diǎn)就是復(fù)用喂走。我們知道,Listview中的Adapter中可以實(shí)現(xiàn)ViewHolder的復(fù)用谋作。RecyclerView提供了一個(gè)耦合度更低的方式來(lái)復(fù)用ViewHolder芋肠,并且可以輕松的實(shí)現(xiàn)ListView、GridView以及瀑布流的效果遵蚜。
LayoutManager
這個(gè)LayoutManager類決定視圖被放在畫面中哪個(gè)位置业栅,但這只是它的眾多職責(zé)之一。它可以管理滾動(dòng)和循環(huán)利用谬晕。LayoutManager只有一個(gè)叫做LinearLayoutManager的實(shí)現(xiàn)類,我們可以設(shè)置它的橫向和縱向碘裕。
ItemAnimator
ItemAnimator簡(jiǎn)單來(lái)說(shuō)是會(huì)根據(jù)適配器上收到的相關(guān)通知去動(dòng)畫的顯示組件的修改,添加和刪除等攒钳。它會(huì)自動(dòng)添加和移除item的動(dòng)畫帮孔。自帶的默認(rèn)效果也不錯(cuò),已經(jīng)非常好了不撑。
優(yōu)點(diǎn):
RecyclerView本身它是不關(guān)心視圖相關(guān)的問(wèn)題的文兢,由于ListView的緊耦合的問(wèn)題,google的改進(jìn)就是RecyclerView本身不參與任何視圖相關(guān)的問(wèn)題焕檬。它不關(guān)心如何將子View放在合適的位置姆坚,也不關(guān)心如何分割這些子View,更不關(guān)心每個(gè)子View各自的外觀实愚。更進(jìn)一步來(lái)說(shuō)就是RecyclerView它只負(fù)責(zé)回收和重用的工作兼呵,這也是它名字的由來(lái)。
所有關(guān)于布局腊敲、繪制和其他相關(guān)的問(wèn)題击喂,也就是跟數(shù)據(jù)展示相關(guān)的所有問(wèn)題,都被委派給了一些”插件化”的類來(lái)處理碰辅。這使得RecyclerView的API變得非常靈活懂昂。你需要一個(gè)新的布局么?接入另一個(gè)LayoutManager就可以了没宾!你想要不同的動(dòng)畫么凌彬?接入一個(gè)新的ItemAnimator就可以了,諸如此類等等循衰。還能局部刷新铲敛。
缺點(diǎn):
在RecyclerView中,沒(méi)有一個(gè)onItemClickListener方法羹蚣。所以目前在適配器中處理這樣的事件比較好原探。如果想要從適配器上添加或移除條目乱凿,需要明確通知適配器顽素。這與先前的notifyDataSetChanged()方法稍微有些不同咽弦。具體操作在適配器代碼中就可以體現(xiàn)。
事件分發(fā)機(jī)制(看書)
動(dòng)畫(看書)
okhttp****支不支持優(yōu)先級(jí)
本篇文章的網(wǎng)絡(luò)層是OKHttp胁出,既然選擇了OkHttp型型,如果要在onDestroy中取消未開始執(zhí)行以及已經(jīng)開始執(zhí)行的網(wǎng)絡(luò)請(qǐng)求,就必須給每一個(gè)請(qǐng)求設(shè)置一個(gè)tag全蝶,然后通過(guò)該tag來(lái)需要網(wǎng)絡(luò)請(qǐng)求闹蒜。比較明智的做法是以該Activity的上下文的hash值作為tag。取消請(qǐng)求時(shí)將hash值傳入抑淫,則該界面所有的請(qǐng)求都可以取消绷落。
底層Request暴露優(yōu)先級(jí)之后我們需要實(shí)現(xiàn)一個(gè)比較器,根據(jù)優(yōu)先級(jí)由大到小進(jìn)行排序始苇。
ssl****握手誰(shuí)實(shí)現(xiàn)的
首先tcp三次握手:Platform.get().connectSocket
其次獲得I/O流:source = Okio.buffer(Okio.source(rawSocket));sink = Okio.buffer(Okio.sink(rawSocket));
然后判斷是否需要ssl砌烁,如果需要?jiǎng)t進(jìn)行ssl:connectTls(readTimeout, writeTimeout, connectionSpecSelector);
在connectTls中,忽略其他的催式,ssl握手發(fā)生在
sslSocket.startHandshake();
底層實(shí)現(xiàn)握手函喉。
okhttp websocket****應(yīng)用
https://blog.csdn.net/tq08g2z/article/details/77311767
什么情況會(huì)產(chǎn)生ANR
在Android上,如果你的應(yīng)用程序有一段時(shí)間響應(yīng)不夠靈敏荣月,系統(tǒng)會(huì)向用戶顯示一個(gè)對(duì)話框管呵,這個(gè)對(duì)話框稱作應(yīng)用程序無(wú)響應(yīng)(ANR:Application Not Responding)對(duì)話框。默認(rèn)情況下哺窄,在android中Activity的最長(zhǎng)執(zhí)行時(shí)間是5秒捐下,BroadcastReceiver的最長(zhǎng)執(zhí)行時(shí)間則是10秒。
產(chǎn)生原因:由于主線程有很多重要事情要做萌业,比如響應(yīng)點(diǎn)擊事件等蔑担。如果在主線程里做了太多耗時(shí)的操作,則有可能引發(fā)ANR咽白。盡量將耗時(shí)的操作放在子線程里啤握。
由于主線程導(dǎo)致的情況:
1.耗時(shí)網(wǎng)絡(luò)訪問(wèn)
2.當(dāng)有大量數(shù)據(jù)讀寫操作時(shí)再請(qǐng)求數(shù)據(jù)讀寫
3.數(shù)據(jù)庫(kù)操作(比如其他大數(shù)據(jù)量應(yīng)用訪問(wèn)數(shù)據(jù)庫(kù)導(dǎo)致數(shù)據(jù)庫(kù)負(fù)載過(guò)重時(shí))
4.硬件操作(比如Camera)
非主線程導(dǎo)致的情況:
1.非主線程持有l(wèi)ock,導(dǎo)致主線程等待lock超時(shí)
2.非主線程終止或者崩潰導(dǎo)致主線程一直等待
廣播****onReceive****方法調(diào)用線程
不建議開線程晶框,建議開service排抬,onreceive一下子就失活了
靜態(tài)廣播接收流程
廣播的發(fā)送是通過(guò)sendBroadcast 執(zhí)行的,
總結(jié):
廣播的注冊(cè)過(guò)程 :最終在ActivityManagerService中將遠(yuǎn)程的InnerInnerReceiver以及Intent-filter對(duì)象存儲(chǔ)起來(lái)授段。
廣播的發(fā)送以及接受:內(nèi)部會(huì)首先根據(jù)傳入的Intent-filter 查找出匹配的廣播接受者蹲蒲,并將改接受者放到BroadcastQueue中,緊接著系統(tǒng)會(huì)遍歷ArrayList中的廣播侵贵,并將其發(fā)送給它們對(duì)應(yīng)的廣播接受者届搁,最后調(diào)用到廣播接受者的onReceiver方法。
動(dòng)態(tài)廣播能不能重復(fù)注冊(cè)
第三次被踢就會(huì)出現(xiàn)三次…..我在程序中使用的是動(dòng)態(tài)注冊(cè),結(jié)果我換成靜態(tài)注冊(cè)就沒(méi)問(wèn)題了卡睦。想了想貌似問(wèn)題就在這里宴胧,應(yīng)該是重復(fù)注冊(cè)了廣播接收。找到問(wèn)題以后將廣播接收對(duì)象定義為靜態(tài)對(duì)象表锻,只初始化一次恕齐,問(wèn)題迎刃而解。也就是說(shuō)瞬逊,使用動(dòng)態(tài)注冊(cè)显歧,每注冊(cè)一次就會(huì)生成一個(gè)廣播接收的對(duì)象。
Butterknife****工作原理
ButterKnife是通過(guò)使用注解方式來(lái)自動(dòng)生成模板代碼确镊,從而來(lái)將Activity中的字段和方法與View綁定在一起士骤。
butterknife的原理主要分為三個(gè)部分來(lái)介紹,主要為:注解生成模板代碼分析蕾域、butterknife.bind()方法分析敦间、生成的模板類代碼分析。
butterknife注冊(cè)的注解器為ButterKnifeProcessor
http://www.reibang.com/p/5cba8a5514cb
Android****仿微信朋友圈圖片展示效果****,
1.透明Activity
2.計(jì)算gridView下imageView Item所在位置
3.一張圖大小
4.圖片展示動(dòng)畫
LeakCanary****原理
[圖片上傳失敗...(image-36b1d8-1533601512661)]
Activity****啟動(dòng)模式及幾個(gè)模式的應(yīng)用場(chǎng)景
[圖片上傳失敗...(image-ff924c-1533601512660)]
1. SingleTask模式的運(yùn)用場(chǎng)景
最常見的應(yīng)用場(chǎng)景就是保持我們應(yīng)用開啟后僅僅有一個(gè)Activity的實(shí)例束铭。最典型的樣例就是應(yīng)用中展示的主頁(yè)(Home頁(yè))廓块。假設(shè)用戶在主頁(yè)跳轉(zhuǎn)到其他頁(yè)面,運(yùn)行多次操作后想返回到主頁(yè)契沫,假設(shè)不使用SingleTask模式带猴,在點(diǎn)擊返回的過(guò)程中會(huì)多次看到主頁(yè),這明顯就是設(shè)計(jì)不合理了懈万。
還有:
|
LauchMode
|
Instance
|
| --- | --- |
|
standard
|
mainfest中沒(méi)有配置就默認(rèn)標(biāo)準(zhǔn)模式
|
|
singleTop
|
登錄頁(yè)面拴清、WXPayEntryActivity、WXEntryActivity 会通、推送通知欄
|
|
singleTask
|
程序模塊邏輯入口:主頁(yè)面(Fragment的containerActivity)口予、WebView頁(yè)面、掃一掃頁(yè)面
|
|
singleInstance
|
系統(tǒng)Launcher涕侈、鎖屏鍵沪停、來(lái)電顯示等系統(tǒng)應(yīng)用
|
滅屏?xí)粫?huì)觸發(fā)onSavedInstance
會(huì)的。
調(diào)用時(shí)機(jī) : Activity 容易被銷毀的時(shí)候調(diào)用, 注意是容易被銷毀, 也可能沒(méi)有銷毀就調(diào)用了;
按下Home鍵 : Activity 進(jìn)入了后臺(tái), 此時(shí)會(huì)調(diào)用該方法;
按下電源鍵 : 屏幕關(guān)閉, Activity 進(jìn)入后臺(tái);
啟動(dòng)其它 Activity : Activity 被壓入了任務(wù)棧的棧底;
橫豎屏切換 : 會(huì)銷毀當(dāng)前 Activity 并重新創(chuàng)建;
onRestoreInstanceState和onSavedInstanceState是否成對(duì)出現(xiàn)
onSavedInstanceState()和onRestoreInstanceState()并不是activity生命周期的方法裳涛。
onSaveInstanceState()會(huì)在onPause()或onStop()之前執(zhí)行木张,onRestoreInstanceState()會(huì)在onStart()和onResume()之間執(zhí)行。
當(dāng)應(yīng)用遇到意外情況(內(nèi)存不足端三,用戶直接按home鍵)由系統(tǒng)直接銷毀一個(gè)Activity時(shí)舷礼,onSaveInstanceState()就會(huì)調(diào)用,但是當(dāng)用戶主動(dòng)銷毀activity郊闯,如按back鍵妻献,或直接執(zhí)行finish(),這種情況下onSaveInstanceState()就不會(huì)執(zhí)行蛛株,因?yàn)檫@種情況下,用戶的行為決定了不需要保存Activity的狀態(tài)育拨。
那么onRestoreInstanceState()會(huì)跟onSaveInstanceState()成對(duì)出現(xiàn)嗎谨履? 答案是不會(huì)成對(duì)出現(xiàn),onSaveInstanceState()需要調(diào)用的時(shí)至朗,activity可能銷毀屉符,也可能沒(méi)有銷毀剧浸,只有在activity銷毀重建的時(shí)候onRestoreInstanceState()才會(huì)調(diào)用锹引。
在onSaveInstanceState()中默認(rèn)情況下具體干些什么?
默認(rèn)情況下默認(rèn)會(huì)自動(dòng)保存Activity中的某些狀態(tài)唆香,比如activity中各種UI的狀態(tài)嫌变,因此在activity被“系統(tǒng)”銷毀和重建的時(shí)候,這些Ui的狀態(tài)會(huì)默認(rèn)保存躬它,但是前提條件是Ui控件必須制定id,如果沒(méi)有指定id的話腾啥,UI的狀態(tài)是無(wú)法保存 的。
總結(jié)下Activity數(shù)據(jù)的保存和恢復(fù):
activity中保存數(shù)據(jù)有兩種方式onPause()冯吓,onSaveInstance(bundle), 恢復(fù)數(shù)據(jù)也有兩種途徑onCreate(Bundle), onRestoreInstanceState(budle)倘待,默認(rèn)情況下onSaveInstanceSate()和onRestoreInstanceState()會(huì)對(duì)UI狀態(tài)進(jìn)行保存和恢復(fù),如果需要保存其他數(shù)據(jù)可以在onSaveInstanceState()组贺,onPause()保存凸舵,但是如果是持久化的數(shù)據(jù)得通過(guò)onPause()保存(google推薦)。
Service生命周期的理解
bindService整個(gè)代碼怎么寫
這個(gè)過(guò)程比較復(fù)雜失尖,但總體來(lái)說(shuō)啊奄,思路還是比較清晰的,整個(gè)調(diào)用過(guò)程為MainActivity.bindService->CounterService.onCreate->CounterService.onBind->MainActivity.ServiceConnection.onServiceConnection->CounterService.CounterBinder.getService掀潮。下面菇夸,我們就先用一個(gè)序列圖來(lái)總體描述這個(gè)服務(wù)綁定的過(guò)程,然后就具體分析每一個(gè)步驟仪吧。
與service通信是否會(huì)阻塞當(dāng)前線程
會(huì)的庄新,因?yàn)閟ervice本來(lái)就是在主線程,為了避免阻塞薯鼠,所以我們往往在service創(chuàng)建子線程摄咆,或者是啟用intentservice,比較常見的Service傳遞數(shù)據(jù)到Activity有三種方式, 通過(guò)Binder, 通過(guò)Broadcast廣播, 自定義接口回調(diào), 都是借助于IBinder暴露Service中的相應(yīng)操作人断。網(wǎng)上還有些文章中提到用觀察者模式吭从,觀察者模式也是接口回調(diào)的一種方式,
如果是耗時(shí)方法恶迈,為什么會(huì)阻塞
是因?yàn)樵谥骶€程涩金,ANR
如果不是耗時(shí)方法谱醇,為什么不會(huì)阻塞
如果遠(yuǎn)端是耗時(shí)操作,怎么不等待結(jié)果讓主線程先運(yùn)行
ANR
startService和bindSerivce對(duì)service生命周期的影響
一步做、正常情況(應(yīng)該大家都很熟了副渴,簡(jiǎn)單介紹):
(1)單獨(dú)使用startService():
onCreate()->onStartCommand()->Service running->onDestroy()->Service shut down
(2)單獨(dú)使用bindService():
onCreate()->onBind()->Clients are bound to service->onUnbind()->onDestroy()->Service shut down
例子一:按順序1,2,3,4執(zhí)行
(1)startServic:調(diào)用onCreate()->onStartCommand()
(2)bindService:調(diào)用onBind()
(3)stopService:沒(méi)有調(diào)用onDestory() Service仍然在運(yùn)行!
(4)unbindService:調(diào)用onUnbind()->onDestory() 此時(shí)Service關(guān)閉全度!
例子二:將例子一3,4調(diào)換
(1)startServic:調(diào)用onCreate()->onStartCommand()
(2)bindService:調(diào)用onBind()
(3)unbindService:調(diào)用onUnbind() Service仍然在運(yùn)行煮剧!
(4)stopService:調(diào)用onDestory() 此時(shí)Service才關(guān)閉今穿!
aidl傳遞Bitmap需要注意的事項(xiàng)
攒暇?瘾境?低零?線程問(wèn)題馋劈?注意實(shí)體類實(shí)現(xiàn)Parcelable借口梗夸。
EventBus原理
在3.0的版本中陪踩,使用的注解類型為Runtime
三要素:
A,Event:事件,
B,Publisher:發(fā)布者,可以在任意線程發(fā)布事件
C,Subscrible:訂閱者,
1,采用單利雙重鎖模式創(chuàng)建對(duì)象
2,構(gòu)造方法
2.1,粘性事件,保存到ConCurrenHashMap集合,(在構(gòu)造方法中實(shí)現(xiàn)),
HashMap效率高,但線程不安全,在多線程的情況下,盡量用ConcurrentHashMap,避免多線程并發(fā)異常
3,注冊(cè)register()方法主要做了2件事:
3.1,找到訂閱者的方法.找出傳進(jìn)來(lái)的訂閱者的所有訂閱方法,然后遍歷訂閱者的方法.
A,通過(guò)反射來(lái)獲取訂閱者中所有的方法,并根據(jù)方法的類型,參數(shù)和注解找到訂閱方法.
3.2,訂閱者的注冊(cè)
注解處理器怎么工作猴凹,注解處理器有哪些API
注解處理器是(Annotation Processor)是javac的一個(gè)工具痒筒,用來(lái)在編譯時(shí)掃描和編譯和處理注解宰闰,就是把標(biāo)記了注解的類,變量等作為輸入內(nèi)容簿透,經(jīng)過(guò)注解處理器處理移袍,生成想要生成的java代碼。
處理器的寫法有固定的套路老充,繼承AbstractProcessor葡盗。
init(ProcessingEnvironment processingEnv) 被注解處理工具調(diào)用,參數(shù)ProcessingEnvironment 提供了Element蚂维,F(xiàn)iler戳粒,Messager等工具
getSupportedAnnotationTypes() 指定注解處理器是注冊(cè)給那一個(gè)注解的,它是一個(gè)字符串的集合虫啥,意味著可以支持多個(gè)類型的注解蔚约,并且字符串是合法全名。
getSupportedSourceVersion 指定Java版本
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) 這個(gè)也是最主要的涂籽,在這里掃描和處理你的注解并生成Java代碼苹祟,信息都在參數(shù)RoundEnvironment 里了,后面會(huì)介紹评雌。
Glide原理
郭霖大佬寫 https://blog.csdn.net/guolin_blog/article/details/53759439
Lrucache原理
LinkedHashMap原理
HashMap原理
o 解決Hash沖突的方法
o equals和hashcode作用
o hashcode如何實(shí)現(xiàn)
自己為知筆記
Object類下有什么方法
registerNatives() //私有方法
getClass() //返回此 Object 的運(yùn)行類树枫。
hashCode() //用于獲取對(duì)象的哈希值。
equals(Object obj) //用于確認(rèn)兩個(gè)對(duì)象是否“相同”景东。
clone() //創(chuàng)建并返回此對(duì)象的一個(gè)副本砂轻。
toString() //返回該對(duì)象的字符串表示。
notify() //喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程斤吐。
notifyAll() //喚醒在此對(duì)象監(jiān)視器上等待的所有線程搔涝。
wait(long timeout) //在其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法厨喂,或 者超過(guò)指定的時(shí)間量前,導(dǎo)致當(dāng)前線程等待庄呈。
wait(long timeout, int nanos) //在其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法蜕煌,或者其他某個(gè)線程中斷當(dāng)前線程,或者已超過(guò)某個(gè)實(shí)際時(shí)間量前诬留,導(dǎo)致當(dāng)前線程等待斜纪。
wait() //用于讓當(dāng)前線程失去操作權(quán)限,當(dāng)前線程進(jìn)入等待序列
finalize() //當(dāng)垃圾回收器確定不存在對(duì)該對(duì)象的更多引用時(shí)文兑,由對(duì)象的垃圾回收器調(diào)用此方法盒刚。
Android中的類加載器
類加載器之間的區(qū)別
Android中的ClassLoader類型也可分為系統(tǒng)ClassLoader和自定義ClassLoader。其中系統(tǒng)ClassLoader包括3種分別是:
BootClassLoader彩届,Android系統(tǒng)啟動(dòng)時(shí)會(huì)使用BootClassLoader來(lái)預(yù)加載常用類伪冰,與Java中的Bootstrap ClassLoader不同的是誓酒,它并不是由C/C++代碼實(shí)現(xiàn)樟蠕,而是由Java實(shí)現(xiàn)的。BootClassLoader是ClassLoader的一個(gè)內(nèi)部類靠柑。
PathClassLoader寨辩,全名是dalvik/system.PathClassLoader,可以加載已經(jīng)安裝的Apk歼冰,也就是/data/app/package 下的apk文件靡狞,也可以加載/vendor/lib, /system/lib下的nativeLibrary。
DexClassLoader隔嫡,全名是dalvik/system.DexClassLoader甸怕,可以加載一個(gè)未安裝的apk文件。
從打印的結(jié)果也可以證實(shí):App系統(tǒng)類加載器是PathClassLoader腮恩,而BootClassLoader是其parent類加載器梢杭。
Dex融合用的哪種類加載器
DexClassLoader是一個(gè)可以從包含classes.dex實(shí)體的.jar或.apk文件中加載classes的類加載器〗盏危可以用于實(shí)現(xiàn)dex的動(dòng)態(tài)加載武契、代碼熱更新等等。這個(gè)類加載器必須要一個(gè)app的私有荡含、可寫目錄來(lái)緩存經(jīng)過(guò)優(yōu)化的classes(odex文件)咒唆,使用Context.getDir(String, int)方法可以創(chuàng)建一個(gè)這樣的目錄。
父類是什么及三者之間的關(guān)系
是ClassLoader释液,也不同于Java的ClassLoader全释。
https://blog.csdn.net/mynameishuangshuai/article/details/52737581
雙親委派模型
如果使用委托機(jī)制,會(huì)遞歸的向父類查找误债,也就是首選用Bootstrap嘗試加載浸船,如果找不到再向下符衔。這里的System就能在Bootstrap中找到然后加載,如果此時(shí)類B也要加載System糟袁,也從Bootstrap開始判族,此時(shí)Bootstrap發(fā)現(xiàn)已經(jīng)加載過(guò)了System那么直接返回內(nèi)存中的System即可而不需要重新加載,這樣內(nèi)存中就只有一份System的字節(jié)碼了
Android中的動(dòng)畫及區(qū)別(看書项戴,或者其他
Android中序列化方式
兩者區(qū)別
永久的保存對(duì)象數(shù)據(jù)
通過(guò)序列化操作將對(duì)象數(shù)據(jù)在網(wǎng)絡(luò)上進(jìn)行傳輸
將對(duì)象數(shù)據(jù)在進(jìn)程之間進(jìn)行傳遞
Serializable 直接implement
Parcelable 不僅要implement 還要還需要實(shí)現(xiàn)內(nèi)部的相應(yīng)方法
為什么Parcelable性能更好
Parcelable與Serializable的性能比較
首先Parcelable的性能要強(qiáng)于Serializable的原因我需要簡(jiǎn)單的闡述一下
1). 在內(nèi)存的使用中,前者在性能方面要強(qiáng)于后者
2). 后者在序列化操作的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,(原因是使用了反射機(jī)制)從而導(dǎo)致GC的頻繁調(diào)用,因此在性能上會(huì)稍微遜色
3). Parcelable是以Ibinder作為信息載體的.在內(nèi)存上的開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞的時(shí)候,Android推薦使用Parcelable,既然是內(nèi)存方面比價(jià)有優(yōu)勢(shì),那么自然就要優(yōu)先選擇.
4). 在讀寫數(shù)據(jù)的時(shí)候,Parcelable是在內(nèi)存中直接進(jìn)行讀寫,而Serializable是通過(guò)使用IO流的形式將數(shù)據(jù)讀寫入在硬盤上.
但是:雖然Parcelable的性能要強(qiáng)于Serializable,但是仍然有特殊的情況需要使用Serializable,而不去使用Parcelable,因?yàn)?***Parcelable****無(wú)法將數(shù)據(jù)進(jìn)行持久化,因此在將數(shù)據(jù)保存在磁盤的時(shí)候,仍然需要使用后者,因?yàn)榍罢邿o(wú)法很好的將數(shù)據(jù)進(jìn)行持久化.(原因是在不同的Android版本當(dāng)中,Parcelable可能會(huì)不同,因此數(shù)據(jù)的持久化方面仍然是使用Serializable)
序列化UID作用
輔助完成序列化和反序列化形帮,當(dāng)一個(gè)類實(shí)現(xiàn)SerSerializable接口,沒(méi)有添加serialVersionUID的作用字段時(shí)周叮,IDE會(huì)發(fā)出警告辩撑,這個(gè)字段可以手動(dòng)指定一個(gè)值,比如1L仿耽,也可指定為IED根據(jù)類的結(jié)構(gòu)生成一個(gè)long值合冀,它們的效果是一樣的。在序列化時(shí)會(huì)將這個(gè)值寫入存儲(chǔ)介質(zhì)项贺,反序列化時(shí)就校驗(yàn)本地類的serialVersionUID和序列化介質(zhì)中的是否一致君躺,不一致將拋出異常 java.io.InvalidClassException
(1)若不指定:系統(tǒng)會(huì)根據(jù)類的結(jié)構(gòu)計(jì)算出一個(gè)serialVersionUID,一旦類的結(jié)構(gòu)發(fā)生改變這個(gè)值就會(huì)改變开缎,將導(dǎo)致反序列化失斪亟小;
(2)指定一個(gè)值:當(dāng)類的結(jié)構(gòu)發(fā)生改變時(shí)奕删,也可以不修改serialVersionUID的值俺泣,這種情況下能最大程度上通過(guò)反序列化回復(fù)數(shù)據(jù),若類的結(jié)構(gòu)發(fā)生毀滅性的改變完残,例如字段數(shù)據(jù)類型改變了伏钠,也會(huì)導(dǎo)致反序列失敗。
VLayout實(shí)現(xiàn)原理
從官網(wǎng)的介紹VLayout擴(kuò)展的主要是LayoutManager谨设,具體布局邏輯在LayoutHelper中實(shí)現(xiàn)熟掂。Vlayout提供了各種各樣的LayoutHelper實(shí)現(xiàn),同時(shí)也可以非常容易地?cái)U(kuò)展實(shí)現(xiàn)自定義的LayoutHelper铝宵。
[圖片上傳失敗...(image-216067-1533601512655)]
可以看的上圖的列表中有各種各樣的布局打掘,這些都是LayoutHelper的功勞。DelegateAdapter負(fù)責(zé)組合多個(gè)adapter鹏秋,展示在RecyclerView中尊蚁。
這有一個(gè)比較明顯的缺點(diǎn)是,每個(gè)子adapter只負(fù)責(zé)一個(gè)連續(xù)的區(qū)域侣夷,如果横朋,有多個(gè)區(qū)域需要同樣的adapter,就需要添加兩次adapter百拓。
VLayout為什么不用RecyclerView實(shí)現(xiàn)多Item
HTTPS連接過(guò)程
分為http鏈接和SSL鏈接過(guò)程
應(yīng)用構(gòu)建過(guò)程
[圖片上傳失敗...(image-71cd6a-1533601512656)]
通常的構(gòu)建過(guò)程就是如上圖所示琴锭,下面是具體描述:
1.AAPT(Android Asset Packaging Tool)工具會(huì)打包應(yīng)用中的資源文件晰甚,如AndroidManifest.xml、layout布局中的xml等决帖,并將xml文件編譯為二進(jìn)制形式厕九,當(dāng)然assets文件夾中的文件不會(huì)被編譯,圖片及raw文件夾中的資源也會(huì)保持原來(lái)的形態(tài)地回,需要注意的是raw文件夾中的資源也會(huì)生成資源id扁远。AAPT編譯完成之后會(huì)生成R.java文件。
2.AIDL工具會(huì)將所有的aidl接口轉(zhuǎn)化為java接口刻像。
3.所有的java代碼畅买,包括R.java與aidl文件都會(huì)被Java編譯器編譯成.class文件。
4.Dex工具會(huì)將上述產(chǎn)生的.class文件及第三庫(kù)及其他.class文件編譯成.dex文件(dex文件是Dalvik虛擬機(jī)可以執(zhí)行的格式)细睡,dex文件最終會(huì)被打包進(jìn)APK文件谷羞。
5.ApkBuilder工具會(huì)將編譯過(guò)的資源及未編譯過(guò)的資源(如圖片等)以及.dex文件打包成APK文件。
6.生成APK文件后溜徙,需要對(duì)其簽名才可安裝到設(shè)備湃缎,平時(shí)測(cè)試時(shí)會(huì)使用debug keystore,當(dāng)正式發(fā)布應(yīng)用時(shí)必須使用release版的keystore對(duì)應(yīng)用進(jìn)行簽名萌京。
7.如果對(duì)APK正式簽名雁歌,還需要使用zipalign工具對(duì)APK進(jìn)行對(duì)齊操作宏浩,這樣做的好處是當(dāng)應(yīng)用運(yùn)行時(shí)會(huì)提高速度知残,但是相應(yīng)的會(huì)增加內(nèi)存的開銷。
簽名機(jī)制流程:
1比庄、對(duì)Apk中的每個(gè)文件做一次算法(數(shù)據(jù)摘要+Base64編碼)求妹,保存到MANIFEST.MF文件中
2、對(duì)MANIFEST.MF整個(gè)文件做一次算法(數(shù)據(jù)摘要+Base64編碼)佳窑,存放到CERT.SF文件的頭屬性中制恍,在對(duì)MANIFEST.MF文件中各個(gè)屬性塊做一次算法(數(shù)據(jù)摘要+Base64編碼),存到到一個(gè)屬性塊中神凑。
3净神、對(duì)CERT.SF文件做簽名,內(nèi)容存檔到CERT.RSA中
應(yīng)用簽名校驗(yàn)過(guò)程
1溉委、驗(yàn)證Apk中的每個(gè)文件的算法(數(shù)據(jù)摘要+Base64編碼)和MANIFEST.MF文件中的對(duì)應(yīng)屬性塊內(nèi)容是否配對(duì)
2鹃唯、驗(yàn)證CERT.SF文件的簽名信息和CERT.RSA中的內(nèi)容是否一致
3、MANIFEST.MF整個(gè)文件簽名在CERT.SF文件中頭屬性中的值是否匹配以及驗(yàn)證MANIFEST.MF文件中的各個(gè)屬性塊的簽名在CERT.SF文件中是否匹配
V1簽名和V2簽名區(qū)別
v1簽名是對(duì)jar進(jìn)行簽名瓣喊,V2簽名是對(duì)整個(gè)apk簽名:官方介紹就是:v2簽名是在整個(gè)APK文件的二進(jìn)制內(nèi)容上計(jì)算和驗(yàn)證的坡慌,v1是在歸檔文件中解壓縮文件內(nèi)容。
二者簽名所產(chǎn)生的結(jié)果:
v1:在v1中只對(duì)未壓縮的文件內(nèi)容進(jìn)行了驗(yàn)證藻三,所以在APK簽名之后可以進(jìn)行很多修改——文件可以移動(dòng)洪橘,甚至可以重新壓縮跪者。即可以對(duì)簽名后的文件在進(jìn)行處理 。
v2:v2簽名驗(yàn)證了歸檔中的所有字節(jié)熄求,而不是單獨(dú)的ZIP條目渣玲,如果您在構(gòu)建過(guò)程中有任何定制任務(wù),包括篡改或處理APK文件弟晚,請(qǐng)確保禁用它們柜蜈,否則您可能會(huì)使v2簽名失效,從而使您的APKs與Android 7.0和以上版本不兼容指巡。
Dex加固原理
下面就來(lái)看一下Android中加殼的原理:
[圖片上傳失敗...(image-2f742d-1533601512656)]
我們?cè)诩庸痰倪^(guò)程中需要三個(gè)對(duì)象:
1****淑履、需要加密的****Apk(****源****Apk)
2****、殼程序****Apk(****負(fù)責(zé)解密****Apk****工作****)
3****藻雪、加密工具****(****將源****Apk****進(jìn)行加密和殼****Dex****合并成新的****Dex)
主要步驟:
我們拿到需要加密的****Apk****和自己的殼程序****Apk****秘噪,然后用加密算法對(duì)源****Apk****進(jìn)行加密在將殼****Apk****進(jìn)行合并得到新的****Dex****文件,最后替換殼程序中的****dex****文件即可勉耀,得到新的****Apk,****那么這個(gè)新的****Apk****我們也叫作脫殼程序****Apk.****他已經(jīng)不是一個(gè)完整意義上的****Apk****程序了指煎,他的主要工作是:負(fù)責(zé)解密源****Apk.****然后加載****Apk,****讓其正常運(yùn)行起來(lái)。
APK瘦身
1便斥、使用一套資源
對(duì)于絕大對(duì)數(shù)APP來(lái)說(shuō)至壤,只需要取一套設(shè)計(jì)圖就足夠了。鑒于現(xiàn)在分辨率的趨勢(shì)枢纠,建議取720p的資源像街,放到xhdpi目錄。
2晋渺、開啟minifyEnabled混淆代碼
在gradle使用minifyEnabled進(jìn)行Proguard混淆的配置镰绎,可大大減小APP大小
3、開啟shrinkResources去除無(wú)用資源
在gradle使用shrinkResources去除無(wú)用資源木西,效果非常好畴栖。
4、清理無(wú)用資源
Lint在檢查完成后八千,會(huì)提供一份詳細(xì)的資源文件清單吗讶,并且將沒(méi)有用到的資源在 UnusedResources:Unused resources 區(qū)域。只要我們沒(méi)有通過(guò)反射使用這些資源恋捆,就可以放心的刪掉它們了照皆。版本迭代過(guò)程中,不但有廢棄代碼冗余鸠信,肯定會(huì)有無(wú)用的圖片存在纵寝。真正起效果只能通過(guò)Android Studio自帶的 “Remove Unused Resources”小插件來(lái)實(shí)現(xiàn)了
5、刪除無(wú)用的語(yǔ)言資源
大部分應(yīng)用其實(shí)并不需要支持幾十種語(yǔ)言的國(guó)際化支持。還好強(qiáng)大的gradle支持語(yǔ)言的配置爽茴,比如國(guó)內(nèi)應(yīng)用只支持中文:
6葬凳、使用jpg格式,使用webp格式室奏,縮小大圖
7火焰、刪除armable-v7包下的so ,刪除x86包下的so
8胧沫、使用微信資源壓縮打包工具