第十章:Android的消息機(jī)制
- Handler是Android消息機(jī)制的上層接口慕蔚,開發(fā)人員只需要與它交互即可,底層需要Looper與MessageQueue的支持站削,MessageQueue是單鏈表數(shù)據(jù)結(jié)構(gòu)存儲Message坊萝,Looper存儲在ThreadLocal中孵稽,與線程關(guān)聯(lián)许起,三者配合完成指定邏輯在指定線程的順序執(zhí)行十偶。
- Handler是在某一個線程中創(chuàng)建的(并提前完成了消息的實(shí)現(xiàn)邏輯),然后在其他線程中去發(fā)送消息园细,原線程的Looper執(zhí)行到該消息會調(diào)用Handler的消息處理邏輯(在原線程)惦积,因此Handler可以輕松的實(shí)現(xiàn)將業(yè)務(wù)邏輯在指定線程中去調(diào)用,最常見的就是更新UI的業(yè)務(wù)邏輯在子線程觸發(fā)猛频,在主線程處理狮崩。
- 由于控件的更新不是線程安全的,因此要使用主線程Handler去更新UI鹿寻,使得更新邏輯在單線程順序執(zhí)行睦柴,避免了并發(fā)帶來的問題。PS:處理并發(fā)也可以在控件的更新方法上synchronized加鎖毡熏,但是效率低坦敌,因此采用單線程處理,類似Java并發(fā)中的阻塞隊(duì)列痢法。
- 線程默認(rèn)是沒有Looper的狱窘,創(chuàng)建Handler時也會進(jìn)行檢查,沒有Looper的話會拋出異常财搁,因此在子線程使用Handler要先自己創(chuàng)建Looper并開啟循環(huán)蘸炸;主線程默認(rèn)已有Looper且已啟動了
Looper.loop()
,因此主線程可以直接創(chuàng)建Handler尖奔,通過Looper.getMainLooper()
可直接獲取到主線程Looper搭儒。 - Handler、Looper提茁、MessageQueue大致工作流程:
一個線程只能創(chuàng)建一個Looper仗嗦,業(yè)務(wù)過多可以創(chuàng)建多個Handler;Looper內(nèi)部創(chuàng)建并管理一個messageQueue甘凭,線程中創(chuàng)建Handler時也會檢查是否已經(jīng)含有Looper(主要為了與其中的messageQueue建立關(guān)聯(lián))稀拐,然后looper.loop()
方法會不斷循環(huán)messageQueue去取消息,messageQueue的next()
方法取消息是阻塞的丹弱,因此looper無限循環(huán)取調(diào)用德撬,是阻塞的;其他線程想要執(zhí)行指定邏輯到創(chuàng)建Handler的線程時躲胳,就可以調(diào)用Handler的send()
方法或post()
方法蜓洪,最終消息會被Handler發(fā)到其內(nèi)部的messageQueue中,然后Looper會單線程順序拿取里面的消息同時再調(diào)msg.target(其實(shí)就是handler)的dispatchMessage()
方法坯苹,dispatchMessage()
方法是Handler里的隆檀,最終會被下發(fā)到handleMessage()
方法中,也就是最初創(chuàng)建Handler時實(shí)現(xiàn)的重寫方法,這樣一來消息就是提前在創(chuàng)建者中實(shí)現(xiàn)恐仑,然后在調(diào)用者中去觸發(fā)泉坐,完成將指定好的邏輯放到指定線程中執(zhí)行的功能。 - ThreadLocal的工作原理:
一般而言線程是內(nèi)存數(shù)據(jù)共享的裳仆,ThreadLocal是個類似全局?jǐn)?shù)據(jù)線程作用域管理類腕让,可以輕松實(shí)現(xiàn)不同線程存取不同的數(shù)據(jù)副本,Looper就是一個需要每個線程獨(dú)享的數(shù)據(jù)歧斟,當(dāng)然實(shí)現(xiàn)類似方式也可用對象一級一級傳遞下去(始終保持同一個引用)纯丸,或者全局維護(hù)靜態(tài)單例對象等;ThreadLocal有個內(nèi)部類ThreadLocalMap(不是Map静袖,里面有個table數(shù)組)觉鼻,里面有個弱引用Entry包裝類(弱引用ThreadLocal自己),包裝了value
(具體數(shù)據(jù))队橙,table
數(shù)組存著這些Entry滑凉,每個線程有個threadLocals
數(shù)據(jù)域(ThreadLocalMap類型);當(dāng)使用ThreadLocal存數(shù)據(jù)時喘帚,set()
方法會根據(jù)線程去取其中的threadLocals
畅姊,然后將ThreadLocal作為key去尋找放入位置,創(chuàng)建Entry對象包裝value
并放入table數(shù)組吹由;當(dāng)取數(shù)據(jù)時若未,get()
方法會根據(jù)線程去取其中的threadLocals
,然后將ThreadLocal作為key去table
數(shù)組中找到其中的Entry倾鲫,然后拿到Entry的value
返回數(shù)據(jù)粗合;這里ThreadLocal作為key主要是通過hash等去找出要存儲的位置下標(biāo),table是個數(shù)組乌昔,將生成的Entry包裝類放入數(shù)組指定的位置隙疚。 - MessageQueue的工作原理:
MessageQueue叫消息隊(duì)列,但本身的實(shí)現(xiàn)并不是隊(duì)列磕道,是一個單鏈表的數(shù)據(jù)結(jié)構(gòu)供屉,在插入和刪除上比較有優(yōu)勢(不過順序插入刪除沒啥體現(xiàn)吧);它只有插入enqueueMessage()
和讀取next()
兩個操作溺蕉,讀取伴隨著刪除伶丐,next()
方法是阻塞的。 - Looper的工作原理:
Looper.prepare()
方法會創(chuàng)建一個looper疯特,內(nèi)部創(chuàng)建并管理一個messageQueue哗魂,Looper.loop()
方法會執(zhí)行for循環(huán)無限循環(huán)消息隊(duì)列去取消息,由于消息隊(duì)列的next()
方法是阻塞的漓雅,因此它的loop()
方法也是阻塞的录别;由于主線程比較特殊朽色,Looper提供了getMainLooper()
方法可以在任何地方獲取到主線程的Looper對象;Looper提供了quit()
和quitSafely()
方法用于退出Looper组题,類似線程池的shutdown()
方法葫男,前者立即退出,后者等消息隊(duì)列處理完后退出往踢,退出后Handler的send()
方法會返回false,在子線程中我們自己創(chuàng)建的Looper在不再使用時最好手動quit()
一下終止循環(huán)徘层;當(dāng)Looper標(biāo)記成退出狀態(tài)后峻呕,messageQueue的next()
方法就會返回null,for循環(huán)就會return退出趣效。 - Handler的工作原理:
Handler就是發(fā)送和處理消息瘦癌,發(fā)送使消息順序執(zhí)行,處理使任務(wù)到指定線程中執(zhí)行跷敬,發(fā)送消息時直接調(diào)用queue.enqueueMessag()
將消息插入隊(duì)列讯私,查詢方法由Looper來調(diào),是阻塞的西傀;消息分發(fā)判斷順序?yàn)?code>post發(fā)出的Runnable > 創(chuàng)建Handler的callback > Handler的handleMessage()斤寇;Handler其實(shí)就是提前包裝好業(yè)務(wù)邏輯,由其他線程發(fā)起消息拥褂,然后原線程Looper循環(huán)到消息后調(diào)用Handler來處理消息娘锁;Handler的構(gòu)造方法中可以直接傳入一個Looper,就可以指定消息發(fā)送到該線程處理了饺鹃,HandlerThread就是一個自帶Looper的線程莫秆,提供出Looper來給外部創(chuàng)建Handler使用,實(shí)現(xiàn)單線程消息循環(huán)機(jī)制悔详。 - Android的主線程就是ActivityThread镊屎,主線程入口方法為
main()
方法,內(nèi)部會Looper.prepareMainLooper()
來創(chuàng)建主線程的Looper以及MessageQueue茄螃,并通過Looper.loop()
來開啟主線程消息循環(huán)缝驳,ActivityThread中的H用來給AMS的Binder回調(diào)回來提交到主線程做一些回調(diào)使用。
主線程的消息循環(huán)需要的Handler就是ActivityThread.H归苍。 - 兩個問題:
? 主線程Looper的死循環(huán)為何不會導(dǎo)致卡死:循環(huán)里queue.next()
會阻塞党巾,所以死循環(huán)并不會一直執(zhí)行,相反的霜医,大部分時間是沒有消息的齿拂,所以主線程大多數(shù)時候都是處于休眠狀態(tài),也就不會消耗太多的CPU資源導(dǎo)致卡死肴敛;阻塞的原理是使用Linux的管道機(jī)制實(shí)現(xiàn)的署海,主線程沒有消息處理時阻塞在管道的讀端吗购,Binder線程會往主線程消息隊(duì)列里添加消息,然后往管道寫端寫一個字節(jié)砸狞,這樣就能喚醒主線程從管道讀端返回捻勉,也就是說looper循環(huán)里queue.next()
會調(diào)用返回。
? 主線程的死循環(huán)阻塞了如何處理其它事務(wù):在Looper.prepareMainLooper()
和Looper.loop()
之間創(chuàng)建了ActivityThread并thread.attach(false)
刀森,已經(jīng)將主線程的Handler放出去了踱启,自己的代碼邏輯都是通過H來驅(qū)動執(zhí)行的,只需要通過binder線程向H發(fā)送消息即可研底,所以說Android應(yīng)用程序也是依靠消息驅(qū)動來工作的埠偿。
第十一章:Android的線程和線程池
- 線程是操作系統(tǒng)調(diào)度的最小單元,同時也是一種有限的系統(tǒng)資源榜晦,不可無限制的創(chuàng)建冠蒋,線程不可能做到絕對的并行,除非線程數(shù)小于等于CPU的核心數(shù)乾胶,一般來說是不會的抖剿。
- 主線程指進(jìn)程鎖擁有的線程,與Java一樣识窿,進(jìn)程一開始就有一個線程(主線程或UI線程)斩郎,主線程要保持較高的響應(yīng)速度(與用戶前臺交互),因此不能有太多的耗時操作喻频,主線程以外都是子線程孽拷,子線程也叫工作線程,用于異步執(zhí)行耗時操作半抱。
- Andorid中的幾種線程形態(tài):
①AsyncTask:
? 輕量級異步任務(wù)類脓恕,底層使用線程池+Handler實(shí)現(xiàn),提供了幾種方法回調(diào)(不同線程中)窿侈,方便開發(fā)者在子線程中做耗時任務(wù)后更新UI炼幔。
? 幾點(diǎn)限制:必須主線程加載(4.1及以上版本已經(jīng)被系統(tǒng)自動完成);必須在主線程創(chuàng)建(內(nèi)部的Handler是static的史简,類加載時就會創(chuàng)建乃秀,因此必須在主線程);excute方法必須在主線程調(diào)用圆兵;一個AsyncTask只能執(zhí)行一次跺讯,后續(xù)會拋異常。
? 1.6之前任務(wù)是串行執(zhí)行殉农,1.6~3.0使用線程池并發(fā)執(zhí)行刀脏,3.0以又是串行執(zhí)行,但可以通過executeOnExecutor()
來強(qiáng)制并行執(zhí)行超凳;因此3.0以前最大128個線程并發(fā)愈污,10個緩沖隊(duì)列耀态,多了就默認(rèn)拒絕策略拋異常了;3.0以后用一個Deque包裝Runnable暂雹,循環(huán)取消息并執(zhí)行首装,因此進(jìn)程中的所有AsyncTask都在這串行線程池中排隊(duì)執(zhí)行。
? 不建議執(zhí)行特別耗時的任務(wù)杭跪,特別耗時的任務(wù)建議使用線程池來執(zhí)行仙逻。
②HandlerThread:
? 內(nèi)部創(chuàng)建了Looper并開啟了循環(huán),可以在外部獲取此Looper來創(chuàng)建Handler涧尿,將任務(wù)封裝到外部來調(diào)用系奉,在該Thread中順序執(zhí)行,類似主線程消息機(jī)制现斋。
? 由于內(nèi)部Looper是無限循環(huán)的喜最,使用完畢記得調(diào)用quit()
或quitSafely()
一下來終止線程偎蘸。
③IntentService:
? 本身是個服務(wù)庄蹋,內(nèi)部有一個worker(HandlerThread)線程可以執(zhí)行耗時任務(wù),當(dāng)onHnadleIntent()
方法執(zhí)行結(jié)束后迷雪,它會自動stopSelf(id)
來結(jié)束自己限书,stopSelf()
會立即停止服務(wù),stopSelf(id)
會等所有消息處理完畢后停止服務(wù)章咧;每次啟動一次服務(wù)倦西,都會向Handler發(fā)出一條message,多個任務(wù)順序執(zhí)行赁严。
? 服務(wù)的優(yōu)先級更高一些扰柠,如果是一個后臺線程,沒有四大組件的依賴在內(nèi)存不足時容易被殺死疼约,而服務(wù)的優(yōu)先級較高卤档,這種在服務(wù)中再開啟線程執(zhí)行任務(wù),任務(wù)線程則不易被殺死程剥。 - 線程池相關(guān):
? 作用:重用線程劝枣、有效控制最大并發(fā)數(shù)、簡單的線程管理织鲸。
? 構(gòu)造方法7個參數(shù):corePooSize(核心線程)舔腾、maximumPoolSize(最大線程)、keepAliveTime(空閑線程存活時間)搂擦、unit(時間單位)稳诚、workQueue(阻塞隊(duì)列)、threadFactory(線程創(chuàng)建工廠)瀑踢、rejectedExecutionHandler(拒絕策略)采桃。
? 拒絕策略:AbortPolicy(拋異常 默認(rèn))懒熙、CallerRunsPolicy(在調(diào)用者線程直接用)、DiscardPolicy(拒絕不拋異常)普办、DiscardOldestPolicy(將最早任務(wù)刪掉加入任務(wù)隊(duì)列)工扎。
? 內(nèi)部調(diào)用順序:核心線程 > 任務(wù)隊(duì)列 > 最大線程 > 拒絕策略。 - 四種常見線程池:
? FixedThreadPool:核心數(shù)=最大數(shù)衔蹲,0存活時間肢娘;核心線程不會被回收,適用于快速處理任務(wù)舆驶。
? SingleThreadExecutor:核心數(shù)=最大數(shù)=1橱健,0存活時間;特殊的Fix線程池沙廉,單線程順序執(zhí)行拘荡,不需要處理同步問題。
? CachedThreadPool:核心數(shù)=0撬陵,最大數(shù)=MAX珊皿,存活時間60s,無任務(wù)隊(duì)列巨税;適用于大量短任務(wù)蟋定,有空閑的直接用,沒有就創(chuàng)建草添。
? ScheduledThreadPool:核心=自定義固定值驶兜,最大=MAX,0存活時間远寸;適用于固定周期的重復(fù)任務(wù)執(zhí)行抄淑。
注意:
主線程方法體中使用handler的post()
方法,由于同在主線程驰后,當(dāng)前方法體執(zhí)行完后才會可能開始執(zhí)行msgQueue隊(duì)列中的下一個消息肆资;而thread.start()之后子線程就處于準(zhǔn)備狀態(tài)要開始并行執(zhí)行了,與當(dāng)前方法體下一行代碼的執(zhí)行順序是不可控的(不過一般在子線程做的都是耗時的邏輯倡怎,不會那么快跑完)迅耘。
- 多線程相關(guān)類:
? Runnable可以在線程或線程池中執(zhí)行,Callable监署、Future颤专、FutureTask只能在線程池中執(zhí)行。
? Callable:也是一個業(yè)務(wù)包裝類钠乏,包裝到call方法栖秕,有返回值<T>。
? Future:本身是一個接口晓避,封裝了cancel()簇捍、isDone()只壳、get()等方法,get()
是等待業(yè)務(wù)返回值(阻塞方法)暑塑。
? FutureTask:實(shí)現(xiàn)了RunnableFuture<V>接口吼句,RunnableFuture<V>繼承自Future和Runnable接口,算是一個接口適配器模式(類似Stub類)事格,可以把一個Runnable惕艳、Callable或FutureTask來submit()
給線程池,完了拿到Future<?>后可以對任務(wù)進(jìn)行管理和獲取返回值(阻塞)驹愚。 - 程序中的優(yōu)化策略:
? CopyOnWriteArrayList:讀寫List远搪,基于讀寫分離思想,可并發(fā)讀逢捺,寫的時候copy了一份副本谁鳍,因此寫占用了雙倍內(nèi)存,用的少劫瞳。
? ConcurrentHaspMap:分段鎖HashMap倘潜,分段加鎖技術(shù),可以實(shí)現(xiàn)并發(fā)HashMap柠新。
? BlockingQueue:
add()/move():可用返回true/false窍荧,不可用拋異常辉巡。
offer()/peek():可用返回true/false恨憎,不可用返回null,不阻塞郊楣。
pull(time,unit)/element():等待取出隊(duì)首元素憔恳;拋異常。
put()/take():阻塞方法净蚤。
ArrayBlockQueue(數(shù)組實(shí)現(xiàn)/有界/線程安全/阻塞隊(duì)列)钥组、LinkedBlockQueue(單向鏈表實(shí)現(xiàn)/先進(jìn)先出/無界/線程安全/阻塞隊(duì)列);LinkedBlockDeque(雙向列表實(shí)現(xiàn)/容量可指定也可不指定/線程安全/阻塞隊(duì)列)今瀑、ConcurrentLinkedQueue(鏈接節(jié)點(diǎn)實(shí)現(xiàn)/線程安全/無界)程梦。 - 同步鎖技術(shù):
? synchronized:粗略鎖,作用于對象橘荠、方法或class屿附,利用對象的內(nèi)部鎖;當(dāng)作用于函數(shù)時哥童,鎖的是該對象中所有同步實(shí)例方法挺份,當(dāng)作用于static函數(shù)時,鎖的是該類的所有同步靜態(tài)方法贮懈;方法加鎖鎖的是整段業(yè)務(wù)邏輯匀泊;在拿到鎖后优训,sleep()
方法不會改變鎖的情況。
? ReentrankLock/Condition:每個對象有內(nèi)部鎖各聘,也可以new一個條件Lock揣非,它具有靈活性、可通信性躲因,可以實(shí)現(xiàn)輪訓(xùn)和定時鎖妆兑,利用Condition可以實(shí)現(xiàn)線程間條件通信。
? Semaphore:信號量毛仪,本質(zhì)是一個共享鎖搁嗓,維護(hù)一個信號量許可集,自定義許可集大小箱靴,其余線程可以調(diào)用smp.aquire()/release()
方法來獲取和釋放鎖腺逛,用于控制線程通過量色建。
? CyclicBarrier:循環(huán)柵欄侥蒙,自定義等待集大小和業(yè)務(wù)Runnable()椒功,其余線程調(diào)用cb.await()
進(jìn)入等待富雅,等待線程夠數(shù)時執(zhí)行定義好的業(yè)務(wù)run()
方法障般,完了線程繼續(xù)執(zhí)行耘眨。
? CountDownLatch:同步輔助類扰魂,計時等待肃叶,自定義等待次數(shù)怖现,本線程調(diào)用cd.await()
進(jìn)入等待茁帽,其余線程執(zhí)行完調(diào)用cd.countDown()
等待計數(shù)減一,等待次數(shù)結(jié)束本線程繼續(xù)執(zhí)行屈嗤。
第十二章:Bitmap的加載和Cache
- 比較常用的緩存策略是內(nèi)存緩存LruCache和磁盤緩存DiskLruCache潘拨,這種緩存策略為:當(dāng)緩存快滿時,會淘汰近期最少是用的緩存目標(biāo)饶号。
- Bitmap的高效加載是先將BitmapFactory.Options的
inJustDecodeBounds
設(shè)為true铁追,這樣不會真正的加載圖片,只是獲取圖片的原始寬高茫船,然后計算出縮略比例設(shè)置inSampleSize
參數(shù)琅束,完了再將inJustDecodeBounds
設(shè)為false,最后再用Factory加載出真正的Bitmap就行了算谈。注意:采樣率一般設(shè)為2的倍數(shù)涩禀,計算時一般也是先用長寬/2
,如果還大再去壓縮濒生,避免過度壓縮埋泵,具體算法的話因人而異。 - LruCache是一個泛型類,內(nèi)部采用LinkedHashMap存儲緩存對象的強(qiáng)引用丽声,當(dāng)內(nèi)存不足時礁蔗,會回收最近最少使用目標(biāo),內(nèi)部已經(jīng)實(shí)現(xiàn)了線程安全雁社;DiskLruCache將緩存對象寫入文件系統(tǒng)浴井,目錄默認(rèn)是
sdcard/Andorid/data/package_name/cache
下,也可根據(jù)需求自定義目錄霉撵。也可使用ACache進(jìn)行緩存磺浙,類似SP文件。 - 四種引用類型:強(qiáng)引用(直接的對象引用)徒坡、軟引用(系統(tǒng)內(nèi)存不足時會被gc回收)撕氧、弱引用(只要被gc掃到就會回收)、虛引用(結(jié)合引用隊(duì)列使用)喇完。
-
Runtime.getRuntime().maxMemory()
可獲取系統(tǒng)運(yùn)行最大內(nèi)存大新啄唷;Runtime.getRuntime().availableProcessors()
可獲取CPU核心數(shù)锦溪。 - 優(yōu)化列表卡頓的幾種做法:異步加載圖片不脯、控制加載頻率、滑動停止加載刻诊、開啟硬件加速防楷。
第十三章:綜合技術(shù)
- 異常處理器:
? 程序運(yùn)行時如果有未捕獲異常則會崩潰,這時候如果用setDefaultUncaughtExceptionHandler()
給線程設(shè)置一個異常處理器CrashHandler则涯,當(dāng)程序崩潰時就會回調(diào)uncaughtException()
方法复局。
? 默認(rèn)的異常處理器是Thread的靜態(tài)成員,因此它作用于當(dāng)前進(jìn)程的所有線程(也可給線程設(shè)置單獨(dú)的異常處理器)是整,我們一般是先獲取到默認(rèn)的異常處理器肖揣,再設(shè)置自己的異常處理器民假,當(dāng)崩潰時自己先上傳log浮入,然后繼續(xù)讓默認(rèn)異常處理器去終止進(jìn)程。 - APK分包:
? Android中單個dex文件能包含的最大方法數(shù)為65536羊异,包括AndroidFramework事秀、依賴的jar包和本身的所有代碼,超數(shù)后無法編譯通過且拋出異常野舶;有時候方法數(shù)并沒有到65536易迹,能編譯通過但是低版本手機(jī)不能安裝,這是因?yàn)閼?yīng)用在安裝時平道,dexopt程序會為了優(yōu)化apk睹欲,將dex文件的所有方法信息提前放到一個固定大小的方法緩沖區(qū)LinearAlloc內(nèi),不同版本的緩沖區(qū)大小是不同的,超過了就裝不上窘疮。
? Android5.0以前使用multidex需要引入android-support-multidex.jar
包袋哼,5.0以后默認(rèn)支持了multidex,注意BuildTools要升級到21.1以上闸衫。
? 在gradle文件中的defaultConfig里開啟分包multiDexEnabled true
涛贯,引入jar包,manifest中將application標(biāo)簽設(shè)為MultiDexApplication
或 Application繼承自MultiDexApplication蔚出,在attachBaseContex()
中設(shè)置MultiDex.install(this)
即可弟翘。
? gradle里可以寫腳本--main-dex-list
配置哪些類打到主dex中,注意multidex的jar包中9個類必須打進(jìn)主dex骄酗,Application的成員變量是先于attachBaseContex()
方法的稀余,因此不要提前聲明其他dex中的類。 - dex2jar和jd-jui可以查看apk文件解壓后的dex中的java代碼趋翻;
apktool可以對apk進(jìn)行解包滚躯、修改和二次打包,二次打包后需要重新簽名嘿歌。
? 解包:apktool.bat d -f xxx.apk yyy
? 打包:apktool.bat b yyy xxx2.apk
? 簽名:java -jar signapk.jar testkey.x509.pem testkey.pk8 xxx2.apk xxx2_signed.apk - 簽名掸掏、證書相關(guān):
? .keystore/.jks/.pem/.pk8是同級的,都是簽名文件宙帝,都可以給apk簽名丧凤。
? keytool:是個秘鑰和證書管理工具,可以用來生成秘鑰庫步脓,如Eclipse生成的是.keystore愿待,AS生成的是.jks,內(nèi)部可按別名存多組密鑰的信息靴患。
? jarsigner:工具利用密鑰庫中的信息來校驗(yàn)java存檔文件的數(shù)字簽名和簽名jar/apk文件用的仍侥。
? keytool用法:
keytool生成證書:keytool -genkey -keystore xxx.keystore -alias xxx -keyals RSA -validity 1000;
keytool查看證書:test keytool -list -keystore test.keystore;
jarsigner簽名apk:jarsigner -verbose -keystrore xxx.keystore -signerjar signed.apk unsign.apk 'xxx';
? .jks是一個秘鑰管理倉庫,只存放一類東西(密鑰)鸳君,庫中的密鑰類型分為私鑰农渊、公鑰和密鑰對,都有別名或颊,公鑰進(jìn)入倉庫就能拿走砸紊,私鑰是需要密碼的。
? .keystore是Java下keytool生成的秘鑰管理倉庫囱挑,存放兩類東西(密鑰實(shí)體和證書)醉顽,將密鑰實(shí)體和證書信息存在keystore中(證書只有公鑰)。
- ClassLoader動態(tài)加載過程:
? Dalvik虛擬機(jī)和其他Java虛擬機(jī)一樣平挑,在程序運(yùn)行時首先要將類加載到內(nèi)存中游添,類可以從class文件中讀取也可以從其他形式的二進(jìn)制流中讀取系草。
? Andorid平臺上虛擬機(jī)運(yùn)行的是Dex字節(jié)碼,一種對class文件優(yōu)化后的產(chǎn)物唆涝,Android將所有class文件合并優(yōu)化悄但,生成xxx.dex,如果分包的話會有多個dex文件石抡。
? ClassLoader分類:Classloader -> BootClassLoader檐嚣、UrlClassLoader、BaseDexClassLoader(DexPathList) -> PathClassLoader(只能加載已安裝的apk啰扛,art虛擬機(jī)還是可以加載未安裝的)嚎京、DexClassLoader(支持其他apk、dex和jar文件)
隐解。
? ClassLoader構(gòu)造方法中傳入父Classloader鞍帝,無的話默認(rèn)創(chuàng)建PathClassLoader -> BootClassLoader
,找類的時候先判斷是否被加載過煞茫,然后先從父loader中加載(雙親委派機(jī)制)帕涌。
? BootClassLoader是ClassLoader內(nèi)部類我們沒法使用,UrlClassLoader只能加載jar 不能用(dalvik虛擬機(jī)不能直接識別jar)续徽,BaseDexClassLoader我們一般修改這個類蚓曼;BaseDexClassLoader(dexPath,optimizedDirectory,libraryPath,parentCl)四個參數(shù)為:目標(biāo)dex文件路徑、odex優(yōu)化后的dex存放路徑钦扭、c/c++庫的路徑纫版、父classloader(一般context.getClassLoader())。
? 類加載過程會從pathList
中尋找class客情,DexPathList中dex包裝成Element存在一個數(shù)組里(一般只有一個element)其弊,dexElement
中的dexFile
存著最終的dex文件,因此將新的dex文件插入dexElement數(shù)組前面可以實(shí)現(xiàn)熱更新膀斋。
? Android5.0以前是Dalvik虛擬機(jī)梭伐,5.0以后是Art虛擬機(jī),據(jù)說Art在程序第一次安裝時就把字節(jié)碼預(yù)先編譯成機(jī)器碼(預(yù)編譯)并優(yōu)化了性能等細(xì)節(jié)(將.dex轉(zhuǎn)為oat文件)仰担,接口是一致的糊识,實(shí)現(xiàn)了一套完全兼容Java虛擬機(jī)的接口。
第十四章:JNI和NDK編程
- JNI是Java Native Interface(Java本地接口)惰匙,為了方便Java調(diào)用C技掏、C++層的一層本地封裝接口,NDK是Android提供的一套工具集合项鬼,方便通過JNI來訪問本地代碼和編譯生成各版本的so庫。
- JNI編程的好處是提高安全性難以反編譯劲阎、動態(tài)庫便于平臺移植和提供某些計算類代碼執(zhí)行效率等绘盟。
- C層JNIEnv*是一個指向JNI環(huán)境的指針,類似Java里的this,可以通過它訪問JNI提供的接口方法龄毡;jobject是Java層的引用(Java對象的this)吠卷,可以通過它訪問Java層代碼,類似回調(diào)沦零;JNIEXPORT和JNICALL都是JNI定義的宏祭隔。
- JNI開發(fā)流程:定義native方法 -> System.loadLibrary()加載生成的.so庫 -> javac/javah生成頭文件 -> 實(shí)現(xiàn).cpp/.c文件 -> gcc編譯出.so庫 -> java -D運(yùn)行類即可。
- NDK提供了一系列工具集合便于生成.so庫和調(diào)用.so庫路操,Eclipse中需要配置.mk文件疾渴,AS中直接在gradle中配置即可,ndk-build命令可以導(dǎo)出.so庫屯仗,就可以用了搞坝,創(chuàng)建so庫除了使用ndk-build命令,也可使用gradle自動編譯產(chǎn)生so庫魁袜。
- JNI數(shù)據(jù)類型分為基本類型和引用類型桩撮;基本類型以j開頭(jint),引用類型有類峰弹、對象和數(shù)組店量,以j開頭(jobject)。
- JNI類型標(biāo)簽標(biāo)識了一個特定的Java類型鞠呈,可以是類和方法也可以是數(shù)據(jù)類型垫桂;
? 類:L+包名+類名+;
,如Ljava/lang/String粟按;
? 基本類型:大寫字母
诬滩,如int為I;
? 數(shù)組:[+大寫字母/類標(biāo)簽
灭将,如[D/疼鸟、[Ljava/lang/String;
? 方法:參數(shù)類型+返回值
庙曙,如boolean fun(int a, double b, int[]c)為(ID[I)Z空镜。 - JNI調(diào)Java層方法時,會先找類捌朴,再找方法的id吴攒,然后通過id來調(diào)方法(如果是非靜態(tài)方法要先構(gòu)造類對象)。
第十五章:Andorid性能優(yōu)化
- 布局優(yōu)化:多用LinearLayout砂蔽、FrameLayout洼怔;<include/>標(biāo)簽(id覆蓋,width/height都指定其余屬性才生效)左驾、<merge/>標(biāo)簽(什么屬性都沒有)镣隶、<ViewStub/>標(biāo)簽(加載后就沒stub這個對象了极谊,用inflatedId或生成的View)。
- 繪制優(yōu)化:onDraw()方法由于會頻繁調(diào)用安岂,因此不要創(chuàng)建過多局部變量轻猖、不要做耗時操作等,該方法在主線程執(zhí)行域那;屬性動畫咙边、補(bǔ)間動畫等在頁面
onDestroy()
或View的onDetachedFromWindow()
后記得關(guān)閉。 - 內(nèi)存泄露優(yōu)化:無論何時靜態(tài)變量不要持有activity的引用次员、靜態(tài)內(nèi)部類+弱引用败许、流的關(guān)閉、監(jiān)聽者的解注冊等翠肘。
- 線程優(yōu)化:ANR發(fā)生時在/data/anr目錄下會有一個traces.txt文件記錄log檐束。
注意:盡量不要讓主線程也去與子線程競爭鎖,比如都去訪問synchronized方法束倍,如果鎖被其他子線程持有且耗時被丧,主線程就會阻塞住。 - ListView和Bitmap優(yōu)化:
getView()
不要做耗時操作绪妹,開啟硬件加速甥桂;Options采樣壓縮等邮旷。 - 其他優(yōu)化:使用線程池、不要過多使用枚舉婶肩、常量使用final修飾、SparseArray等數(shù)據(jù)結(jié)構(gòu)律歼、適當(dāng)軟引用和弱引用、靜態(tài)內(nèi)部類避免持有外部類引用等。
補(bǔ)充知識點(diǎn)
SDK區(qū)別:
?minSDKVersion <= targetSDKVersion <= compileSDKVersion
覆积;
? minSDKVersion:要求的最低API版本(安卓系統(tǒng));
? targetSDKVersion:程序適配的API版本(在這個版本的系統(tǒng)上運(yùn)行效率高一些,權(quán)限等功能會體現(xiàn)出來);
? compileSDKVersion:代碼編譯時的API版本(提示方法的更新之宿、棄用等)枷莉;
注意:每一次安卓系統(tǒng)的發(fā)布都會伴隨新的SDK和support庫一起發(fā)布辜限,support庫的大版本號需要和compileSDKVersion保持一致(只是官方建議,不一致也能編譯通過)严蓖,有些類只在高版本SDK里有薄嫡,所以如果compileSDKVersion編譯時判斷通過則編譯打包沒事氧急,但是運(yùn)行時在低版本可能就會崩潰了。
? 因此才會有SupportV4毫深、V7等支持包吩坝,包含了如Fragment、Material Design控件等兼容包哑蔫,引入V7默認(rèn)也會引入V4下的所有類钉寝,同時引入也不會報錯。Jar包沖突的本質(zhì):
? 問題:Java應(yīng)用程序因某種因素闸迷,加載到不正確的類而導(dǎo)致其行為跟預(yù)期不一致嵌纲。
? 兩種情況:同一個Jar包出現(xiàn)了多個不同版本;不同Jar包內(nèi)含有包名類名相同的類腥沽。
? 解決方法:對于第一類逮走,maven和gradle有一套自己的處理邏輯可以選擇出較高的版本進(jìn)行編譯,但是如果不符合預(yù)期就還是有可能報錯今阳;對于第二類师溅,如果類的包名、類名酣栈、方法信息完全一致編譯就不會報錯险胰,classloader會選出靠前的jar包中的類,但是運(yùn)行時可能會因?yàn)楣δ懿粚Χ_(dá)不到預(yù)期或者報錯矿筝。
? 安卓中:相同的V4包重復(fù)引入起便,只要相同方式但版本不同,不會報錯窖维,會自動選擇最高版本榆综;如果是maven和本地jar同時映引入但版本相同,也不會報錯铸史;只有maven和本地jar同時引入切版本不同才會報錯鼻疮;如果是多module打包,對于庫的引入可以使用exclude來避免重復(fù)琳轿。安卓抓包工具:
Fidder判沟,必須在同一網(wǎng)段內(nèi)可抓包,PC默認(rèn)監(jiān)聽127.0.0.1:8888端口崭篡,手機(jī)連接PC代理挪哄;捕獲手機(jī)Https請求時,需要手機(jī)訪問PC的IP并下載cer證書琉闪,有些手機(jī)下載需完要到安全設(shè)置->從文件夾讀取證書來手動安裝cer證書迹炼。
功能:捕獲Https、Http統(tǒng)計、命令行斯入、斷點(diǎn)break point砂碉、AutoResponder返回本地文件、配置Host刻两、過濾會話Filters增蹭、會話比較、會話查詢闹伪、會話保存沪铭、編碼工具等壮池。getMeasuredWidth()和getWith()的區(qū)別:
?getMeasuredWidth()
方法獲取的值是setMeasuredDimension()
中設(shè)置的值偏瓤,它是在measure()
方法后確定的;getWidth()
方法獲取的值mRight-mLeft
椰憋,它是在layout()
方法后確定的厅克,因此比getMeasuredWidth()
值獲取的晚。
? 這兩個值其實(shí)沒什么不同橙依,一般在onLayout()
方法中可以使用getMeasuredWidth()
方法獲取寬度证舟,而在onLayout()
之后使用getWidth()
就行了。
注意:在Activity的onWindowFocusChanged()
窗骑、或view.post()
方法后view的getWidth()
才被賦值女责。LayoutInfalter三個方法的區(qū)別:
?Inflate(resId,null)
:只創(chuàng)建temp,返回temp创译,外層寬高無效抵知;
?Inflate(resId,parent,false)
:創(chuàng)建temp,然后執(zhí)行temp.setLayoutParams(params)
软族,返回temp刷喜,外層寬高有效(ListView中也有效,用于getView()方法獲取單獨(dú)的View)立砸;
?Inflate(resId,parent,true)
:創(chuàng)建temp掖疮,然后執(zhí)行root.addView(temp, params)
,最后返回root颗祝,外層寬高有效(ListView中會報錯浊闪,ListView不允許直接addView(),正常布局沒事)螺戳。WebViewClient方法加載順序:
? 如果沒有設(shè)置WebViewClient則會打開默認(rèn)瀏覽器搁宾;設(shè)置后如果返回true,則表示代碼處理url温峭,webView不處理猛铅,因此后面一般再跟一句webView.load(url)
;如果直接返回false凤藏,表示由webView來處理該url奸忽,不用寫額外代碼堕伪。
?onReceiveTitle()
每次都會執(zhí)行,包括goBack()
后也會執(zhí)行栗菜,重定向時title可能會有問題欠雌,需要自己維持title棧和url棧的來解決重定向問題。
第一頁:onPageStarted() -> onPageFinished()
疙筹;如果無連接則onPageStarted() -> onReceivedError -> onPageFinished()
富俄。
第二頁:shouldOverrideUrlLoading() -> onPageStarted() -> onPageFinished()
;無連接時同上而咆。
返回上一頁:onPageStarted() -> onPageFinished()
霍比;返回上一頁不調(diào)overrideUrl,無網(wǎng)絡(luò)連接不影響返回上一頁暴备。
注意:onReceivedError和shouldOverrideUrlLoading兩個被棄用的方法悠瞬,有時候不會被回調(diào),重寫新方法即可(情況處理更多樣)涯捻,但是新方法回調(diào)順序可能有變浅妆,如onPageStarted() -> onPageFinished() -> onReceivedError()
。熱修復(fù)原理:
①DEX分包方案:
? JVM執(zhí)行.class文件障癌,DVM執(zhí)行.dex文件凌外,dx工具會把.class文件轉(zhuǎn)為.dex包,再與資源文件一起打成.apk包涛浙,所以.apk包其實(shí)就是.zip康辑,包含dex文件、資源文件蝗拿、assets包等晾捏。
? 安卓應(yīng)用默認(rèn)只打一個dex包,安卓初次裝應(yīng)用的時候會進(jìn)行dex優(yōu)化哀托,使用DexOpt工具惦辛,加載過程中會生成ODdex文件,執(zhí)行效率會更高仓手;DexOpt會把一個dex包的所有方法id檢索起來存在一個鏈表結(jié)構(gòu)里胖齐,鏈表長度使用short
類型來保存,因此一個dex方法數(shù)不能超過65536個嗽冒;另外呀伙,DexOpt使用LinearAlloc
來存儲應(yīng)用方法信息,緩沖區(qū)大小不同版本限制不同添坊,因此低版本可能方法還沒達(dá)到65536剿另,可以編譯但是無法安裝。
? 注意,再打完多個dex包后雨女,應(yīng)用只會加載主dex包谚攒,其他的dex包需要我們自己手動去加載,加載還是比較耗時的氛堕,不建議放到主線程馏臭,未加載之前調(diào)用其他dex包中的類會報錯。
②熱修復(fù)技術(shù):
? ClassLoader中包含一個pathList
對象讼稚,pathList里有個dexElements
數(shù)組(每個dex都包裝成了一個Element)括儒,同一個dex里類不能重復(fù),但不同dex里類可重復(fù)锐想,因此類被加載的時候會去遍歷dex數(shù)組找類帮寻,找到第一個后就返回了,這時候我們可以把類單獨(dú)打成補(bǔ)丁dex包痛倚,插入到Elements最前面即可规婆。
? 注意,如果兩個相互引用的類不在同一個dex包中蝉稳,就會報錯,這是由于引用者被打上CLASS_ISPREVERIFIED
標(biāo)簽掘鄙,就會進(jìn)行dex校驗(yàn)的結(jié)果(5.0以上ART運(yùn)行時機(jī)制耘戚,odex優(yōu)化不會有此問題);因此安卓應(yīng)用在第一次安裝的時候會優(yōu)化dex->odex操漠,虛擬機(jī)啟動的時候有個啟動參數(shù)vertify如果為被打開收津,就會進(jìn)行dvmVertifyClass校驗(yàn),如果兩個類的直接引用在同一個dex下浊伙,則校驗(yàn)成功撞秋,打上CLASS_ISPREVERIFIED標(biāo)簽,那我們要做的事就是阻止類被打上這個標(biāo)簽(不讓同一個dex包中的類被直接引用)嚣鄙,到時候就不會做校驗(yàn)了吻贿。
? 空間團(tuán)隊(duì)的做法是打了一個hack.dex包,里面只有一個AntilazyLoad.class類哑子,在dx工具執(zhí)行之前舅列,修改所有.class文件,在每個類的構(gòu)造方法中打印了一行Syetem.out.println(AntilazyLoad.class)
卧蜓,這就導(dǎo)致后面校驗(yàn)不成功帐要,就不會打上CLASS_ISPREVERIFIED標(biāo)簽了(javassist的使用,類似反射)弥奸;注意應(yīng)用啟動的時候必須先加載進(jìn)來hack.dex榨惠,否則后面AntilazyLoad.class類就會找不到報錯,另外,類被打上CLASS_ISPREVERIFIED實(shí)際上是為了提高性能的赠橙,因此我們強(qiáng)制不打伸蚯,略有性能損失。
③熱修復(fù)實(shí)現(xiàn):
? 第一步简烤,通過javassist修改每個.class文件剂邮,引入hack.jar中的類,阻止類被打上CLASS_ISPREVERIFIED標(biāo)簽(注意hack.jar需要dex優(yōu)化過)横侦;
? 第二步挥萌,gradle中編寫task,實(shí)現(xiàn)在dx前執(zhí)行第一步的操作枉侧;
? 第三步引瀑,hack.jar可以放入assets包打入apk,然后在Application中onCreate()中先將hack.jar復(fù)制到項(xiàng)目private的dex目錄下榨馁;
? 第四步憨栽,執(zhí)行patch操作,通過反射拿到pathClassLoader中的主項(xiàng)目dexElements翼虫,再拿到插件中dexElements屑柔,合并兩個數(shù)組即可;
? 第五步珍剑,熱修復(fù)完成掸宛,補(bǔ)丁dex包與hack.jar過程一致。Java 內(nèi)存分配策略:
Java 程序運(yùn)行時的內(nèi)存分配策略有三種招拙,分別是靜態(tài)分配唧瘾、棧式分配、堆式分配别凤,對應(yīng)的三種分配策略使用的內(nèi)存空間分別是靜態(tài)存儲區(qū)(也稱方法區(qū))饰序、棧區(qū)、堆區(qū)规哪。
? 靜態(tài)存儲區(qū)(方法區(qū)):主要存放靜態(tài)數(shù)據(jù)求豫、全局static數(shù)據(jù)和常量,這塊內(nèi)存在程序編譯時就已經(jīng)分配好由缆,并且在程序整個運(yùn)行期間都存在注祖;
? 棧區(qū):當(dāng)方法被執(zhí)行時,方法體內(nèi)的局部變量(其中包括基礎(chǔ)數(shù)據(jù)類型均唉、對象的引用)都在棧上創(chuàng)建是晨,并在方法執(zhí)行結(jié)束時這些局部變量所持有的內(nèi)存將會自動被釋放,因?yàn)闂?nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中舔箭,所以效率很高罩缴,但是分配的內(nèi)存容量有限蚊逢;
? 堆區(qū):又稱動態(tài)內(nèi)存分配,通常就是指在程序運(yùn)行時直接new出來的內(nèi)存箫章,也就是對象的實(shí)例所在的區(qū)域烙荷,這部分內(nèi)存在不使用時將會由Java垃圾回收器來負(fù)責(zé)回收。
-棧與堆的區(qū)別:
? 在方法體內(nèi)定義的局部變量檬寂、一些基本類型的變量和對象的引用變量都是在方法的棧內(nèi)存中分配的终抽,當(dāng)在一段方法塊中定義一個變量時,Java 就會在棧中為該變量分配內(nèi)存空間桶至,當(dāng)超過該變量的作用域后昼伴,該變量也就無效了,分配給它的內(nèi)存空間也將被釋放掉镣屹,該內(nèi)存空間可以被重新使用圃郊。
? 堆內(nèi)存用來存放所有由new創(chuàng)建的對象(包括該對象其中的所有成員變量)和數(shù)組,在堆中分配的內(nèi)存女蜈,將由Java垃圾回收器來自動管理持舆,在堆中產(chǎn)生了一個數(shù)組或者對象后,還可以在棧中定義一個特殊的變量伪窖,這個變量的取值等于數(shù)組或者對象在堆內(nèi)存中的首地址逸寓,這個特殊的變量就是我們上面說的引用變量,我們可以通過這個引用變量來訪問堆中的對象或者數(shù)組惰许。內(nèi)存泄漏相關(guān):
? 定義:當(dāng)一個對象已經(jīng)不需要再使用了席覆,本該被回收時,而有另外一個生命周期較長且正在使用的對象持有它的引用從而導(dǎo)致它不能被回收汹买,這導(dǎo)致本該被回收的對象不能被回收而停留在堆內(nèi)存中,這就產(chǎn)生了內(nèi)存泄漏聊倔。
? 常見場景:
非靜態(tài)內(nèi)部類的靜態(tài)實(shí)例化(會持有外部類引用晦毙,靜態(tài)實(shí)例化后不會銷毀,外部引用就不會銷毀)耙蔑、單例持有Context(類似上條见妒,單例常駐內(nèi)存,如果持有Activity甸陌,導(dǎo)致Activity不會被回收)须揣、非靜態(tài)Handler做耗時操作(類似上條,持有外部Activity的引用钱豁,耗時操作導(dǎo)致外部Activity不會被回收)耻卡、io等資源使用后沒有關(guān)閉(用到緩沖,存在于虛擬機(jī)內(nèi)和外牲尺,需要手動close再置null)卵酪、Bitmap沒有標(biāo)記回收(使用完畢需要標(biāo)記一下可回收幌蚊,否則Bitmap一般是比較占內(nèi)存的)、事件總線庫的注冊與解注冊(注冊后被觀察者會持有觀察者的引用溃卡,如果是Activity不解注冊會內(nèi)存泄漏)溢豆。
? 總結(jié):確保對象能夠在正確的時機(jī)被回收掉,在需要的時候使用瘸羡,不需要的時候及時可以被回收漩仙,因此注意生命周期長的對象不要一直持有生命周期短且應(yīng)該被回收的對象。
? 補(bǔ)充關(guān)于Handler內(nèi)存泄漏的解釋:Handler非靜態(tài)內(nèi)部類會持有外部類引用this犹赖,所以可以直接使用Activity中的屬性队他、方法等,因此如果有耗時操作會導(dǎo)致外部Activity泄漏冷尉∈妫可以使用靜態(tài)內(nèi)部類,杜絕直接對外類的引用雀哨,但是無法訪問外部類實(shí)例域和方法了磕谅,只能訪問靜態(tài)域,如果必須要使用外部類的實(shí)例域雾棺,則可以使用弱引用從構(gòu)造方法中傳入外部類即可膊夹。Java注解:
? 元注解:
@Retention:注解保留的生命周期,有SOURCE/CLASS/RUNTIME三種捌浩;
@Target:注解對象的作用范圍放刨,有TYPE/FIELD/METHOD/PARAMETER/CONSTRUCTOR/PACKAGE等;
@Inherited:所標(biāo)注的注解在作用的類上是否可被繼承尸饺,使用時进统,子類會繼承該注解,但只作用于類浪听,對方法螟碎、屬性無效;
@Documented:javadoc的工具化文檔迹栓。
? 作用:降低項(xiàng)目耦合度掉分、自動完成一些規(guī)律性代碼、自動生成一些Java代碼克伊。
? 注意:注解使用反射娇未,可以拿到屬性的注解對象和注解值來進(jìn)行一些額外操作毅贮,因此運(yùn)行時注解其實(shí)使用反射是比較影響效率的实辑,可以使用編譯時注解在編譯時通過反射獲取注解浊洞,自動生成一些代碼,如ButterKinfe等庫的實(shí)現(xiàn)洗搂,編譯時注解依賴于注解處理器AbstractProcessor消返,是javac的一款工具载弄,用來編譯時掃描和處理注解,并生成Java文件注入到源碼中撵颊。編碼相關(guān):
編碼:ASCII最早表示英文宇攻、Unicode是中文字符集可表示更多字符但是會混淆和浪費(fèi)空間(比較常見)、UTF-8/UTF-16是可變長編碼方式倡勇;因此英文使用ASCII不會有問題逞刷,中文不能使用ASCII,中英都可以使用Unicode或UTF-8妻熊。加密相關(guān):
? Base64:一般網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)要進(jìn)行Base64編碼一次夸浅,它不是加密算法,是一種編碼轉(zhuǎn)換扔役,因?yàn)锳SCII碼的128~255之間是不可見字符帆喇,網(wǎng)上傳輸數(shù)據(jù)時可能會解析錯誤,因此Base64的實(shí)際作用就是將二進(jìn)制轉(zhuǎn)為可見的字符形式的數(shù)據(jù)形式亿胸。
? MD5/SHA1:MD5和SHA1都是一套摘要算法坯钦,不能算是加密算法,它可以把任何一個文件侈玄、程序婉刀、圖像等類型,不管體積有多大序仙,轉(zhuǎn)化成一個固定長度的MD5或哈希值突颊,不可逆,因此可以用來判斷文件潘悼、數(shù)據(jù)是否被更改律秃,SHA1略叼一點(diǎn),但是MD5略快一點(diǎn)治唤。
? DES/3DES/AES:對稱加密算法友绝,DES->3DES->AES是越來越叼,使用向量可以增強(qiáng)加密強(qiáng)度肝劲,一般用于與RSA合作雙向加密。
? RSA:非對稱加密算法郭宝,公鑰加密辞槐、私鑰解密,或者私鑰加密粘室、公鑰解密榄檬,甲方發(fā)起請求,使用乙方的公鑰加密衔统,乙方使用自己的私鑰解密鹿榜;簽名認(rèn)證:對發(fā)送的數(shù)據(jù)摘要出MD5值海雪,用私鑰加密與內(nèi)容一起發(fā)送,接收方使用公鑰解密得到MD5值舱殿,摘要出內(nèi)容對比MD5是否一致即可奥裸。
-項(xiàng)目加密過程:
? 登錄模式下,用戶登錄時獲取隨機(jī)AES_KEY沪袭,然后用AES加密請求數(shù)據(jù)湾宙,用RSA加密AES_KEY,一起發(fā)送給服務(wù)端冈绊,服務(wù)端保存用戶AES_KEY侠鳄,返回數(shù)據(jù)僅用AES加密,本地用AES解密死宣,服務(wù)端同時會返回token伟恶,下次請求直接帶token過去,服務(wù)端就找到相應(yīng)的AES_KEY進(jìn)行解密毅该。
? 游客模式下博秫,用戶每次都隨機(jī)生成AES_KEY(實(shí)際上也就進(jìn)入APP生成一個),然后用AES加密請求數(shù)據(jù)鹃骂,用RSA加密AES_KEY台盯,一起發(fā)送給服務(wù)端,返回數(shù)據(jù)僅用AES加密畏线,本地用AES解密静盅。絕對路徑和相對路徑:
? 絕對路徑形如:E:\book\代碼\test.java
,不同主機(jī)磁盤文件路徑不同寝殴,一般不使用絕對路徑蒿叠;相對路徑形如:gg.png
,以"/"分隔蚣常,一般使用相對路徑市咽,通用。
-相對路徑三種格式:
?../
當(dāng)前路徑的上一級路徑抵蚊;
?./
當(dāng)前路徑(可以省略不寫)施绎;
?/
虛擬目錄的根路徑;正則表達(dá)式:
?\d
:匹配一個數(shù)字贞绳;\w
:匹配一個字母或數(shù)字谷醉;\s
:匹配至少一個空格。
?.
:匹配任意1個字符冈闭;?
:匹配0個或1個字符俱尼;*
:匹配0個或多個字符;+
:匹配1個或多個字符萎攒。
?{n}
:表示n個字符遇八,用{n,m}
表示n-m個字符矛绘;如\d{3}
表示匹配3個數(shù)字,\d{3,8}
表示3-8個數(shù)字刃永。
?[]
:表示一個字符范圍货矮,如[0-9a-zA-Z\_]
或[a-zA-Z\_\$][0-9a-zA-Z\_\$]*
。
?A|B
:匹配A或B揽碘;^
:表示行的開頭次屠,如^\d
表示必須以數(shù)字開頭;$
:表示行的結(jié)束雳刺,如\d$
表示必須以數(shù)字結(jié)束劫灶。Labmda表達(dá)式:
? 定義:lambda是匿名函數(shù)的別名,用于簡化匿名內(nèi)部類的調(diào)用掖桦。
? 語法:lambda表達(dá)式展示了一個接口抽象方法最有價值的兩點(diǎn)本昏,即參數(shù)列表和具體實(shí)現(xiàn),(參數(shù)列表...)->{語句塊}
枪汪。
? lambda表達(dá)式共有三種形式:函數(shù)式接口涌穆、方法引用和構(gòu)造器引用。
? 參數(shù)只有一個時不用發(fā)加括號雀久,沒有或大于一個需要括號宿稀;當(dāng)表達(dá)式只有一句話大括號和return都可以不用加,有多行的話需要大括號赖捌,也就需要return祝沸。
? 方法引用的三種格式:object::instanceMethod / ClassName::staticMethod / ClassName::instanceMethod
,第三種比較少見越庇,參數(shù)列表的第一個參數(shù)為方法的調(diào)用者罩锐,其余參數(shù)為方法參數(shù);構(gòu)造器引用:ClassName::New
卤唉。
? Java8中新增了一個effective final功能涩惑,如int effectiveFinalInt = 666
,不用加final修飾符可以在接口回調(diào)里使用該變量桑驱,前提是確認(rèn)它是不會被修改的竭恬。
? 匿名內(nèi)部類中,this關(guān)鍵字指匿名內(nèi)部類本身的對象熬的;而在lambda表達(dá)式中萍聊,this指向外圍的類對象。
? lambda表達(dá)式經(jīng)過編譯器編譯后悦析,每個表達(dá)式會增加1~2個方法數(shù),Android方法數(shù)有65536限制此衅,需注意强戴。