這是我近段時間收集的面試題,獻給打算年後找工作的同學們。文中涉及的知識比較廣也可能比較零散,並且一些較為基礎的知識我都略去了(比如Android四大元件是什麼這類問題),有些我附上了自己的理解,有些附上了詳細的相關文章連結。大家挑自己感興趣的內容檢視即可,後期我也會繼續不斷補充。
基礎元件篇
橫豎屏切換時Activity的生命週期變化?
1.如果自己沒有配置android:ConfigChanges,這時預設讓系統處理,就會重建Activity,此時Activity的生命週期會走一遍。
onSaveInstanceState() 與onRestoreIntanceState()
資源相關的系統配置發生改變或者資源不足:例如螢幕旋轉,當前Activity會銷燬,並且在onStop之前回撥onSaveInstanceState儲存資料,在重新建立Activity的時候在onStart之後回撥onRestoreInstanceState。其中Bundle資料會傳到onCreate(不一定有資料)和onRestoreInstanceState(一定有資料)。 使用者或者程式設計師主動去銷燬一個Activity的時候不會回撥,其他情況都會呼叫,來儲存介面資訊。如程式碼中finish()或使用者按下back,不會回撥。
2.如果設定 android:configChanges="orientation|keyboardHidden|screenSize">
,此時Activity的生命週期不會重走一遍,Activity不會重建,只會回撥onConfigurationChanged方法。
activity的startActivity和context的startActivity區別
(1)從Activity中啟動新的Activity時可以直接mContext.startActivity(intent)
就好,
(2)如果從其他Context中啟動Activity則必須給intent設定Flag:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ;
mContext.startActivity(intent);
複製程式碼
介紹下Android應用程式啟動過程
整個應用程式的啟動過程要執行很多步驟,但是整體來看,主要分為以下五個階段:
一. :Launcher通過Binder程式間通訊機制通知ActivityManagerService,它要啟動一個Activity;
二.:ActivityManagerService通過Binder程式間通訊機制通知Launcher進入Paused狀態;
三.:Launcher通過Binder程式間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,於是ActivityManagerService就建立一個新的程式,用來啟動一個ActivityThread例項,即將要啟動的Activity就是在這個ActivityThread例項中執行;
四. :ActivityThread通過Binder程式間通訊機制將一個ApplicationThread型別的Binder物件傳遞給ActivityManagerService,以便以後ActivityManagerService能夠通過這個Binder物件和它進行通訊;
五 :ActivityManagerService通過Binder程式間通訊機制通知ActivityThread,現在一切準備就緒,它可以真正執行Activity的啟動操作了。
複製程式碼
相關文章:Android應用程式啟動過程原始碼分析
如何保證Service不被殺死?
- 提供程式優先順序,降低程式被殺死的概率 方法一:監控手機鎖屏解鎖事件,在螢幕鎖屏時啟動1個畫素的 Activity,在使用者解鎖時將 Activity 銷燬掉。 方法二:啟動前臺service。 方法三:提升service優先順序: 在AndroidManifest.xml檔案中對於intent-filter可以通過android:priority = "1000"這個屬性設定最高優先順序,1000是最高值,如果數字越小則優先順序越低,同時適用於廣播。
- 在程式被殺死後,進行拉活 方法一:註冊高頻率廣播接收器,喚起程式。如網路變化,解鎖螢幕,開機等 方法二:雙程式相互喚起。 方法三:依靠系統喚起。 方法四:onDestroy方法裡重啟service:service +broadcast 方式,就是當service走ondestory的時候,傳送一個自定義的廣播,當收到廣播的時候,重新啟動service;
- 依靠第三方 根據終端不同,在小米手機(包括 MIUI)接入小米推送、華為手機接入華為推送;其他手機可以考慮接入騰訊信鴿或極光推送與小米推送做 A/B Test。
簡述下Acitivty任務棧和使用方法
任務棧是一種後進先出的結構。位於棧頂的Activity處於焦點狀態,當按下back按鈕的時候,棧內的Activity會一個一個的出棧,並且呼叫其onDestory()方法。如果棧內沒有Activity,那麼系統就會回收這個棧,每個APP預設只有一個棧,以APP的包名來命名. 1、standard:預設模式:每次啟動都會建立一個新的activity物件,放到目標任務棧中
2、singleTop:判斷當前的任務棧頂是否存在相同的activity物件,如果存在,則直接使用,如果不存在,那麼建立新的activity物件放入棧中
3、singleTask:在任務棧中會判斷是否存在相同的activity,如果存在,那麼會清除該activity之上的其他activity物件顯示,如果不存在,則會建立一個新的activity放入棧頂
4、singleIntance:會在一個新的任務棧中建立activity,並且該任務棧種只允許存在一個activity例項,其他呼叫該activity的元件會直接使用該任務棧種的activity物件
方法一:
使用android:launchMode="standard|singleInstance|single Task|singleTop"來控制Acivity任務棧。
方法二:
Intent Flags:
Intent intent=new Intent();
intent.setClass(MainActivity.this, MainActivity2.class);
intent.addFlags(Intent. FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
複製程式碼
Flags有很多,比如:
Intent.FLAG_ACTIVITY_NEW_TASK 相當於singleTask
Intent. FLAG_ACTIVITY_CLEAR_TOP 相當於singleTop
相關文章:Android總結篇系列:Activity Intent Flags及Task相關屬性
Context相關問題
Activity和Service以及Application的Context是不一樣的,Activity繼承自ContextThemeWraper.其他的繼承自ContextWrapper.
每一個Activity和Service以及Application的Context都是一個新的ContextImpl物件
getApplication()用來獲取Application例項的,但是這個方法只有在Activity和Service中才能呼叫的到。那麼也許在絕大多數情況下我們都是在Activity或者Service中使用Application的,但是如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的例項,這時就可以藉助getApplicationContext()方法.
getApplicationContext()比getApplication()方法的作用域會更廣一些,任何一個Context的例項,只要呼叫getApplicationContext()方法都可以拿到我們的Application物件。
Context的數量等於Activity的個數 + Service的個數 + 1,這個1為Application.
那Broadcast Receiver,Content Provider呢?Broadcast Receiver,Content Provider並不是Context的子類,他們所持有的Context都是其他地方傳過去的,所以並不計入Context總數。
怎麼在Service中建立Dialog對話方塊
1.在我們取得Dialog物件後,需給它設定型別,即:dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
2.在Manifest中加上許可權:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
View篇
非UI執行緒可以更新UI嗎?
可以
當訪問UI時,ViewRootImpl會呼叫checkThread
方法去檢查當前訪問UI的執行緒是哪個,如果不是UI執行緒則會丟擲異常
執行onCreate方法的那個時候ViewRootImpl還沒建立,無法去檢查當前執行緒.ViewRootImpl的建立在onResume方法回撥之後.
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
複製程式碼
非UI執行緒是可以重新整理UI的,前提是它要擁有自己的ViewRoot,即更新UI的執行緒和建立ViewRoot是同一個,或者在執行checkThread()
前更新UI.
相關文章:Android子執行緒真的不能更新UI麼
解決ScrollView巢狀ListView和GridView衝突的方法
重寫ListView的onMeasure方法,來自定義高度:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
複製程式碼
主要考察對MeasureSpec的三種模式的理解,相關文章.
自定義View優化策略
為了加速你的view,對於頻繁呼叫的方法,需要儘量減少不必要的程式碼。先從onDraw開始,需要特別注意不應該在這裡做記憶體分配的事情,因為它會導致GC,從而導致卡頓。在初始化或者動畫間隙期間做分配記憶體的動作。不要在動畫正在執行的時候做記憶體分配的事情。
你還需要儘可能的減少onDraw被呼叫的次數,大多數時候導致onDraw都是因為呼叫了invalidate().因此請儘量減少呼叫invaildate()的次數。如果可能的話,儘量呼叫含有4個引數的invalidate()方法而不是沒有引數的invalidate()。沒有引數的invalidate會強制重繪整個view。
另外一個非常耗時的操作是請求layout。任何時候執行requestLayout(),會使得Android UI系統去遍歷整個View的層級來計算出每一個view的大小。如果找到有衝突的值,它會需要重新計算好幾次。另外需要儘量保持View的層級是扁平化的,這樣對提高效率很有幫助。
如果你有一個複雜的UI,你應該考慮寫一個自定義的ViewGroup來執行他的layout操作。與內建的view不同,自定義的view可以使得程式僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小。這個PieChart 例子展示瞭如何繼承ViewGroup作為自定義view的一部分。PieChart 有子views,但是它從來不測量它們。而是根據他自身的layout法則,直接設定它們的大小。
執行緒篇
Handler、Message、Looper、MessageQueue
一、相關概念的解釋
主執行緒(UI執行緒)
定義:當程式第一次啟動時,Android會同時啟動一條主執行緒(Main Thread)
作用:主執行緒主要負責處理與UI相關的事件
Message(訊息)
定義:Handler接收和處理的訊息物件(Bean物件)
作用:通訊時相關資訊的存放和傳遞
ThreadLocal
定義:ThreadLocal是執行緒內部的儲存類,通過它可以實現在每個執行緒中儲存自己的私有資料。即資料儲存以後,只能在指定的執行緒中獲取這個儲存的物件,而其它執行緒則不能獲取到當前執行緒儲存的這個物件。
作用:負責儲存和獲取本執行緒的Looper
MessageQueue(訊息佇列)
定義:採用單連結串列的資料結構來儲存訊息列表
作用:用來存放通過Handler發過來的Message,按照先進先出執行
Handler(處理者)
定義:Message的主要處理者
作用:負責傳送Message到訊息佇列&處理Looper分派過來的Message
Looper(迴圈器)
定義:扮演Message Queue和Handler之間橋樑的角色
作用:
訊息迴圈:迴圈取出Message Queue的Message
訊息派發:將取出的Message交付給相應的Handler
2、自己畫下圖解
3、Handler傳送訊息有哪幾種方式?
一、sendMessage(Message msg) 二、post(Ruunable r)
4、Handler處理訊息有哪幾種方式?
這個直接看dispatchMessage()
原始碼:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//1. post()方法的處理方法
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//2. sendMessage()方法的處理方法
handleMessage(msg);
}
}
//1. post()方法的最終處理方法
private static void handleCallback(Message message) {
message.callback.run();
}
//2. sendMessage()方法的最終處理方法
public void handleMessage(Message msg) {
}
複製程式碼
5.Message、Handler、MessageQueue、Looper的之間的關係?
首先,是這個MessagQueue,MessageQueue是一個訊息佇列,它可以儲存Handler傳送過來的訊息,其內部提供了進隊和出隊的方法來管理這個訊息佇列,其出隊和進隊的原理是採用單連結串列的資料結構進行插入和刪除的,即enqueueMessage()方法和next()方法。這裡提到的Message,其實就是一個Bean物件,裡面的屬性用來記錄Message的各種資訊。
然後,是這個Looper,Looper是一個迴圈器,它可以迴圈的取出MessageQueue中的Message,其內部提供了Looper的初始化和迴圈出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper會關聯一個MessageQueue,而且將Looper存進一個ThreadLocal中,在loop()方法中,通過ThreadLocal取出Looper,使用MessageQueue的next()方法取出Message後,判斷Message是否為空,如果是則Looper阻塞,如果不是,則通過dispatchMessage()方法分發該Message到Handler中,而Handler執行handlerMessage()方法,由於handlerMessage()方法是個空方法,這也是為什麼需要在Handler中重寫handlerMessage()方法的原因。這裡要注意的是Looper只能在一個執行緒中只能存在一個。這裡提到的ThreadLocal,其實就是一個物件,用來在不同執行緒中存放對應執行緒的Looper。
最後,是這個Handler,Handler是Looper和MessageQueue的橋樑,Handler內部提供了傳送Message的一系列方法,最終會通過MessageQueue的enqueueMessage()方法將Message存進MessageQueue中。我們平時可以直接在主執行緒中使用Handler,那是因為在應用程式啟動時,在入口的main方法中已經預設為我們建立好了Looper。
相關文章:blog.csdn.net/qq_30379689…
6.為什麼在子執行緒中建立Handler會拋異常?
Handler的工作是依賴於Looper的,而Looper(與訊息佇列)又是屬於某一個執行緒(ThreadLocal是執行緒內部的資料儲存類,通過它可以在指定執行緒中儲存資料,其他執行緒則無法獲取到),其他執行緒不能訪問。因此Handler就是間接跟執行緒是繫結在一起了。因此要使用Handler必須要保證Handler所建立的執行緒中有Looper物件並且啟動迴圈。因為子執行緒中預設是沒有Looper的,所以會報錯。 正確的使用方法是:
private final class WorkThread extends Thread {
private Handler mHandler;
public Handler getHandler() {
return mHandler;
}
public void quit() {
mHandler.getLooper().quit();
}
@Override
public void run() {
super.run();
//建立該執行緒對應的Looper,
// 內部實現
// 1。new Looper()
// 2。將1步中的lopper 放在ThreadLocal裡,ThreadLocal是儲存資料的,主要應用場景是:執行緒間資料互不影響的情況
// 3。在1步中的Looper的建構函式中new MessageQueue();
//其實就是建立了該執行緒對用的Looper,Looper裡建立MessageQueue來實現訊息機制
//對訊息機制不懂得同學可以查閱資料,網上很多也講的很不錯。
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
}
};
//開啟訊息的死迴圈處理即:dispatchMessage
Looper.loop();
//注意這3個的順序不能顛倒
Log.d("WorkThread", "end");
}
}
複製程式碼
HandlerThread
1、HandlerThread作用
當系統有多個耗時任務需要執行時,每個任務都會開啟一個新執行緒去執行耗時任務,這樣會導致系統多次建立和銷燬執行緒,從而影響效能。為了解決這一問題,Google提供了HandlerThread,HandlerThread是線上程中建立一個Looper迴圈器,讓Looper輪詢訊息佇列,當有耗時任務進入佇列時,則不需要開啟新執行緒,在原有的執行緒中執行耗時任務即可,否則執行緒阻塞。
2、HanlderThread的優缺點
- HandlerThread本質上是一個執行緒類,它繼承了Thread;
- HandlerThread有自己的內部Looper物件,可以進行looper迴圈;
- 通過獲取HandlerThread的looper物件傳遞給Handler物件,可以在
handleMessage()
方法中執行非同步任務。 - 建立HandlerThread後必須先呼叫
HandlerThread.start()
方法,Thread會先呼叫run方法,建立Looper物件。 - HandlerThread優點是非同步不會堵塞,減少對效能的消耗
- HandlerThread缺點是不能同時繼續進行多工處理,需要等待進行處理,處理效率較低
- HandlerThread與執行緒池不同,HandlerThread是一個序列佇列,背後只有一個執行緒。
相關文章:Android 多執行緒之IntentService 完全詳解
IntentService
-
它本質是一種特殊的Service,繼承自Service並且本身就是一個抽象類
-
它可以用於在後臺執行耗時的非同步任務,當任務完成後會自動停止
-
它擁有較高的優先順序,不易被系統殺死(繼承自Service的緣故),因此比較適合執行一些高優先順序的非同步任務 它內部通過HandlerThread和Handler實現非同步操作
-
建立IntentService時,只需實現onHandleIntent和構造方法,onHandleIntent為非同步方法,可以執行耗時操作
-
即使我們多次啟動IntentService,但IntentService的例項只有一個,這跟傳統的Service是一樣的,最終IntentService會去呼叫onHandleIntent執行非同步任務。
-
當任務完成後,IntentService會自動停止,而不需要手動呼叫
stopSelf()
。另外,可以多次啟動IntentService,每個耗時操作都會以工作佇列的方式在IntentService
中onHandlerIntent()
回撥方法中執行,並且每次只會執行一個工作執行緒。
相關文章:Android 多執行緒之IntentService 完全詳解
AsyncTask
1、AsyncTask是什麼
AsyncTask是一種輕量級的非同步任務類,它可以線上程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主執行緒並主執行緒中更新UI,通過AsyncTask可以更加方便執行後臺任務以及在主執行緒中訪問UI,但是AsyncTask並不適合進行特別耗時的後臺任務,對於特別耗時的任務來說,建議使用執行緒池。
2、AsyncTask使用方法
三個引數 Params:表示後臺任務執行時的引數型別,該引數會傳給AysncTask的doInBackground()方法 Progress:表示後臺任務的執行進度的引數型別,該引數會作為onProgressUpdate()方法的引數 Result:表示後臺任務的返回結果的引數型別,該引數會作為onPostExecute()方法的引數 五個方法 onPreExecute():非同步任務開啟之前回撥,在主執行緒中執行 doInBackground():執行非同步任務,線上程池中執行 onProgressUpdate():當doInBackground中呼叫publishProgress時回撥,在主執行緒中執行 onPostExecute():在非同步任務執行之後回撥,在主執行緒中執行 onCancelled():在非同步任務被取消時回撥
3、AsyncTask引起的記憶體洩漏
原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放
解決:
AsyncTask內部持有外部Activity的弱引用
AsyncTask改為靜態內部類
Activity的onDestory()中呼叫AsyncTask.cancel()
4.結果丟失
螢幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新建立,之前執行的AsyncTask會持有一個之前Activity的引用,這個引用已經無效,這時呼叫onPostExecute()再去更新介面將不再生效。
5、AsyncTask並行or序列
AsyncTask在Android 2.3之前預設採用並行執行任務,AsyncTask在Android 2.3之後預設採用序列執行任務
如果需要在Android 2.3之後採用並行執行任務,可以呼叫AsyncTask的executeOnExecutor();
6.AsyncTask內部的執行緒池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
sDefaultExecutor
是SerialExecutor
的一個例項,而且它是個靜態變數。也就是說,一個程式裡面所有AsyncTask物件都共享同一個SerialExecutor
物件。
相關文章:android多執行緒-AsyncTask之工作原理深入解析
一些面試心經
一般情況下第一輪都是基礎面試,需要紮實的基礎
最常用的Android 基礎知識
Java 基礎知識
瞭解一些 常用東西的原理,例如:handler, thread 等
專案中的技術點
第二輪的時候需要了解更深層次的東西
Android 事件分發機制原理
Android 繪圖機制原理
WindowManager 的相關知識
程式間傳輸方式
Java 記憶體管理機制
一些常用的 list,map 原理,以及子類之間的差別
能進入第三輪基本沒什麼問題,但是要注意以下問題
該輪一般是 老大或者部門負責人,問的問題一般都看 深度與廣度
當問及薪水的時候,要說一個合適的,小公司隨意,大公司一定要慎重,當心裡沒底的時候,可以告訴對方,讓對方給一個合理的薪資。一般都是在原工資基礎之上增長,聽獵頭說一般漲幅都在15%-30%,超 NB 的可以要30%及以上,如果感覺自己還不錯的,挺厲害的,建議最高20%,一般人就定在15% 左右最靠譜。公司內部一般有一套機制,根據公司情況而定。
我們的面試原則就是拿到合理薪資,得到 offer
個人發展情況,這個問題很難回答,如果和公司方向不符合,極有可能和公司無緣。建議多試探性的問問公司缺少什麼,你能否給予公司對應的東西。當然對於有自我追求的人,那可以放心大膽的提。我的方向就是架構師,哈哈哈,挺極端的,別學我哦。我感覺選擇都是雙向的,因此我知道自己需要的是什麼。
你最擅長什麼UI 還是其他什麼?這個問題更不好回答。你要說你擅長 UI,是不是意味著你其他能力就不行?雖然我不知道面試官的用意,但是我能感覺到,這個問題不是那麼好回答,我會回答說自己都行,來什麼業務接什麼需求。可能回答不太好,總之和公司的職位吻合就行,這樣總不至於出錯吧。
摘自文章:一個五年Android開發者百度、阿里、聚美、映客的面試心經
簡歷模板推薦:
更多面試題庫:
www.jackywang.tech/AndroidInte…
github.com/AweiLoveAnd…
github.com/hadyang/int…
jingbin.me/2017/02/20/…
github.com/Freelander/…
www.jianshu.com/p/1d3a2227f…
github.com/stormzhang/…
www.zhihu.com/question/37…
Android 2018 最新面試題(3-5年經驗個人面試經歷)