面試複習——Android工程師之Android面試大綱
Activity面試題
1、Activity是什麼
Activity是四大元件之一,它提供一個介面讓使用者點選和各種滑動操作,這就是Activity
2、Activity四種狀態
- runing
- paused
- stopped
- killed
3、Activity生命週期
- onCreate()
- onStart()
- onResume()
- onPause()
- onStop()
- onDestroy()
- onRestart()
4、程序的優先順序
oom_adj
是linux核心分配給每個系統程序的一個值,代表程序的優先順序,程序回收機制就是根據這個優先順序來決定是否進行回收。程序的oom_adj
oom_adj
>=0,系統程序的oom_adj
才可能小於0。程序優先順序從小到大如下
- 空程序
- 後臺程序
- 服務程序
- 可見程序
- 前臺程序
5、Activity任務棧
- 先進後出
6、Activity啟動模式
- standard
- singletop
- singletask
- singleinstance
7、scheme跳轉協議
android中的scheme是一種頁面內跳轉協議,通過定義自己的scheme協議,可以跳轉到app中的各個頁面
- 伺服器可以定製化告訴app跳轉哪個頁面
- App可以通過跳轉到另一個App頁面
- 可以通過H5頁面跳轉頁面
8、Context、Activity、Application之間有什麼區別
Activity和Application都是Context的子類。Context從字面上理解就是上下文的意思,在實際應用中它也確實是起到了管理上下文環境中各個引數和變數的總用,方便我們可以簡單的訪問到各種資源。雖然Activity和Application都是Context的子類,但是他們維護的生命週期不一樣。前者維護一個Acitivity的生命週期,所以其對應的Context也只能訪問該activity內的各種資源。後者則是維護一個Application的生命週期
9、Activity啟動過程
- 在安裝應用的時候,系統會啟動PackaManagerService管理服務,這個管理服務會對AndroidManifest進行解析,從而得到應用程式中的相關資訊,比如service,activity,Broadcast等等,然後獲得相關元件的資訊
- 當用戶點選應用圖示時,就會呼叫
startActivitySately()
,而這個方法內部則是呼叫startActivty()
,startActivity()
最終還是會呼叫startActivityForResult()
。由於startActivityForResult()
是有返回結果的,系統直接返回-1,表示不需要返回結果 startActivityForResult()
通過Instrumentation類中的execStartActivity()
來啟動activity,Instrumentation這個類主要作用是監控程式和系統之間的互動。在這個execStartActivity()
中會獲取ActivityManagerService的代理物件,通過這個代理物件進行啟動activity- 在ActivityManagerService的代理物件中,通過Binder通訊,呼叫到
ApplicationThread.scheduleLaunchActivity()
進行啟動activity,在這個方法中建立一個ActivityClientRecord物件,用來記錄啟動Activity元件的資訊,然後通過handler將ActivityClientRecord傳送出去 - 在handler收到訊息後,呼叫
ActivityThread.handleLaunchActivity()
啟動Activity
10、簡述Activity,View,Window三者關係
- Activity本質是上下文,View本質是檢視,Window本質是視窗
- Activity構造的時候會初始化一個Window,其具體實現是PhoneWindow
- PhoneWindow中有一個ViewRoot(View或ViewGroup),是最初始的根檢視
- ViewRoot通過addView()將View新增到根檢視上,實際上是將View交給了PhoneWindow處理
- View的事件監聽是由WindowManagerService來接受訊息,並且回撥Activity函式
Fragment面試題
1、Fragment為什麼被稱為第五大元件
Fragment比Activity更節省記憶體,其切換模式也更加舒適,使用頻率不低於四大元件,且有自己的生命週期,而且必須依附於Activity
2、Activity建立Fragment的方式
- 靜態建立
- 動態建立
3、FragmentPageAdapter和FragmentPageStateAdapter的區別
- FragmentPageAdapter在每次切換頁面的的時候,是將Fragment進行分離,適合頁面較少的Fragment使用以儲存一些記憶體,對系統記憶體不會多大影響
- FragmentPageStateAdapter在每次切換頁面的時候,是將Fragment進行回收,適合頁面較多的Fragment使用,這樣就不會消耗更多的記憶體
4、Fragment生命週期
- onAttach()
- onCreate()
- onCreateView()
- onActivityCreated()
- onStart()
- onResume()
- onPause()
- onStop()
- onDestroyView()
- onDestroy()
- onDetach()
5、Fragment的通訊
- Fragment呼叫Activity中的方法:getActivity
- Activity呼叫Fragment中的方法:介面回撥
- Fragment呼叫Fragment中的方法:FragmentManager.findFragmentById
6、Fragment的replace、add、remove方法
- replace:替代Fragment的棧頂頁面
- add:新增Fragment到棧頂頁面
- remove:移除Fragment棧頂頁面
Service面試題
1、Service是什麼
Service是四大元件之一,它可以在後臺執行長時間執行操作而沒有使用者介面的應用元件
2、Service和Thread的區別
- Service是安卓中系統的元件,它執行在獨立程序的主執行緒中,不可以執行耗時操作。Thread是程式執行的最小單元,分配CPU的基本單位,可以開啟子執行緒執行耗時操作
- Service在不同Activity中可以獲取自身例項,可以方便的對Service進行操作。Thread在不同的Activity中難以獲取自身例項,如果Activity被銷燬,Thread例項就很難再獲取得到
3、Service啟動方式
- startService
- bindService
4、Service生命週期
startService
- onCreate()
- onStartCommand()
- onDestroy()
bindService
- onCreate()
- onBind()
- onUnbind()
- onDestroy()
Broadcast Receiver面試題
1、Broadcast Receiver是什麼
Broadcast是四大元件之一,是一種廣泛運用在應用程式之間傳輸資訊的機制,通過傳送Intent來傳送我們的資料
2、Broadcast Receiver的使用場景
- 同一App具有多個程序的不同元件之間的訊息通訊
- 不同App之間的元件之間的訊息通訊
3、Broadcast Receiver的種類
- 普通廣播
- 有序廣播
- 本地廣播
- Sticky廣播
4、Broadcast Receiver的實現
- 靜態註冊:註冊後一直執行,儘管Activity、程序、App被殺死還是可以接收到廣播
- 動態註冊:跟隨Activity的生命週期
5、Broadcast Receiver實現機制
- 自定義廣播類繼承BroadcastReceiver,複寫onReceiver()
- 通過Binder機制向AMS進行註冊廣播
- 廣播發送者通過Binder機制向AMS傳送廣播
- AMS查詢符合相應條件的廣播發送到BroadcastReceiver相應的迴圈佇列中
- 訊息佇列執行拿到廣播,回撥BroadcastReceiver的onReceiver()
6、LocalBroadcastManager特點
- 本地廣播只能在自身App內傳播,不必擔心洩漏隱私資料
- 本地廣播不允許其他App對你的App傳送該廣播,不必擔心安全漏洞被利用
- 本地廣播比全域性廣播更高效
- 以上三點都是源於其內部是用Handler實現的
WebView面試題
1、WebView安全漏洞
- API16之前存在遠端程式碼執行安全漏洞,該漏洞源於程式沒有正確限制使用WebView.addJavascriptInterface方法,遠端攻擊者可通過使用Java反射機制利用該漏洞執行任意Java物件的方法
2、WebView銷燬步驟
- WebView在其他容器上時(如:LinearLayout),當銷燬Activity時,需要在onDestroy()中先移除容器上的WebView,然後再將WebView.destroy(),這樣就不會導致記憶體洩漏
3、WebView的jsbridge
- 客戶端和服務端之間可以通過Javascript來互相呼叫各自的方法
4、WebViewClient的onPageFinished
- WebViewClient的onPageFinished在每次完成頁面的時候呼叫,但是遇到未載入完成的頁面跳轉其他頁面時,就會一直呼叫,使用WebChromeClient.onProgressChanged可以替代
5、WebView後臺耗電
- 在WebView載入頁面的時候,會自動開啟執行緒去載入,如果不很好的關閉這些執行緒,就會導致電量消耗加大,可以採用暴力的方法,直接在onDestroy方法中System.exit(0)結束當前正在執行中的java虛擬機器
6、WebView硬體加速
Android3.0引入硬體加速,預設會開啟,WebView在硬體加速的情況下滑動更加平滑,效能更加好,但是會出現白塊或者頁面閃爍的副作用,建議WebView暫時關閉硬體加速
7、WebView記憶體洩漏
由於WebView是依附於Activity的,Activity的生命週期和WebView啟動的執行緒的生命週期是不一致的,這會導致WebView一直持有對這個Activity的引用而無法釋放,解決方案如下
- 獨立程序,簡單暴力,不過可能涉及到程序間通訊(推薦)
- 動態新增WebView,對傳入WebView中使用的Context使用弱引用
Binder面試題
1、Linux核心的基本知識
- 程序隔離/虛擬地址空間:程序間是不可以共享資料的,相當於被隔離,每個程序被分配到不同的虛擬地址中
- 系統呼叫:Linux核心對應用有訪問許可權,使用者只能在應用層通過系統呼叫,呼叫核心的某些程式
- binder驅動:它負責各個使用者的程序,通過binder通訊核心來進行互動的模組
2、為什麼使用Binder
- 效能上,相比傳統的Socket更加高效
- 安全性高,支援協議雙方互相校驗
3、Binder通訊原理
- Service端通過Binder驅動在ServiceManager的查詢表中註冊Object物件的add方法
- Client端通過Binder驅動在ServiceManager的查詢表中找到Object物件的add方法,並返回proxy物件的add方法,add方法是個空實現,proxy物件也不是真正的Object物件,是通過Binder驅動封裝好的代理類的add方法
- 當Client端呼叫add方法時,Client端會呼叫proxy物件的add方法,通過Binder驅動去請求ServiceManager來找到Service端真正物件,然後呼叫Service端的add方法
4、AIDL
- 客戶端通過aidl檔案的Stub.asInterface()方法,拿到Proxy代理類
- 通過呼叫Proxy代理類的方法,將引數進行封包後,呼叫底層的transact()方法
- transact()方法會回撥onTransact()方法,進行引數的解封
- 在onTransact()方法中呼叫服務端對應的方法,並將結果返回
5、BpBinder和BBinder
BpBinder(客戶端)物件和BBinder(服務端)物件,它們都從IBinder類中派生而來,BpBinder(客戶端)物件是BBinder(服務端)物件的代理物件,關係圖如下
- client端:BpBinder.transact()來發送事務請求
- server端:BBinder.onTransact()會接收到相應事務
Handler面試題
1、Handler是什麼
Handler通過傳送和處理Message和Runnable物件來關聯相對應執行緒的MessageQueue
2、Handler使用方法
- post(runnable)
- sendMessage(message)
3、Handler工作原理
4、Handler引起的記憶體洩漏
- 原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放
- 解決:
- Handler內部持有外部Activity的弱引用
- Handler改為靜態內部類
- Handler.removeCallback()
AsyncTask面試題
1、AsyncTask是什麼
它本質上就是一個封裝了執行緒池和Handler的非同步框架
2、AsyncTask使用方法
三個引數
- Params:表示後臺任務執行時的引數型別,該引數會傳給AysncTask的doInBackground()方法
- Progress:表示後臺任務的執行進度的引數型別,該引數會作為onProgressUpdate()方法的引數
- Result:表示後臺任務的返回結果的引數型別,該引數會作為onPostExecute()方法的引數
五個方法
- onPreExecute():非同步任務開啟之前回調,在主執行緒中執行
- doInBackground():執行非同步任務,線上程池中執行
- onProgressUpdate():當doInBackground中呼叫publishProgress時回撥,在主執行緒中執行
- onPostExecute():在非同步任務執行之後回撥,在主執行緒中執行
- onCancelled():在非同步任務被取消時回撥
3、AsyncTask工作原理
4、AsyncTask引起的記憶體洩漏
- 原因:非靜態內部類持有外部類的匿名引用,導致Activity無法釋放
- 解決:
- AsyncTask內部持有外部Activity的弱引用
- AsyncTask改為靜態內部類
- AsyncTask.cancel()
5、AsyncTask生命週期
在Activity銷燬之前,取消AsyncTask的執行,以此來保證程式的穩定
6、AsyncTask結果丟失
由於螢幕旋轉、Activity在記憶體緊張時被回收等情況下,Activity會被重新建立,此時,舊的AsyncTask持有舊的Activity引用,這個時候會導致AsyncTask的onPostExecute()對UI更新無效
7、AsyncTask並行or序列
- AsyncTask在Android 2.3之前預設採用並行執行任務,AsyncTask在Android 2.3之後預設採用序列執行任務
- 如果需要在Android 2.3之後採用並行執行任務,可以呼叫AsyncTask的executeOnExecutor()
HandlerThread面試題
1、HandlerThread產生背景
當系統有多個耗時任務需要執行時,每個任務都會開啟一個新執行緒去執行耗時任務,這樣會導致系統多次建立和銷燬執行緒,從而影響效能。為了解決這一問題,Google提供了HandlerThread,HandlerThread是線上程中建立一個Looper迴圈器,讓Looper輪詢訊息佇列,當有耗時任務進入佇列時,則不需要開啟新執行緒,在原有的執行緒中執行耗時任務即可,否則執行緒阻塞
2、HanlderThread的特點、
- HandlerThread本質上是一個執行緒,繼承自Thread
- HandlerThread有自己的Looper物件,可以進行Looper迴圈,可以建立Handler
- HandlerThread可以在Handler的handlerMessage中執行非同步方法
- HandlerThread優點是非同步不會堵塞,減少對效能的消耗
- HandlerThread缺點是不能同時繼續進行多工處理,需要等待進行處理,處理效率較低
- HandlerThread與執行緒池不同,HandlerThread是一個序列佇列,背後只有一個執行緒
IntentService面試題
1、IntentService是什麼
IntentService是繼承自Service並處理非同步請求的一個類,其內部採用HandlerThread和Handler實現的,在IntentService內有一個工作執行緒來處理耗時操作,其優先順序比普通Service高。當任務完成後,IntentService會自動停止,而不需要手動呼叫stopSelf()。另外,可以多次啟動IntentService,每個耗時操作都會以工作佇列的方式在IntentService中onHandlerIntent()回撥方法中執行,並且每次只會執行一個工作執行緒
2、IntentService使用方法
- 建立Service繼承自IntentService
- 覆寫構造方法和onHandlerIntent()方法
- 在onHandlerIntent()中執行耗時操作
檢視工作機制面試題
事件分發機制面試題
ListView面試題
1、ListView是什麼
ListView是能將一個數據集合以動態滾動的方式展示到使用者介面上的View
2、ListView的RecycleBin機制
3、ListView的優化
- 重用convertView
- 使用ViewHolder
- 圖片三級快取
- 監聽滑動事件
- 少用透明View
- 開啟硬體加速
4、ListView和RecyclerView的區別
- RecyclerView可以完成ListView,GridView的效果,還可以完成瀑布流的效果,同時還可以設定列表的滾動方向
- RecyclerView中View的複用不需要開發者自己寫程式碼,系統已經幫封裝完成了
- RecyclerView提供了API來實現item的動畫效果
- RecyclerView可以進行區域性重新整理
Android專案構建面試題
1、Android構建流程
2、jenkins持續整合構建
- 這裡可參考蒲公英文件
3、git常用命令
- git init:倉庫的初始化
- git status:檢視當前倉庫的狀態
- git diff:檢視倉庫與上次修改的內容
- git add:將檔案放進暫存區
- git commit:提交程式碼
- git clone:克隆程式碼
- git bransh:檢視當前分支
- git checkout:切換當前分支
4、git工作流
- fork/clone(主流)
- fork:將別人的倉庫程式碼fork到自己的倉庫上
- clone:克隆下自己倉庫的程式碼
- update、commit:修改程式碼並提交到自己的倉庫
- push:提交到自己的倉庫
- pull request:請求新增到別人的倉庫
- clone
5、proguard是什麼
ProGuard工具是用於壓縮、優化和混淆我們的程式碼,其主作用是移除或混淆程式碼中無用類、欄位、方法和屬性
6、proguard技術功能
- 壓縮
- 優化
- 混淆
- 預檢測
7、proguard工作原理
將無用的欄位或方法存入到EntryPoint中,將非EntryPoint的欄位和方法進行替換
8、為什麼要混淆
由於Java是一門跨平臺的解釋性語言,其原始碼被編譯成class位元組碼來適應其他平臺,而class檔案包含了Java原始碼資訊,很容易被反編譯
9、annotationProcessor與compileOnly的區別
annotationProcessor與compileOnly都是隻編譯並不打入apk中
- annotationProcessor:編譯時生成程式碼,編譯完就不需要了
- compileOnly:有重複的庫時,可以剃除重複庫,只保留一個庫
ANR面試題
1、什麼是ANR
Application Not Responding,頁面無響應的對話方塊
2、發生ANR的條件
應用程式的響應性是由ActivityManager和WindowManager系統服務監視的,當ANR發生條件滿足時,就會彈出ANR的對話方塊
- Activity超過5秒無響應
- BroadcastReceiver超過10秒無響應
- Service超過20秒無響應
3、造成ANR的主要原因
主執行緒被IO操作阻塞
- Activity的所有生命週期回撥都是執行在主執行緒的
- Service預設執行在主執行緒中
- BoardcastReceiver的回撥onReceive()執行在主執行緒中
- AsyncTask的回撥除了doInBackground,其他都是在主執行緒中
- 沒有使用子執行緒Looper的Handler的handlerMessage,post(Runnable)都是執行在主執行緒中
4、如何解決ANR
- 使用AsyncTask處理耗時IO操作
- 使用Thread或HandlerThread提高優先順序
- 使用Handler處理工作執行緒的耗時操作
- Activity的onCreate和onResume回撥儘量避免耗時操作
OOM面試題
1、什麼是OOM
OOM指Out of memory(記憶體溢位),當前佔用記憶體加上我們申請的記憶體資源超過了Dalvik虛擬機器的最大記憶體限制就會丟擲Out of memory異常
2、OOM相關概念
- 記憶體溢位:指程式在申請記憶體時,沒有足夠的空間供其使用
- 記憶體洩漏:指程式分配出去的記憶體不再使用,無法進行回收
- 記憶體抖動:指程式短時間內大量建立物件,然後回收的現象
3、解決OOM
Bitmap相關
- 圖片壓縮
- 載入縮圖
- 在滾動時不載入圖片
- 回收Bitmap
- 使用inBitmap屬性
- 捕獲異常
其他相關
- listview重用convertView、使用lru
- 避免onDraw方法執行物件的建立
- 謹慎使用多程序
Bitmap面試題
1、recycle
- 在安卓3.0以前Bitmap是存放在堆中的,我們只要回收堆記憶體即可
- 在安卓3.0以後Bitmap是存放在記憶體中的,我們需要回收native層和Java層的記憶體
- 官方建議我們3.0以後使用recycle方法進行回收,該方法也可以不主動呼叫,因為垃圾回收器會自動收集不可用的Bitmap物件進行回收
- recycle方法會判斷Bitmap在不可用的情況下,將傳送指令到垃圾回收器,讓其回收native層和Java層的記憶體,則Bitmap進入dead狀態
- recycle方法是不可逆的,如果再次呼叫getPixels()等方法,則獲取不到想要的結果
2、LruCache原理
LruCache是個泛型類,內部採用LinkedHashMap來實現快取機制,它提供get方法和put方法來獲取快取和新增快取,其最重要的方法trimToSize是用來移除最少使用的快取和使用最久的快取,並新增最新的快取到佇列中
3、計算取樣率
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
4、取樣率壓縮(縮圖)
public static Bitmap thumbnail(String path,int maxWidth, int maxHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
options.inJustDecodeBounds = false;
int sampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inPurgeable = true;
options.inInputShareable = true;
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
bitmap = BitmapFactory.decodeFile(path, options);
return bitmap;
}
5、質量壓縮
public static String save(Bitmap bitmap,Bitmap.CompressFormat format, int quality, File destFile) {
try {
FileOutputStream out = new FileOutputStream(destFile);
if (bitmap.compress(format, quality, out)) {
out.flush();
out.close();
}
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
return destFile.getAbsolutePath();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
6、尺寸壓縮
public static void reSize(Bitmap bmp,File file,int ratio){
Bitmap result = Bitmap.createBitmap(bmp.getWidth()/ratio, bmp.getHeight()/ratio,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
RectF rect = new RectF(0, 0, bmp.getWidth()/ratio, bmp.getHeight()/ratio);
canvas.drawBitmap(bmp, null, rect , null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
result.compress(Bitmap.CompressFormat.JPEG, 100, baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
7、儲存到SD卡
public static String save(Bitmap bitmap,Bitmap.CompressFormat format, int quality, Context context) {
if (!Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED)) {
return null;
}
File dir = new File(Environment.getExternalStorageDirectory()
+ "/" + context.getPackageName() + "/save/");
if (!dir.exists()) {
dir.mkdirs();
}
File destFile = new File(dir, UUID.randomUUID().toString());
return save(bitmap, format, quality, destFile);
}
8、三級快取
- 網路快取
- 本地快取
- 記憶體快取
9、NDK壓縮
libjpeg.so
10、webp壓縮
UI卡頓面試題
1、UI卡頓原理
View的繪製幀數保持60fps是最佳,這要求每幀的繪製時間不超過16ms(1000/60),如果安卓不能在16ms內完成介面的渲染,那麼就會出現卡頓現象
2、UI卡頓的原因分析
- 在UI執行緒中做輕微的耗時操作,導致UI執行緒卡頓
- 佈局Layout過於複雜,無法在16ms內完成渲染
- 同一時間動畫執行的次數過多,導致CPU和GPU負載過重
- overDraw,導致畫素在同一幀的時間內被繪製多次,使CPU和GPU負載過重
- View頻繁的觸發measure、layout,導致measure、layout累計耗時過多和整個View頻繁的重新渲染
- 頻繁的觸發GC操作導致執行緒暫停,會使得安卓系統在16ms內無法完成繪製
- 冗餘資源及邏輯等導致載入和執行緩慢
- ANR
3、UI卡頓的優化
- 佈局優化
- 使用include、ViewStub、merge
- 不要出現過於巢狀和冗餘的佈局
- 使用自定義View取代複雜的View
- ListView優化
- 複用convertView
- 滑動不載入
- 背景和圖片優化
- 縮圖
- 圖片壓縮
- 避免ANR
- 不要在UI執行緒中做耗時操作
記憶體洩漏面試題
1、Java記憶體洩漏引起的主要原因
長生命週期的物件持有短生命週期物件的引用就很可能發生記憶體洩漏
2、Java記憶體分配策略
- 靜態儲存區:又稱方法區,主要儲存全域性變數和靜態變數,在整個程式執行期間都存在
- 棧區:方法體的區域性變數會在棧區建立空間,並在方法執行結束後會自動釋放變數的空間和記憶體
- 堆區:儲存動態產生的資料,如:new出來的物件和陣列,在不使用的時候由Java回收器自動回收
3、Android解決記憶體洩漏的例子
- 單例造成的記憶體洩漏:在單例中,使用context.getApplicationContext()作為單例的context
- 匿名內部類造成的記憶體洩漏:由於非靜態內部類持有匿名外部類的引用,必須將內部類設定為static
- Handler造成的記憶體洩漏:使用static的Handler內部類,同時在實現內部類中持有Context的弱引用
- 避免使用static變數:由於static變數會跟Activity生命週期一致,當Activity退出後臺被後臺回收時,static變數是不安全,所以也要管理好static變數的生命週期
- 資源未關閉造成的記憶體洩漏:比如Socket、Broadcast、Cursor、Bitmap、ListView等,使用完後要關閉
- AsyncTask造成的記憶體洩漏:由於非靜態內部類持有匿名內部類的引用而造成記憶體洩漏,可以通過AsyncTask內部持有外部Activity的弱引用同時改為靜態內部類或在onDestroy()中執行AsyncTask.cancel()進行修復
記憶體管理面試題
1、Android記憶體管理機制
- 分配機制
- 管理機制
2、記憶體管理機制的特點
- 更少的佔用記憶體
- 在合適的時候,合理的釋放系統資源
- 在系統記憶體緊張的時候,能釋放掉大部分不重要的資源
- 能合理的在特殊生命週期中,儲存或還原重要資料
3、記憶體優化方法
- Service完成任務後應停止它,或用IntentService(因為可以自動停止服務)代替Service
- 在UI不可見的時候,釋放其UI資源
- 在系統記憶體緊張的時候,儘可能多的釋放非重要資源
- 避免濫用Bitmap導致記憶體浪費
- 避免使用依賴注入框架
- 使用針對記憶體優化過的資料容器
- 使用ZIP對齊的APK
- 使用多程序
冷啟動和熱啟動面試題
1、什麼是冷啟動和熱啟動
- 冷啟動:在啟動應用前,系統中沒有該應用的任何程序資訊
- 熱啟動:在啟動應用時,在已有的程序上啟動應用(使用者使用返回鍵退出應用,然後馬上又重新啟動應用)
2、冷啟動和熱啟動的區別
- 冷啟動:建立Application後再建立和初始化MainActivity
- 熱啟動:建立和初始化MainActivity即可
3、冷啟動時間的計算
這個時間值從應用啟動(建立程序)開始計算,到完成檢視的第一次繪製為止
4、冷啟動流程
- Zygote程序中fork創建出一個新的程序
- 建立和初始化Application類、建立MainActivity
- inflate佈局、當onCreate/onStart/onResume方法都走完
- contentView的measure/layout/draw顯示在介面上
總結:Application構造方法->attachBaseContext()->onCreate()->Activity構造方法->onCreate()->配置主題中背景等屬性->onStart()->onResume()->測量佈局繪製顯示在介面上
5、冷啟動優化
- 減少第一個介面onCreate()方法的工作量
- 不要讓Application參與業務的操作
- 不要在Application進行耗時操作
- 不要以靜態變數的方式在Application中儲存資料
- 減少佈局的複雜性和深度
- 不要在mainThread中載入資源
- 通過懶載入方式初始化第三方SDK
其他優化面試題
1、Android不用靜態變數儲存資料
- 靜態變數等資料由於程序已經被殺死而被初始化
- 使用其他資料傳輸方式:檔案/sp/contentProvider
2、SharePreference安全問題
- 不能跨程序同步
- 檔案不宜過大
3、記憶體物件序列化
- Serializeble:是java的序列化方式,Serializeble在序列化的時候會產生大量的臨時物件,從而引起頻繁的GC
- Parcelable:是Android的序列化方式,且效能比Serializeble高,Parcelable不能使用在要將資料儲存在硬碟上的情況
4、避免在UI執行緒中做繁重的操作
架構模式面試題
外掛化面試題
1、外掛化解決的問題
- 動態載入APK(反射、類載入器)
- 資源載入(反射、AssetManager、獨立資源、分段資源)
- 程式碼載入(反射獲取生命週期)
2、類載入器(Java中位元組碼新增到虛擬機器中)
- DexClassLoader:能夠載入未安裝的jar/apk/dex,主要用於動態載入和程式碼熱更新
- PathClassLoader:載入/data/app目錄下的apk檔案,只要用來載入系統中已經安裝過的apk
熱更新面試題
1、熱更新主要流程
- 線上檢查到Crash
- 拉出Bugfix分支修復Crash問題
- jenkins構建和補丁生成
- app通過推送或主動拉取補丁檔案
- 將Bugfix程式碼合到master上
2、熱更新主流框架
- Dexposed
- AndFix
- Nuwa
3、熱更新的原理
- 在ClassLoader建立一個dexElements陣列
- 將修復好的dex檔案存放在dexElements陣列的最前面
- ClassLoader會遍歷dexElements陣列,找到最前面的dex檔案優先載入
程序保活面試題
1、程序的優先順序
- 空程序
- 後臺程序
- 服務程序
- 可見程序
- 前臺程序
2、Android程序回收策略
- Low memory Killer(定時執行):通過一些比較複雜的評分機制,對程序進行打分,然後將分數高的程序判定為bad程序,殺死並釋放記憶體
- OOM_ODJ:判別程序的優先順序
3、Android保活方案
- 利用系統廣播拉活
- 利用系統Service機制拉活
- 利用Native程序拉活
- 利用JobScheduler機制拉活
- 利用賬號同步機制拉活
Lint面試題
1、什麼是Android Lint
Android Lint是一個靜態程式碼分析工具,它能夠對你的Android專案中潛在的Bug、可優化的程式碼、安全性、效能、可用性、可訪問性、國際化等進行檢查
2、Lint工作流程
3、配置Lint
- 建立Lint.xml到根目錄下,自定義Lint安全等級等
- 在Java檔案中可以使用@suppressLint(“NewApi”)來忽視Lint的報錯
- 在xml檔案中可以使用tool:ignore(“UnusedResources”)來忽視Lint的報錯
- 自定義Lint檢查,可以建立類,繼承Detector和實現JavaPsiScanner
Kotlin面試題
1、什麼是Kotlin
- Kotlin是一種基於JVM的程式語言
- 對Java的一種拓展,比Java更簡潔
- Kotlin支援函數語言程式設計
- Kotlin類和Java類可以相互呼叫
2、Kotlin環境搭建
- 直接在Plugin中下載Kotlin外掛即可
- 系統會自動配置到Kotlin環境
虛擬機器面試題
1、JVM與Dalvik不同
- 類載入系統區別比較大
- JVM是基於棧,Dalvik是基於暫存器
- JVM執行的是java位元組碼檔案,Dalvik執行的是dex位元組碼檔案
- Dalvik可以同時存在多個,即使一個退出了也不會影響其他程式
2、Dalvik與ART不同
- Dalvik使用JIT(Just In Time 執行時編譯)來將位元組碼轉換成機器碼,效率低
- ART採用AOT(Ahead Of Time 執行前編譯)預編譯技術,執行速度更快
- ART會佔用更多的應用安裝時間和儲存空間
註解面試題
1、什麼是Annotation
Java提供的一種元程式中的元素關聯任何資訊和任何元資料(metadata)的途徑和方法
2、什麼是metadata
- 元資料以標籤的形式存在於Java程式碼中
- 元資料描述的資訊是型別安全的
- 元資料需要編譯器之外的工具額外的處理用來生成其他的程式部件
- 元資料可以只存在於Java原始碼級別,也可以存在於編譯之後的Class檔案內部
3、註解分類
- 系統內建標準註解
- @Override:表示重寫
- @Deprecated:表示已過時
- @SuppressWarnnings:表示抑制警告
- 元註解
- @Target:表示註解的修飾範圍
- @Retention:表示註解的程式碼生存期
- @Documented:表示註解為程式設計師的API
- @Inherited:表示註解可以繼承
- @Target
- ElementType.CONSTRUCTOR:構造器宣告
- ElementType.FIELD:成員變數、物件、屬性(包括enum例項)
- ElementType.LOCAL_VARIABLE:區域性變數宣告
- ElementType.METHOD:方法宣告
- ElementType.PACKAGE:包宣告
- ElementType.PARAMETER:引數宣告
- ElementType.TYPE:類、介面(包括註解型別)或enum宣告
- @Retention
- RetentionPolicy.SOURCE:在原始檔中有效,當Java檔案編譯成class檔案的時候,註解被遺棄
- RetentionPolicy.CLASS:在class檔案中有效,當jvm載入class檔案時候被遺棄
- RetentionPolicy.RUNTIME:在執行時有效,當jvm載入class檔案之後,仍然存在
- 生命週期
- RUNTIME > CLASS > SOURCE
- .java檔案 –> .class檔案 –> 記憶體中的位元組碼
4、註解宣告
註解支援填寫陣列,同時支援多種範圍
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModuleWrapper {}
5、Android Support Annotation
- Nullness註解(@NonNull、@Nullable):表示指定的空型別或非空型別
- ResourceType註解(@StringRes、@IntegerRes、@ColorRes等):表示指定的變數型別
- Threading註解(@WorkerThread、@UIThread、@MainThread等):表示指定的執行緒型別
- Range註解(@IntRange、@FloatRange等):表示允許傳值的範圍
- OverridingMethods註解(@CallSuper等):表示呼叫父類的方法
6、註解的用途
- 約束類
- 約束變數
- 約束入參
- 約束返回值