RecyclerView與ListView 對(duì)比淺析:緩存機(jī)制
重要
一. 背景
PS:相關(guān)知識(shí):
ListView與RecyclerView緩存機(jī)制原理大致相似劳吠,如下圖所示:
滑動(dòng)過程中迫筑,離屏的ItemView即被回收至緩存宪赶,入屏的ItemView則會(huì)優(yōu)先從緩存中獲取,只是ListView與RecyclerView的實(shí)現(xiàn)細(xì)節(jié)有差異.(這只是緩存使用的其中一個(gè)場(chǎng)景脯燃,還有如刷新等)
二. 正文
2.1 緩存機(jī)制對(duì)比
1. 層級(jí)不同:
RecyclerView比ListView多兩級(jí)緩存搂妻,支持多個(gè)離ItemView緩存辕棚,支持開發(fā)者自定義緩存處理邏輯扁瓢,支持所有RecyclerView共用同一個(gè)RecyclerViewPool(緩存池)。
具體來說:
ListView(兩級(jí)緩存):
RecyclerView(四級(jí)緩存):
ListView和RecyclerView緩存機(jī)制基本一致:
1). mActiveViews和mAttachedScrap功能相似楣铁,意義在于快速重用屏幕上可見的列表項(xiàng)ItemView玖雁,而不需要重新createView和bindView;
2). mScrapView和mCachedViews + mReyclerViewPool功能相似民褂,意義在于緩存離開屏幕的ItemView茄菊,目的是讓即將進(jìn)入屏幕的ItemView重用.
3). RecyclerView的優(yōu)勢(shì)在于a.mCacheViews的使用,可以做到屏幕外的列表項(xiàng)ItemView進(jìn)入屏幕內(nèi)時(shí)也無須bindView快速重用赊堪;b.mRecyclerPool可以供多個(gè)RecyclerView共同使用面殖,在特定場(chǎng)景下,如viewpaper+多個(gè)列表頁下有優(yōu)勢(shì).客觀來說哭廉,RecyclerView在特定場(chǎng)景下對(duì)ListView的緩存機(jī)制做了補(bǔ)強(qiáng)和完善脊僚。
2. 緩存不同:
1). RecyclerView緩存RecyclerView.ViewHolder,抽象可理解為:
View + ViewHolder(避免每次createView時(shí)調(diào)用findViewById) + flag(標(biāo)識(shí)狀態(tài))遵绰;
2). ListView緩存View辽幌。
緩存不同,二者在緩存的使用上也略有差別椿访,具體來說:
ListView獲取緩存的流程:
RecyclerView獲取緩存的流程:
1). RecyclerView中mCacheViews(屏幕外)獲取緩存時(shí)乌企,是通過匹配pos獲取目標(biāo)位置的緩存,這樣做的好處是成玫,當(dāng)數(shù)據(jù)源數(shù)據(jù)不變的情況下加酵,無須重新bindView:
而同樣是離屏緩存,ListView從mScrapViews根據(jù)pos獲取相應(yīng)的緩存哭当,但是并沒有直接使用猪腕,而是重新getView(即必定會(huì)重新bindView),相關(guān)代碼如下:
//AbsListView源碼:line2345
//通過匹配pos從mScrapView中獲取緩存
final View scrapView = mRecycler.getScrapView(position);
//無論是否成功都直接調(diào)用getView,導(dǎo)致必定會(huì)調(diào)用createView
final View child = mAdapter.getView(position, scrapView, this);
if (scrapView != null) {
? ? if (child != scrapView) {
? ? ? ? mRecycler.addScrapView(scrapView, position);
? ? } else {
? ? ? ? ? ? ? ? ...
? ? }
}
2). ListView中通過pos獲取的是view钦勘,即pos-->view陋葡;
RecyclerView中通過pos獲取的是viewholder,即pos --> (view彻采,viewHolder腐缤,flag);
從流程圖中可以看出肛响,標(biāo)志flag的作用是判斷view是否需要重新bindView岭粤,這也是RecyclerView實(shí)現(xiàn)局部刷新的一個(gè)核心。
2.2 局部刷新
由上文可知终惑,RecyclerView的緩存機(jī)制確實(shí)更加完善,但還不算質(zhì)的變化门扇,RecyclerView更大的亮點(diǎn)在于提供了局部刷新的接口雹有,通過局部刷新偿渡,就能避免調(diào)用許多無用的bindView。
(RecyclerView和ListView添加霸奕,移除Item效果對(duì)比)
結(jié)合RecyclerView的緩存機(jī)制溜宽,看看局部刷新是如何實(shí)現(xiàn)的:
以RecyclerView中notifyItemRemoved(1)為例,最終會(huì)調(diào)用requestLayout()质帅,使整個(gè)RecyclerView重新繪制适揉,過程為:
onMeasure()-->onLayout()-->onDraw()
其中,onLayout()為重點(diǎn)煤惩,分為三步:
dispathLayoutStep1():記錄RecyclerView刷新前列表項(xiàng)ItemView的各種信息嫉嘀,如Top,Left,Bottom,Right,用于動(dòng)畫的相關(guān)計(jì)算魄揉;
dispathLayoutStep2():真正測(cè)量布局大小剪侮,位置,核心函數(shù)為layoutChildren()洛退;
dispathLayoutStep3():計(jì)算布局前后各個(gè)ItemView的狀態(tài)瓣俯,如Remove,Add兵怯,Move彩匕,Update等,如有必要執(zhí)行相應(yīng)的動(dòng)畫.
其中媒区,layoutChildren()流程圖:
當(dāng)調(diào)用notifyItemRemoved時(shí)驼仪,會(huì)對(duì)屏幕內(nèi)ItemView做預(yù)處理,修改ItemView相應(yīng)的pos以及flag(流程圖中紅色部分):
當(dāng)調(diào)用fill()中RecyclerView.getViewForPosition(pos)時(shí)驻仅,RecyclerView通過對(duì)pos和flag的預(yù)處理谅畅,使得bindview只調(diào)用一次.
需要指出,ListView和RecyclerView最大的區(qū)別在于數(shù)據(jù)源改變時(shí)的緩存的處理邏輯,ListView是"一鍋端"始绍,將所有的mActiveViews都移入了二級(jí)緩存mScrapViews晕窑,而RecyclerView則是更加靈活地對(duì)每個(gè)View修改標(biāo)志位,區(qū)分是否重新bindView仇味。
三.結(jié)論
1、在一些場(chǎng)景下雹顺,如界面初始化丹墨,滑動(dòng)等,ListView和RecyclerView都能很好地工作嬉愧,兩者并沒有很大的差異:
文章的開頭便拋出了這樣一個(gè)問題贩挣,微信Android客戶端卡券模塊,大部分UI都是以列表頁的形式展示,實(shí)現(xiàn)方式為L(zhǎng)istView王财,是否有必要將其替換成RecyclerView呢卵迂?
答案是否定的,從性能上看绒净,RecyclerView并沒有帶來顯著的提升见咒,不需要頻繁更新,暫不支持用動(dòng)畫挂疆,意味著RecyclerView優(yōu)勢(shì)也不太明顯改览,沒有太大的吸引力,ListView已經(jīng)能很好地滿足業(yè)務(wù)需求缤言。
2宝当、數(shù)據(jù)源頻繁更新的場(chǎng)景,如彈幕:http://www.reibang.com/p/2232a63442d6?等RecyclerView的優(yōu)勢(shì)會(huì)非常明顯墨闲;
進(jìn)一步來講今妄,結(jié)論是:
列表頁展示界面,需要支持動(dòng)畫鸳碧,或者頻繁更新盾鳞,局部刷新,建議使用RecyclerView瞻离,更加強(qiáng)大完善腾仅,易擴(kuò)展;其它情況(如微信卡包列表頁)兩者都OK套利,但ListView在使用上會(huì)更加方便推励,快捷
如果有個(gè)100M大的文件,需要上傳至服務(wù)器中肉迫,而服務(wù)器form表單最大只能上傳2M验辞,可以用什么方法。
重要
首先來說使用http協(xié)議上傳數(shù)據(jù)喊衫,特別在android下跌造,跟form沒什么關(guān)系。
傳統(tǒng)的在web中族购,在form中寫文件上傳壳贪,其實(shí)瀏覽器所做的就是將我們的數(shù)據(jù)進(jìn)行解析組拼成字符串,以流的方式發(fā)送到服務(wù)器寝杖,且上傳文件用的都是POST方式违施,POST方式對(duì)大小沒什么限制。
回到題目瑟幕,可以說假設(shè)每次真的只能上傳2M磕蒲,那么可能我們只能把文件截?cái)嗔袅剩缓蠓謩e上傳了,斷點(diǎn)上傳辣往。
即時(shí)通訊是是怎么做的?
重要
使用asmark 開源框架實(shí)現(xiàn)的即時(shí)通訊功能.該框架基于開源的 XMPP 即時(shí)通信協(xié)議愤兵,采用 C/S 體系結(jié)構(gòu),通過 GPRS 無線網(wǎng)絡(luò)用 TCP 協(xié)議連接到服務(wù)器排吴,以架設(shè)開源的Openfn'e 服務(wù)器作為即時(shí)通訊平臺(tái)。
客戶端基于 Android 平臺(tái)進(jìn)行開發(fā)懦鼠。負(fù)責(zé)初始化通信過程钻哩,進(jìn)行即時(shí)通信時(shí),由客戶端負(fù)責(zé)向服務(wù)器發(fā)起創(chuàng)建連接請(qǐng)求肛冶。系統(tǒng)通過 GPRS 無線網(wǎng)絡(luò)與 Internet 網(wǎng)絡(luò)建立連接街氢,通過服務(wù)器實(shí)現(xiàn)與Android 客戶端的即時(shí)通信腳。
服務(wù)器端則采用 Openfire 作為服務(wù)器睦袖。 允許多個(gè)客戶端同時(shí)登錄并且并發(fā)的連接到一個(gè)服務(wù)器上珊肃。服務(wù)器對(duì)每個(gè)客戶端的連接進(jìn)行認(rèn)證,對(duì)認(rèn)證通過的客戶端創(chuàng)建會(huì)話馅笙,客戶端與服務(wù)器端之間的通信就在該會(huì)話的上下文中進(jìn)行伦乔。
說說 LruCache 底層原理
重要
LruCache 使用一個(gè) LinkedHashMap 簡(jiǎn)單的實(shí)現(xiàn)內(nèi)存的緩存,沒有軟引用董习,都是強(qiáng)引用烈和。
如果添加的數(shù)據(jù)大于設(shè)置的最大值,就刪除最先緩存的數(shù)據(jù)來調(diào)整內(nèi)存皿淋。maxSize 是通過構(gòu)造方法初始化的值招刹,他表示這個(gè)緩存能緩存的最大值是多少。
size 在添加和移除緩存都被更新值窝趣, 他通過 safeSizeOf 這個(gè)方法更新值疯暑。 safeSizeOf 默認(rèn)返回 1,但一般我們會(huì)根據(jù) maxSize 重寫這個(gè)方法哑舒,比如認(rèn)為 maxSize 代表是 KB 的話妇拯,那么就以 KB 為單位返回該項(xiàng)所占的內(nèi)存大小。
除異常外散址,首先會(huì)判斷 size 是否超過 maxSize乖阵,如果超過了就取出最先插入的緩存,如果不為空就刪掉预麸,并把 size 減去該項(xiàng)所占的大小瞪浸。這個(gè)操作將一直循環(huán)下去,直到 size 比 maxSize 小或者緩存為空吏祸。
andorid 應(yīng)用第二次登錄實(shí)現(xiàn)自動(dòng)登錄
重要
前置條件是所有用戶相關(guān)接口都走 https对蒲,非用戶相關(guān)列表類數(shù)據(jù)走 http钩蚊。
步驟
第一次登陸 getUserInfo 里帶有一個(gè)長(zhǎng)效 token,該長(zhǎng)效 token 用來判斷用戶是否登陸和換取短 token
把長(zhǎng)效 token 保存到 SharedPreferences
接口請(qǐng)求用長(zhǎng)效 token 換取短token蹈矮,短 token 服務(wù)端可以根據(jù)你的接口最后一次請(qǐng)求作為標(biāo)示砰逻,超時(shí)時(shí)間為一天。
所有接口都用短效 token
如果返回短效 token 失效泛鸟,執(zhí)行第3步蝠咆,再直接當(dāng)前接口
如果長(zhǎng)效 token 失效(用戶換設(shè)備或超過一月),提示用戶登錄
談?wù)勀銓?duì) Bitmap 的理解, 什么時(shí)候應(yīng)該手動(dòng)調(diào)用 bitmap.recycle()
重要
Bitmap 是 android 中經(jīng)常使用的一個(gè)類北滥,它代表了一個(gè)圖片資源刚操。 Bitmap 消耗內(nèi)存很嚴(yán)重,如果不注意優(yōu)化代碼再芋,經(jīng)常會(huì)出現(xiàn) OOM 問題菊霜,優(yōu)化方式通常有這么幾種:
使用緩存;
壓縮圖片济赎;
及時(shí)回收鉴逞;復(fù)制代碼
至于什么時(shí)候需要手動(dòng)調(diào)用 recycle,這就看具體場(chǎng)景了司训,原則是當(dāng)我們不再使用 Bitmap 時(shí)构捡,需要回收之。另外壳猜,我們需要注意叭喜,2.3 之前 Bitmap 對(duì)象與像素?cái)?shù)據(jù)是分開存放的,Bitmap 對(duì)象存在java Heap 中而像素?cái)?shù)據(jù)存放在 Native Memory 中蓖谢, 這時(shí)很有必要調(diào)用 recycle 回收內(nèi)存捂蕴。 但是 2.3之后,Bitmap 對(duì)象和像素?cái)?shù)據(jù)都是存在 Heap 中闪幽,GC 可以回收其內(nèi)存啥辨。
屬性動(dòng)畫,例如一個(gè) button 從 A 移動(dòng)到 B 點(diǎn)盯腌,B 點(diǎn)還是可以響應(yīng)點(diǎn)擊事件溉知,這個(gè)原理是什么?
重要
補(bǔ)間動(dòng)畫只是顯示的位置變動(dòng)腕够,View 的實(shí)際位置未改變级乍,表現(xiàn)為 View 移動(dòng)到其他地方,點(diǎn)擊事件仍在原處才能響應(yīng)帚湘。而屬性動(dòng)畫控件移動(dòng)后事件相應(yīng)就在控件移動(dòng)后本身進(jìn)行處理
都使用過哪些自定義控件
pull2RefreshListView
LazyViewPager
SlidingMenu
SmoothProgressBar
自定義組合控件
ToggleButton
自定義Toast
Android與服務(wù)器交互的方式中的對(duì)稱加密和非對(duì)稱加密是什么?
重要
對(duì)稱加密玫荣,就是加密和解密數(shù)據(jù)都是使用同一個(gè)key,這方面的算法有DES大诸。
非對(duì)稱加密捅厂,加密和解密是使用不同的key贯卦。發(fā)送數(shù)據(jù)之前要先和服務(wù)端約定生成公鑰和私鑰,使用公鑰加密的數(shù)據(jù)可以用私鑰解密焙贷,反之撵割。這方面的算法有RSA。ssh 和 ssl都是典型的非對(duì)稱加密辙芍。
如何修改 Activity 進(jìn)入和退出動(dòng)畫
重要
可 以 通 過 兩 種 方 式 啡彬, 一 是 通 過 定 義 Activity 的 主 題 , 二 是 通 過 覆 寫 Activity 的overridePendingTransition 方法故硅。
通過設(shè)置主題樣式在 styles.xml 中編輯如下代碼:
添加themes.xml文件:
在AndroidManifest.xml中給指定的Activity指定theme外遇。
覆寫 overridePendingTransition 方法
overridePendingTransition(R.anim.fade,R.anim.hold);
請(qǐng)解釋下 Android 程序運(yùn)行時(shí)權(quán)限與文件系統(tǒng)權(quán)限的區(qū)別?
重要
apk 程序是運(yùn)行在虛擬機(jī)上的,對(duì)應(yīng)的是 Android 獨(dú)特的權(quán)限機(jī)制契吉,只有體現(xiàn)到文件系統(tǒng)上時(shí)才
使用 linux 的權(quán)限設(shè)置。
linux 文件系統(tǒng)上的權(quán)限
-rwxr-x--xsystemsystem41562010-04-3016:13test.apk
代表的是相應(yīng)的用戶/用戶組及其他人對(duì)此文件的訪問權(quán)限诡渴,與此文件運(yùn)行起來具有的權(quán)限完全不相關(guān)捐晶。比如上面的例子只能說明system用戶擁有對(duì)此文件的讀寫執(zhí)行權(quán)限;system組的用戶對(duì)此文件擁有讀妄辩、執(zhí)行權(quán)限惑灵;其他人對(duì)此文件只具有執(zhí)行權(quán)限。而 test.apk 運(yùn)行起來后可以干哪些事情眼耀,跟這個(gè)就不相關(guān)了英支。千萬不要看 apk 文件系統(tǒng)上屬于system/system用戶及用戶組,或者root/root 用戶及用戶組哮伟,就認(rèn)為 apk 具有system或 root 權(quán)限復(fù)制代碼
Android 的權(quán)限規(guī)則
Android中的apk必須簽名
基于UserID的進(jìn)程級(jí)別的安全機(jī)制
默認(rèn)apk生成的數(shù)據(jù)對(duì)外是不可見的
AndroidManifest.xml中的顯式權(quán)限聲明
網(wǎng)絡(luò)框架有哪些干花?他們之間的區(qū)別是什么?
重要
Xutils
這個(gè)框架非常全面楞黄,可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求池凄,可以進(jìn)行圖片加載處理,可以數(shù)據(jù)儲(chǔ)存鬼廓,還可以對(duì)view進(jìn)行注解肿仑,使用這個(gè)框架非常方便,但是缺點(diǎn)也是非常明顯的碎税,使用這個(gè)項(xiàng)目尤慰,會(huì)導(dǎo)致項(xiàng)目對(duì)這個(gè)框架依賴非常的嚴(yán)重,一旦這個(gè)框架出現(xiàn)問題雷蹂,那么對(duì)項(xiàng)目來說影響非常大的
OKhttp
Android開發(fā)中是可以直接使用現(xiàn)成的api進(jìn)行網(wǎng)絡(luò)請(qǐng)求的伟端。就是使用HttpClient,HttpUrlConnection進(jìn)行操作。okhttp針對(duì)Java和Android程序匪煌,封裝的一個(gè)高性能的http請(qǐng)求庫荔泳,支持同步蕉饼,異步,而且okhttp又封裝了線程池玛歌,封裝了數(shù)據(jù)轉(zhuǎn)換昧港,封裝了參數(shù)的使用,錯(cuò)誤處理等支子。API使用起來更加的方便创肥。但是我們?cè)陧?xiàng)目中使用的時(shí)候仍然需要自己在做一層封裝,這樣才能使用的更加的順手值朋。
Volley
Volley是Google官方出的一套小而巧的異步請(qǐng)求庫叹侄,該框架封裝的擴(kuò)展性很強(qiáng),支持HttpClient昨登、HttpUrlConnection趾代, 甚至支持OkHttp,而且Volley里面也封裝了ImageLoader丰辣,所以如果你愿意你甚至不需要使用圖片加載框架撒强,不過這塊功能沒有一些專門的圖片加載框架強(qiáng)大,對(duì)于簡(jiǎn)單的需求可以使用笙什,稍復(fù)雜點(diǎn)的需求還是需要用到專門的圖片加載框架飘哨。Volley也有缺陷,比如不支持post大數(shù)據(jù)琐凭,所以不適合上傳文件芽隆。不過Volley設(shè)計(jì)的初衷本身也就是為頻繁的、數(shù)據(jù)量小的網(wǎng)絡(luò)請(qǐng)求而生统屈。
Retrofit
Retrofit是Square公司出品的默認(rèn)基于OkHttp封裝的一套R(shí)ESTful網(wǎng)絡(luò)請(qǐng)求框架胚吁,RESTful是目前流行的一套api設(shè)計(jì)的風(fēng)格, 并不是標(biāo)準(zhǔn)愁憔。Retrofit的封裝可以說是很強(qiáng)大囤采,里面涉及到一堆的設(shè)計(jì)模式,可以通過注解直接配置請(qǐng)求,可以使用不同的http客戶端惩淳,雖然默認(rèn)是用http 蕉毯,可以使用不同Json Converter 來序列化數(shù)據(jù),同時(shí)提供對(duì)RxJava的支持思犁,使用Retrofit + OkHttp + RxJava + Dagger2 可以說是目前比較潮的一套框架代虾,但是需要有比較高的門檻。
Volley VS OkHttp
Volley的優(yōu)勢(shì)在于封裝的更好激蹲,而使用OkHttp你需要有足夠的能力再進(jìn)行一次封裝棉磨。而OkHttp的優(yōu)勢(shì)在于性能更高,因?yàn)?OkHttp基于NIO和Okio 学辱,所以性能上要比 Volley更快乘瓤。IO 和 NIO這兩個(gè)都是Java中的概念环形,如果我從硬盤讀取數(shù)據(jù),第一種方式就是程序一直等衙傀,數(shù)據(jù)讀完后才能繼續(xù)操作這種是最簡(jiǎn)單的也叫阻塞式IO,還有一種是你讀你的,程序接著往下執(zhí)行抬吟,等數(shù)據(jù)處理完你再來通知我,然后再處理回調(diào)统抬。而第二種就是 NIO 的方式火本,非阻塞式, 所以NIO當(dāng)然要比IO的性能要好了,而 Okio是 Square 公司基于IO和NIO基礎(chǔ)上做的一個(gè)更簡(jiǎn)單聪建、高效處理數(shù)據(jù)流的一個(gè)庫钙畔。理論上如果Volley和OkHttp對(duì)比的話,更傾向于使用 Volley金麸,因?yàn)閂olley內(nèi)部同樣支持使用OkHttp,這點(diǎn)OkHttp的性能優(yōu)勢(shì)就沒了擎析, 而且 Volley 本身封裝的也更易用,擴(kuò)展性更好些挥下。
OkHttp VS Retrofit
毫無疑問揍魂,Retrofit 默認(rèn)是基于 OkHttp 而做的封裝,這點(diǎn)來說沒有可比性见秽,肯定首選 Retrofit。
Volley VS Retrofit
這兩個(gè)庫都做了不錯(cuò)的封裝讨盒,但Retrofit解耦的更徹底,尤其Retrofit2.0出來解取,Jake對(duì)之前1.0設(shè)計(jì)不合理的地方做了大量重構(gòu), 職責(zé)更細(xì)分返顺,而且Retrofit默認(rèn)使用OkHttp,性能上也要比Volley占優(yōu)勢(shì)禀苦,再有如果你的項(xiàng)目如果采用了RxJava ,那更該使用 Retrofit 遂鹊。所以這兩個(gè)庫相比振乏,Retrofit更有優(yōu)勢(shì),在能掌握兩個(gè)框架的前提下該優(yōu)先使用 Retrofit秉扑。但是Retrofit門檻要比Volley稍高些慧邮,要理解他的原理,各種用法舟陆,想徹底搞明白還是需要花些功夫的误澳,如果你對(duì)它一知半解,那還是建議在商業(yè)項(xiàng)目使用Volley吧秦躯。
打包原理
重要
Android的包文件APK分為兩個(gè)部分:代碼和資源忆谓,所以打包方面也分為資源打包和代碼打包兩個(gè)方面,這篇文章就來分析資源和代碼的編譯打包原理踱承。
具體說來:
通過AAPT工具進(jìn)行資源文件(包括AndroidManifest.xml倡缠、布局文件哨免、各種xml資源等)的打包,生成R.java文件昙沦。
通過AIDL工具處理AIDL文件琢唾,生成相應(yīng)的Java文件。
通過Javac工具編譯項(xiàng)目源碼桅滋,生成Class文件慧耍。
通過DX工具將所有的Class文件轉(zhuǎn)換成DEX文件,該過程主要完成Java字節(jié)碼轉(zhuǎn)換成Dalvik字節(jié)碼丐谋,壓縮常量池以及清除冗余信息等工作芍碧。
通過ApkBuilder工具將資源文件、DEX文件打包生成APK文件号俐。
利用KeyStore對(duì)生成的APK文件進(jìn)行簽名泌豆。
如果是正式版的APK,還會(huì)利用ZipAlign工具進(jìn)行對(duì)齊處理吏饿,對(duì)齊的過程就是將APK文件中所有的資源文件舉例文件的起始距離都偏移4字節(jié)的整數(shù)倍踪危,這樣通過內(nèi)存映射訪問APK文件的速度會(huì)更快。
性能優(yōu)化
重要
參考鏈接:https://blog.csdn.net/csdn_aiyang/article/details/74989318
Android的性能優(yōu)化猪落,主要是從以下幾個(gè)方面進(jìn)行優(yōu)化的:
穩(wěn)定(內(nèi)存溢出贞远、崩潰)
流暢(卡頓)
耗損(耗電、流量)
安裝包(APK瘦身)
影響穩(wěn)定性的原因很多笨忌,比如內(nèi)存使用不合理蓝仲、代碼異常場(chǎng)景考慮不周全、代碼邏輯不合理等官疲,都會(huì)對(duì)應(yīng)用的穩(wěn)定性造成影響袱结。其中最常見的兩個(gè)場(chǎng)景是:Crash 和 ANR,這兩個(gè)錯(cuò)誤將會(huì)使得程序無法使用途凫。所以做好Crash全局監(jiān)控垢夹,處理閃退同時(shí)把崩潰信息、異常信息收集記錄起來维费,以便后續(xù)分析;合理使用主線程處理業(yè)務(wù)果元,不要在主線程中做耗時(shí)操作驹沿,防止ANR程序無響應(yīng)發(fā)生贱鄙。
(一)穩(wěn)定——內(nèi)存優(yōu)化
(1)Memory Monitor 工具:
它是Android Studio自帶的一個(gè)內(nèi)存監(jiān)視工具,它可以很好地幫助我們進(jìn)行內(nèi)存實(shí)時(shí)分析蜂莉。通過點(diǎn)擊Android Studio右下角的Memory Monitor標(biāo)簽且蓬,打開工具可以看見較淺藍(lán)色代表free的內(nèi)存欣硼,而深色的部分代表使用的內(nèi)存從內(nèi)存變換的走勢(shì)圖變換,可以判斷關(guān)于內(nèi)存的使用狀態(tài),例如當(dāng)內(nèi)存持續(xù)增高時(shí)诈胜,可能發(fā)生內(nèi)存泄漏豹障;當(dāng)內(nèi)存突然減少時(shí),可能發(fā)生GC等焦匈,如下圖所示血公。
LeakCanary工具:
LeakCanary是Square公司基于MAT開發(fā)的一款監(jiān)控Android內(nèi)存泄漏的開源框架。其工作的原理是:
監(jiān)測(cè)機(jī)制利用了Java的WeakReference和ReferenceQueue缓熟,通過將Activity包裝到WeakReference中累魔,被WeakReference包裝過的Activity對(duì)象如果被回收,該WeakReference引用會(huì)被放到ReferenceQueue中够滑,通過監(jiān)測(cè)ReferenceQueue里面的內(nèi)容就能檢查到Activity是否能夠被回收(在ReferenceQueue中說明可以被回收垦写,不存在泄漏;否則彰触,可能存在泄漏梯投,LeakCanary是執(zhí)行一遍GC,若還未在ReferenceQueue中况毅,就會(huì)認(rèn)定為泄漏)分蓖。
如果Activity被認(rèn)定為泄露了,就抓取內(nèi)存dump文件(Debug.dumpHprofData)尔许;之后通過HeapAnalyzerService.runAnalysis進(jìn)行分析內(nèi)存文件分析么鹤;接著通過HeapAnalyzer (checkForLeak—findLeakingReference—findLeakTrace)來進(jìn)行內(nèi)存泄漏分析。最后通過DisplayLeakService進(jìn)行內(nèi)存泄漏的展示味廊。
(3)Android Lint 工具:
Android Lint Tool 是Android Sutido種集成的一個(gè)Android代碼提示工具蒸甜,它可以給你布局、代碼提供非常強(qiáng)大的幫助毡们。硬編碼會(huì)提示以級(jí)別警告迅皇,例如:在布局文件中寫了三層冗余的LinearLayout布局昧辽、直接在TextView中寫要顯示的文字衙熔、字體大小使用dp而不是sp為單位,就會(huì)在編輯器右邊看到提示搅荞。
(二)流暢——卡頓優(yōu)化
卡頓的場(chǎng)景通常是發(fā)生在用戶交互體驗(yàn)最直接的方面红氯。影響卡頓的兩大因素,分別是界面繪制和數(shù)據(jù)處理咕痛。
界面繪制:主要原因是繪制的層級(jí)深痢甘、頁面復(fù)雜、刷新不合理茉贡,由于這些原因?qū)е驴D的場(chǎng)景更多出現(xiàn)在 UI 和啟動(dòng)后的初始界面以及跳轉(zhuǎn)到頁面的繪制上塞栅。
數(shù)據(jù)處理:導(dǎo)致這種卡頓場(chǎng)景的原因是數(shù)據(jù)處理量太大,一般分為三種情況腔丧,一是數(shù)據(jù)在處理 UI 線程放椰,二是數(shù)據(jù)處理占用 CPU 高作烟,導(dǎo)致主線程拿不到時(shí)間片,三是內(nèi)存增加導(dǎo)致 GC 頻繁砾医,從而引起卡頓拿撩。
(1)布局優(yōu)化
在Android種系統(tǒng)對(duì)View進(jìn)行測(cè)量、布局和繪制時(shí)如蚜,都是通過對(duì)View數(shù)的遍歷來進(jìn)行操作的压恒。如果一個(gè)View數(shù)的高度太高就會(huì)嚴(yán)重影響測(cè)量、布局和繪制的速度错邦。Google也在其API文檔中建議View高度不宜哦過10層√胶眨現(xiàn)在版本種Google使用RelativeLayout替代LineraLayout作為默認(rèn)根布局,目的就是降低LineraLayout嵌套產(chǎn)生布局樹的高度兴猩,從而提高UI渲染的效率期吓。
布局復(fù)用,使用標(biāo)簽重用layout倾芝;
提高顯示速度讨勤,使用延遲View加載;
減少層級(jí)晨另,使用標(biāo)簽替換父級(jí)布局潭千;
注意使用wrap_content,會(huì)增加measure計(jì)算成本借尿;
刪除控件中無用屬性刨晴;
(2)繪制優(yōu)化
過度繪制是指在屏幕上的某個(gè)像素在同一幀的時(shí)間內(nèi)被繪制了多次。在多層次重疊的 UI 結(jié)構(gòu)中路翻,如果不可見的 UI 也在做繪制的操作狈癞,就會(huì)導(dǎo)致某些像素區(qū)域被繪制了多次,從而浪費(fèi)了多余的 CPU 以及 GPU 資源茂契。如何避免過度繪制蝶桶?
布局上的優(yōu)化。移除 XML 中非必須的背景掉冶,移除 Window 默認(rèn)的背景真竖、按需顯示占位背景圖片
自定義View優(yōu)化。使用 canvas.clipRect() 幫助系統(tǒng)識(shí)別那些可見的區(qū)域厌小,只有在這個(gè)區(qū)域內(nèi)才會(huì)被繪制恢共。
(3)啟動(dòng)優(yōu)化
應(yīng)用一般都有閃屏頁SplashActivity,優(yōu)化閃屏頁的 UI 布局璧亚,可以通過 Profile GPU Rendering 檢測(cè)丟幀情況讨韭。
(三)節(jié)省——耗電優(yōu)化
在 Android5.0 以前,關(guān)于應(yīng)用電量消耗的測(cè)試即麻煩又不準(zhǔn)確,而5.0 之后Google專門引入了一個(gè)獲取設(shè)備上電量消耗信息的API—— Battery Historian透硝。Battery Historian 是一款由 Google 提供的 Android 系統(tǒng)電量分析工具吉嚣,直觀地展示出手機(jī)的電量消耗過程,通過輸入電量分析文件蹬铺,顯示消耗情況尝哆。
最后提供一些可供參考耗電優(yōu)化的方法:
(1)計(jì)算優(yōu)化。算法甜攀、for循環(huán)優(yōu)化秋泄、Switch…case替代if…else、避開浮點(diǎn)運(yùn)算规阀。
浮點(diǎn)運(yùn)算:計(jì)算機(jī)里整數(shù)和小數(shù)形式就是按普通格式進(jìn)行存儲(chǔ)恒序,例如1024、3.1415926等等谁撼,這個(gè)沒什么特點(diǎn)歧胁,但是這樣的數(shù)精度不高,表達(dá)也不夠全面厉碟,為了能夠有一種數(shù)的通用表示法喊巍,就發(fā)明了浮點(diǎn)數(shù)。浮點(diǎn)數(shù)的表示形式有點(diǎn)像科學(xué)計(jì)數(shù)法(.×10***)箍鼓,它的表示形式是0.*****×10崭参,在計(jì)算機(jī)中的形式為 .*** e ±**),其中前面的星號(hào)代表定點(diǎn)小數(shù)款咖,也就是整數(shù)部分為0的純小數(shù)何暮,后面的指數(shù)部分是定點(diǎn)整數(shù)。利用這樣的形式就能表示出任意一個(gè)整數(shù)和小數(shù)铐殃,例如1024就能表示成0.1024×10^4海洼,也就是 .1024e+004,3.1415926就能表示成0.31415926×10^1富腊,也就是 .31415926e+001坏逢,這就是浮點(diǎn)數(shù)。浮點(diǎn)數(shù)進(jìn)行的運(yùn)算就是浮點(diǎn)運(yùn)算蟹肘。浮點(diǎn)運(yùn)算比常規(guī)運(yùn)算更復(fù)雜词疼,因此計(jì)算機(jī)進(jìn)行浮點(diǎn)運(yùn)算速度要比進(jìn)行常規(guī)運(yùn)算慢得多俯树。
(2)避免 Wake Lock 使用不當(dāng)帘腹。
Wake Lock是一種鎖的機(jī)制,主要是相對(duì)系統(tǒng)的休眠而言的许饿,,只要有人拿著這個(gè)鎖阳欲,系統(tǒng)就無法進(jìn)入休眠意思就是我的程序給CPU加了這個(gè)鎖那系統(tǒng)就不會(huì)休眠了,這樣做的目的是為了全力配合我們程序的運(yùn)行。有的情況如果不這么做就會(huì)出現(xiàn)一些問題球化,比如微信等及時(shí)通訊的心跳包會(huì)在熄屏不久后停止網(wǎng)絡(luò)訪問等問題秽晚。所以微信里面是有大量使用到了Wake_Lock鎖。系統(tǒng)為了節(jié)省電量筒愚,CPU在沒有任務(wù)忙的時(shí)候就會(huì)自動(dòng)進(jìn)入休眠赴蝇。有任務(wù)需要喚醒CPU高效執(zhí)行的時(shí)候,就會(huì)給CPU加Wake_Lock鎖巢掺。大家經(jīng)常犯的錯(cuò)誤句伶,我們很容易去喚醒CPU來工作,但是很容易忘記釋放Wake_Lock陆淀。
(3)使用 Job Scheduler 管理后臺(tái)任務(wù)考余。
在Android 5.0 API 21 中,google提供了一個(gè)叫做JobScheduler API的組件轧苫,來處理當(dāng)某個(gè)時(shí)間點(diǎn)或者當(dāng)滿足某個(gè)特定的條件時(shí)執(zhí)行一個(gè)任務(wù)的場(chǎng)景楚堤,例如當(dāng)用戶在夜間休息時(shí)或設(shè)備接通電源適配器連接WiFi啟動(dòng)下載更新的任務(wù)。這樣可以在減少資源消耗的同時(shí)提升應(yīng)用的效率含懊。
(四)安裝包——APK瘦身
(1)安裝包的組成結(jié)構(gòu)
assets文件夾身冬。存放一些配置文件、資源文件岔乔,assets不會(huì)自動(dòng)生成對(duì)應(yīng)的 ID吏恭,而是通過 AssetManager 類的接口獲取。
res重罪。res 是 resource 的縮寫樱哼,這個(gè)目錄存放資源文件,會(huì)自動(dòng)生成對(duì)應(yīng)的 ID 并映射到 .R 文件中剿配,訪問直接使用資源 ID搅幅。
META-INF。保存應(yīng)用的簽名信息呼胚,簽名信息可以驗(yàn)證 APK 文件的完整性茄唐。
AndroidManifest.xml。這個(gè)文件用來描述 Android 應(yīng)用的配置信息蝇更,一些組件的注冊(cè)信息沪编、可使用權(quán)限等。
classes.dex年扩。Dalvik 字節(jié)碼程序蚁廓,讓 Dalvik 虛擬機(jī)可執(zhí)行,一般情況下厨幻,Android 應(yīng)用在打包時(shí)通過 Android SDK 中的 dx 工具將 Java 字節(jié)碼轉(zhuǎn)換為 Dalvik 字節(jié)碼相嵌。
resources.arsc腿时。記錄著資源文件和資源 ID 之間的映射關(guān)系,用來根據(jù)資源 ID 尋找資源饭宾。
(2)減少安裝包大小
代碼混淆批糟。使用IDE 自帶的 proGuard 代碼混淆器工具 ,它包括壓縮看铆、優(yōu)化徽鼎、混淆等功能。
資源優(yōu)化弹惦。比如使用 Android Lint 刪除冗余資源纬傲,資源文件最少化等。
圖片優(yōu)化肤频。比如利用 PNG優(yōu)化工具 對(duì)圖片做壓縮處理叹括。推薦目前最先進(jìn)的壓縮工具Googlek開源庫zopfli。如果應(yīng)用在0版本以上宵荒,推薦使用 WebP圖片格式汁雷。
避免重復(fù)或無用功能的第三方庫。例如报咳,百度地圖接入基礎(chǔ)地圖即可侠讯、訊飛語音無需接入離線、圖片庫GlidePicasso等暑刃。
插件化開發(fā)厢漩。比如功能模塊放在服務(wù)器上,按需下載岩臣,可以減少安裝包大小溜嗜。
可以使用微信開源資源文件混淆工具——AndResGuard。一般可以壓縮apk的1M左右大架谎。
冷啟動(dòng)與熱啟動(dòng)
參考鏈接:http://www.reibang.com/p/03c0fd3fc245
冷啟動(dòng)
在啟動(dòng)應(yīng)用時(shí)炸宵,系統(tǒng)中沒有該應(yīng)用的進(jìn)程,這時(shí)系統(tǒng)會(huì)創(chuàng)建一個(gè)新的進(jìn)程分配給該應(yīng)用谷扣;
熱啟動(dòng)
在啟動(dòng)應(yīng)用時(shí)土全,系統(tǒng)中已有該應(yīng)用的進(jìn)程(例:按back鍵、home鍵会涎,應(yīng)用雖然會(huì)退出裹匙,但是該應(yīng)用的進(jìn)程還是保留在后臺(tái));
區(qū)別
冷啟動(dòng):系統(tǒng)沒有該應(yīng)用的進(jìn)程末秃,需要?jiǎng)?chuàng)建一個(gè)新的進(jìn)程分配給應(yīng)用概页,所以會(huì)先創(chuàng)建和初始化Application類,再創(chuàng)建和初始化MainActivity類(包括一系列的測(cè)量蛔溃、布局绰沥、繪制),最后顯示在界面上贺待。 熱啟動(dòng): 從已有的進(jìn)程中來啟動(dòng)徽曲,不會(huì)創(chuàng)建和初始化Application類,直接創(chuàng)建和初始化MainActivity類(包括一系列的測(cè)量麸塞、布局秃臣、繪制),最后顯示在界面上哪工。
冷啟動(dòng)流程
Zygote進(jìn)程中fork創(chuàng)建出一個(gè)新的進(jìn)程奥此; 創(chuàng)建和初始化Application類、創(chuàng)建MainActivity雁比; inflate布局稚虎、當(dāng)onCreate/onStart/onResume方法都走完; contentView的measure/layout/draw顯示在界面上偎捎。
冷啟動(dòng)優(yōu)化
減少在Application和第一個(gè)Activity的onCreate()方法的工作量蠢终; 不要讓Application參與業(yè)務(wù)的操作; 不要在Application進(jìn)行耗時(shí)操作茴她; 不要以靜態(tài)變量的方式在Application中保存數(shù)據(jù)寻拂; 減少布局的復(fù)雜性和深度;
屏幕適配
重要
基本概念
屏幕尺寸
含義:手機(jī)對(duì)角線的物理尺寸 單位:英寸(inch)丈牢,1英寸=2.54cm
Android手機(jī)常見的尺寸有5寸祭钉、5.5寸、6寸己沛,6.5寸等等
屏幕分辨率
含義:手機(jī)在橫向慌核、縱向上的像素點(diǎn)數(shù)總和
一般描述成屏幕的”寬x高”=AxB 含義:屏幕在橫向方向(寬度)上有A個(gè)像素點(diǎn),在縱向方向
(高)有B個(gè)像素點(diǎn) 例子:1080x1920申尼,即寬度方向上有1080個(gè)像素點(diǎn)遂铡,在高度方向上有1920個(gè)像素點(diǎn)
單位:px(pixel),1px=1像素點(diǎn)
UI設(shè)計(jì)師的設(shè)計(jì)圖會(huì)以px作為統(tǒng)一的計(jì)量單位
Android手機(jī)常見的分辨率:320x480晶姊、480x800扒接、720x1280、1080x1920
屏幕像素密度
含義:每英寸的像素點(diǎn)數(shù) 單位:dpi(dots per ich)
假設(shè)設(shè)備內(nèi)每英寸有160個(gè)像素们衙,那么該設(shè)備的屏幕像素密度=160dpi
適配方法
1.支持各種屏幕尺寸: 使用wrap_content, match_parent, weight.要確保布局的靈活性并適應(yīng)各種尺寸的屏幕钾怔,應(yīng)使用 “wrap_content”、“match_parent” 控制某些視圖組件的寬度和高度蒙挑。
2.使用相對(duì)布局宗侦,禁用絕對(duì)布局。
3.使用LinearLayout的weight屬性
假如我們的寬度不是0dp(wrap_content和0dp的效果相同)忆蚀,則是match_parent呢矾利?
android:layout_weight的真實(shí)含義是:如果View設(shè)置了該屬性并且有效姑裂,那么該 View的寬度等于原有寬度(android:layout_width)加上剩余空間的占比。
從這個(gè)角度我們來解釋一下上面的現(xiàn)象男旗。在上面的代碼中舶斧,我們?cè)O(shè)置每個(gè)Button的寬度都是match_parent,假設(shè)屏幕寬度為L(zhǎng)察皇,那么每個(gè)Button的寬度也應(yīng)該都為L(zhǎng)茴厉,剩余寬度就等于L-(L+L)= -L。
Button1的weight=1什荣,剩余寬度占比為1/(1+2)= 1/3矾缓,所以最終寬度為L(zhǎng)+1/3*(-L)=2/3L,Button2的計(jì)算類似稻爬,最終寬度為L(zhǎng)+2/3(-L)=1/3L嗜闻。
4.使用.9圖片
今日頭條屏幕適配
參考鏈接:https://juejin.im/post/5bce688e6fb9a05cf715d1c2
Activity啟動(dòng)流程
重要
Activity啟動(dòng)流程
用戶從Launcher程序點(diǎn)擊應(yīng)用圖標(biāo)可啟動(dòng)應(yīng)用的入口Activity,Activity啟動(dòng)時(shí)需要多個(gè)進(jìn)程之間的交互桅锄,Android系統(tǒng)中有一個(gè)zygote進(jìn)程專用于孵化Android框架層和應(yīng)用層程序的進(jìn)程泞辐。還有一個(gè)system_server進(jìn)程,該進(jìn)程里運(yùn)行了很多binder service竞滓。例如ActivityManagerService咐吼,PackageManagerService,WindowManagerService商佑,這些binder service分別運(yùn)行在不同的線程中锯茄,其中ActivityManagerService負(fù)責(zé)管理Activity棧,應(yīng)用進(jìn)程茶没,task肌幽。
點(diǎn)擊Launcher圖標(biāo)來啟動(dòng)Activity
用戶在Launcher程序里點(diǎn)擊應(yīng)用圖標(biāo)時(shí),會(huì)通知ActivityManagerService啟動(dòng)應(yīng)用的入口Activity抓半,ActivityManagerService發(fā)現(xiàn)這個(gè)應(yīng)用還未啟動(dòng)喂急,則會(huì)通知Zygote進(jìn)程孵化出應(yīng)用進(jìn)程,然后在這個(gè)dalvik應(yīng)用進(jìn)程里執(zhí)行ActivityThread的main方法笛求。應(yīng)用進(jìn)程接下來通知ActivityManagerService應(yīng)用進(jìn)程已啟動(dòng)廊移,ActivityManagerService保存應(yīng)用進(jìn)程的一個(gè)代理對(duì)象,這樣ActivityManagerService可以通過這個(gè)代理對(duì)象控制應(yīng)用進(jìn)程探入,然后ActivityManagerService通知應(yīng)用進(jìn)程創(chuàng)建入口Activity的實(shí)例狡孔,并執(zhí)行它的生命周期方法。
Android繪制流程窗口啟動(dòng)流程分析
4.2蜂嗽、Activity生命周期
(1)Activity的形態(tài)
Active/Running:
Activity處于活動(dòng)狀態(tài)苗膝,此時(shí)Activity處于棧頂,是可見狀態(tài)植旧,可與用戶進(jìn)行交互辱揭。
Paused:
當(dāng)Activity失去焦點(diǎn)時(shí)离唐,或被一個(gè)新的非全屏的Activity,或被一個(gè)透明的Activity放置在棧頂時(shí)问窃,Activity就轉(zhuǎn)化為Paused狀態(tài)亥鬓。但我們需要明白,此時(shí)Activity只是失去了與用戶交互的能力泡躯,其所有的狀態(tài)信息及其成員變量都還存在贮竟,只有在系統(tǒng)內(nèi)存緊張的情況下丽焊,才有可能被系統(tǒng)回收掉较剃。
Stopped:
當(dāng)一個(gè)Activity被另一個(gè)Activity完全覆蓋時(shí),被覆蓋的Activity就會(huì)進(jìn)入Stopped狀態(tài)技健,此時(shí)它不再可見写穴,但是跟Paused狀態(tài)一樣保持著其所有狀態(tài)信息及其成員變量。
Killed:
當(dāng)Activity被系統(tǒng)回收掉時(shí)雌贱,Activity就處于Killed狀態(tài)啊送。
Activity會(huì)在以上四種形態(tài)中相互切換,至于如何切換欣孤,這因用戶的操作不同而異喜颁。了解了Activity的4種形態(tài)后遣铝,我們就來聊聊Activity的生命周期。
Activity的生命周期
所謂的典型的生命周期就是在有用戶參與的情況下,Activity經(jīng)歷從創(chuàng)建鬓长,運(yùn)行,停止玻佩,銷毀等正常的生命周期過程谐鼎。
onCreate
該方法是在Activity被創(chuàng)建時(shí)回調(diào),它是生命周期第一個(gè)調(diào)用的方法段只,我們?cè)趧?chuàng)建Activity時(shí)一般都需要重寫該方法腮猖,然后在該方法中做一些初始化的操作,如通過setContentView設(shè)置界面布局的資源赞枕,初始化所需要的組件信息等澈缺。
onStart
此方法被回調(diào)時(shí)表示Activity正在啟動(dòng),此時(shí)Activity已處于可見狀態(tài)炕婶,只是還沒有在前臺(tái)顯示谍椅,因此無法與用戶進(jìn)行交互」呕埃可以簡(jiǎn)單理解為Activity已顯示而我們無法看見擺了雏吭。
onResume
當(dāng)此方法回調(diào)時(shí),則說明Activity已在前臺(tái)可見陪踩,可與用戶交互了(處于前面所說的Active/Running形態(tài))杖们,onResume方法與onStart的相同點(diǎn)是兩者都表示Activity可見悉抵,只不過onStart回調(diào)時(shí)Activity還是后臺(tái)無法與用戶交互,而onResume則已顯示在前臺(tái)摘完,可與用戶交互姥饰。當(dāng)然從流程圖,我們也可以看出當(dāng)Activity停止后(onPause方法和onStop方法被調(diào)用)孝治,重新回到前臺(tái)時(shí)也會(huì)調(diào)用onResume方法列粪,因此我們也可以在onResume方法中初始化一些資源,比如重新初始化在onPause或者onStop方法中釋放的資源谈飒。
onPause
此方法被回調(diào)時(shí)則表示Activity正在停止(Paused形態(tài))岂座,一般情況下onStop方法會(huì)緊接著被回調(diào)。但通過流程圖我們還可以看到一種情況是onPause方法執(zhí)行后直接執(zhí)行了onResume方法杭措,這屬于比較極端的現(xiàn)象了费什,這可能是用戶操作使當(dāng)前Activity退居后臺(tái)后又迅速地再回到到當(dāng)前的Activity,此時(shí)onResume方法就會(huì)被回調(diào)手素。當(dāng)然鸳址,在onPause方法中我們可以做一些數(shù)據(jù)存儲(chǔ)或者動(dòng)畫停止或者資源回收的操作,但是不能太耗時(shí)泉懦,因?yàn)檫@可能會(huì)影響到新的Activity的顯示——onPause方法執(zhí)行完成后稿黍,新Activity的onResume方法才會(huì)被執(zhí)行。
onStop
一般在onPause方法執(zhí)行完成直接執(zhí)行崩哩,表示Activity即將停止或者完全被覆蓋(Stopped形態(tài))巡球,此時(shí)Activity不可見,僅在后臺(tái)運(yùn)行琢锋。同樣地辕漂,在onStop方法可以做一些資源釋放的操作(不能太耗時(shí))。
onRestart
表示Activity正在重新啟動(dòng)吴超,當(dāng)Activity由不可見變?yōu)榭梢姞顟B(tài)時(shí)钉嘹,該方法被回調(diào)。這種情況一般是用戶打開了一個(gè)新的Activity時(shí)鲸阻,當(dāng)前的Activity就會(huì)被暫停(onPause和onStop被執(zhí)行了)跋涣,接著又回到當(dāng)前Activity頁面時(shí),onRestart方法就會(huì)被回調(diào)鸟悴。
onDestroy
此時(shí)Activity正在被銷毀陈辱,也是生命周期最后一個(gè)執(zhí)行的方法,一般我們可以在此方法中做一些回收工作和最終的資源釋放细诸。
小結(jié)
到這里我們來個(gè)小結(jié)沛贪,當(dāng)Activity啟動(dòng)時(shí),依次會(huì)調(diào)用onCreate(),onStart(),onResume(),而當(dāng)Activity退居后臺(tái)時(shí)(不可見利赋,點(diǎn)擊Home或者被新的Activity完全覆蓋)水评,onPause()和onStop()會(huì)依次被調(diào)用。當(dāng)Activity重新回到前臺(tái)(從桌面回到原Activity或者被覆蓋后又回到原Activity)時(shí)媚送,onRestart()中燥,onStart(),onResume()會(huì)依次被調(diào)用塘偎。當(dāng)Activity退出銷毀時(shí)(點(diǎn)擊back鍵)疗涉,onPause(),onStop()吟秩,onDestroy()會(huì)依次被調(diào)用咱扣,到此Activity的整個(gè)生命周期方法回調(diào)完成。現(xiàn)在我們?cè)倩仡^看看之前的流程圖峰尝,應(yīng)該是相當(dāng)清晰了吧偏窝。嗯收恢,這就是Activity整個(gè)典型的生命周期過程武学。
ANR是怎么觸發(fā)的?
重要
ActivityManagerService和WindowManagerService會(huì)檢測(cè)APP的響應(yīng)時(shí)間伦意,在應(yīng)用進(jìn)程的主線程處理特定的事件之前火窒,用AMS/BroadcastQueue等相關(guān)的Handler像系統(tǒng)進(jìn)程的Looper發(fā)送一個(gè)延時(shí)消息,在延時(shí)的時(shí)間之內(nèi)驮肉,如果特定事件被執(zhí)行完熏矿,則會(huì)移除掉MessageQueue中加入的那個(gè)延時(shí)消息;否則离钝,如果特定的事件沒有執(zhí)行完票编,則不會(huì)移除那個(gè)消息,相應(yīng)的Looper會(huì)取出該消息進(jìn)行處理卵渴,從而觸發(fā)ANR慧域。這就是觸發(fā)ANR的原理。
android anr之后如何導(dǎo)出traces.txt
重要
Anr Application not responding 即程序無響應(yīng)
Anr的類型
1> 按鍵或觸摸事件無響應(yīng) 5s
2>BroadCastTimeout在特定時(shí)間無響應(yīng) 10s
3>小概率 ServicesTimeout servicez在特定時(shí)間無法處理 20s
產(chǎn)生anr的原因
1>當(dāng)前事件沒有機(jī)會(huì)處理 (當(dāng)ui線程正在處理其他的工作浪读,沒空處理當(dāng)前事件昔榴,也就是當(dāng)前事件被阻塞啦)
2>正在處理當(dāng)前事件,但是沒有處理完(即沒有反饋)
導(dǎo)出traces.txt 文件
adb pull /data/anr/traces.txt d:/ 《導(dǎo)出文件至某盤
adb shell ls /data/anr/ 《查看文件
1碘橘、adb shell
2互订、cat /data/anr/xxx >/mnt/sdcard/yy/zz.txt
3、exit
4痘拆、adb pull /mnt/sdcard/yy/zz.txt d: ,即可將文件導(dǎo)出到了d盤仰禽。
AsyncTask原理
非常重要
AsyncTask工作組成:
? ? ? ? ? ? ? ? ? ? ? ?? 是由倆個(gè)線程池+Hanlder構(gòu)成
? ? ? ? ? ? ? ? ? ? ? ?? 任務(wù)隊(duì)列SerialExecutor線程池
? ? ? ? ? ? ? ? ? ? ? ?? 執(zhí)行 THREAD_POOL_EXECUTOR線程池
? ? ? ? ? ? ? ? ? ? ? ?? 內(nèi)部Handler 異步通信+消息傳遞
如果不設(shè)置執(zhí)行線程數(shù)那么默認(rèn)是4,如果有6個(gè)線程那么有兩個(gè)就會(huì)在任務(wù)隊(duì)列等待,執(zhí)行線程中執(zhí)行4個(gè)
具體去看源碼這個(gè)說不清楚
LRUCache原理
重要
LRUCache的核心思想就是維護(hù)一個(gè)緩存對(duì)象列表吐葵,其中對(duì)象列表的排列方式是按照訪問順序?qū)崿F(xiàn)的勇边,即一直沒有訪問的對(duì)象放在隊(duì)尾,即將被淘汰折联。而最近訪問的對(duì)象將放在隊(duì)頭粒褒,最后被淘汰。
缺圖片
Bitmap如何處理大圖诚镰,如一張30M的大圖奕坟,如何預(yù)防OOM
重要
需要將這張大圖進(jìn)行壓縮。
使用圖片緩存清笨。
https://blog.csdn.net/guolin_blog/article/details/9316683
自定義View注意事項(xiàng)
重要
減少不必要的invalidate()方法月杉。
(1)、降低刷新頻率
(2)抠艾、使用硬件加速
(3)苛萎、初始化時(shí)創(chuàng)建對(duì)象;不要在onDraw方法內(nèi)創(chuàng)建繪制對(duì)象检号,一般都在構(gòu)造函數(shù)里初始化對(duì)象腌歉。
(4)、狀態(tài)存儲(chǔ)和恢復(fù):如果內(nèi)存不足時(shí)齐苛,而恰好我們的Activity置于后臺(tái)翘盖,不行被重啟,或者用戶旋轉(zhuǎn)屏幕造成Activity重啟凹蜂,我們的View也應(yīng)該盡量的去保存自己的屬性馍驯。
App啟動(dòng)崩潰異常捕捉
重要
由于我們寫的代碼難免會(huì)出現(xiàn)一些bug,以及由于測(cè)試環(huán)境和生產(chǎn)環(huán)境差異導(dǎo)致出現(xiàn)的一些異常玛痊,在測(cè)試過程中沒有發(fā)現(xiàn)汰瘫,而在app上線之后會(huì)偶然出現(xiàn)的一些bug,以至于app在使用過程中出現(xiàn)ANR擂煞,這是個(gè)令人蛋疼的現(xiàn)象混弥,app卡死、出現(xiàn)黑屏等颈娜,總之當(dāng)app出現(xiàn)異常時(shí)的用戶體驗(yàn)不友好剑逃,我們開發(fā)者需要去捕獲這些異常,收集這些異常信息官辽,并且上傳到服務(wù)器蛹磺,利于開發(fā)人員去解決這些問題,同時(shí)同仆,我們還需要給用戶一個(gè)友好的交互體驗(yàn)萤捆。
我也查找了許多捕獲崩潰異常的文章來看,但大多都千篇一律,只說了怎么捕獲異常俗或,處理異常市怎,并沒有對(duì)捕獲異常后的界面交互做出很好的處理,系統(tǒng)出現(xiàn)ANR時(shí)會(huì)先卡一段時(shí)間(這個(gè)時(shí)間還有點(diǎn)長(zhǎng))然后彈出一個(gè)系統(tǒng)默認(rèn)的對(duì)話框辛慰,但是我現(xiàn)在需要的是當(dāng)出現(xiàn)異常情況時(shí)区匠,立馬彈出對(duì)話框,并且對(duì)話框我想自定義界面帅腌。
最終實(shí)現(xiàn)的效果如圖:
<img src="http://upload-images.jianshu.io/upload_images/1159224-cdf0b4169183ecaf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" width="40%"? alt="最終效果圖" align="center" />
首先需要自定義Application驰弄,并且在AndroidManifest.xml中進(jìn)行配置
AndroidManifest.xml.png
還需要自定義一個(gè)應(yīng)用異常捕獲類AppUncaughtExceptionHandler,它必須得實(shí)現(xiàn)Thread.UncaughtExceptionHandler接口速客,另外還需要重寫uncaughtException方法戚篙,去按我們自己的方式來處理異常
在Application中我們只需要初始化自定義的異常捕獲類即可:
@OverridepublicvoidonCreate(){super.onCreate();mInstance=this;// 初始化文件目錄SdcardConfig.getInstance().initSdcard();// 捕捉異常AppUncaughtExceptionHandlercrashHandler=AppUncaughtExceptionHandler.getInstance();crashHandler.init(getApplicationContext());}
其中文件目錄是異常信息保存在sd卡中的目錄,還有我們是整個(gè)app中全局捕獲異常溺职,所以我們自定義的捕獲類是單例岔擂。
/**
* 初始化捕獲類
*
* @param context
*/publicvoidinit(Contextcontext){applicationContext=context.getApplicationContext();crashing=false;//獲取系統(tǒng)默認(rèn)的UncaughtException處理器mDefaultHandler=Thread.getDefaultUncaughtExceptionHandler();//設(shè)置該CrashHandler為程序的默認(rèn)處理器Thread.setDefaultUncaughtExceptionHandler(this);}
完成以上過程后,接著需要重寫uncaughtException方法:
@OverridepublicvoiduncaughtException(Threadthread,Throwableex){if(crashing){return;}crashing=true;// 打印異常信息ex.printStackTrace();// 我們沒有處理異常 并且默認(rèn)異常處理不為空 則交給系統(tǒng)處理if(!handlelException(ex)&&mDefaultHandler!=null){// 系統(tǒng)處理mDefaultHandler.uncaughtException(thread,ex);}byebye();}privatevoidbyebye(){android.os.Process.killProcess(android.os.Process.myPid());System.exit(0);}
既然是我們自己處理異常浪耘,所以會(huì)先執(zhí)行handlelException(ex)方法:
privatebooleanhandlelException(Throwableex){if(ex==null){returnfalse;}try{// 異常信息StringcrashReport=getCrashReport(ex);// TODO: 上傳日志到服務(wù)器// 保存到sd卡saveExceptionToSdcard(crashReport);// 提示對(duì)話框showPatchDialog();}catch(Exceptione){returnfalse;}returntrue;}
獲取的異常信息包括系統(tǒng)信息乱灵,app版本信息,以及手機(jī)制造商信息等:
/**
* 獲取異常信息
* @param ex
* @return
*/privateStringgetCrashReport(Throwableex){StringBufferexceptionStr=newStringBuffer();PackageInfopinfo=CrashApplication.getInstance().getLocalPackageInfo();if(pinfo!=null){if(ex!=null){//app版本信息exceptionStr.append("App Version:"+pinfo.versionName);exceptionStr.append("_"+pinfo.versionCode+"\n");//手機(jī)系統(tǒng)信息exceptionStr.append("OS Version:"+Build.VERSION.RELEASE);exceptionStr.append("_");exceptionStr.append(Build.VERSION.SDK_INT+"\n");//手機(jī)制造商exceptionStr.append("Vendor: "+Build.MANUFACTURER+"\n");//手機(jī)型號(hào)exceptionStr.append("Model: "+Build.MODEL+"\n");StringerrorStr=ex.getLocalizedMessage();if(TextUtils.isEmpty(errorStr)){errorStr=ex.getMessage();}if(TextUtils.isEmpty(errorStr)){errorStr=ex.toString();}exceptionStr.append("Exception: "+errorStr+"\n");StackTraceElement[]elements=ex.getStackTrace();if(elements!=null){for(inti=0;i<elements.length;i++){exceptionStr.append(elements[i].toString()+"\n");}}}else{exceptionStr.append("no exception. Throwable is null\n");}returnexceptionStr.toString();}else{return"";}}
將異常信息保存到sd卡這個(gè)我覺得可選吧点待,但是上傳到服務(wù)端還是很有必要的:
/**
* 保存錯(cuò)誤報(bào)告到sd卡
* @param errorReason
*/privatevoidsaveExceptionToSdcard(StringerrorReason){try{Log.e("CrashDemo","AppUncaughtExceptionHandler執(zhí)行了一次");Stringtime=mFormatter.format(newDate());StringfileName="Crash-"+time+".log";if(SdcardConfig.getInstance().hasSDCard()){Stringpath=SdcardConfig.LOG_FOLDER;Filedir=newFile(path);if(!dir.exists()){dir.mkdirs();}FileOutputStreamfos=newFileOutputStream(path+fileName);fos.write(errorReason.getBytes());fos.close();}}catch(Exceptione){Log.e("CrashDemo","an error occured while writing file..."+e.getMessage());}}
保存在sd卡中的異常文件格式:
異常信息
至于上傳到服務(wù)器阔蛉,就比較靈活了弃舒,可以將整個(gè)文件上傳癞埠,或者上傳異常信息的字符串,可以和后端開發(fā)人員配合聋呢。
因?yàn)椴东@異常后我要馬上關(guān)閉掉app即上面的byebye方法苗踪,是將app整個(gè)進(jìn)程殺死,如果接著要顯示提示對(duì)話框削锰,則需要在新的任務(wù)棧中打開activity:
publicstaticIntentnewIntent(Contextcontext,Stringtitle,StringultimateMessage){Intentintent=newIntent();intent.setClass(context,PatchDialogActivity.class);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.putExtra(EXTRA_TITLE,title);intent.putExtra(EXTRA_ULTIMATE_MESSAGE,ultimateMessage);returnintent;}
對(duì)話框中給出了重啟操作的選項(xiàng)通铲,重啟過程的實(shí)現(xiàn):
privatevoidrestart(){Intentintent=getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName());intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);startActivity(intent);super.onDestroy();}
源代碼地址:https://github.com/shenhuniurou/BlogDemos/tree/master/CrashDemo歡迎star。
作者:shenhuniurou
鏈接:http://www.reibang.com/p/fb28a5322d8a
來源:簡(jiǎn)書
啟動(dòng)太慢怎么解決器贩?
重要
應(yīng)用啟動(dòng)速度取決于application中做了什么事情颅夺,比如繼承了很多sdk,并且sdk的init操作都需要在主線程中實(shí)現(xiàn)蛹稍,那自然就慢了吧黄。在非必要的情況下,可以把加載延后唆姐,或丟給子線程拗慨。
統(tǒng)計(jì)啟動(dòng)時(shí)長(zhǎng),標(biāo)準(zhǔn)
重要
啟動(dòng)類型
(1)、冷啟動(dòng):application沒有被創(chuàng)建,需要先創(chuàng)建進(jìn)程赵抢,然后啟動(dòng)MainActivity剧蹂。由于這個(gè)過程需要fork一個(gè)新進(jìn)程,所以耗時(shí)烦却。
(2)宠叼、熱啟動(dòng):同上面對(duì)照,已經(jīng)啟動(dòng)過application其爵,并駐留在系統(tǒng)內(nèi)存內(nèi)车吹,只是需要喚醒該進(jìn)程,并啟動(dòng)MainActivity醋闭。
啟動(dòng)時(shí)長(zhǎng)統(tǒng)計(jì):
adb統(tǒng)計(jì):adb shell am start -w pageage/activityname 通過這條命令啟動(dòng)窄驹,可以獲得啟動(dòng)時(shí)間。
從0設(shè)計(jì)一款A(yù)pp整體架構(gòu)证逻,如何去做乐埠?
重要
(1)、構(gòu)建方式:Gradle或Maven
(2)囚企、網(wǎng)絡(luò)框架:包括網(wǎng)絡(luò)請(qǐng)求丈咐,圖片異步加載,圖片緩存龙宏,網(wǎng)絡(luò)緩存棵逊,力求簡(jiǎn)單易用。
(3)银酗、適配資源辆影。
(4)、設(shè)計(jì)模式:MVC黍特、MVP蛙讥、MVVM
說說EventBus作用,實(shí)現(xiàn)方式灭衷,代替EventBus的方式
重要
EventBus作用:簡(jiǎn)化各組件間的通信次慢,讓我們書寫代碼更簡(jiǎn)單,能有效的分離事件發(fā)送方和事件接收方(也就是解耦合)翔曲,能避免復(fù)雜和容易出錯(cuò)的依賴性和生命周期的問題迫像。
RxJava可以替代EventBus,也可以使用Handler或AsyncTask
談?wù)剬?duì)RxJava的理解
重要
RxJava說到底瞳遍,本質(zhì)可以壓縮為異步一個(gè)詞闻妓。它就是一個(gè)異步操作的庫,其他定于都是基于這之上的傅蹂。
RxJava的異步實(shí)現(xiàn)是通過一種擴(kuò)展的觀察者模式來實(shí)現(xiàn)的纷闺。
RxJava的功能與原理實(shí)現(xiàn)
RxJava原理就是創(chuàng)建一個(gè)Observable對(duì)象來干活算凿,然后使用各種操作符建立起來的鏈?zhǔn)讲僮鳎腿缤魉€一樣犁功,把你想要處理的數(shù)據(jù)一步一步加工成你想要的成品氓轰,然后發(fā)射給Subscriber處理。
RxJava的作用浸卦,與平時(shí)使用的異步操作來比的優(yōu)缺點(diǎn)
異步操作有Handler署鸡、AsyncTask等,但使用Rxjava限嫌,就算再多的異步操作靴庆,代碼邏輯越來越復(fù)雜,RxJava依然可以保持清晰的邏輯怒医。
http://www.reibang.com/p/57b5c6567791
適配器模式炉抒,裝飾者模式,外觀模式的異同稚叹?
重要
裝飾器模式:能動(dòng)態(tài)的新增或組合對(duì)象的行為焰薄。
適配器模式:是對(duì)其他對(duì)象接口的一種轉(zhuǎn)換行為,將原接口轉(zhuǎn)換為目標(biāo)接口扒袖,達(dá)到適配的效果塞茅。
外觀模式:外觀對(duì)象提供對(duì)子系統(tǒng)各元件功能的簡(jiǎn)化為共同層次的調(diào)用接口,它主要起簡(jiǎn)化作用季率。
裝飾者是“新增行為”野瘦,適配器模式是“轉(zhuǎn)換行為”,外觀者模式是“簡(jiǎn)化行為”飒泻。
寫出觀察者模式的代碼
重要
缺圖片
從上邊的例子可以看出鞭光,定義了四個(gè)訂閱者,一個(gè)發(fā)布者蠢络,當(dāng)發(fā)布者更新一個(gè)消息時(shí)衰猛,四個(gè)訂閱者都收到消息,根據(jù)發(fā)布者更新的信息執(zhí)行對(duì)應(yīng)的更新操作刹孔。
手寫生產(chǎn)者/消費(fèi)者模式
重要
生產(chǎn)者消費(fèi)者問題是線程模型中的經(jīng)典問題:生產(chǎn)者和消費(fèi)者在同一時(shí)間段內(nèi)共用同一存儲(chǔ)空間,生產(chǎn)者向空間里生產(chǎn)數(shù)據(jù)娜睛,而消費(fèi)者取走數(shù)據(jù)髓霞。
實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模式有三點(diǎn):
(1)、一般使用隊(duì)列作為緩沖區(qū)畦戒,給生產(chǎn)者和消費(fèi)者解耦方库,平衡了生產(chǎn)者和消費(fèi)者的處理能力。
(2)障斋、構(gòu)建生產(chǎn)者纵潦,隊(duì)列滿使得生產(chǎn)者線程阻塞徐鹤。
(3)、構(gòu)建消費(fèi)者邀层,隊(duì)列空使得消費(fèi)者線程阻塞返敬。
缺圖片
BlockingQueue是一個(gè)阻塞隊(duì)列,它的存取可以保證只有一個(gè)線程在進(jìn)行寥院,所以根據(jù)邏輯劲赠,生產(chǎn)者在內(nèi)存滿的時(shí)候進(jìn)行等待,并喚醒消費(fèi)者隊(duì)列秸谢,反過來消費(fèi)者在饑餓狀態(tài)下凛澎,等待并喚醒生產(chǎn)者生產(chǎn)。
項(xiàng)目中常用的設(shè)計(jì)模式
重要
(1)估蹄、模板方法模式
定義一個(gè)操作中的算法的骨架塑煎,而將一些步驟延遲到子類中,如jdbcTemplate
(2)臭蚁、代理模式
spring的Proxy模式在AOP中有體現(xiàn)
(3)轧叽、觀察者模式
定義對(duì)象的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí)刊棕,所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新
(4)炭晒、適配器模式
MethodBeforeAdviceAdapter類。
(5)甥角、策略模式
使用了java的繼承和多態(tài)
(6)网严、單例模式
解決了一個(gè)全局使用的類頻繁的創(chuàng)建與銷毀。
(7)嗤无、工廠模式
分為三種:簡(jiǎn)單工廠震束,工廠方法,抽象工廠当犯。
你所知道的設(shè)計(jì)模式有哪些垢村?
重要
MVC MVP MVVM原理和區(qū)別
重要
MVC是指Modle,View和Controller嚎卫,將界面嘉栓,業(yè)務(wù)邏輯和控制器分開,是一種低耦合的設(shè)計(jì)方式拓诸,適用于簡(jiǎn)單的應(yīng)用開發(fā)侵佃。
這種設(shè)計(jì)模式最簡(jiǎn)單,但問題有三:
(1)奠支、View和Model相互可見馋辈,耦合度高。
(2)倍谜、如果程序復(fù)雜迈螟,那么Activity這個(gè)Controller將十分繁瑣復(fù)雜叉抡,不容易維護(hù)。
(3)答毫、Activity角色模糊褥民,View或Model。
MVP:P是指Presenter烙常,即實(shí)現(xiàn)者轴捎,功能與Controller類似。Presenter實(shí)質(zhì)為Interface類的運(yùn)用蚕脏,用于降低M侦副、V、C間的耦合度驼鞭,主旨是為Activity或Fragment瘦身秦驯。
MVP模式容易維護(hù),可拆卸挣棕,可擴(kuò)展译隘,耦合性叫MVC較小,結(jié)構(gòu)清晰洛心。
MVP的缺點(diǎn)固耘,在于開發(fā)開銷相對(duì)較大。與MVC相比词身,需要維護(hù)更多的接口厅目。
MVVM:MVVM由三部分組成,M法严,V损敷,VM,分別代表Modle深啤,View拗馒,ViewModle。談?wù)揗VVM的前提是需要了解DataBinding溯街,即Modle與View的進(jìn)行綁定诱桂,包括單向綁定(Modle影響View)雷则,雙向綁定(Modle與View相互影響)充岛。
MVVM的側(cè)重點(diǎn)在于數(shù)據(jù)與UI的聯(lián)動(dòng),自動(dòng)更新锋拖,而非降低耦合度韩肝。對(duì)于耦合度的問題,其實(shí)還是需要結(jié)合MVP模式來解決九榔。
模塊化實(shí)現(xiàn)(好處哀峻,原因)
重要
(1)涡相、結(jié)構(gòu)清晰,各個(gè)模塊的代碼實(shí)現(xiàn)分離剩蟀,不會(huì)攪在一起催蝗。在代碼review或者二次開發(fā)的時(shí)候一目了然,不會(huì)滿世界去找代碼育特。
(2)丙号、協(xié)同開發(fā)的時(shí)候更靈活,不用再等同組的其他同事的模塊開發(fā)完成后才能運(yùn)行app缰冤,自己負(fù)責(zé)的模塊稍加修改就可以當(dāng)做主App直接跑起來犬缨。
(3)、便于維護(hù)棉浸。每個(gè)模塊的代碼怀薛、布局文件、資源文件可以隨時(shí)從項(xiàng)目中通過gradle配置去掉迷郑。
數(shù)據(jù)庫框架對(duì)比和源碼分析
重要
greenDao是一種Android數(shù)據(jù)庫ORM框架枝恋,與OrmLite、ActiveOrm嗡害、LitePal等數(shù)據(jù)庫相比焚碌,單位時(shí)間內(nèi)可以插入、更新和查詢更多數(shù)據(jù)霸妹,而且提供了大量的靈活通用的接口十电。
https://blog.csdn.net/u012702547/article/details/52226163
自己去設(shè)計(jì)網(wǎng)絡(luò)請(qǐng)求框架,怎么做抑堡?
重要
網(wǎng)絡(luò)請(qǐng)求框架的核心就是封裝了網(wǎng)絡(luò)請(qǐng)求+異步+數(shù)據(jù)處理
其中網(wǎng)絡(luò)請(qǐng)求一般使用Android的網(wǎng)絡(luò)請(qǐng)求原生方法(HttpClient或HttpURLConnection)
一般而言摆出,我們需要考慮的方面有:
1、構(gòu)建我們的請(qǐng)求Request:包含url,請(qǐng)求方法首妖,請(qǐng)求參數(shù)偎漫,請(qǐng)求頭,編碼有缆,請(qǐng)求體象踊,編碼格式,超時(shí)時(shí)間棚壁,代理端口杯矩,代理主機(jī)等。
2袖外、由Dispatcher分發(fā)器并行分發(fā)我們的Request請(qǐng)求史隆,這里的分發(fā)器實(shí)際是一個(gè)線程池。
3曼验、準(zhǔn)備開始連接服務(wù)器泌射,連接http獲取https服務(wù)器地址,https需要考慮自簽名證書粘姜、雙向SSL驗(yàn)證等安全問題。
4熔酷、Response得到服務(wù)器的回調(diào)孤紧,考慮輸入流關(guān)閉的問題,大文件傳輸?shù)膯栴}拒秘,線程切換問題号显,緩存問題。
數(shù)據(jù)庫的優(yōu)化
重要
數(shù)據(jù)庫性能上:
(1)躺酒、批量事務(wù)插入押蚤,提升數(shù)據(jù)插入的性能。
(2)阴颖、單條sql由于多條sql
(3)活喊、讀和寫操作是互斥的,寫操作過程中可以休眠讓讀操作進(jìn)行量愧。
(4)钾菊、使用索引。
(5)偎肃、使用聯(lián)合索引
(6)煞烫、勿使用過多索引
(7)、增加查詢條件
(8)累颂、提前將字段的index映射好滞详。
數(shù)據(jù)庫設(shè)計(jì)上:
(1)、通過冗余換取查詢速度
(2)紊馏、減少數(shù)據(jù)來提升查詢速度料饥。
(3)、避免大數(shù)據(jù)多表的聯(lián)合查詢朱监。
4岸啡、數(shù)據(jù)庫數(shù)據(jù)遷移問題
(1)、將表A重命名赫编,重命名為A_temp巡蘸。
(2)、創(chuàng)建新表A擂送。
(3)悦荒、將表A_temp中的數(shù)據(jù)插入到新表A中。
(4)嘹吨、刪除表A_temp搬味。
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ā)送廣播。
apk打包流程
一般
aapt工具打包資源文件炕柔,生成R.java文件
aidl工具處理AIDL文件酌泰,生成對(duì)應(yīng)的.java文件
javac工具編譯Java文件,生成對(duì)應(yīng)的.class文件
把.class文件轉(zhuǎn)化成Davik VM支持的.dex文件
apkbuilder工具打包生成未簽名的.apk文件
jarsigner對(duì)未簽名.apk文件進(jìn)行簽名
zipalign工具對(duì)簽名后的.apk文件進(jìn)行對(duì)齊處理
序列化的方式
重要
Serializable是Java提供的一個(gè)序列化接口匕累,是一個(gè)空接口陵刹,用于標(biāo)示對(duì)象是否可以支持序列化,通過ObjectOutputStrean及ObjectInputStream實(shí)現(xiàn)序列化和反序列化的過程欢嘿。注意可以為需要序列化的對(duì)象設(shè)置一個(gè)serialVersionUID衰琐,在反序列化的時(shí)候系統(tǒng)會(huì)檢測(cè)文件中的serialVersionUID是否與當(dāng)前類的值一致,如果不一致則說明類發(fā)生了修改炼蹦,反序列化失敗羡宙。因此對(duì)于可能會(huì)修改的類最好指定serialVersionUID的值。
Parcelable是Android特有的一個(gè)實(shí)現(xiàn)序列化的接口掐隐,在Parcel內(nèi)部包裝了可序列化的數(shù)據(jù)狗热,可以在Binder中自由傳輸。序列化的功能由writeToParcel方法來完成虑省,最終通過Parcel的一系列write方法完成匿刮。反序列化功能由CREAOR來完成,其內(nèi)部標(biāo)明了如何創(chuàng)建序列化對(duì)象和數(shù)組探颈,并通過Parcel的一系列read方法來完成反序列化的過程熟丸。
同步屏障機(jī)制(sync barrier)
一般
同步屏障可以通過MessageQueue.postSyncBarrier函數(shù)來設(shè)置。該方法發(fā)送了一個(gè)沒有target的Message到Queue中膝擂,在next方法中獲取消息時(shí)虑啤,如果發(fā)現(xiàn)沒有target的Message,則在一定的時(shí)間內(nèi)跳過同步消息架馋,優(yōu)先執(zhí)行異步消息狞山。再換句話說,同步屏障為Handler消息機(jī)制增加了一種簡(jiǎn)單的優(yōu)先級(jí)機(jī)制叉寂,異步消息的優(yōu)先級(jí)要高于同步消息萍启。在創(chuàng)建Handler時(shí)有一個(gè)async參數(shù),傳true表示此handler發(fā)送的時(shí)異步消息。ViewRootImpl.scheduleTraversals方法就使用了同步屏障勘纯,保證UI繪制優(yōu)先執(zhí)行局服。
圖片加載框架有哪些?他們之間的區(qū)別是什么驳遵?
重要
ImageLoader :
優(yōu)點(diǎn):
① 支持下載進(jìn)度監(jiān)聽淫奔;
② 可以在 View 滾動(dòng)中暫停圖片加載;
③ 默認(rèn)實(shí)現(xiàn)多種內(nèi)存緩存算法這幾個(gè)圖片緩存都可以配置緩存算法堤结,不過 ImageLoader 默認(rèn)實(shí)現(xiàn)了較多緩存算法唆迁,如 Size 最大先刪除、使用最少先刪除竞穷、最近最少使用唐责、先進(jìn)先刪除、時(shí)間最長(zhǎng)先刪除等瘾带;
④ 支持本地緩存文件名規(guī)則定義鼠哥;
缺點(diǎn):
缺點(diǎn)在于不支持GIF圖片加載, 緩存機(jī)制沒有和http的緩存很好的結(jié)合, 完全是自己的一套緩存機(jī)制
Picasso:
優(yōu)點(diǎn):
① 自帶統(tǒng)計(jì)監(jiān)控功能,支持圖片緩存使用的監(jiān)控看政,包括緩存命中率朴恳、已使用內(nèi)存大小、節(jié)省的流量等帽衙。
② 支持優(yōu)先級(jí)處理
③ 支持延遲到圖片尺寸計(jì)算完成加載
④ 支持飛行模式菜皂、并發(fā)線程數(shù)根據(jù)網(wǎng)絡(luò)類型而變,手機(jī)切換到飛行模式或網(wǎng)絡(luò)類型變換時(shí)會(huì)自動(dòng)調(diào)整線程池最大并發(fā)數(shù)厉萝。
⑤ “無”本地緩存恍飘。Picasso 自己沒有實(shí)現(xiàn)本地緩存,而由okhttp 去實(shí)現(xiàn)谴垫,這樣的好處是可以通過請(qǐng)求 Response Header 中的 Cache-Control 及 Expired 控制圖片的過期時(shí)間章母。
缺點(diǎn):
于不支持GIF,默認(rèn)使用ARGB_8888格式緩存圖片翩剪,緩存體積大乳怎。
Glide:
優(yōu)點(diǎn):
① 圖片緩存->媒體緩存 ,支持 Gif前弯、WebP蚪缀、縮略圖。甚至是 Video恕出。
② 支持優(yōu)先級(jí)處理
③ 與 Activity/Fragment 生命周期一致询枚,支持 trimMemory
④ 支持 okhttp、Volley浙巫。Glide 默認(rèn)通過 UrlConnection 獲取數(shù)據(jù)金蜀,可以配合 okhttp 或是 Volley 使用刷后。實(shí)際 ImageLoader、Picasso 也都支持 okhttp渊抄、Volley尝胆。
⑤ 內(nèi)存友好,內(nèi)存緩存更小圖片护桦,圖片默認(rèn)使用默認(rèn) RGB565 而不是 ARGB888
缺點(diǎn):
清晰度差含衔,但可以設(shè)置
Fresco:
優(yōu)點(diǎn):
① 圖片存儲(chǔ)在安卓系統(tǒng)的匿名共享內(nèi)存, 而不是虛擬機(jī)的堆內(nèi)存中,所以不會(huì)因?yàn)閳D片加載而導(dǎo)致oom, 同時(shí)也減少垃圾回收器頻繁調(diào)用回收Bitmap導(dǎo)致的界面卡頓,性能更高.
② 漸進(jìn)式加載JPEG圖片, 支持圖片從模糊到清晰加載
③ 圖片可以以任意的中心點(diǎn)顯示在ImageView, 而不僅僅是圖片的中心.
④ JPEG圖片改變大小也是在native進(jìn)行的, 不是在虛擬機(jī)的堆內(nèi)存, 同樣減少OOM
⑤ 很好的支持GIF圖片的顯示
缺點(diǎn):
框架較大, 影響Apk體積,使用較繁瑣
平時(shí)開發(fā)中設(shè)計(jì)到哪些性能優(yōu)化嘶炭,你是從哪些地方來優(yōu)化抱慌,你是通過什么工具來分析的?
重要
籠統(tǒng)的說:就是讓App反應(yīng)更快眨猎,使用更穩(wěn),流量强经、電量更省睡陪,apk更小。
具體的說:省電優(yōu)化匿情、內(nèi)存優(yōu)化兰迫、網(wǎng)絡(luò)優(yōu)化、圖片優(yōu)化炬称、UI優(yōu)化汁果。
更快:使用時(shí)避免出現(xiàn)卡頓,響應(yīng)速度快玲躯,減少用戶等待的時(shí)間据德,滿足用戶期望。
UI優(yōu)化:
分析工具:Systrace
(1)減少層級(jí)跷车,合理使用 RelativeLayout 和 LinerLayout棘利,合理使用Merge,Include朽缴。
(2)提高顯示速度善玫,使用 ViewStub,它是一個(gè)看不見的密强、不占布局位置茅郎、占用資源非常小的視圖對(duì)象。
(3)布局復(fù)用或渤,可以通過標(biāo)簽來提高復(fù)用系冗。
(4)盡可能少用wrap_content,wrap_content 會(huì)增加布局 measure 時(shí)計(jì)算成本劳坑,在已知寬高為固定值時(shí)毕谴,不用wrap_content 。
(5)刪除控件中無用的屬性。
更穩(wěn):減低 Crash 率和 ANR 率涝开,不要在用戶使用過程中崩潰和無響應(yīng)循帐。
(1)增加相應(yīng)的判斷,以及異常處理舀武。
(2)避免在主線程做耗時(shí)操作拄养。
更省:節(jié)省流量和耗電银舱,節(jié)約內(nèi)存瘪匿,減少用戶使用成本,避免使用時(shí)導(dǎo)致手機(jī)發(fā)燙寻馏。
耗電分析工具:Battery Historian
(1)避免浮點(diǎn)運(yùn)算棋弥。
(2)根據(jù)客戶端圖片的大小要求叫UI做相應(yīng)大小的圖提供給服務(wù)器,避免過大消耗更多流量和電量诚欠。
(3)不用的廣播顽染,服務(wù)記得及時(shí)關(guān)閉。
內(nèi)存分析工具:Memory Monitor
(1)對(duì)象引用:強(qiáng)引用轰绵、軟引用粉寞、弱引用、虛引用四種引用類型左腔,根據(jù)業(yè)務(wù)需求合理使用不同唧垦,選擇不同的引用類型。
(2)減少不必要的內(nèi)存開銷:注意自動(dòng)裝箱液样,增加內(nèi)存復(fù)用振亮,比如有效利用系統(tǒng)自帶的資源、視圖復(fù)用蓄愁、對(duì)象池双炕、Bitmap對(duì)象的復(fù)用。
(3)使用最優(yōu)的數(shù)據(jù)類型:比如針對(duì)數(shù)據(jù)類容器結(jié)構(gòu)撮抓,可以使用ArrayMap數(shù)據(jù)結(jié)構(gòu)妇斤,避免使用枚舉類型,使用緩存Lrucache等丹拯。
(4)圖片內(nèi)存優(yōu)化:點(diǎn)9圖減少圖片大小以及可以設(shè)置位圖規(guī)格站超,根據(jù)采樣因子做壓縮,用一些圖片緩存方式對(duì)圖片進(jìn)行管理等乖酬。
更兴老唷:安裝包小可以降低用戶的安裝成本。
(1)做混淆優(yōu)化代碼咬像。
(2)刪除無用的代碼及圖片相應(yīng)的本地庫算撮。
(3)Lint優(yōu)化生宛。
(4)zip壓縮。
Xutils, OKhttp, Volley, Retrofit對(duì)比
重要
Xutils這個(gè)框架非常全面肮柜,可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求陷舅,可以進(jìn)行圖片加載處理,可以數(shù)據(jù)儲(chǔ)存审洞,還可以對(duì)view進(jìn)行注解莱睁,使用這個(gè)框架非常方便,但是缺點(diǎn)也是非常明顯的芒澜,使用這個(gè)項(xiàng)目仰剿,會(huì)導(dǎo)致項(xiàng)目對(duì)這個(gè)框架依賴非常的嚴(yán)重,一旦這個(gè)框架出現(xiàn)問題痴晦,那么對(duì)項(xiàng)目來說影響非常大的南吮。、
OKhttp:Android開發(fā)中是可以直接使用現(xiàn)成的api進(jìn)行網(wǎng)絡(luò)請(qǐng)求的阅酪。就是使用HttpClient,HttpUrlConnection進(jìn)行操作旨袒。okhttp針對(duì)Java和Android程序,封裝的一個(gè)高性能的http請(qǐng)求庫术辐,支持同步,異步施无,而且okhttp又封裝了線程池辉词,封裝了數(shù)據(jù)轉(zhuǎn)換,封裝了參數(shù)的使用猾骡,錯(cuò)誤處理等瑞躺。API使用起來更加的方便。但是我們?cè)陧?xiàng)目中使用的時(shí)候仍然需要自己在做一層封裝兴想,這樣才能使用的更加的順手幢哨。
Volley:Volley是Google官方出的一套小而巧的異步請(qǐng)求庫,該框架封裝的擴(kuò)展性很強(qiáng)嫂便,支持HttpClient捞镰、HttpUrlConnection, 甚至支持OkHttp毙替,而且Volley里面也封裝了ImageLoader岸售,所以如果你愿意你甚至不需要使用圖片加載框架,不過這塊功能沒有一些專門的圖片加載框架強(qiáng)大厂画,對(duì)于簡(jiǎn)單的需求可以使用凸丸,稍復(fù)雜點(diǎn)的需求還是需要用到專門的圖片加載框架。Volley也有缺陷袱院,比如不支持post大數(shù)據(jù)屎慢,所以不適合上傳文件瞭稼。不過Volley設(shè)計(jì)的初衷本身也就是為頻繁的、數(shù)據(jù)量小的網(wǎng)絡(luò)請(qǐng)求而生腻惠。
Retrofit:Retrofit是Square公司出品的默認(rèn)基于OkHttp封裝的一套R(shí)ESTful網(wǎng)絡(luò)請(qǐng)求框架环肘,RESTful是目前流行的一套api設(shè)計(jì)的風(fēng)格, 并不是標(biāo)準(zhǔn)妖枚。Retrofit的封裝可以說是很強(qiáng)大廷臼,里面涉及到一堆的設(shè)計(jì)模式,可以通過注解直接配置請(qǐng)求,可以使用不同的http客戶端绝页,雖然默認(rèn)是用http 荠商,可以使用不同Json Converter 來序列化數(shù)據(jù),同時(shí)提供對(duì)RxJava的支持续誉,使用Retrofit + OkHttp + RxJava + Dagger2 可以說是目前比較潮的一套框架莱没,但是需要有比較高的門檻。
Volley VS OkHttp
Volley的優(yōu)勢(shì)在于封裝的更好酷鸦,而使用OkHttp你需要有足夠的能力再進(jìn)行一次封裝饰躲。而OkHttp的優(yōu)勢(shì)在于性能更高,因?yàn)?OkHttp基于NIO和Okio 臼隔,所以性能上要比 Volley更快嘹裂。IO 和 NIO這兩個(gè)都是Java中的概念,如果我從硬盤讀取數(shù)據(jù)摔握,第一種方式就是程序一直等寄狼,數(shù)據(jù)讀完后才能繼續(xù)操作這種是最簡(jiǎn)單的也叫阻塞式IO,還有一種是你讀你的,程序接著往下執(zhí)行,等數(shù)據(jù)處理完你再來通知我氨淌,然后再處理回調(diào)泊愧。而第二種就是 NIO 的方式,非阻塞式盛正, 所以NIO當(dāng)然要比IO的性能要好了,而 Okio是 Square 公司基于IO和NIO基礎(chǔ)上做的一個(gè)更簡(jiǎn)單删咱、高效處理數(shù)據(jù)流的一個(gè)庫。理論上如果Volley和OkHttp對(duì)比的話豪筝,更傾向于使用 Volley痰滋,因?yàn)閂olley內(nèi)部同樣支持使用OkHttp,這點(diǎn)OkHttp的性能優(yōu)勢(shì)就沒了,? 而且 Volley 本身封裝的也更易用壤蚜,擴(kuò)展性更好些即寡。
OkHttp VS Retrofit
毫無疑問,Retrofit 默認(rèn)是基于 OkHttp 而做的封裝袜刷,這點(diǎn)來說沒有可比性聪富,肯定首選 Retrofit。
Volley VS Retrofit
這兩個(gè)庫都做了不錯(cuò)的封裝著蟹,但Retrofit解耦的更徹底,尤其Retrofit2.0出來墩蔓,Jake對(duì)之前1.0設(shè)計(jì)不合理的地方做了大量重構(gòu)梢莽, 職責(zé)更細(xì)分,而且Retrofit默認(rèn)使用OkHttp,性能上也要比Volley占優(yōu)勢(shì)奸披,再有如果你的項(xiàng)目如果采用了RxJava 昏名,那更該使用? Retrofit 。所以這兩個(gè)庫相比阵面,Retrofit更有優(yōu)勢(shì)轻局,在能掌握兩個(gè)框架的前提下該優(yōu)先使用 Retrofit。但是Retrofit門檻要比Volley稍高些样刷,要理解他的原理仑扑,各種用法,想徹底搞明白還是需要花些功夫的置鼻,如果你對(duì)它一知半解镇饮,那還是建議在商業(yè)項(xiàng)目使用Volley吧。
Universal-ImageLoader箕母,Picasso储藐,F(xiàn)resco,Glide對(duì)比
重要
Fresco?是?Facebook?推出的開源圖片緩存工具嘶是,主要特點(diǎn)包括:兩個(gè)內(nèi)存緩存加上 Native 緩存構(gòu)成了三級(jí)緩存钙勃,
優(yōu)點(diǎn):
1. 圖片存儲(chǔ)在安卓系統(tǒng)的匿名共享內(nèi)存, 而不是虛擬機(jī)的堆內(nèi)存中, 圖片的中間緩沖數(shù)據(jù)也存放在本地堆內(nèi)存, 所以, 應(yīng)用程序有更多的內(nèi)存使用, 不會(huì)因?yàn)閳D片加載而導(dǎo)致oom, 同時(shí)也減少垃圾回收器頻繁調(diào)用回收 Bitmap 導(dǎo)致的界面卡頓, 性能更高。
2. 漸進(jìn)式加載 JPEG 圖片, 支持圖片從模糊到清晰加載聂喇。
3. 圖片可以以任意的中心點(diǎn)顯示在 ImageView, 而不僅僅是圖片的中心肺缕。
4. JPEG 圖片改變大小也是在 native 進(jìn)行的, 不是在虛擬機(jī)的堆內(nèi)存, 同樣減少 OOM。
5. 很好的支持 GIF 圖片的顯示授帕。
缺點(diǎn):
1. 框架較大, 影響 Apk 體積
2. 使用較繁瑣
Universal-ImageLoader:(估計(jì)由于HttpClient被Google放棄,作者就放棄維護(hù)這個(gè)框架)
優(yōu)點(diǎn):
1.支持下載進(jìn)度監(jiān)聽
2.可以在 View 滾動(dòng)中暫停圖片加載浮梢,通過 PauseOnScrollListener 接口可以在 View 滾動(dòng)中暫停圖片加載跛十。
3.默認(rèn)實(shí)現(xiàn)多種內(nèi)存緩存算法 這幾個(gè)圖片緩存都可以配置緩存算法,不過 ImageLoader 默認(rèn)實(shí)現(xiàn)了較多緩存算法秕硝,如 Size 最大先刪除芥映、使用最少先刪除、最近最少使用远豺、先進(jìn)先刪除奈偏、時(shí)間最長(zhǎng)先刪除等。
4.支持本地緩存文件名規(guī)則定義
Picasso 優(yōu)點(diǎn)
1. 自帶統(tǒng)計(jì)監(jiān)控功能躯护。支持圖片緩存使用的監(jiān)控惊来,包括緩存命中率、已使用內(nèi)存大小棺滞、節(jié)省的流量等裁蚁。
2.支持優(yōu)先級(jí)處理矢渊。每次任務(wù)調(diào)度前會(huì)選擇優(yōu)先級(jí)高的任務(wù),比如 App 頁面中 Banner 的優(yōu)先級(jí)高于 Icon 時(shí)就很適用枉证。
3.支持延遲到圖片尺寸計(jì)算完成加載
4.支持飛行模式矮男、并發(fā)線程數(shù)根據(jù)網(wǎng)絡(luò)類型而變。 手機(jī)切換到飛行模式或網(wǎng)絡(luò)類型變換時(shí)會(huì)自動(dòng)調(diào)整線程池最大并發(fā)數(shù),比如 wifi 最大并發(fā)為 4,4g 為 3鱼冀,3g 為 2眨唬。? 這里 Picasso 根據(jù)網(wǎng)絡(luò)類型來決定最大并發(fā)數(shù),而不是 CPU 核數(shù)葛碧。
5.“無”本地緩存。無”本地緩存,不是說沒有本地緩存撑螺,而是 Picasso 自己沒有實(shí)現(xiàn),交給了 Square 的另外一個(gè)網(wǎng)絡(luò)庫 okhttp 去實(shí)現(xiàn)崎弃,這樣的好處是可以通過請(qǐng)求 Response Header 中的 Cache-Control 及 Expired 控制圖片的過期時(shí)間甘晤。
?Glide 優(yōu)點(diǎn)
1. 不僅僅可以進(jìn)行圖片緩存還可以緩存媒體文件。Glide 不僅是一個(gè)圖片緩存饲做,它支持 Gif线婚、WebP、縮略圖盆均。甚至是 Video塞弊,所以更該當(dāng)做一個(gè)媒體緩存。
2. 支持優(yōu)先級(jí)處理泪姨。
3. 與 Activity/Fragment 生命周期一致游沿,支持 trimMemory。Glide 對(duì)每個(gè) context 都保持一個(gè) RequestManager肮砾,通過 FragmentTransaction 保持與 Activity/Fragment 生命周期一致诀黍,并且有對(duì)應(yīng)的 trimMemory 接口實(shí)現(xiàn)可供調(diào)用。
4. 支持 okhttp仗处、Volley眯勾。Glide 默認(rèn)通過 UrlConnection 獲取數(shù)據(jù),可以配合 okhttp 或是 Volley 使用婆誓。實(shí)際 ImageLoader吃环、Picasso 也都支持 okhttp、Volley洋幻。
5. 內(nèi)存友好郁轻。Glide 的內(nèi)存緩存有個(gè) active 的設(shè)計(jì),從內(nèi)存緩存中取數(shù)據(jù)時(shí)鞋屈,不像一般的實(shí)現(xiàn)用 get范咨,而是用 remove故觅,再將這個(gè)緩存數(shù)據(jù)放到一個(gè) value 為軟引用的 activeResources map 中,并計(jì)數(shù)引用數(shù)渠啊,在圖片加載完成后進(jìn)行判斷输吏,如果引用計(jì)數(shù)為空則回收掉。內(nèi)存緩存更小圖片替蛉,Glide 以 url贯溅、view_width、view_height躲查、屏幕的分辨率等做為聯(lián)合 key它浅,將處理后的圖片緩存在內(nèi)存緩存中,而不是原始圖片以節(jié)省大小與 Activity/Fragment 生命周期一致镣煮,支持 trimMemory姐霍。圖片默認(rèn)使用默認(rèn) RGB_565 而不是 ARGB_888,雖然清晰度差些典唇,但圖片更小镊折,也可配置到 ARGB_888。
6.Glide 可以通過 signature 或不使用本地緩存支持 url 過期
ANR是什么介衔?怎樣避免和解決ANR
重要
Application Not Responding恨胚,即應(yīng)用無響應(yīng)
出現(xiàn)的原因有三種:
a)KeyDispatchTimeout(5 seconds)主要類型按鍵或觸摸事件在特定時(shí)間內(nèi)無響應(yīng)
b)BroadcastTimeout(10 seconds)BoradcastReceiver在特定的時(shí)間內(nèi)無法處理
c)ServiceTimeout(20 seconds)小概率類型Service在特定的時(shí)間內(nèi)無法處理完成
避免ANR最核心的一點(diǎn)就是在主線程減少耗時(shí)操作。通常需要從那個(gè)以下幾個(gè)方案下手:
a)使用子線程處理耗時(shí)IO操作
b)降低子線程優(yōu)先級(jí)炎咖,使用Thread或者HandlerThread時(shí)赃泡,調(diào)用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設(shè)置優(yōu)先級(jí),否則仍然會(huì)降低程序響應(yīng)乘盼,因?yàn)槟J(rèn)Thread的優(yōu)先級(jí)和主線程相同
c)使用Handler處理子線程結(jié)果升熊,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程
d)Activity的onCreate和onResume回調(diào)中盡量避免耗時(shí)的代碼
e)BroadcastReceiver中onReceiver代碼也要盡量減少耗時(shí)操作,建議使用intentService處理绸栅。intentService是一個(gè)異步的僚碎,會(huì)自動(dòng)停止的服務(wù),很好解決了傳統(tǒng)的Service中處理完耗時(shí)操作忘記停止并銷毀Service的問題
圖片優(yōu)化
重要
(1)對(duì)圖片本身進(jìn)行操作阴幌。盡量不要使用setImageBitmap、setImageResource卷中、BitmapFactory.decodeResource來設(shè)置一張大圖矛双,因?yàn)檫@些方法在完成decode后,
最終都是通過java層的createBitmap來完成的蟆豫,需要消耗更多內(nèi)存.
(2)圖片進(jìn)行縮放的比例议忽,SDK中建議其值是2的指數(shù)值,值越大會(huì)導(dǎo)致圖片不清晰。
(3)不用的圖片記得調(diào)用圖片的recycle()方法
Android內(nèi)存泄露及管理
非常重要
(1)內(nèi)存溢出(OOM)和內(nèi)存泄露(對(duì)象無法被回收)的區(qū)別十减。?
(2)引起內(nèi)存泄露的原因
(3) 內(nèi)存泄露檢測(cè)工具 ------>LeakCanary
內(nèi)存溢出 out of memory:是指程序在申請(qǐng)內(nèi)存時(shí)栈幸,沒有足夠的內(nèi)存空間供其使用愤估,出現(xiàn)out of memory;比如申請(qǐng)了一個(gè)integer,但給它存了long才能存下的數(shù)速址,那就是內(nèi)存溢出玩焰。內(nèi)存溢出通俗的講就是內(nèi)存不夠用。
內(nèi)存泄露 memory leak:是指程序在申請(qǐng)內(nèi)存后芍锚,無法釋放已申請(qǐng)的內(nèi)存空間昔园,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重并炮,無論多少內(nèi)存,遲早會(huì)被占光
內(nèi)存泄露原因:
一默刚、Handler 引起的內(nèi)存泄漏。
解決:將Handler聲明為靜態(tài)內(nèi)部類逃魄,就不會(huì)持有外部類SecondActivity的引用荤西,其生命周期就和外部類無關(guān),
如果Handler里面需要context的話伍俘,可以通過弱引用方式引用外部類
二邪锌、單例模式引起的內(nèi)存泄漏。
解決:Context是ApplicationContext养篓,由于ApplicationContext的生命周期是和app一致的秃流,不會(huì)導(dǎo)致內(nèi)存泄漏
三、非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實(shí)例引起的內(nèi)存泄漏柳弄。
解決:把內(nèi)部類修改為靜態(tài)的就可以避免內(nèi)存泄漏了
四舶胀、非靜態(tài)匿名內(nèi)部類引起的內(nèi)存泄漏。
解決:將匿名內(nèi)部類設(shè)置為靜態(tài)的碧注。
五嚣伐、注冊(cè)/反注冊(cè)未成對(duì)使用引起的內(nèi)存泄漏。
注冊(cè)廣播接受器萍丐、EventBus等轩端,記得解綁。
六逝变、資源對(duì)象沒有關(guān)閉引起的內(nèi)存泄漏基茵。
在這些資源不使用的時(shí)候,記得調(diào)用相應(yīng)的類似close()壳影、destroy()拱层、recycler()、release()等方法釋放宴咧。
七根灯、集合對(duì)象沒有及時(shí)清理引起的內(nèi)存泄漏。
通常會(huì)把一些對(duì)象裝入到集合中,當(dāng)不使用的時(shí)候一定要記得及時(shí)清理集合烙肺,讓相關(guān)對(duì)象不再被引用纳猪。
熱修復(fù)的原理
重要
我們知道Java虛擬機(jī) —— JVM 是加載類的class文件的,而Android虛擬機(jī)——Dalvik/ART VM 是加載類的dex文件桃笙,
而他們加載類的時(shí)候都需要ClassLoader,ClassLoader有一個(gè)子類BaseDexClassLoader氏堤,而BaseDexClassLoader下有一個(gè)
數(shù)組——DexPathList,是用來存放dex文件怎栽,當(dāng)BaseDexClassLoader通過調(diào)用findClass方法時(shí)丽猬,實(shí)際上就是遍歷數(shù)組,
找到相應(yīng)的dex文件熏瞄,找到脚祟,則直接將它return。而熱修復(fù)的解決方法就是將新的dex添加到該集合中强饮,并且是在舊的dex的前面由桌,
所以就會(huì)優(yōu)先被取出來并且return返回。
AIDL理解
重要
AIDL:
每一個(gè)進(jìn)程都有自己的Dalvik VM實(shí)例邮丰,都有自己的一塊獨(dú)立的內(nèi)存行您,都在自己的內(nèi)存上存儲(chǔ)自己的數(shù)據(jù),執(zhí)行著自己的操作剪廉,都在自己的那片狹小的空間里過完自己的一生娃循。而aidl就類似與兩個(gè)進(jìn)程之間的橋梁,使得兩個(gè)進(jìn)程之間可以進(jìn)行數(shù)據(jù)的傳輸斗蒋,跨進(jìn)程通信有多種選擇捌斧,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系統(tǒng)資源比較多泉沾,如果是頻繁的跨進(jìn)程通信的話顯然是不可取的捞蚂;Messenger 進(jìn)行跨進(jìn)程通信時(shí)請(qǐng)求隊(duì)列是同步進(jìn)行的,無法并發(fā)執(zhí)行跷究。
Binde機(jī)制簡(jiǎn)單理解:
在Android系統(tǒng)的Binder機(jī)制中姓迅,是有Client,Service,ServiceManager,Binder驅(qū)動(dòng)程序組成的,其中Client俊马,service丁存,Service Manager運(yùn)行在用戶空間,Binder驅(qū)動(dòng)程序是運(yùn)行在內(nèi)核空間的柴我。而Binder就是把這4種組件粘合在一塊的粘合劑柱嫌,其中核心的組件就是Binder驅(qū)動(dòng)程序,Service Manager提供輔助管理的功能屯换,而Client和Service正是在Binder驅(qū)動(dòng)程序和Service Manager提供的基礎(chǔ)設(shè)施上實(shí)現(xiàn)C/S 之間的通信。其中Binder驅(qū)動(dòng)程序提供設(shè)備文件/dev/binder與用戶控件進(jìn)行交互,
Client彤悔、Service嘉抓,Service Manager通過open和ioctl文件操作相應(yīng)的方法與Binder驅(qū)動(dòng)程序進(jìn)行通信。而Client和Service之間的進(jìn)程間通信是通過Binder驅(qū)動(dòng)程序間接實(shí)現(xiàn)的晕窑。而Binder Manager是一個(gè)守護(hù)進(jìn)程抑片,用來管理Service,并向Client提供查詢Service接口的能力杨赤。
APK打包及安裝流程
重要
打包原理
Android的包文件APK分為兩個(gè)部分:代碼和資源敞斋,所以打包方面也分為資源打包和代碼打包兩個(gè)方面,這篇文章就來分析資源和代碼的編譯打包原理疾牲。
具體說來:
通過AAPT工具進(jìn)行資源文件(包括AndroidManifest.xml植捎、布局文件、各種xml資源等)的打包阳柔,生成R.java文件焰枢。
通過AIDL工具處理AIDL文件,生成相應(yīng)的Java文件舌剂。
通過Javac工具編譯項(xiàng)目源碼济锄,生成Class文件。
通過DX工具將所有的Class文件轉(zhuǎn)換成DEX文件霍转,該過程主要完成Java字節(jié)碼轉(zhuǎn)換成Dalvik字節(jié)碼荐绝,壓縮常量池以及清除冗余信息等工作。
通過ApkBuilder工具將資源文件避消、DEX文件打包生成APK文件低滩。
利用KeyStore對(duì)生成的APK文件進(jìn)行簽名。
如果是正式版的APK沾谓,還會(huì)利用ZipAlign工具進(jìn)行對(duì)齊處理委造,對(duì)齊的過程就是將APK文件中所有的資源文件舉例文件的起始距離都偏移4字節(jié)的整數(shù)倍,這樣通過內(nèi)存映射訪問APK文件的速度會(huì)更快均驶。
3.2昏兆、安裝流程
Android 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ā)送廣播赞警。
可以使用下面的圖表示: