一奠旺, 背景
入行android已近三年蜘澜,做過(guò)的項(xiàng)目不多,但是也算是國(guó)內(nèi)知名app响疚。針對(duì)于此鄙信,是時(shí)候總結(jié)一下android的由淺入深的知識(shí)點(diǎn),同時(shí)也加深自己對(duì)以往知識(shí)的回顧忿晕。
二装诡,技術(shù)點(diǎn)介紹
Android的四大組件和生命周期
每一個(gè)android的開(kāi)發(fā)成員一開(kāi)始必須要知道的四大組件分別是:
Activity:一個(gè)暴漏給開(kāi)發(fā)最基本的單元,通常是一個(gè)可見(jiàn)的界面践盼,也有可能是浮在界面上的一個(gè)dialog鸦采。跳轉(zhuǎn)和通信都是基于intent進(jìn)行的。其中個(gè)人覺(jué)得對(duì)此要掌握的是四種啟動(dòng)模式和生命周期:
下面是這四種模式的作用:
(1)standard
默認(rèn)模式咕幻,可以不用寫(xiě)配置渔伯。在這個(gè)模式下,都會(huì)默認(rèn)創(chuàng)建一個(gè)新的實(shí)例谅河。因此咱旱,在這種模式下确丢,可以有多個(gè)相同的實(shí)例,也允許多個(gè)相同Activity疊加吐限。
(2)singleTop
可以有多個(gè)實(shí)例鲜侥,但是不允許多個(gè)相同Activity疊加。即诸典,如果Activity在棧頂?shù)臅r(shí)候描函,啟動(dòng)相同的Activity,不會(huì)創(chuàng)建新的實(shí)例狐粱,而會(huì)調(diào)用其onNewIntent方法舀寓。
(3)singleTask
只有一個(gè)實(shí)例。在同一個(gè)應(yīng)用程序中啟動(dòng)他的時(shí)候肌蜻,若Activity不存在互墓,則會(huì)在當(dāng)前task創(chuàng)建一個(gè)新的實(shí)例,若存在蒋搜,則會(huì)把task中在其之上的其它Activity destory掉并調(diào)用它的onNewIntent方法篡撵。
如果是在別的應(yīng)用程序中啟動(dòng)它,則會(huì)新建一個(gè)task豆挽,并在該task中啟動(dòng)這個(gè)Activity育谬,singleTask允許別的Activity與其在一個(gè)task中共存,也就是說(shuō)帮哈,如果我在這個(gè)singleTask的實(shí)例中再打開(kāi)新的Activity膛檀,這個(gè)新的Activity還是會(huì)在singleTask的實(shí)例的task中。
生命周期:
Service:相對(duì)于Activity娘侍,如果說(shuō)Activity是在人前咖刃,那么service就是在人后。顧名思義他是一種服務(wù)私蕾。一般來(lái)說(shuō)他是用來(lái)操作一些耗時(shí)的任務(wù)僵缺,或者是執(zhí)行長(zhǎng)期運(yùn)行的任務(wù)。
啟動(dòng)service的方法有兩種踩叭,startService和bindservice磕潮。
(1)startService
執(zhí)行startService時(shí),Service會(huì)經(jīng)歷onCreate->onStartCommand容贝。當(dāng)執(zhí)行stopService時(shí)自脯,直接調(diào)用onDestroy方法。調(diào)用者如果沒(méi)有stopService斤富,Service會(huì)一直在后臺(tái)運(yùn)行膏潮,下次調(diào)用者再起來(lái)仍然可以stopService。
(2)bindService
執(zhí)行bindService時(shí)满力,Service會(huì)經(jīng)歷onCreate->onBind焕参。這個(gè)時(shí)候調(diào)用者和Service綁定在一起轻纪。調(diào)用者調(diào)用unbindService方法或者調(diào)用者Context不存在了(如Activity被finish了),Service就會(huì)調(diào)用onUnbind->onDestroy叠纷。這里所謂的綁定在一起就是說(shuō)兩者共存亡了刻帚。
(3)Service和Activity是如何通信的
通常的用法就是在onStartCommand方法里執(zhí)行任務(wù),這也是Activity和service最直接的通信了涩嚣。比如以下的代碼崇众。
通過(guò)這種運(yùn)用,感覺(jué)他倆和陌生啊航厚,就是通知以下服務(wù)你該執(zhí)行了顷歌。難道服務(wù)就不該主動(dòng)撩一下Activity嗎?當(dāng)然可以幔睬,onBind不是吃素的眯漩。這個(gè)方法里返回一個(gè)IBinder接口,這個(gè)里面我們可以做很多事情溪窒,但是如果用到這個(gè)坤塞,啟動(dòng)對(duì)應(yīng)的service方法是onBindservice。
@Override
publicIBinder onBind(Intent intent) {
mServiceInUse=true;
returnmBinder;
}
ContentProvider:為存儲(chǔ)和獲取數(shù)據(jù)提供統(tǒng)一的接口澈蚌,可以在不同應(yīng)用之間共享數(shù)據(jù)。比如訪(fǎng)問(wèn)電話(huà)通訊錄灼狰。每一個(gè)ContentProvider都有一個(gè)uri給其提供數(shù)據(jù)宛瞄,可以進(jìn)行增刪改除的操作。
BroadcastReiver:廣播的注冊(cè)方式分兩種靜態(tài)(在manifest里面靜態(tài)注冊(cè))和動(dòng)態(tài)(在代碼里注冊(cè))
需要注意的是在onReceive里面執(zhí)行的操作不能超過(guò)五秒交胚,否則會(huì)ANR份汗,換句話(huà)說(shuō)這里面不要進(jìn)行復(fù)雜的操作。
Android的Framework和通信機(jī)制
首先放一張大家的熟知的Framework結(jié)構(gòu)圖:
依次為:Applicaiton--framework--library--linux kernal. 個(gè)人喜歡依次稱(chēng)之為app, api,jni以及binder驅(qū)動(dòng)蝴簇。
消息機(jī)制
消息的存在就是就是為了子線(xiàn)程和主線(xiàn)程的通信杯活。android界面的刷新只能放在主線(xiàn)程,而耗時(shí)的操作我們只能放在子線(xiàn)程熬词。消息就是兩者之間的傳話(huà)者旁钧,至于消息內(nèi)部是怎樣實(shí)現(xiàn)的。大致的流程如下
說(shuō)明:Handler必須要在主線(xiàn)程生命互拾,實(shí)際上handler是關(guān)聯(lián)了messagueue歪今,looper在messagequeque里面取消息關(guān)聯(lián)一個(gè)threadlocal線(xiàn)程,說(shuō)白了就是hander在messagequeque取消息交給looper出來(lái)的子線(xiàn)程threadlocal去處理颜矿。
注意:hanlder使用不當(dāng)會(huì)產(chǎn)生內(nèi)存泄漏寄猩。一般發(fā)生在非靜態(tài)內(nèi)部類(lèi)。
關(guān)于他們只見(jiàn)的關(guān)系骑疆,可以如下圖表示:
參考文獻(xiàn):http://www.cnblogs.com/angeldevil/p/3340644.html
JNI淺談
通過(guò)上面Android的框架圖可以看出田篇,application和framework層都是java編寫(xiě)的替废,而library和linux內(nèi)核都是C或者C++編寫(xiě)的。上層的java如何和下層的c聯(lián)系起來(lái)泊柬,jni就是兩者關(guān)聯(lián)的橋梁舶担。
關(guān)于如何寫(xiě)一個(gè)jni的例子,網(wǎng)上有很多彬呻。下面我就總結(jié)一下:
java 調(diào)用jni衣陶,一般是先加載一個(gè)jni的so庫(kù)。例如:
System.loadLibrary("test.so");
然后就是定義和jni里面匹配的方法闸氮,記得要加關(guān)鍵字:Native剪况。例如:
private Native void testJava();
jni也可以調(diào)用java(通常說(shuō)的反射):
jclass clazz = (*env)->FindClass(env, "包名/MainActivity");
//jmethodID? (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID methodID = (*env)->GetMethodID(env, clazz, "方法名", "(Ljava/lang/String;)V");
//void? ? ? ? (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
SDK的開(kāi)發(fā)與應(yīng)用
sdk全程Software Development Kit。軟件開(kāi)發(fā)的工具包蒲跨。簡(jiǎn)單的來(lái)講他就像是一個(gè)api接口的大整合译断,也包括一些文檔等。
在這里不羅嗦了或悲,有i經(jīng)驗(yàn)的人都知道自己的sdk開(kāi)發(fā)需要注意什么孙咪。
View的渲染機(jī)制
看完這張圖,至少知道了android對(duì)view的渲染都是誰(shuí)在主事巡语。大家都知道我們?nèi)搜哿鲿车漠?huà)面是每秒60幀翎蹈。換句話(huà)說(shuō)小于等于60幀的界面對(duì)我們而言都是流暢的,否則就會(huì)出現(xiàn)卡頓男公。android沒(méi)隔16ms就會(huì)觸發(fā)一次ui的渲染荤堪。
說(shuō)到這,暫且不去細(xì)說(shuō)android view的繪制流程枢赔,onmesure onlayout和ondraw的聯(lián)系澄阳,個(gè)人覺(jué)得分析一下有關(guān)ui方面的優(yōu)化來(lái)的更重要。
了解到以上的情況踏拜,我們首先要保證fps是小于60的碎赢。這就要從幾方面入手:
第一,減少不必要的層級(jí)速梗,避免增加GPU的工作量導(dǎo)致過(guò)度繪制肮塞。我們可以打開(kāi)手機(jī)的GPU繪制看哪個(gè)布局出現(xiàn)過(guò)渡繪制的情況。也可以用Hierarchy Viewer工具镀琉。
第二峦嗤,圖片的正確使用,使用png代替jpg圖片
第三屋摔,清理不必要的背景烁设,當(dāng)實(shí)在需要時(shí)可以設(shè)置透明。
第四,如果是自定義的view装黑,記得優(yōu)化自己自定義的代碼副瀑。
動(dòng)畫(huà)機(jī)制
這個(gè)我已經(jīng)專(zhuān)門(mén)寫(xiě)過(guò)一篇文檔:http://www.reibang.com/writer#/notebooks/10152985/notes/9503294
常用的架構(gòu)開(kāi)發(fā)架構(gòu)模式
1. MVC
可以看出是一個(gè)單向的,view有數(shù)據(jù)請(qǐng)求要先通知c恋谭,C告訴model糠睡。model再給view反饋。
不足:
(1)增加了系統(tǒng)結(jié)構(gòu)和實(shí)現(xiàn)的復(fù)雜性疚颊。對(duì)于簡(jiǎn)單的界面狈孔,嚴(yán)格遵循MVC,使模型材义、視圖與控制器分離均抽,會(huì)增加結(jié)構(gòu)的復(fù)雜性,并可能產(chǎn)生過(guò)多的更新操作其掂,降低運(yùn)行效率油挥。
(2)視圖與控制器間的過(guò)于緊密的連接。視圖與控制器是相互分離款熬,但確實(shí)聯(lián)系緊密的部件深寥,視圖沒(méi)有控制器的存在,其應(yīng)用是很有限的贤牛,反之亦然惋鹅,這樣就妨礙了他們的獨(dú)立重用。
(3)視圖對(duì)模型數(shù)據(jù)的低效率訪(fǎng)問(wèn)盔夜。依據(jù)模型操作接口的不同负饲,視圖可能需要多次調(diào)用才能獲得足夠的顯示數(shù)據(jù)。對(duì)未變化數(shù)據(jù)的不必要的頻繁訪(fǎng)問(wèn)喂链,也將損害操作性能。
(4) 目前妥泉,一般高級(jí)的界面工具或構(gòu)造器不支持MVC架構(gòu)椭微。改造這些工具以適應(yīng)MVC需要和建立分離的部件的代價(jià)是很高的,從而造成使用MVC的困難盲链。
2. MVP
通過(guò)上圖可以看出:
1. 各部分之間的通信蝇率,都是雙向的。
2. View 與 Model 不發(fā)生聯(lián)系刽沾,都通過(guò) Presenter 傳遞本慕。
3. View 非常薄,不部署任何業(yè)務(wù)邏輯侧漓,稱(chēng)為"被動(dòng)視圖"(Passive View)锅尘,即沒(méi)有任何主動(dòng)性,而 Presenter非常厚布蔗,所有邏輯都部署在那里藤违。
3. MVVM
MVVM 模式將 Presenter 改名為 ViewModel浪腐,基本上與 MVP 模式完全一致。
和MVP唯一的區(qū)別是顿乒,它采用雙向綁定(data-binding):View的變動(dòng)议街,自動(dòng)反映在 ViewModel,反之亦然璧榄。Angular 和 Ember 都采用這種模式
多線(xiàn)程和線(xiàn)程池
(1)Android多線(xiàn)程處理
AsyncTask:為 UI 線(xiàn)程與工作線(xiàn)程之間進(jìn)行快速的切換提供一種簡(jiǎn)單便捷的機(jī)制特漩。適用于當(dāng)下立即需要啟動(dòng),但是異步執(zhí)行的生命周期短暫的使用場(chǎng)景骨杂。
HandlerThread:為某些回調(diào)方法或者等待某些任務(wù)的執(zhí)行設(shè)置一個(gè)專(zhuān)屬的線(xiàn)程涂身,并提供線(xiàn)程任務(wù)的調(diào)度機(jī)制。
ThreadPool:把任務(wù)分解成不同的單元腊脱,分發(fā)到各個(gè)不同的線(xiàn)程上访得,進(jìn)行同時(shí)并發(fā)處理。
IntentService:適合于執(zhí)行由 UI 觸發(fā)的后臺(tái) Service 任務(wù)陕凹,并可以把后臺(tái)任務(wù)執(zhí)行的情況通過(guò)一定的機(jī)制反饋給 UI悍抑。
雖然android提供了以上四種應(yīng)付并發(fā)處理的方案,但是在什么時(shí)候選擇什么樣的方式很重要杜耙,稍有不慎就有可能oom搜骡。這里我不能一一詳細(xì)講解,但是我們需要知道一點(diǎn)佑女,不管那種處理方式其最終的處理原理其實(shí)是交給了Handle记靡,meesagequeue,looper這三個(gè)家伙。通過(guò)打開(kāi)我們常用的AsyncTask源碼便可知轿亮,畢竟我們只是用喧笔,一些繁瑣的任務(wù)處理,執(zhí)行還是以上三個(gè)家伙協(xié)助完成的寸痢。
有關(guān)詳細(xì)介紹,參考大神的文:http://www.cnblogs.com/bugly/p/5519510.html
http://blog.csdn.net/github_33304260/article/details/70213300?
(2)線(xiàn)程池
顯而易見(jiàn)紊选,線(xiàn)程池是為線(xiàn)程服務(wù)的啼止。首先看一個(gè)常用的例子:
new Thread(new Runnable() {
@Override
public void run() {
//do sth .
}
}).start();
我們經(jīng)常會(huì)這樣使用,還覺(jué)得妙不可言兵罢,但是過(guò)多頻繁的使用就會(huì)引發(fā)以下問(wèn)題:
1献烦、線(xiàn)程的創(chuàng)建和銷(xiāo)毀都需要時(shí)間,當(dāng)有大量的線(xiàn)程創(chuàng)建和銷(xiāo)毀時(shí)卖词,那么這些時(shí)間的消耗則比較明顯巩那,將導(dǎo)致性能上的缺失
2、大量的線(xiàn)程創(chuàng)建、執(zhí)行和銷(xiāo)毀是非常耗cpu和內(nèi)存的拢操,這樣將直接影響系統(tǒng)的吞吐量锦亦,導(dǎo)致性能急劇下降,如果內(nèi)存資源占用的比較多令境,還很可能造成OOM
3杠园、大量的線(xiàn)程的創(chuàng)建和銷(xiāo)毀很容易導(dǎo)致GC頻繁的執(zhí)行,從而發(fā)生內(nèi)存抖動(dòng)現(xiàn)象舔庶,而發(fā)生了內(nèi)存抖動(dòng)抛蚁,對(duì)于移動(dòng)端來(lái)說(shuō),最大的影響就是造成界面卡頓
為了解決以上問(wèn)題而又可以這樣使用惕橙,那么線(xiàn)程池應(yīng)運(yùn)而生了瞧甩。他的出現(xiàn)像是誕生了一位leader,要統(tǒng)一管理這些隨心所欲的家伙了弥鹦。他帶來(lái)的好處如下:
1肚逸、線(xiàn)程的創(chuàng)建和銷(xiāo)毀由線(xiàn)程池維護(hù),一個(gè)線(xiàn)程在完成任務(wù)后并不會(huì)立即銷(xiāo)毀彬坏,而是由后續(xù)的任務(wù)復(fù)用這個(gè)線(xiàn)程朦促,從而減少線(xiàn)程的創(chuàng)建和銷(xiāo)毀,節(jié)約系統(tǒng)的開(kāi)銷(xiāo)
2栓始、線(xiàn)程池旨在線(xiàn)程的復(fù)用务冕,這就可以節(jié)約我們用以往的方式創(chuàng)建線(xiàn)程和銷(xiāo)毀所消耗的時(shí)間,減少線(xiàn)程頻繁調(diào)度的開(kāi)銷(xiāo)幻赚,從而節(jié)約系統(tǒng)資源禀忆,提高系統(tǒng)吞吐量
3、在執(zhí)行大量異步任務(wù)時(shí)提高了性能
4落恼、Java內(nèi)置的一套ExecutorService線(xiàn)程池相關(guān)的api箩退,可以更方便的控制線(xiàn)程的最大并發(fā)數(shù)、線(xiàn)程的定時(shí)任務(wù)佳谦、單線(xiàn)程的順序執(zhí)行等
同時(shí)他管理有方乏德,把線(xiàn)程歸類(lèi),總有一款適合你的‘池子’:
1吠昭、newFixedThreadPool() :
作用:該方法返回一個(gè)固定線(xiàn)程數(shù)量的線(xiàn)程池,該線(xiàn)程池中的線(xiàn)程數(shù)量始終不變胧瓜,即不會(huì)再創(chuàng)建新的線(xiàn)程矢棚,也不會(huì)銷(xiāo)毀已經(jīng)創(chuàng)建好的線(xiàn)程,自始自終都是那幾個(gè)固定的線(xiàn)程在工作府喳,所以該線(xiàn)程池可以控制線(xiàn)程的最大并發(fā)數(shù)蒲肋。
假如有一個(gè)新任務(wù)提交時(shí),線(xiàn)程池中如果有空閑的線(xiàn)程則立即使用空閑線(xiàn)程來(lái)處理任務(wù),如果沒(méi)有兜粘,則會(huì)把這個(gè)新任務(wù)存在一個(gè)任務(wù)隊(duì)列中申窘,一旦有線(xiàn)程空閑了,則按FIFO方式處理任務(wù)隊(duì)列中的任務(wù)孔轴。
2剃法、newCachedThreadPool() :
作用:該方法返回一個(gè)可以根據(jù)實(shí)際情況調(diào)整線(xiàn)程池中線(xiàn)程的數(shù)量的線(xiàn)程池。即該線(xiàn)程池中的線(xiàn)程數(shù)量不確定路鹰,是根據(jù)實(shí)際情況動(dòng)態(tài)調(diào)整的贷洲。
假如該線(xiàn)程池中的所有線(xiàn)程都正在工作,而此時(shí)有新任務(wù)提交晋柱,那么將會(huì)創(chuàng)建新的線(xiàn)程去處理該任務(wù)优构,而此時(shí)假如之前有一些線(xiàn)程完成了任務(wù),現(xiàn)在又有新任務(wù)提交雁竞,那么將不會(huì)創(chuàng)建新線(xiàn)程去處理钦椭,而是復(fù)用空閑的線(xiàn)程去處理新任務(wù)。那么此時(shí)有人有疑問(wèn)了碑诉,那這樣來(lái)說(shuō)該線(xiàn)程池的線(xiàn)程豈不是會(huì)越集越多彪腔?其實(shí)并不會(huì),因?yàn)榫€(xiàn)程池中的線(xiàn)程都有一個(gè)“保持活動(dòng)時(shí)間”的參數(shù)联贩,通過(guò)配置它漫仆,如果線(xiàn)程池中的空閑線(xiàn)程的空閑時(shí)間超過(guò)該“保存活動(dòng)時(shí)間”則立刻停止該線(xiàn)程,而該線(xiàn)程池默認(rèn)的“保持活動(dòng)時(shí)間”為60s泪幌。
3盲厌、newSingleThreadExecutor() :
作用:該方法返回一個(gè)只有一個(gè)線(xiàn)程的線(xiàn)程池,即每次只能執(zhí)行一個(gè)線(xiàn)程任務(wù)祸泪,多余的任務(wù)會(huì)保存到一個(gè)任務(wù)隊(duì)列中吗浩,等待這一個(gè)線(xiàn)程空閑,當(dāng)這個(gè)線(xiàn)程空閑了再按FIFO方式順序執(zhí)行任務(wù)隊(duì)列中的任務(wù)没隘。
4懂扼、newScheduledThreadPool() :
作用:該方法返回一個(gè)可以控制線(xiàn)程池內(nèi)線(xiàn)程定時(shí)或周期性執(zhí)行某任務(wù)的線(xiàn)程池。
5右蒲、newSingleThreadScheduledExecutor() :
作用:該方法返回一個(gè)可以控制線(xiàn)程池內(nèi)線(xiàn)程定時(shí)或周期性執(zhí)行某任務(wù)的線(xiàn)程池阀湿。只不過(guò)和上面的區(qū)別是該線(xiàn)程池大小為1,而上面的可以指定線(xiàn)程池的大小瑰妄。
參考文獻(xiàn):http://blog.csdn.net/u010687392/article/details/49850803
常用的圖片加載模式
(1)Glide && Picasso
之所以把這兩個(gè)拿出來(lái)一起說(shuō)事陷嘴,是因?yàn)閮烧叩氖褂梅绞椒浅O嘞瘛@缂虞d一張圖片:
Glide.with(getContext())
.load(url)
.placeholder(Drawables.sPlaceholderDrawable) //初始化顯示
.error(Drawables.sErrorDrawable)? //加載失誤顯示
.into(mImageView);
Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);
相同點(diǎn):通過(guò)以上例子看起來(lái)用法簡(jiǎn)直一毛一樣间坐。用法簡(jiǎn)單灾挨,體積小邑退。
不同點(diǎn):picasso不支持縮略圖,不支持gif劳澄,加載速度一般地技。但是體積小。Glide支持這兩種秒拔,且加載速度高于picasso莫矗。
參考文獻(xiàn):http://blog.csdn.net/qq_25690935/article/details/50548457
(2)Fresco
Pipeline
它負(fù)責(zé)從網(wǎng)絡(luò),從本地文件系統(tǒng)溯警,本地資源加載圖片趣苏。為了最大限度節(jié)省空間和CPU時(shí)間,它含有3級(jí)緩存設(shè)計(jì)(2級(jí)內(nèi)存梯轻,1級(jí)磁盤(pán))
內(nèi)存管理
在5.0以下系統(tǒng)食磕,F(xiàn)resco將圖片放到一個(gè)特別的內(nèi)存區(qū)域,在圖片不顯示的時(shí)候喳挑,占用的內(nèi)存會(huì)自動(dòng)被釋放彬伦。這會(huì)使得APP更加流暢,減少因圖片內(nèi)存占用而引發(fā)的OOM伊诵。
動(dòng)圖加載(加載Gif和Webp)
圖片加載
Fresco的Image Pipeline允許你用很多種方式來(lái)自定義圖片加載過(guò)程单绑,比如:
為同一個(gè)圖片指定不同的遠(yuǎn)程路徑,或者使用已經(jīng)存在本地緩存中的圖片
先顯示一個(gè)低清晰度的圖片曹宴,等高清圖下載完之后再顯示高清圖加載完成回調(diào)通知
對(duì)于本地圖搂橙,如有EXIF縮略圖,在大圖加載完成之前笛坦,可先顯示縮略圖縮放或者旋轉(zhuǎn)圖片對(duì)已下載的圖片再次處理区转,支持WebP解碼
圖片漸進(jìn)式呈現(xiàn)
Android 本身的圖片庫(kù)不支持此格式,但是Fresco支持版扩。使用時(shí)僅需要提供一個(gè)圖片的URI即可废离,剩下的事情,F(xiàn)resco會(huì)處理礁芦。
Drawable
Fresco 中設(shè)計(jì)有一個(gè)叫做 Drawees 模塊蜻韭,它會(huì)在圖片加載完成前顯示占位圖,加載成功后自動(dòng)替換為目標(biāo)圖片柿扣。當(dāng)圖片不再顯示在屏幕上時(shí)肖方,它會(huì)及時(shí)地釋放內(nèi)存和空間占用
參考文獻(xiàn):https://www.fresco-cn.org/
插件化的運(yùn)用
插件化曾經(jīng)在app開(kāi)發(fā)風(fēng)靡一時(shí),包括現(xiàn)在也很多項(xiàng)目都在使用未状,很多人都認(rèn)為插件化的誕生是為了解決65535的問(wèn)題窥妇,而實(shí)際上他的好處并不僅僅只有這一處。
宿主和插件分開(kāi)編譯
并發(fā)開(kāi)發(fā)
動(dòng)態(tài)更新插件
按需下載模塊
方法數(shù)或變量數(shù)爆棚
開(kāi)源的插件化框架
Qihoo360/DroidPlugin
CtripMobile/DynamicAPK
mmin18/AndroidDynamicLoader
singwhatiwanna/dynamic-load-apk
houkx/android-pluginmgr
bunnyblue/ACDD
wequick/Small
參考文獻(xiàn):http://www.reibang.com/p/353514d315a7
熱補(bǔ)丁
和上面說(shuō)講到的插件化的部分原理很像娩践。先看一下android的類(lèi)加載:
我們常用的DExCalssLoader和PathClassLoader最終還是會(huì)走到classloader里面去活翩,通過(guò)對(duì)calssloader的解讀,我們找到關(guān)鍵的兩個(gè)東西:dexElements數(shù)據(jù)和findclass方法:
public Class findClass(String name, Listsuppressed) {
//遍歷該數(shù)組
for (Element element : dexElements) {
//初始化DexFile
DexFile dex = element.dexFile;
if (dex != null) {
//調(diào)用DexFile類(lèi)的loadClassBinaryName方法返回Class實(shí)例
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
return clazz;
}
}
}
return null;
}
因此翻伺,講白一點(diǎn)材泄,我們所謂的熱修復(fù)就是偷梁換柱,把我們的dex文件放在最前面吨岭。
性能優(yōu)化
這個(gè)話(huà)題基本上是每個(gè)技術(shù)人員長(zhǎng)掛在嘴邊的問(wèn)題拉宗,但是真正做起來(lái)并非那么的容易,有余android系統(tǒng)本身具備某些因素導(dǎo)致我們使用不當(dāng)就會(huì)造成性能問(wèn)題辣辫。比如常見(jiàn)的問(wèn)題oom旦事。
Android內(nèi)存本身面臨的問(wèn)題:
1.有限的堆內(nèi)存,原始只有16M
2.內(nèi)存大小消耗等根據(jù)設(shè)備急灭,操作系統(tǒng)等級(jí)姐浮,屏幕尺寸的不同而不同
3.程序不能直接控制
4.支持后臺(tái)多任務(wù)處理(multitasking)
5.運(yùn)行在虛擬機(jī)之上
針對(duì)以上問(wèn)題,我們都知道5r原則葬馋,也盡可能的去做到卖鲤,何謂5r?
答曰:
1.Reckon(計(jì)算)
首先需要知道你的app所消耗內(nèi)存的情況畴嘶,知己知彼才能百戰(zhàn)不殆
2.Reduce(減少)
消耗更少的資源
3.Reuse(重用)
當(dāng)?shù)谝淮问褂猛暌院蟮坝猓M量給其他的使用
4.Review(檢查)
5.Recycle(回收)
返回資源給生產(chǎn)流
有關(guān)5r的東西可以說(shuō)是泛泛之談,每個(gè)人真正理解使用的都不一樣窗悯,就個(gè)人經(jīng)驗(yàn)而言区匣,我們想要提高性能,從兩方面入手蒋院,看的見(jiàn)的和看不見(jiàn)的亏钩。
看的見(jiàn)的就是呈現(xiàn)眼前的ui,看不見(jiàn)的就是關(guān)系到整個(gè)app生死存亡的內(nèi)存悦污。
對(duì)于UI铸屉,上面講道的ui渲染,我們大概清楚了切端,避免過(guò)度繪制彻坛。另外就是我們常見(jiàn)的oom很多都是發(fā)生在bitmap上,圖片處理一直是個(gè)大頭踏枣,我們最好采用fresco昌屉,畢竟他本身對(duì)性能做了處理,同時(shí)我們也要做到手動(dòng)回收茵瀑,捕獲異常间驮,盡可能的用若引用代替強(qiáng)引用。合理利用緩存機(jī)制马昨。
另外就是看不見(jiàn)的內(nèi)存分配竞帽。在這里我只說(shuō)兩塊扛施,棧(stack)和堆(heap),熟悉的人都知道屹篓,前者是程序控制不了疙渣,后者就是我們能控制的,經(jīng)常會(huì)通過(guò)new或者malloc去分配空間堆巧。所以妄荔,由我們控制的東西就有可能出現(xiàn)問(wèn)題,為此我們要格外小心谍肤。之所以會(huì)出現(xiàn)oom啦租,是因?yàn)閍ndroid分給heap的大小是固定一般是16m,如果java申請(qǐng)內(nèi)存超過(guò)所分配的閾值就會(huì)出現(xiàn)oom荒揣。
網(wǎng)絡(luò)加載模式
android的通信都是基于http的協(xié)議篷角。內(nèi)部http協(xié)議的通信主要有兩種方式,HttpUrlConnection和HttpClient乳附。為了更好的處理網(wǎng)絡(luò)的異步加載提高性能内地,現(xiàn)在比較常用的網(wǎng)絡(luò)加載框架如下:
(1)Okhttp:和Vollery一樣OkHttp也是一款HTTP框架,它支持get請(qǐng)求和post請(qǐng)求赋除,支持基于Http的文件上傳和下載阱缓,支持加載圖片,支持下載文件透明的GZIP壓縮举农,支持響應(yīng)緩存避免重復(fù)的網(wǎng)絡(luò)請(qǐng)求荆针,支持使用連接池來(lái)降低響應(yīng)延遲問(wèn)題。
由于目前我自己的項(xiàng)目使用的也是此框架颁糟,所以對(duì)此多說(shuō)一點(diǎn)航背。首先我們需要大概知道他的原理:
上圖是一個(gè)很簡(jiǎn)單的流程,任何網(wǎng)絡(luò)請(qǐng)求無(wú)外乎就是基于http協(xié)議完成對(duì)數(shù)據(jù)的訪(fǎng)問(wèn)棱貌。更深一層次來(lái)說(shuō)是基于TCP/IP協(xié)議玖媚,至于哪個(gè)更牛逼就看訪(fǎng)問(wèn)速度,高效性婚脱,容錯(cuò)性了今魔。
okhttp在請(qǐng)求的時(shí)候會(huì)有一個(gè)engine,這個(gè)engine就像是一個(gè)管理站障贸,他控制著需要那種方式的請(qǐng)求错森,比如是異步還是同步。在connetion的時(shí)候篮洁,就是真正建立連接的時(shí)候涩维,有自己的線(xiàn)程池機(jī)制,更選擇更優(yōu)化的處理方式袁波。而真正達(dá)到服務(wù)端的時(shí)候瓦阐,會(huì)先經(jīng)過(guò)路由蜗侈,路由其實(shí)就是一個(gè)注冊(cè)表(個(gè)人理解)是一套協(xié)議,比如代理垄分,IP地址等宛篇,選擇正確的協(xié)議去打開(kāi)server的大門(mén)。下面是請(qǐng)求的流程:
用法:
一般來(lái)說(shuō)項(xiàng)目中的使用薄湿,也不會(huì)直接使用,還是會(huì)此進(jìn)行再次封裝偷卧。比如:
真正使用很簡(jiǎn)單:
(2)Vollery:高并發(fā)的處理方式豺瘤,對(duì)http協(xié)議進(jìn)行了很好的封裝,用法簡(jiǎn)單听诸,但是對(duì)于大數(shù)據(jù)的訪(fǎng)問(wèn)坐求,比如大圖片的下載,視頻的下載等就會(huì)表現(xiàn)的很糟糕晌梨。換句話(huà)說(shuō)他雖然在性能上進(jìn)行了優(yōu)化桥嗤,但是只是適合對(duì)小數(shù)據(jù)量的操作。
每一種網(wǎng)絡(luò)請(qǐng)求仔蝌,我們都真正關(guān)心的都是response返回的數(shù)據(jù)泛领,為此我們需要掌握兩個(gè)常見(jiàn)的數(shù)據(jù)類(lèi)型。
A.StringRequest
StringRequest stringRequest =newStringRequest("http://www.baidu.com",
newResponse.Listener()?{
@Override
publicvoidonResponse(String?response)?{
// TODO your task
}
},newResponse.ErrorListener()?{
@Override
publicvoidonErrorResponse(VolleyError?error)?{
// returned error message
}
});
B.JsonRequest
由于JsonRequest是一個(gè)抽象類(lèi)無(wú)法直接創(chuàng)建它的實(shí)例.但是天無(wú)絕人之路敛惊,他有JsonRequest有JsonObjectRequest和JsonArrayRequest兩個(gè)子類(lèi)渊鞋。
JsonObjectRequest jsonObjectRequest =newJsonObjectRequest("http://www.baidu.com",null,
newResponse.Listener()?{
@Override
publicvoidonResponse(JSONObject?response)?{
// Todo}
},newResponse.ErrorListener()?{
@Override
publicvoidonErrorResponse(VolleyError?error)?{
// Todo
}
});
可以看出用起來(lái)很簡(jiǎn)單,也很相似瞧挤。不過(guò)值得提一句的是 在發(fā)送請(qǐng)求之前我們都需要new出來(lái)一個(gè)RequestQueue锡宋。然后把返回的結(jié)果丟進(jìn)去就行了。這是一個(gè)請(qǐng)求隊(duì)列特恬,可以很好的處理高并發(fā)执俩。
常用的加密方式
1. MD5:數(shù)字摘要。是指通過(guò)算法將長(zhǎng)數(shù)據(jù)變?yōu)槎虜?shù)據(jù)癌刽,通常用來(lái)標(biāo)識(shí)數(shù)據(jù)的唯一性役首。具有不可逆性,通常情況下為了讓加密過(guò)程變得不可預(yù)測(cè)妒穴,如下代碼:
public static String digest(String content){
StringBuilder builder = new StringBuilder();
try {
MessageDigest msgDitest = MessageDigest.getInstance("MD5");
msgDitest.update(content.getBytes());
byte[] digests = msgDitest.digest();
builder.append(Integer.toHexString(digests[i] & 0xff ));//
宋税。。讼油。杰赛。。
2. SHA1:sha1也具有不可逆性矮台,比md5長(zhǎng)度更長(zhǎng)乏屯,所以更安全根时,但是機(jī)密的效率md5要慢一些,如文件的秒傳功能辰晕,以及相同的v4包沖突都是可以根據(jù)sha1值進(jìn)行比對(duì)的蛤迎。
public static String digest(String content){
StringBuilder builder = new StringBuilder();
try {
MessageDigest msgDitest = MessageDigest.getInstance("SHA-1");
msgDitest.update(content.getBytes());
byte[] digests = msgDitest.digest();
//將每個(gè)字節(jié)轉(zhuǎn)為16進(jìn)制
for (int i=0;i <= digests.length(); i++){
builder.append(Integer.toHexString(digests[i] & 0xff +8));//+8為加鹽操作
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return? builder.toString();
}
3. RSA:非對(duì)稱(chēng)加密算法,最流行的公鑰密碼算法含友,使用長(zhǎng)度可變的秘鑰不可逆替裆,既能用于數(shù)據(jù)加密,也可以應(yīng)用于數(shù)字簽名窘问,但是RSA非對(duì)稱(chēng)加密內(nèi)容長(zhǎng)度有限制辆童,1024位key的最多只能加密127位數(shù)據(jù)
//1.生成密鑰對(duì),設(shè)計(jì)口號(hào)try {? ? MapgenKeyPair = RSACrypt.genKeyPair();
//2.獲取公鑰
publicKey = RSACrypt.getPublicKey(genKeyPair);
//3.獲取私鑰
privateKey = RSACrypt.getPrivateKey(genKeyPair);
} catch (Exception e) {
e.printStackTrace();
}
private boolean isRSAEncrypt = false;
protected void useRSA() {
try {
if(isRSAEncrypt){
//公鑰解密
String str = text.getText().toString();
byte[] bs = RSACrypt.decryptByPublicKey(RSACrypt.decode(str), publicKey);
text.setText(new String(bs));
}else {
//私鑰加密
byte[] bs = RSACrypt.encryptByPrivateKey(data.getBytes(), privateKey);
text.setText(RSACrypt.encode(bs));
}
isRSAEncrypt = !isRSAEncrypt;
} catch (Exception e) {
e.printStackTrace();
}
}
4.Base64:算不上什么加密算法惠赫,只是對(duì)數(shù)據(jù)進(jìn)行編碼傳輸把鉴。
5. Des&&3Des:對(duì)稱(chēng)加密,算法公開(kāi)儿咱,計(jì)算量小庭砍,速度快!但是一旦別人拿到密鑰全盤(pán)結(jié)束混埠,安全性低怠缸。
String data = "aaaaa";
String desKey = "cccccccc";// 密鑰,口號(hào)
boolean isDesEncrypt = false;
private void useDes() {
try {
if(isDesEncrypt){
//解密
text.setText(Des.decrypt(text.getText().toString(), desKey));
}else {
//加密
text.setText(Des.encrypt(data, desKey));
}
isDesEncrypt = !isDesEncrypt;
} catch (Exception e) {
e.printStackTrace();
}
}
代碼的管理工具
SVN和GIt岔冀。
備注:使用性的東西暫時(shí)不過(guò)多的解釋凯旭。
其他:
3G技術(shù):移動(dòng)多媒體通信系統(tǒng)。三種主流的3g技術(shù):WCDMA使套,CDMA2000罐呼,TD-SCDMA
VideoView:用于視頻播放,視頻播放的原理一般是系統(tǒng)會(huì)首先確定視頻的格式侦高,然后得到視頻的編碼..然后對(duì)編碼進(jìn)行解碼嫉柴,得到一幀一幀的圖像,最后在畫(huà)布上進(jìn)行迅速更新,顯然需要在獨(dú)立的線(xiàn)程中完成,這時(shí)就需要使用surfaceView了奉呛。
基本使用(加載視頻):
setVideoPath(String Path)计螺;加載路徑下的視頻
setVideoURL(URL url);加載url所對(duì)應(yīng)的視頻。
播放視頻:
mVideoView.setVideoPath(“路徑”);
mVideoView.setMediaController(new MediaController(MainActivity.this));
mVideoView.start();
SurfaceView:它擁有獨(dú)立的繪圖表面瞧壮,即它不與其宿主窗口共享同一個(gè)繪圖表面登馒。由于擁有獨(dú)立的繪圖表面,因此SurfaceView的UI就可以在一個(gè)獨(dú)立的線(xiàn)程中進(jìn)行繪制咆槽。又由于不會(huì)占用主線(xiàn)程資源陈轿,SurfaceView一方面可以實(shí)現(xiàn)復(fù)雜而高效的UI,另一方面又不會(huì)導(dǎo)致用戶(hù)輸入得不到及時(shí)響應(yīng)。
一般來(lái)說(shuō)麦射,每一個(gè)窗口在SurfaceFlinger服務(wù)中都對(duì)應(yīng)有一個(gè)Layer蛾娶,用來(lái)描述它的繪圖表面。對(duì)于那些具有SurfaceView的窗口來(lái)說(shuō)潜秋,每一個(gè)SurfaceView在SurfaceFlinger服務(wù)中還對(duì)應(yīng)有一個(gè)獨(dú)立的Layer或者LayerBuffer蛔琅,用來(lái)單獨(dú)描述它的繪圖表面,以區(qū)別于它的宿主窗口的繪圖表面峻呛。
無(wú)論是LayerBuffer罗售,還是Layer,它們都是以L(fǎng)ayerBase為基類(lèi)的钩述,也就是說(shuō)莽囤,SurfaceFlinger服務(wù)把所有的LayerBuffer和Layer都抽象為L(zhǎng)ayerBase,因此就可以用統(tǒng)一的流程來(lái)繪制和合成它們的UI切距。由于LayerBuffer的繪制和合成與Layer的繪制和合成是類(lèi)似的,因此本文不打算對(duì)LayerBuffer的繪制和合成操作進(jìn)行分析惨远。
繼承SurfaceView谜悟,并實(shí)現(xiàn)SurfaceHolder.Callback接口,實(shí)現(xiàn)它的三個(gè)方法:surfaceCreated北秽,surfaceChanged葡幸,surfaceDestroyed。
surfaceCreated(SurfaceHolder holder):surface創(chuàng)建的時(shí)候調(diào)用贺氓,一般在該方法中啟動(dòng)繪圖的線(xiàn)程蔚叨。
surfaceChanged(SurfaceHolder holder, int format, int width,int height):surface尺寸發(fā)生改變的時(shí)候調(diào)用,如橫豎屏切換辙培。
surfaceDestroyed(SurfaceHolder holder) :surface被銷(xiāo)毀的時(shí)候調(diào)用蔑水,如退出游戲畫(huà)面,一般在該方法中停止繪圖線(xiàn)程扬蕊。
還需要獲得SurfaceHolder搀别,并添加回調(diào)函數(shù),這樣這三個(gè)方法才會(huì)執(zhí)行尾抑。
RecycleView:
自定義布局歇父,請(qǐng)通過(guò)布局管理器LayoutManager
自定義控制Item間的間隔(可繪制),請(qǐng)通過(guò)ItemDecoration
自定義控制Item增刪的動(dòng)畫(huà)再愈,請(qǐng)通過(guò)ItemAnimator
備注:這個(gè)是我們目前最常用的控件榜苫,就不再做過(guò)多的介紹。和listview翎冲,gridview等比較起來(lái)學(xué)習(xí)效果會(huì)更好垂睬。
三,總結(jié)
不知道這篇文章能否給大家?guī)ナ找妫菍?duì)自身而言羔飞,受益匪淺肺樟。