公共技術點之 Java 動態代理java
公共技術點之依賴注入linux
公共技術點之 View 事件傳遞android
底層的Binder驅動,IPC的核心,SGL 2D繪圖,OpenGL 3D繪圖github
AsyncTask:web
關於線程池:asynctask對應的線程池ThreadPoolExecutor都是進程範圍內共享的,都是static的,因此是asynctask控制着進程範圍內全部的子類實例。因爲這個限制的存在,當使用默認線程池時,若是線程數超過線程池的最大容量,線程池就會爆掉(3.0後默認串行執行,不會出現這個問題)。針對這種狀況,能夠嘗試自定義線程池,配合asynctask使用。數據庫
關於默認線程池:核心線程池中最多有CPU_COUNT+1個,最多有CPU_COUNT*2+1個,線程等待隊列的最大等待數爲128,可是能夠自定義線程池。線程池是由AsyncTask來管理的,線程池容許tasks並行運行,xuyao注意的是併發狀況下數據的一致性問題,新數據可能會被老數據覆蓋掉,相似volatile變量。因此但願tasks可以串行運行的話,使用SERIAL_EXECUTOR。json
自定義線程池:executeOnExecutor(Executor exec,Params… params) 自定義Executor
execute(Params… params){return executeOnExecutor(sDefaultExecutor,params);}
AsyncTask在不一樣的SDK版本中的區別:
調用AsyncTask的excute方法不能當即執行程序的緣由分析及改善方案
經過查閱官方文檔發現,AsyncTask首次引入時,異步任務是在一個獨立的線程中順序的執行,也就是說一次只能執行一個任務,不能並行的執行,從1.6開始,AsyncTask引入了線程池,支持同時執行5個異步任務,也就是說同時只能有5個線程運行,超過的線程只能等待,等待前面的線程某個執行完了才被調度和運行。換句話說,若是一個進程中的AsyncTask實例個數超過5個,那麼假如前5個都運行很長時間的話,那麼第6個只能等待機會了。這是AsyncTask的一個限制,並且對於2.3之前的版本沒法解決。若是你的應用須要大量的後臺線程去執行任務,那麼你只能放棄使用AsyncTask,本身建立線程池來管理Thread,或者乾脆不用線程池直接使用Thread也無妨。不得不說,雖然AsyncTask較Thread使用起來方便,可是它最多隻能同時運行5個線程,這也大大侷限了它的實力,你必需要當心設計你的應用,錯開使用AsyncTask的時間,盡力作到分時,或者保證數量不會大於5個,不然就會遇到上次提到的問題。多是Google意識到了AsyncTask的侷限性了,從Android3.0開始對AsyncTask的API做出了一些調整:每次只啓動一個線程執行一個任務,完成以後再執行第二個任務,也就是至關於只有一個後臺線程在執行所提交的任務。
一、生命週期
不少開發者會認爲一個在Activity中建立的AsyncTask會隨着Activity的銷燬而銷燬。然而事實並不是如此。AsyncTask會一直執行,直到doInBackground()方法執行完畢。而後,若是cancel(boolean)被調用,那麼onCancelled(Result result)方法會被執行;不然,執行onPostExecute(Result result)方法。若是咱們的Activity銷燬以前,沒有取消AsyncTask,這有可能讓咱們的AsyncTask崩潰(crash)。由於它想要處理的view已經不在了。因此,咱們老是必須確保在銷燬活動以前取消任務。總之,咱們使用AsyncTask須要確保AsyncTask正確的取消。
二、內存泄漏
若是AsyncTask被聲明爲Activity的非靜態的內部類,那麼AsyncTask會保留一個對Activity的引用。若是Activity已經被銷燬,AsyncTask的後臺線程還在執行,它將繼續在內存裏保留這個引用,致使Activity沒法被回收,引發內存泄漏。
三、結果丟失
屏幕旋轉或Activity在後臺被系統殺掉等狀況會致使Activity的從新建立,以前運行的AsyncTask會持有一個以前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將再也不生效。
四、並行仍是串行
在Android1.6以前的版本,AsyncTask是串行的,在1.6至2.3的版本,改爲了並行的。在2.3以後的版本又作了 修改,能夠支持並行和串行,當想要串行執行時,直接執行execute()方法,若是須要執行executeOnExecutor(Executor)。
(1)Linux Sandbox 沙箱機制:Android將數據分爲system和data兩個區。其中system是隻讀的,dada用來存放應用本身的數據,這保證了系統數據不會被隨意改寫。
應用之間的數據相互獨立,每一個應用都會有一個user id和group id,只有相同的user id而且來自同一個做者,才能訪問它們的數據。做者經過對apk簽名來標識本身,簽名和uid構成了雙重的保證。
(2)用戶權限機制:文件權限,UID,GID
(3)用戶權限機制:android permission機制限制應用訪問特定的資源,例如照相機、網絡、外部存儲等api
如何讓兩個app運行在同一個進程裏?
跨進程間通訊(IPC):四大組件之間經過Intent互相跳轉,Android實現IPC的方式是binder機制。
android中的跨進程通訊的實現(一)——遠程調用過程和aidl
In the Android platform, the binder is used for nearly everything that happens accross in the core plateform.
最底層的是Android的ashmen(Anonymous shared memoryy)機制,它負責輔助實現內存的分配,以及跨進程間通訊所須要的內存共享。AIDL(Android Interface Definition Language)對BInder的使用進行了封裝,可讓開發者方便的進行方法的遠程調用,後面會詳細介紹。Intent是最高一層的抽象,方便開發者進行經常使用的跨進程調用。
從英文字面上意思看,Binder具備粘結劑的意思,那麼它把什麼東西粘結在一塊兒呢?在Android系統的Binder機制中,由一系統組件組成,分別是Client、Server、Service Manager和Binder驅動程序,其中Client、Server和Service Manager運行在用戶區間,Binder驅動程序運行內核空間。Binder就是一種把這四個組件粘合在一塊兒的粘結劑了,其中,核心組件即是Binder驅動了,ServiceManager提供了輔助管理的功能,Client和Server正是在Binder驅動和ServiceManager提供的基礎設施上,進行Client-Server之間的通訊。
服務器端:一個Binder服務器就是一個Binder類的對象。當建立一個Binder對象後,內部就會開啓一個線程,這個線程用語接收binder驅動發送的信息,收到消息後,會執行相關的服務代碼。
Binder驅動:當服務端成功建立一個Binder對象後,Binder驅動也會相應的建立一個mRemote對象,該對象的類型也是也是Binder類。客戶就能夠藉助這個mRemote對象來訪問遠程服務。
客戶端:客戶想要訪問Binder的遠程服務,就必須獲取遠程服務的Binder對象在binder驅動層對應的mRemote引用。當獲取到mRemote對象的引用後,就能夠調用相應Binder對象的服務了。
在這裏,咱們能夠看到,客戶端是經過Binder驅動來調用服務端的相關服務。首先,在服務端建立一個Binder對象,而後相應的在Binder驅動中建立一個Binder對象,接着客戶端經過獲取Binder對象的引用來調用服務端的服務。在Binder機制中正是藉着Binder驅動將不一樣進程間的組件bind(粘連)在一塊兒,實現通訊。
mmap將一個文件或者其餘對象映射進內存。文件被映射到多個頁上,若是文件的大小不是全部頁的大小之和,最後一個頁不被使用的空間將會清零。munmap執行相反的操做,刪除特定地址區域的對象映射。
當使用mmap映射文件到進程後,就能夠直接操做這段虛擬內存進行文件的讀寫等操做,沒必要再調用read,write等系統調用。但需注意,直接對該段內存寫時不會寫入超過當前文件大小的內容。
採用共享內存通訊的一個顯而易見的好處是效率高,由於進程能夠直接讀寫內存,而不須要任何數據的拷貝。對於像管道和消息隊列等通訊方式,則須要在內核和用戶空間進行四次的數據拷貝,而共享內存只拷貝兩次數據:一次從輸入文件到共享內存區,另外一次從共享內存區輸出文件。實際上,進程之間在共享內存時,並不老是讀寫少許數據後就解除映射,有新的通訊時,再從新創建共享內存區域。而是保存共享區域,直到通訊完畢爲止,這樣,數據內容一直保存在共享內存中,並無寫回文件。共享內存中的內容每每是在解除映射時才寫回文件的。所以,採用共享內存的通訊方式效率是很是高的。
aidl主要就是幫助咱們完成了包裝數據和解包的過程,並調用了transact過程,而用來傳遞的數據包咱們就稱爲parcel
AIDL: xxx.aidl->xxx.java,註冊service
Dalvik虛擬機在調用一個成員函數的時候,若是發現該成員函數是一個JNI方法,那麼就會直接跳到它的地址去執行。也就是說,JNI方法是直接在本地操做系統執行的,而不是Dalvik虛擬機解釋器執行。由此也能夠看出,JNI方法是Android應用程序與本地操做系統直接進行通訊的一個手段。
JNI原理:
例子:當libnanosleep.so文件被加載的時候,函數JNI_OnLoad就會被調用。在函數JNI_OnLoad中,參數vm描述的是當前線程中的Dalvik虛擬機,經過調用它的成員函數GetEnv就能夠得到一個JNIEnv對象。有了這個JNIEnv對象以後,咱們就能夠調用另一個函數jniRegisterNativeMethods來向當前進程的Dalvik虛擬機註冊一個JNI方法。
App啓動過程:
從桌面點擊到activity啓動的過程
一、Launcher線程捕獲onclick的點擊事件,調用Launcher.startActivitySafely,進一步調用Launcher.startActivity,最後調用父類Activity的startActivity。
二、Activity和ActivityManagerService交互,引入Instrumentation,將啓動請求交給Instrumentation,調用Instrumentation.execStartActivity。
三、調用ActivityManagerService的startActivity方法,這裏作了進程切換(具體過程請查看源碼)。
四、開啓Activity,調用onCreate方法
常見的例子:程序正運行着來電話了,這個程序咋辦呢?停止了唄,若是停止的時候新出的一個Activity是全屏的onPause->onStop,恢復的時候onStart->onResume,若是打斷這個應用程序的是一個Theme爲Translucent或者Dialog的Activity那麼只是onPause,恢復的時候onResume。
onPause,onstop,onDestroy,三種狀態下,Activity都有可能被系統幹掉。
啓動另外一個Activity而後finish,先調用舊Activity的onPause方法,而後調用新的Activity和onCreate->onStart->onResume方法,而後調用舊Activity的onStop->onDestroy方法。
若是沒有調用finish那麼onDestroy方法不會被調用,並且在onStop以前還會調用onSavedInstanceState方法
onRestart方法執行完了以後還會調用onStart方法
fragment:[SupportFragmentManager,childFragment]
service:
Service和Intent Service:沒啥區別,只是IntentService在onCreate方法中開啓新的HandlerThread去執行。
Service運行的進程和線程:當它運行的時候若是是LocalService,那麼對應的Service是運行在主進程的main線程上的。如onCreate,onStart這些函數都是在系統調用的時候在主進程的main線程上運行的。若是是RemoteSevice,那麼對應的Service則是運行在獨立的main線程上。
Thread的運行是獨立於Activity的,也就是說當一個Activity被finish以後,若是你沒有主動中止Thread或者Thread裏的run方法沒有執行完畢的話,Thread就會一直執行。
View的繪製主要涉及三個方法:onMeasure()、onLayout()、onDraw()
進度條組件:
android 事件處理機制總結,ScrollView ViewPager ListView GridView嵌套小結
當手指觸摸到屏幕時,系統就會調用相應View的onTouchEvent,並傳入一系列的action。
dispatchTouchEvent的執行順序爲:
這就解釋了重寫ViewGroup時必須調用super.dispatchTouchEvent();
(1)dispatchTouchEvent:
此方法通常用於初步處理事件,由於動做是由此分發,因此一般會調用super.dispatchTouchEvent。這樣就會繼續調用onInterceptTouchEvent,再由onInterceptTouchEvent決定事件流向。
(2)onInterceptTouchEvent:
若返回值爲true事件會傳遞到本身的onTouchEvent();若返回值爲false傳遞到下一個View的dispatchTouchEvent();
(3)onTouchEvent():
若返回值爲true,事件由本身消耗,後續動做讓其處理;若返回值爲false,本身不消耗事件了,向上返回讓其餘的父View的onTouchEvent接受處理
三大方法關係的僞代碼:若是當前View攔截事件,就交給本身的onTouchEvent去處理,不然就丟給子View繼續走相同的流程。
public boolean dispatchTouchEvent(MotionEvent ev)
{
boolean consume = false;
if(onInterceptTouchEvent(ev))
{
consume = onTouchEvent(ev);
}
else
{
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
複製代碼
onTouchEvent的傳遞:
當有多個層級的View時,在父層級容許的狀況下,這個action會一直傳遞直到遇到最深層的View。因此touch事件最早調用的是最底層View的onTouchEvent,若是View的onTouchEvent接收到某個touch action並作了相應處理,最後有兩種返回方式return true和return false;return true會告訴系統當前的View須要處理此次的touch事件,之後的系統發出的ACTION_MOVE,ACTION_UP仍是須要繼續監聽並接收的,而且此次的action已經被處理掉了,父層的View是不可能觸發onTouchEvent的了。因此每個action最多隻能有一個onTouchEvent接口返回true。若是返回false,便會通知系統,當前View不關心這一次的touch事件,此時這個action會傳向父級,調用父級View的onTouchEvent。可是這一次的touch事件以後發出任何action,該View都不在接受,onTouchEvent在這一次的touch事件中不再會觸發,也就是說一旦View返回false,那麼以後的ACTION_MOVE,ACTION_UP等ACTION就不會在傳入這個View,可是下一次touch事件的action仍是會傳進來的。
父層的onInterceptTouchEvent
前面說了底層的View可以接收到此次的事件有一個前提條件:在父層容許的狀況下。假設不改變父層級的dispatch方法,在系統調用底層onTouchEvent以前會調用父View的onInterceptTouchEvent方法判斷,父層View是否要截獲本次touch事件以後的action。若是onInterceptTouchEvent返回了true,那麼本次touch事件以後的全部action都不會向深層的View傳遞,通通都會傳給父層View的onTouchEvent,就是說父層已經截獲了此次touch事件,以後的action也沒必要詢問onInterceptTouchEvent,在此次的touch事件以後發出的action時onInterceptTouchEvent不會再被調用,直到下一次touch事件的來臨。若是onInterceptTouchEvent返回false,那麼本次action將發送給更深層的View,而且以後的每一次action都會詢問父層的onInterceptTouchEvent需不須要截獲本次touch事件。只有ViewGroup纔有onInterceptTouchEvent方法,由於一個普通的View確定是位於最深層的View,只有ViewGroup纔有onInterceptTouchEvent方法,由於一個普通的View確定是位於最深層的View,touch可以傳到這裏已是最後一站了,確定會調用View的onTouchEvent()。
底層View的getParent().requestDisallowInterceptTouchEvent(true)
對於底層的View來講,有一種方法能夠阻止父層的View獲取touch事件,就是調用getParent().requestDisallowInterceptTouchEvent(true)方法。一旦底層View收到touch的action後調用這個方法那麼父層View就不會再調用onInterceptTouchEvent了,也沒法截獲之後的action(若是父層ViewGroup和最底層View須要截獲不一樣焦點,或不一樣手勢的touch,不能使用這個寫死)。
曾經開發過程當中遇到的兩個示例:左邊是處理ViewPager和ListView的衝突,紀錄水平和垂直方向的偏移量,若是水平方向的偏移更多的話就讓ViewPager處理pager滑動
右邊處理的ViewPager和ImageBanner的滑動衝突,一樣是紀錄偏移量,若是發生在ImageBanner上的水平偏移量大於垂直偏移量的話就讓banner滾動
想一想爲何右邊是重寫dispatchTouchEvent方法而不是onInterceptTouchEvent方法?
FixedViewPager
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
switch(ev.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
mX = ev.getX();
mY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float x = ev.getX();
float y = ev.getY();
float dX = x - mX;
float dY = y - mY;
float tmp = Math.abs(dX) / Math.abs(dY);
mX = x;
mY = y;
if(tmp > 1)
{
return true;
}
else
{
return super.omInterceptTouchEvent(ev);
}
}
}
複製代碼
FixedImageLoadBanner
@override
public boolean dispatchTouchEvent(MotionEvent ev)
{
if(mX != 0 || mY != 0)
{
float dY = ev.getRawY() - mY;
float dX = ev.getRawX() - mX;
if(Math.abs(dY) > Math.abs(dX))
{
requestDisallowInterceptTouchEvent(false);
}
else
{
requestDisallowInterceptTouchEvent(true);
}
}
mX = ev.getRawX();
mY = ev.getRawY();
return super.dispatchTouchEvent(ev);
}
複製代碼
art上應用啓動快,運行快,可是耗費更多存儲空間,安裝時間長,總的來講ART的功效就是」空間換時間」。
ART: Ahead of Time Dalvik: Just in Time
什麼是Dalvik:Dalvik是Google公司本身設計用於Android平臺的Java虛擬機。Dalvik虛擬機是Google等廠商合做開發的Android移動設備平臺的核心組成部分之一,它能夠支持已轉換爲.dex(即Dalvik Executable)格式的Java應用程序的運行,.dex格式是專爲Dalvik應用設計的一種壓縮格式,適合內存和處理器速度有限的系統。Dalvik通過優化,容許在有限的內存中同時運行多個虛擬機的實例,而且每個Dalvik應用做爲獨立的Linux進程執行。獨立的進程能夠防止在虛擬機崩潰的時候全部程序都被關閉。
什麼是ART:Android操做系統已經成熟,Google的Android團隊開始將注意力轉向一些底層組件,其中之一是負責應用程序運行的Dalvik運行時。Google開發者已經花了兩年時間開發更快執行效率更高更省電的替代ART運行時。ART表明Android Runtime,其處理應用程序執行的方式徹底不一樣於Dalvik,Dalvik是依靠一個Just-In-Time(JIT)編譯器去解釋字節碼。開發者編譯後的應用代碼須要經過一個解釋器在用戶的設備上運行,這一機制並不高效,但讓應用能更容易在不一樣硬件和架構上運行。ART則徹底改變了這套作法,在應用安裝的時候就預編譯字節碼到機器語言,這一機制叫Ahead-Of-Time(AOT)編譯。在移除解釋代碼這一過程後,應用程序執行將更有效率,啓動更快。
ART優勢:
ART缺點:
Scroller執行流程裏面的三個核心方法
一、在mScroller.startScroll()中爲滑動作了一些初始化準備,好比:起始座標,滑動的距離和方向以及持續時間(有默認值),動畫開始時間等。
二、mScroller.computeScrollOffset()方法主要是根據當前已經消逝的時間來計算當前的座標點。由於在mScroller.startScroll()中設置了動畫時間,那麼在computeScrollOffset()方法中依據已經消逝的時間就很容易獲得當前時刻應該所處的位置並將其保存在變量mCurrX和mCurrY中。除此以外該方法還可判斷動畫是否已經結束。
如何避免後臺進程被殺死?
standard:Activity的默認加載方式,該方法會經過跳轉到一個新的Activity,同時將該實例壓入到棧中(無論該Activity是否已經存在在Task棧中,都是採用new操做,生命週期從onCreate()開始)。例如:棧中順序是A B C D,此時D經過Intent跳轉到A,那麼棧中結構就變成A B C D A,點擊返回按鈕的顯示順序是D C B A,依次摧毀。
singleTop:singleTop模式下,當前Activity D位於棧頂的時候,若是經過Intent跳轉到它自己的Activity(D),那麼不會從新建立一個新的D實例(走onNewIntent()),因此棧中的結構依次爲A B C D,若是跳轉到B,那麼因爲B不處於棧頂,因此會新建一個B實例並壓入到棧中,結構就變成了A B C D B。應用實例:三條推送,點進去都是一個Activity,這確定用singletop
singleTask:singleTask模式下,Task棧中只能有一個對應的Activity實例。例如:Task棧1中結構爲:A B C D。此時D經過Intent跳轉到B(走onNewIntent()),則棧的結構變成了:A,B。其中的C和D被棧彈出銷燬了,也就是說位於B之上的實例都被銷燬了。一般應用於首頁,首頁確定在棧底部,也只能在棧底部。
singleInstance:singleInstance模式下,會將打開的Activity壓入一個新的任務棧中。例如:Task棧1中結構爲:A B C,C經過Intent跳轉到了D(D的模式爲singleInstance),那麼則會新建一個Task,棧1中結構依舊爲A B C,棧2中結構爲D。此時屏幕顯示D,以後D經過Intent跳轉到D,棧2不會壓入新的D,因此兩個棧中的狀況沒發生改變。若是D跳轉到了C,那麼就會根據C對應的launchMode在棧1中進行對應的操做,C若是爲standard,那麼D跳轉到C,棧1的結構爲A B C C ,此時點擊返回按鈕,仍是在C,棧1的結構變爲A B C,而不會回到D。
launchMode爲singleTask的時候,經過Intent啓動到一個Activity,若是系統已經存在一個實例,系統就會將請求發送到這個實例上,但這個時候,系統就不會再調用一般狀況下咱們處理請求數據的onCreate方法,而不是調用onNewIntent方法。
onSavedInstanceState的調用遵循一個重要原則,即當系統」未經你許可」時銷燬了你的Activity,則onSavedInstanceState會被系統調用,這時系統的責任,由於它必需要提供一個機會讓你保存你的數據,至於onRestoreInstanceState方法,須要注意的是,onSavedInstanceState方法和onRestoreInstanceState方法」不必定」是成對調用的。
onRestoreInstanceState被調用的前提是,Activity A確實被系統銷燬了,而若是僅僅是停留在有這種可能性的狀況下,則該方法不會被調用,例如,當正在顯示Activity A的時候,用戶按下HOME鍵回到主界面,而後用戶緊接着又返回到Activity A,這種狀況下Activity A通常不會由於內存的緣由被銷燬,故Activity的onRestoreInstanceState方法不會被執行。
另外,onRestoreInstanceStated的bundle參數也會傳遞到onCreate方法中,你也能夠選擇在onCreate方法中作數據還原。
onSavedInstanceState(Bundle bundle)一般和onRestoreInstanceState(Bundle bundle)不會成對出現,onRestoreInstanceState這玩意不太好觸發,給你們提個好辦法,橫豎屏切換的時候100%會觸發。而後保存在onRestoreInstanceState bundle裏面的數據,就是onCreate的那個參數bundle啦,要怎麼恢復就看開發者了。
Feature機制
如何使用webview在js中調用java方法?
webView.addJavaScriptInterface(new Object(){xxx}, "xxx");
複製代碼
答案:可使用WebView控件執行JavaScript腳本,而且能夠在JavaScript中執行Java代碼。要想讓WebView控件執行JavaScript,須要調用WebSettings.setJavaScriptEnabled方法,代碼以下:
WebView webView = (WebView)findViewById(R.id.webview)
WebSettings webSettings = webView.getSettings()
//設置WebView支持JavaScript
webSettings.setJavaScriptEnabled(true)
webView.setWebChromeClient(new WebChromeClient())
複製代碼
JavaScript調用Java方法須要使用WebView.addJavascriptInterface方法設置JavaScript調用的Java方法,代碼以下:
webView.addJavascriptInterface(new Object()
{
public String process(String value)
{
return result;
}
}, "demo");
複製代碼
可使用下面的JavaScript代碼調用process方法,代碼以下:
function search()
{
result.innerHTML = "" + window.demo.process('data') + "";
}
複製代碼
SurfaceView是在一個新起的單獨線程中能夠從新繪製畫面,而view必須在UI的主線程中更新畫面。
在UI的主線程中更新畫面可能會引起問題,好比你更新的時間過長,那麼你的主UI線程就會被你正在畫的函數阻塞。那麼將沒法響應按鍵、觸屏等消息。當使用SurfaceView因爲是在新的線程中更新畫面因此不會阻塞你的UI主線程。但這也帶來了另一個問題,就是事件同步。好比你觸屏了一下,你須要SurfaceView中thread處理,通常就須要有一個event queue的設計來保存touchevent,這會稍稍複雜一點,由於涉及到線程安全。
簡言之,都是用來解決重複佈局的問題,可是標籤可以在佈局重用的時候減小UI層級結構。 viewStub標籤是用來給其餘的View事先佔據好位置,當須要的時候用inflater()或者是setVisible()方法顯示這些View。
一、ANR排錯通常有三種類型
二、如何避免
三、如何排查
四、監測ANR的Watchdog
======================常見面試問題===============================
一、橫豎屏切換時候Activity的生命週期
二、(圖片相關的)OOM怎麼處理
三、界面優化
太多重疊的背景(overdraw)
這個問題其實最容易解決,建議就是檢查你在佈局和代碼中設置的背景,有些背景是隱藏在底下的,它永遠不可能顯示出來,這種不必的背景必定要移除,由於它極可能會嚴重影響到app的性能。若是採用的是selector的背景,將normal狀態的color設置爲」@android:color/transparent」,也一樣能夠解決問題。
太多重疊的View
第一個建議是 :是喲過ViewStub來加載一些不經常使用的佈局,它是一個輕量級且默認是不可見的視圖,能夠動態的加載一個佈局,只要你用到這個重疊着的View的時候才加載,推遲加載的時間。
第二個建議是:若是使用了相似Viewpager+Fragment這樣的組合或者有多個Fragment在一個界面上,須要控制Fragment的顯示和隱藏,儘可能使用動態的Inflation view,它的性能要比SetVisibility好。
複雜的Layout層級
這裏的建議比較多一些,首先推薦使用Android提供的佈局工具Hierarchy Viewer來檢查和優化佈局。第一個建議是:若是嵌套的線性佈局加深了佈局層次,可使用相對佈局來取代。第二個建議是:用標籤來合併佈局。第三個建議是:用標籤來重用佈局,抽取通用的佈局可讓佈局的邏輯更清晰明瞭。記住,這些建議的最終目的都是使得你的Layout在Hierarchy Viewer裏變得寬而淺,而不是窄而深。
四、移動端獲取網絡數據優化的幾個點
鏈接複用:節省鏈接創建時間,如開啓 keep-alive。 對於Android來講默認狀況下HttpURLConnection和HttpClient都開啓了keep-alive。只是2.2以前HttpURLConnection存在影響鏈接池的Bug,具體可見:Android HttpURLConnection及HttpClient選擇
請求合併:即將多個請求合併爲一個進行請求,比較常見的就是網頁中的CSS Image Sprites。若是某個頁面內請求過多,也能夠考慮作必定的請求合併。
減小請求數據的大小:對於post請求,body能夠作gzip壓縮的,header也能夠作數據壓縮(不過只支持http 2.0)。
返回數據的body也能夠作gzip壓縮,body數據體積能夠縮小到原來的30%左右。(也能夠考慮壓縮返回的json數據的key數據的體積,尤爲是針對返回數據格式變化不大的狀況,支付寶聊天返回的數據用到了)
根據用戶的當前的網絡質量來判斷下載什麼質量的圖片(電商用的比較多)