這是我近段時間收集的面試題,獻給打算年后找工作的同學們。文中涉及的知識比較廣也可能比較零散,并且一些較為基礎的知識我都略去了(比如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中加上權限:
View篇
非UI線程可以更新UI嗎?
可以
當訪問UI時,ViewRootImpl會調用checkThread方法去檢查當前訪問UI的線程是哪個,如果不是UI線程則會拋出異常執行onCreate方法的那個時候ViewRootImpl還沒創建,無法去檢查當前線程.ViewRootImpl的創建在onResume方法回調之后.
voidcheckThread() {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、自己畫下圖解
need-to-insert-img
3、Handler發送消息有哪幾種方式?
一、sendMessage(Message msg)
二、post(Ruunable r)
4、Handler處理消息有哪幾種方式?
這個直接看dispatchMessage()源碼:
publicvoiddispatchMessage(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()方法的最終處理方法privatestaticvoidhandleCallback(Message message){? ? message.callback.run();}//2. sendMessage()方法的最終處理方法publicvoidhandleMessage(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的,所以會報錯。
正確的使用方法是:
privatefinalclassWorkThreadextendsThread{privateHandler mHandler;publicHandlergetHandler(){returnmHandler;? ? ? ? }publicvoidquit(){? ? ? ? ? ? mHandler.getLooper().quit();? ? ? ? }@Overridepublicvoidrun(){super.run();//創建該線程對應的Looper,// 內部實現// 1。new Looper()// 2。將1步中的lopper 放在ThreadLocal里,ThreadLocal是保存數據的,主要應用場景是:線程間數據互不影響的情況// 3。在1步中的Looper的構造函數中new MessageQueue();//其實就是創建了該線程對用的Looper,Looper里創建MessageQueue來實現消息機制//對消息機制不懂得同學可以查閱資料,網上很多也講的很不錯。Looper.prepare();? ? ? ? ? ? mHandler =newHandler() {@OverridepublicvoidhandleMessage(Message msg){super.handleMessage(msg);? ? ? ? ? ? ? ? ? ? Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) +","+ msg.what);? ? ? ? ? ? ? ? }? ? ? ? ? ? };//開啟消息的死循環處理即:dispatchMessageLooper.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…
Android 2018 最新面試題(3-5年經驗個人面試經歷)
網站
作者:大頭呆
鏈接:https://juejin.im/post/5a82a07df265da4e7071c78f
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。