2017年秋招Android面試的21個重難點

Android 面試重難點

Android公共技術:

公共技術點之 Android 動畫基礎html

公共技術點之 Java 動態代理java

公共技術點之依賴注入linux

公共技術點之 View 事件傳遞android

公共技術點之 View 繪製流程git

1.Android的Framework和Android apk的打包過程

底層的Binder驅動,IPC的核心,SGL 2D繪圖,OpenGL 3D繪圖github

2.多線程

AsyncTask:web

AsyncTask的缺陷和問題面試

關於線程池: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)。

3.Android機制

(1)Linux Sandbox 沙箱機制:Android將數據分爲system和data兩個區。其中system是隻讀的,dada用來存放應用本身的數據,這保證了系統數據不會被隨意改寫。

應用之間的數據相互獨立,每一個應用都會有一個user id和group id,只有相同的user id而且來自同一個做者,才能訪問它們的數據。做者經過對apk簽名來標識本身,簽名和uid構成了雙重的保證。

(2)用戶權限機制:文件權限,UID,GID

(3)用戶權限機制:android permission機制限制應用訪問特定的資源,例如照相機、網絡、外部存儲等api

如何讓兩個app運行在同一個進程裏?

  1. 兩個app要用相同的private key來簽名
  2. 兩個app的Manifest文件中要添加同樣的屬性 android:sharedUserId(設置成相同的UID)

4.Binder機制

跨進程間通訊(IPC):四大組件之間經過Intent互相跳轉,Android實現IPC的方式是binder機制。

android中的跨進程通訊的實現(一)——遠程調用過程和aidl

Android中的Binder機制的簡要理解

Android中的Binder機制的簡要理解二

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之間的通訊。

這裏寫圖片描述

  1. Client、Server和ServiceManager實如今用戶空間中,Binder驅動程序實如今內核空間中
  2. Binder驅動程序和ServiceManager在Android平臺已經實現,開發者只須要在用戶空間實現本身的Client和Server
  3. Binder驅動程序提供設備文件/dev/binder與用戶空間交互,Client、Server和ServiceManager經過open和ioctl文件操做函數與Binder驅動程序進行通訊
  4. Client和Server之間的進程間通訊經過Binder驅動程序間接實現
  5. Service Manager是一個守護進程,用來管理Server,並向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

  1. 用aidl定義須要被調用方法接口
  2. 實現這些方法
  3. 調用這些方法

5.NDK

Dalvik虛擬機在調用一個成員函數的時候,若是發現該成員函數是一個JNI方法,那麼就會直接跳到它的地址去執行。也就是說,JNI方法是直接在本地操做系統執行的,而不是Dalvik虛擬機解釋器執行。由此也能夠看出,JNI方法是Android應用程序與本地操做系統直接進行通訊的一個手段。

JNI原理:

Dalvik虛擬機JNI方法的註冊過程分析

例子:當libnanosleep.so文件被加載的時候,函數JNI_OnLoad就會被調用。在函數JNI_OnLoad中,參數vm描述的是當前線程中的Dalvik虛擬機,經過調用它的成員函數GetEnv就能夠得到一個JNIEnv對象。有了這個JNIEnv對象以後,咱們就能夠調用另一個函數jniRegisterNativeMethods來向當前進程的Dalvik虛擬機註冊一個JNI方法。

6.Android系統啓動過程,App啓動過程

App啓動過程:

Activity啓動過程詳解

從桌面點擊到activity啓動的過程

一、Launcher線程捕獲onclick的點擊事件,調用Launcher.startActivitySafely,進一步調用Launcher.startActivity,最後調用父類Activity的startActivity。

二、Activity和ActivityManagerService交互,引入Instrumentation,將啓動請求交給Instrumentation,調用Instrumentation.execStartActivity。

三、調用ActivityManagerService的startActivity方法,這裏作了進程切換(具體過程請查看源碼)。

四、開啓Activity,調用onCreate方法

7.Activity,Fragment,Service生命週期

常見的例子:程序正運行着來電話了,這個程序咋辦呢?停止了唄,若是停止的時候新出的一個Activity是全屏的onPause->onStop,恢復的時候onStart->onResume,若是打斷這個應用程序的是一個Theme爲Translucent或者Dialog的Activity那麼只是onPause,恢復的時候onResume。

  • onPause:恢復的時候onResume
  • onCreate:在這裏建立界面,作一些數據的初始化工做
  • onStart:到這一步變成用戶可見不可交互的
  • onPause:到這一步是可見但不可交互的,系統會中止動畫等消耗CPU的事情,應該在這裏保存你的一些 數據,由於這個時候你的程序的優先級下降,有可能被 系統回收。在這裏保存的數據,應該在onResume裏讀出來。注意:這個方法裏作的事情時間要短,由於下一個Activity不會等到這個方法完成才啓動。
  • onStop:變得不可見,被下一個Activity覆蓋了(onPause和onStop的區別是否可見)
  • onDestroy:這是Activity被幹掉前最後一個被調用方法了,多是外面類調用finish方法或者是系統爲了節省空間將它暫時性的幹掉,能夠用isFinishing()來判斷它,若是你有一個ProgressDialog在線程中轉動,請在onDestroy裏把它cancel掉,否則等線程結束的時候,調用Dialog的cancel會拋出異常的。

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:

Android Service的生命週期

android-Service和Thread的區別

Service和Intent Service:沒啥區別,只是IntentService在onCreate方法中開啓新的HandlerThread去執行。

Service運行的進程和線程:當它運行的時候若是是LocalService,那麼對應的Service是運行在主進程的main線程上的。如onCreate,onStart這些函數都是在系統調用的時候在主進程的main線程上運行的。若是是RemoteSevice,那麼對應的Service則是運行在獨立的main線程上。

  1. 服務不是單一的進程,服務沒有本身的進程,應用程序能夠不一樣,服務運行在相同的進程中
  2. 服務不是線程,能夠在線程中工做
  3. 在應用中,若是是長時間的在後臺運行,並且不須要交互的狀況下,使用服務
  4. 一樣是在後臺運行,不須要交互的狀況下,若是隻是完成某個任務,以後就不須要運行,並且多是多個任務,須要長時間運行的狀況下使用線程
  5. 若是任務佔用CPU時間多,資源大的狀況下,要使用線程

Thread的運行是獨立於Activity的,也就是說當一個Activity被finish以後,若是你沒有主動中止Thread或者Thread裏的run方法沒有執行完畢的話,Thread就會一直執行。

這裏寫圖片描述

8.View繪畫機制

View的繪製主要涉及三個方法:onMeasure()、onLayout()、onDraw()

  1. onMeasure主要用於計算view的大小,onLayout主要用於肯定view在ContentView中的位置,onDraw主要是繪製View。
  2. 在執行onMeasure()、onLayout()方法時都會經過相應的標誌位或者對應的座標點來判斷是否須要執行對應的函數,如咱們常常調用的invalidate方法就只會執行onDraw方法,由於此時的視圖大小和位置均未發生變化,除非調用requestLayout方法完整強制進行view的繪製,從而執行上面三個方法。

進度條組件:

ProgressView

AnnotationView

9.事件傳遞機制

android 事件處理機制總結,ScrollView ViewPager ListView GridView嵌套小結

當手指觸摸到屏幕時,系統就會調用相應View的onTouchEvent,並傳入一系列的action。

dispatchTouchEvent的執行順序爲:

  1. 首先觸發ACTIVITY的dispatchTouchEvent,而後觸發ACTIVITY的onUserInteraction
  2. 而後觸發LAYOUT的dispatchTouchEvent,而後觸發LAYOUT的onInterceptTouchEvent

這就解釋了重寫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);
}
複製代碼

10.ART和Dalvik區別

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優勢:

  1. 系統性能的顯著提高
  2. 應用啓動更快、運行更快、體驗更流暢、觸感反饋更及時。
  3. 更長的電池續航能力
  4. 支持更低的硬件

ART缺點:

  1. 更大的存儲空間佔用,可能會增長10%-20%
  2. 更長的應用安裝時間

Scroller執行流程裏面的三個核心方法

  1. mScroller.startScroll()
  2. mScroller.computeScrollOffset()
  3. view.computeScroll()

一、在mScroller.startScroll()中爲滑動作了一些初始化準備,好比:起始座標,滑動的距離和方向以及持續時間(有默認值),動畫開始時間等。

二、mScroller.computeScrollOffset()方法主要是根據當前已經消逝的時間來計算當前的座標點。由於在mScroller.startScroll()中設置了動畫時間,那麼在computeScrollOffset()方法中依據已經消逝的時間就很容易獲得當前時刻應該所處的位置並將其保存在變量mCurrX和mCurrY中。除此以外該方法還可判斷動畫是否已經結束。

這裏寫圖片描述

12.Activity Manager Service, ActivityThread

13.Android幾種進程

  1. 前臺進程: 即與用戶正在交互的Activity或者Activity用到的Service等,若是系統內存不足時前臺進程是最後被殺死的
  2. 可見線程:能夠是處於暫停狀態(onPause)的Activity或者綁定在其上的Service,即被用戶可見,但因爲失去了焦點而不能與用戶交互
  3. 服務進程:其中運行着使用startService方法啓動的Service,雖然不被用戶可見,可是倒是用戶關係的,例如用戶正在非音樂界面聽的音樂或者正在非下載頁面本身下載的文件等,當系統要用空間運行前二者進程時纔會被終止
  4. 後臺進程:其中運行着執行onStop方法而中止的程序,可是卻不是用戶當前關心的,例如後臺掛着的QQ,這樣的進程系統一旦沒有內存就首先被殺死。
  5. 空進程:不包含任何應用程序的程序組件的進程,這樣的進程系統是通常不會讓他存在的。

如何避免後臺進程被殺死?

  1. 調用startForegound,讓你的Service所在的進程成爲前臺進程
  2. Service的onStartCommand返回START_STICKY或START_REDELIVER_INTENT
  3. Service的onDestroy裏面從新啓動本身

14.Activity啓動模式

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啦,要怎麼恢復就看開發者了。

15.TMImageView圖片庫的設計

Feature機制

16.ListView優化

  1. 首先,雖然你們都知道,仍是提一下,利用好convertView來重用View,切忌每次getView都新建。ListView的核心原理就是重用View。ListView有一個回收器,Item滑出界面的時候view就會回收這裏,須要顯示新的Item的時候,就儘可能重用回收器裏面的View。
  2. 利用好ViewType,例如你的ListView中有幾個類型的Item,須要給每一個類型建立不一樣的View,這樣有利於ListView的回收,固然類型不能太多
  3. 儘可能讓ItemView的Layout層次結構簡單,這時全部Layout都必須遵照的
  4. 善用自定義View,自定義View能夠有效的減少Layout的層級,並且對繪製過程能夠很好的控制
  5. 儘可能保證Adapter的hasStableIds()返回true,這樣在notifyDataSetChanged()的時候,若是id不變,listView將不會從新繪製這個View,達到優化的目的。
  6. 每一個item不能過高,特別是不要超出屏幕的高度,能夠參考Facebook的優化方法,把特別複雜的Item分解爲若干個小的Item.
  7. 爲了保證ListView滑動的流暢性,getView()中要作儘可能少的事情,不要有耗時的操做。特別是滑動的時候不要加載圖片,停下來再加載。
  8. 使用RecyclerView代替。ListView每次更新數據都要notifyDataSetChanged(),有些太暴力了。RecyclerView在性能和可定製性上都有很大的改善,推薦使用。
  9. 有時候,須要從根本上考慮,是否真的要使用listView來實現你的需求,或者是否有其餘選擇?

17.webView

如何使用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') + "";
}
複製代碼

18.SurfaceView和View的最本質的區別

SurfaceView是在一個新起的單獨線程中能夠從新繪製畫面,而view必須在UI的主線程中更新畫面。

在UI的主線程中更新畫面可能會引起問題,好比你更新的時間過長,那麼你的主UI線程就會被你正在畫的函數阻塞。那麼將沒法響應按鍵、觸屏等消息。當使用SurfaceView因爲是在新的線程中更新畫面因此不會阻塞你的UI主線程。但這也帶來了另一個問題,就是事件同步。好比你觸屏了一下,你須要SurfaceView中thread處理,通常就須要有一個event queue的設計來保存touchevent,這會稍稍複雜一點,由於涉及到線程安全。

19.標籤

merge和include

Android ViewStub的基本使用

簡言之,都是用來解決重複佈局的問題,可是標籤可以在佈局重用的時候減小UI層級結構。 viewStub標籤是用來給其餘的View事先佔據好位置,當須要的時候用inflater()或者是setVisible()方法顯示這些View。

20.ANR排錯

一、ANR排錯通常有三種類型

  1. KeyDispatchTimeout(5 seconds) –主要類型按鍵或觸摸事件在特定時間內無響應
  2. BroadcastTimeout(10 secends) –BroadcastReceiver在特定時間內沒法處理完成
  3. ServiceTimeout(20 secends) –小几率事件 Service在特定的時間內沒法處理完成

二、如何避免

  1. UI線程儘可能只作跟UI相關的工做
  2. 耗時的操做(好比數據庫操做,I/O,鏈接網絡或者別的有可能阻塞UI線程的操做)把它放在單獨的線程處理
  3. 儘可能用Handler來處理UIthread和別的thread之間的交互

三、如何排查

  1. 首先分析log
  2. 從trace.txt文件查看調用stack,adb pull data/anr/traces.txt ./mytraces.txt
  3. 看代碼
  4. 仔細查看ANR的成因(iowait?block?memoryleak?)

四、監測ANR的Watchdog

21.fragment生命週期

這裏寫圖片描述

======================常見面試問題===============================

一、橫豎屏切換時候Activity的生命週期

  1. 不設置Activityd餓android:configChanges時,切屏會從新掉喲過各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次
  2. 設置Activity的android:configChanges=」orientation」時,切屏仍是會調用各個生命週期,切換橫豎屏只會執行一次
  3. 設置Activity的android:configChanges=」orientation|keyboardHidden」時,切屏不會從新調用各個生命週期,只會執行onConfigurationChanged方法

二、(圖片相關的)OOM怎麼處理

Android 內存溢出解決方案(OOM) 整理總結

  1. 在內存引用上作些處理,經常使用的有軟引用、弱引用
  2. 在內存中加載圖片時直接在內存中做處理,如:邊界壓縮
  3. 動態回收內存
  4. 優化Dalvik虛擬機的堆內存分配
  5. 自定義堆內存大小

三、界面優化

Android開發——性能優化之如何防止過分繪製

太多重疊的背景(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裏變得寬而淺,而不是窄而深。

四、移動端獲取網絡數據優化的幾個點

  1. 鏈接複用:節省鏈接創建時間,如開啓 keep-alive。 對於Android來講默認狀況下HttpURLConnection和HttpClient都開啓了keep-alive。只是2.2以前HttpURLConnection存在影響鏈接池的Bug,具體可見:Android HttpURLConnection及HttpClient選擇

  2. 請求合併:即將多個請求合併爲一個進行請求,比較常見的就是網頁中的CSS Image Sprites。若是某個頁面內請求過多,也能夠考慮作必定的請求合併。

  3. 減小請求數據的大小:對於post請求,body能夠作gzip壓縮的,header也能夠作數據壓縮(不過只支持http 2.0)。

  4. 返回數據的body也能夠作gzip壓縮,body數據體積能夠縮小到原來的30%左右。(也能夠考慮壓縮返回的json數據的key數據的體積,尤爲是針對返回數據格式變化不大的狀況,支付寶聊天返回的數據用到了)

  5. 根據用戶的當前的網絡質量來判斷下載什麼質量的圖片(電商用的比較多)

更多免費面試資料掃描下方二維碼或搜索qq羣號642482868加羣領取。

image
相關文章
相關標籤/搜索