Android知識(shí)大貫穿

一, 背景


入行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)模式,可以不用寫配置。在這個(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ù)。比如訪問(wèn)電話通訊錄。每一個(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,換句話說(shuō)這里面不要進(jìn)行復(fù)雜的操作。

Android的Framework和通信機(jī)制


首先放一張大家的熟知的Framework結(jié)構(gòu)圖:

依次為:Applicaiton--framework--library--linux kernal. 個(gè)人喜歡依次稱之為app, api,jni以及binder驅(qū)動(dòng)。

消息機(jī)制


消息的存在就是就是為了子線程和主線程的通信。android界面的刷新只能放在主線程,而耗時(shí)的操作我們只能放在子線程。消息就是兩者之間的傳話者,至于消息內(nèi)部是怎樣實(shí)現(xiàn)的。大致的流程如下

說(shuō)明:Handler必須要在主線程生命,實(shí)際上handler是關(guān)聯(lián)了messagueue,looper在messagequeque里面取消息關(guān)聯(lián)一個(gè)threadlocal線程,說(shuō)白了就是hander在messagequeque取消息交給looper出來(lái)的子線程threadlocal去處理。

注意:hanlder使用不當(dāng)會(huì)產(chǎn)生內(nèi)存泄漏。一般發(fā)生在非靜態(tài)內(nèi)部類。

關(guān)于他們只見(jiàn)的關(guān)系,可以如下圖表示:


參考文獻(xiàn):http://www.cnblogs.com/angeldevil/p/3340644.html

JNI淺談


通過(guò)上面Android的框架圖可以看出,application和framework層都是java編寫的,而library和linux內(nèi)核都是C或者C++編寫的。上層的java如何和下層的c聯(lián)系起來(lái),jni就是兩者關(guān)聯(lián)的橋梁。

關(guān)于如何寫一個(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ī)制



z

看完這張圖,至少知道了android對(duì)view的渲染都是誰(shuí)在主事。大家都知道我們?nèi)搜哿鲿车漠嬅媸敲棵?0幀。換句話說(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)畫機(jī)制


這個(gè)我已經(jīng)專門寫過(guò)一篇文檔:http://m.itdecent.cn/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ù)的低效率訪問(wèn)。依據(jù)模型操作接口的不同,視圖可能需要多次調(diào)用才能獲得足夠的顯示數(shù)據(jù)。對(duì)未變化數(shù)據(jù)的不必要的頻繁訪問(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ù)邏輯,稱為"被動(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 都采用這種模式

多線程和線程池


(1)Android多線程處理

AsyncTask:為 UI 線程與工作線程之間進(jìn)行快速的切換提供一種簡(jiǎn)單便捷的機(jī)制。適用于當(dāng)下立即需要啟動(dòng),但是異步執(zhí)行的生命周期短暫的使用場(chǎng)景。

HandlerThread:為某些回調(diào)方法或者等待某些任務(wù)的執(zhí)行設(shè)置一個(gè)專屬的線程,并提供線程任務(wù)的調(diào)度機(jī)制。

ThreadPool:把任務(wù)分解成不同的單元,分發(fā)到各個(gè)不同的線程上,進(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)線程池

顯而易見(jiàn),線程池是為線程服務(wù)的。首先看一個(gè)常用的例子:

new Thread(new Runnable() {

@Override

public void run() {

//do sth .

}

}).start();

我們經(jīng)常會(huì)這樣使用,還覺(jué)得妙不可言,但是過(guò)多頻繁的使用就會(huì)引發(fā)以下問(wèn)題:

1、線程的創(chuàng)建和銷毀都需要時(shí)間,當(dāng)有大量的線程創(chuàng)建和銷毀時(shí),那么這些時(shí)間的消耗則比較明顯,將導(dǎo)致性能上的缺失

2、大量的線程創(chuàng)建、執(zhí)行和銷毀是非常耗cpu和內(nèi)存的,這樣將直接影響系統(tǒng)的吞吐量,導(dǎo)致性能急劇下降,如果內(nèi)存資源占用的比較多,還很可能造成OOM

3、大量的線程的創(chuàng)建和銷毀很容易導(dǎo)致GC頻繁的執(zhí)行,從而發(fā)生內(nèi)存抖動(dòng)現(xiàn)象,而發(fā)生了內(nèi)存抖動(dòng),對(duì)于移動(dòng)端來(lái)說(shuō),最大的影響就是造成界面卡頓

為了解決以上問(wèn)題而又可以這樣使用,那么線程池應(yīng)運(yùn)而生了。他的出現(xiàn)像是誕生了一位leader,要統(tǒng)一管理這些隨心所欲的家伙了。他帶來(lái)的好處如下:

1、線程的創(chuàng)建和銷毀由線程池維護(hù),一個(gè)線程在完成任務(wù)后并不會(huì)立即銷毀,而是由后續(xù)的任務(wù)復(fù)用這個(gè)線程,從而減少線程的創(chuàng)建和銷毀,節(jié)約系統(tǒng)的開(kāi)銷

2、線程池旨在線程的復(fù)用,這就可以節(jié)約我們用以往的方式創(chuàng)建線程和銷毀所消耗的時(shí)間,減少線程頻繁調(diào)度的開(kāi)銷,從而節(jié)約系統(tǒng)資源,提高系統(tǒng)吞吐量

3、在執(zhí)行大量異步任務(wù)時(shí)提高了性能

4、Java內(nèi)置的一套ExecutorService線程池相關(guān)的api,可以更方便的控制線程的最大并發(fā)數(shù)、線程的定時(shí)任務(wù)、單線程的順序執(zhí)行等

同時(shí)他管理有方,把線程歸類,總有一款適合你的‘池子’:

1、newFixedThreadPool() :

作用:該方法返回一個(gè)固定線程數(shù)量的線程池,該線程池中的線程數(shù)量始終不變,即不會(huì)再創(chuàng)建新的線程,也不會(huì)銷毀已經(jīng)創(chuàng)建好的線程,自始自終都是那幾個(gè)固定的線程在工作,所以該線程池可以控制線程的最大并發(fā)數(shù)。

假如有一個(gè)新任務(wù)提交時(shí),線程池中如果有空閑的線程則立即使用空閑線程來(lái)處理任務(wù),如果沒(méi)有,則會(huì)把這個(gè)新任務(wù)存在一個(gè)任務(wù)隊(duì)列中,一旦有線程空閑了,則按FIFO方式處理任務(wù)隊(duì)列中的任務(wù)。

2、newCachedThreadPool() :

作用:該方法返回一個(gè)可以根據(jù)實(shí)際情況調(diào)整線程池中線程的數(shù)量的線程池。即該線程池中的線程數(shù)量不確定,是根據(jù)實(shí)際情況動(dòng)態(tài)調(diào)整的。

假如該線程池中的所有線程都正在工作,而此時(shí)有新任務(wù)提交,那么將會(huì)創(chuàng)建新的線程去處理該任務(wù),而此時(shí)假如之前有一些線程完成了任務(wù),現(xiàn)在又有新任務(wù)提交,那么將不會(huì)創(chuàng)建新線程去處理,而是復(fù)用空閑的線程去處理新任務(wù)。那么此時(shí)有人有疑問(wèn)了,那這樣來(lái)說(shuō)該線程池的線程豈不是會(huì)越集越多?其實(shí)并不會(huì),因?yàn)榫€程池中的線程都有一個(gè)“保持活動(dòng)時(shí)間”的參數(shù),通過(guò)配置它,如果線程池中的空閑線程的空閑時(shí)間超過(guò)該“保存活動(dòng)時(shí)間”則立刻停止該線程,而該線程池默認(rèn)的“保持活動(dòng)時(shí)間”為60s。

3、newSingleThreadExecutor() :

作用:該方法返回一個(gè)只有一個(gè)線程的線程池,即每次只能執(zhí)行一個(gè)線程任務(wù),多余的任務(wù)會(huì)保存到一個(gè)任務(wù)隊(duì)列中,等待這一個(gè)線程空閑,當(dāng)這個(gè)線程空閑了再按FIFO方式順序執(zhí)行任務(wù)隊(duì)列中的任務(wù)。

4、newScheduledThreadPool() :

作用:該方法返回一個(gè)可以控制線程池內(nèi)線程定時(shí)或周期性執(zhí)行某任務(wù)的線程池。

5、newSingleThreadScheduledExecutor() :

作用:該方法返回一個(gè)可以控制線程池內(nèi)線程定時(shí)或周期性執(zhí)行某任務(wù)的線程池。只不過(guò)和上面的區(qū)別是該線程池大小為1,而上面的可以指定線程池的大小。

參考文獻(xiàn):http://blog.csdn.net/u010687392/article/details/49850803

常用的圖片加載模式


(1)Glide && Picasso

之所以把這兩個(gè)拿出來(lái)一起說(shuō)事,是因?yàn)閮烧叩氖褂梅绞椒浅O嘞?。例如加載一張圖片:

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í)磁盤)

內(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://m.itdecent.cn/p/353514d315a7

熱補(bǔ)丁


和上面說(shuō)講到的插件化的部分原理很像。先看一下android的類加載:


我們常用的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類的loadClassBinaryName方法返回Class實(shí)例

Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);

if (clazz != null) {

return clazz;

}

}

}

return null;

}

因此,講白一點(diǎn),我們所謂的熱修復(fù)就是偷梁換柱,把我們的dex文件放在最前面。

性能優(yōu)化


這個(gè)話題基本上是每個(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ù)谝淮问褂猛暌院?,盡量給其他的使用

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ù)的訪問(wèn)。更深一層次來(lái)說(shuō)是基于TCP/IP協(xié)議,至于哪個(gè)更牛逼就看訪問(wèn)速度,高效性,容錯(cuò)性了。

okhttp在請(qǐng)求的時(shí)候會(huì)有一個(gè)engine,這個(gè)engine就像是一個(gè)管理站,他控制著需要那種方式的請(qǐng)求,比如是異步還是同步。在connetion的時(shí)候,就是真正建立連接的時(shí)候,有自己的線程池機(jī)制,更選擇更優(yōu)化的處理方式。而真正達(dá)到服務(wù)端的時(shí)候,會(huì)先經(jīng)過(guò)路由,路由其實(shí)就是一個(gè)注冊(cè)表(個(gè)人理解)是一套協(xié)議,比如代理,IP地址等,選擇正確的協(xié)議去打開(kāi)server的大門。下面是請(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ù)的訪問(wèn),比如大圖片的下載,視頻的下載等就會(huì)表現(xiàn)的很糟糕。換句話說(shuō)他雖然在性能上進(jìn)行了優(yōu)化,但是只是適合對(duì)小數(shù)據(jù)量的操作。

每一種網(wǎng)絡(luò)請(qǐng)求,我們都真正關(guān)心的都是response返回的數(shù)據(jù),為此我們需要掌握兩個(gè)常見(jiàn)的數(shù)據(jù)類型。

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è)抽象類無(wú)法直接創(chuàng)建它的實(shí)例.但是天無(wú)絕人之路,他有JsonRequest有JsonObjectRequest和JsonArrayRequest兩個(gè)子類。

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ì)稱加密算法,最流行的公鑰密碼算法,使用長(zhǎng)度可變的秘鑰不可逆,既能用于數(shù)據(jù)加密,也可以應(yīng)用于數(shù)字簽名,但是RSA非對(duì)稱加密內(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ì)稱加密,算法公開(kāi),計(jì)算量小,速度快!但是一旦別人拿到密鑰全盤結(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)行解碼,得到一幀一幀的圖像,最后在畫布上進(jìn)行迅速更新,顯然需要在獨(dú)立的線程中完成,這時(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ú)立的線程中進(jìn)行繪制。又由于不會(huì)占用主線程資源,SurfaceView一方面可以實(shí)現(xiàn)復(fù)雜而高效的UI,另一方面又不會(huì)導(dǎo)致用戶輸入得不到及時(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,它們都是以LayerBase為基類的,也就是說(shuō),SurfaceFlinger服務(wù)把所有的LayerBuffer和Layer都抽象為L(zhǎng)ayerBase,因此就可以用統(tǒng)一的流程來(lái)繪制和合成它們的UI。由于LayerBuffer的繪制和合成與Layer的繪制和合成是類似的,因此本文不打算對(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)繪圖的線程。

surfaceChanged(SurfaceHolder holder, int format, int width,int height):surface尺寸發(fā)生改變的時(shí)候調(diào)用,如橫豎屏切換。

surfaceDestroyed(SurfaceHolder holder) :surface被銷毀的時(shí)候調(diào)用,如退出游戲畫面,一般在該方法中停止繪圖線程。

還需要獲得SurfaceHolder,并添加回調(diào)函數(shù),這樣這三個(gè)方法才會(huì)執(zhí)行。

RecycleView:

自定義布局,請(qǐng)通過(guò)布局管理器LayoutManager

自定義控制Item間的間隔(可繪制),請(qǐng)通過(guò)ItemDecoration

自定義控制Item增刪的動(dòng)畫,請(qǐng)通過(guò)ItemAnimator

備注:這個(gè)是我們目前最常用的控件,就不再做過(guò)多的介紹。和listview,gridview等比較起來(lái)學(xué)習(xí)效果會(huì)更好。

三,總結(jié)


不知道這篇文章能否給大家?guī)ナ找?,但是?duì)自身而言,受益匪淺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評(píng)論 25 708

友情鏈接更多精彩內(nèi)容