“金三銀四”中那些拿到月薪20+的Android面試都問了些什麼?(含面試答案)

由於肺炎疫情的影響,不少同學年初跳槽的計劃被擱置。雖然計劃被打亂,但是這也給我們留出時間更好的準備面試。隨着企業復工時間的延長,不少公司裁員、降薪甚至倒閉,之後只會更難。

金三銀四跳槽季,相信大家肯定急需一套Android面試寶典,今天就給大家準備了我珍藏已久的Android高階面試寶典,供大家學習 !

1.自定義Handler時如何避免內存泄漏

答案:

一般非靜態內部類持有外部類的引用的情況下,造成外部類在使用完成後不能被系統回收內存,從而造成內存泄漏。爲了避免這個問題,我們可以自定義的Handler聲明爲靜態內部類形式,然後通過弱引用的方式,讓Handler持有外部類的引用,從而可避免內存泄漏問題。

以下是代碼實現

private WeakReference<MainActivity> activityWeakReference;
private MyHandler myHandler;

static class MyHandler extends Handler {
private MainActivity activity;

MyHandler(WeakReference<MainActivity> ref) {
this.activity = ref.get();
}

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
//需要做判空操作
if (activity != null) {
activity.mTextView.setText("new Value");
}
break;
default:
Log.i(TAG, "handleMessage: default ");
break;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在onCreate中初始化
activityWeakReference = new WeakReference<MainActivity>(this);
myHandler = new MyHandler(activityWeakReference);

myHandler.sendEmptyMessage(1);
mTextView = (TextView) findViewById(R.id.tv_test);
}

參考博文:http://blog.csdn.net/ucxiii/article/details/50972747

2.onNewIntent()的調用時機

解析:

在Android應用程序開發的時候,從一個Activity啓動另一個Activity並傳遞一些數據到新的Activity上非常簡單,但是當您需要讓後臺運行的Activity回到前臺並傳遞一些數據可能就會存在一點點小問題。

首先,在默認情況下,當您通過Intent啓到一個Activity的時候,就算已經存在一個相同的正在運行的Activity,系統都會創建一個新的Activity實例並顯示出來。爲了不讓Activity實例化多次,我們需要通過在AndroidManifest.xml配置activity的加載方式(launchMode)以實現單任務模式,如下所示:

<activity android:label="@string/app_name"

android:launchmode="singleTask"android:name="Activity1">

</activity>

launchMode爲singleTask的時候,通過Intent啓到一個Activity,如果系統已經存在一個實例,系統就會將請求發送到這個實例上,但這個時候,系統就不會再調用通常情況下我們處理請求數據的onCreate方法,而是調用onNewIntent方法

答案:

前提:ActivityA已經啓動過,處於當前應用的Activity堆棧中;
當ActivityA的LaunchMode爲SingleTop時,如果ActivityA在棧頂,且現在要再啓動ActivityA,這時會調用onNewIntent()方法

當ActivityA的LaunchMode爲SingleInstance,SingleTask時,如果已經ActivityA已經在堆棧中,那麼此時會調用onNewIntent()方法

當ActivityA的LaunchMode爲Standard時,由於每次啓動ActivityA都是啓動新的實例,和原來啓動的沒關係,所以不會調用原來ActivityA的onNewIntent方法,仍然調用的是onCreate方法

以下是代碼實例

1.設置MainActivity的啓動模式爲SingleTask(棧內複用)

<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

2.MainActivity中重寫onNewIntent方法

public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button mButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.forward_btn);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this, Main2Activity.class));
}
});

@Override
protected void onNewIntent(Intent intent) {
Toast.makeText(this, "onnewIntent", Toast.LENGTH_SHORT).show();
Log.i(TAG, "onNewIntent: i done....");
}

3.Main2Actvity執行點擊跳轉,MainActivity被複用,執行onNewIntent方法

public class Main2Activity extends AppCompatActivity {
private Button mButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);

mButton = (Button)findViewById(R.id.btn);

mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(Main2Activity.this,MainActivity.class));
finish();
}
});

3.RecyclerView相比ListView有哪些優勢

解析:

首先需要解釋下RecyclerView的這個名字了,從它類名上看,RecyclerView代表的意義是,我只管Recycler View,也就是說RecyclerView只管回收與複用View,其他的你可以自己去設置。可以看出其高度的解耦,給予你充分的定製自由(所以你纔可以輕鬆的通過這個控件實現ListView,GirdView,瀑布流等效果)

其次RecyclerView提供了添加、刪除item的動畫 效果,而且可以自定義

RecyclerView相比ListView優勢在於可以輕鬆實現:

  • ListView的功能
  • GridView的功能
  • 橫向ListView的功能
  • 橫向ScrollView的功能
  • 瀑布流效果
  • 便於添加Item增加和移除動畫

不過一個挺鬱悶的地方就是,系統沒有提供ClickListener和LongClickListener。

不過我們也可以自己去添加,只是會多了些代碼而已。
實現的方式比較多,你可以通過mRecyclerView.addOnItemTouchListener去監聽然後去判斷手勢,

當然你也可以通過adapter中自己去提供回調

4.談一談Proguard混淆技術

答案:

Proguard技術有如下功能:

  • 壓縮 --檢查並移除代碼中無用的類
  • 優化--對字節碼的優化,移除無用的字節碼
  • 混淆--混淆定義的名稱,避免反編譯
  • 預監測--在java平臺對處理後的代碼再次進行檢測

代碼混淆只在上線時纔會用到,debug模式下會關閉,是一種可選的技術。

那麼爲什麼要使用代碼混淆呢?

因爲Java是一種跨平臺的解釋性開發語言,而java的源代碼會被編譯成字節碼文件,存儲在.class文件中,由於跨平臺的需要,java的字節碼中包含了很多源代碼信息,諸如變量名、方法名等等。並且通過這些名稱來訪問變量和方法,這些變量很多是無意義的,但是又很容易反編譯成java源代碼,爲了防止這種現象,我們就需要通過proguard來對java的字節碼進行混淆,混淆就是對發佈的程序進行重新組織和處理,使得處理後的代碼與處理前的代碼有相同的功能,和不同的代碼展示,即使被反編譯也很難讀懂代碼的含義,哪些混淆過的代碼仍能按照之前的邏輯執行得到一樣的結果。

但是,某些java類是不能被混淆的,比如實現了序列化的java類是不能被混淆的,否則反序列化時會出問題。

下面這類代碼混淆的時候要注意保留,不能混淆。

  • Android系統組件,系統組件有固定的方法被系統調用。
  • 被Android Resource 文件引用到的。名字已經固定,也不能混淆,比如自定義的View 。
  • Android Parcelable ,需要使用android 序列化的。
  • 其他Anroid 官方建議 不混淆的,如android.app.backup.BackupAgentHelper、android.preference.Preference、com.android.vending.licensing.ILicensingService
  • Java序列化方法,系統序列化需要固定的方法。
  • 枚舉 ,系統需要處理枚舉的固定方法。
  • 本地方法,不能修改本地方法名
  • annotations 註釋
  • 數據庫驅動
  • 有些resource 文件

5.ANR出現的場景及解決方案

在Android中,應用的響應性被活動管理器(Activity Manager)和窗口管理器(Window Manager)這兩個系統服務所監視。當用戶觸發了輸入事件(如鍵盤輸入,點擊按鈕等),如果應用5秒內沒有響應用戶的輸入事件,那麼,Android會認爲該應用無響應,便彈出ANR對話框。而彈出ANR異常,也主要是爲了提升用戶體驗。

解決方案是對於耗時的操作,比如訪問網絡、訪問數據庫等操作,需要開闢子線程,在子線程處理耗時的操作,主線程主要實現UI的操作。

6.Android Handler的機制和原理

主線程使用Handler的過程:

首先在主線程創建一個Handler對象 ,並重寫handleMessage()方法。然後當在子線程中需要進行更新UI的操作,我們就創建一個Message對象,並通過handler發送這條消息出去。之後這條消息被加入到MessageQueue隊列中等待被處理,通過Looper對象會一直嘗試從Message Queue中取出待處理的消息,最後分發會Handler的handler Message()方法中。

7.簡述項目中對於內存優化的幾個細節點

答案:

1.當查詢完數據庫之後,及時關閉Cursor對象。

2.記得在Activity的onPause方法中調用unregisterReceiver()方法,反註冊廣播

3.避免Content內存泄漏,比如在4.0.1之前的版本上不要講Drawer對象置爲static。當一個Drawable綁定到了View上,實際上這個View對象就會成爲這個Drawable的一個callback成員變量,上面的例子中靜態的sBackground持有TextView對象lable的引用,而lable只有Activity的引用,而Activity會持有其他更多對象的引用。sBackground生命週期要長於Activity。當屏幕旋轉時,Activity無法被銷燬,這樣就產生了內存泄露問題。

4.儘量不要在Activity中使用非靜態內部類,因爲非靜態內部類會隱式持有外部類實例的引用,當非靜態內部類的引用的聲明週期長於Activity的聲明週期時,會導致Activity無法被GC正常回收掉。

5.謹慎使用線程Thread!!這條是很多人會犯的錯誤: Java中的Thread有一個特點就是她們都是直接被GC Root所引用,也就是說Dalvik虛擬機對所有被激活狀態的線程都是持有強引用,導致GC永遠都無法回收掉這些線程對象,除非線程被手動停止並置爲null或者用戶直接kill進程操作。所以當使用線程時,一定要考慮在Activity退出時,及時將線程也停止並釋放掉

6.使用Handler時,要麼是放在單獨的類文件中,要麼就是使用靜態內部類。因爲靜態的內部類不會持有外部類的引用,所以不會導致外部類實例的內存泄露

8.TCP和UPD的區別以及使用場景

TCP與UDP基本區別:

1.基於連接與無連接
2.TCP要求系統資源較多,UDP較少;
3.UDP程序結構較簡單
4.流模式(TCP)與數據報模式(UDP);
5.TCP保證數據正確性,UDP可能丟包
6.TCP保證數據順序,UDP不保證

UDP應用場景:

1.面向數據報方式
2.網絡數據大多爲短消息
3.擁有大量Client
4.對數據安全性無特殊要求
5.網絡負擔非常重,但對響應速度要求高

9.簡述一個設計模式的概念,並簡要談談framework層哪些地方用到了什麼設計模式

單例模式:單例模式是一種對象創建模式,它用於產生一個對象的具體實例,它可以確保系統中一個類只產生一個實例。

適配器模式:將一個接口轉換成客戶希望的另一個接口,適配器模式使接口不兼容的那些類可以一起工作,其別名爲包裝器(Wrapper)

裝飾模式:動態地給一個對象增加一些額外的職責,就增加對象功能來說,裝飾模式比生成子類實現更爲靈活。裝飾模式是一種對象結構型模式。

使用場景:

1.在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。
2.當不能採用繼承的方式對系統進行擴展或者採用繼承不利於系統擴展和維護時可以使用裝飾模式。

優點:

1.對於擴展一個對象的功能,裝飾模式比繼承更加靈活,不會導致類的個數急劇增加。
2.可以通過一種動態地方式來擴展一個對象的功能。
3.可以對一個對象進行多次裝飾,通過使用不同的具體裝飾類以及這些裝飾類的排列組合。

實際運用:

Android中Context類的實現

外觀模式: 主要目的在於讓外部減少與子系統內部多個模塊的交互,從而讓外部能夠更簡單得使用子系統。它負責把客戶端的請求轉發給子系統內部的各個模塊進行處理。

使用場景:

1.當你要爲一個複雜的子系統提供一個簡單的接口時
2.客戶程序與抽象類的實現部分之前存在着很大的依賴性
3.當你需要構建一個層次結構的子系統時

組合模式:將對象以樹形結構組織起來,以達成”部分--整體”的層次結構,使得客戶端對單個對象和組合對象的使用具有一致性。

使用場景:

1.需要表示一個對象整體或部分 層次
2.讓客戶端能夠忽略不同對象層次的變化

優點:

1.高層模塊調用簡單
2.節點自由增加

模板模式:是通過一個算法骨架,而將算法中的步驟延遲到子類,這樣子類就可以複寫這些步驟的實現來實現特定的算法。它的使用場景:

1.多個子類有公有的方法,並且邏輯基本相同
2.重要、複雜的算法,可以把核心算法設計爲模板方法
3.重構時,模板方法模式是一個經常使用的模式

觀察者模式:定義對象之間一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知並被自動更新。其使用場景:

1.一個抽象模型有兩個方面,其中一個方面依賴於另一個方面
2.一個對象的改變將導致一個或多個其他對象也 發生改變
3.需要在 系統中創建一個 觸發鏈

具體應用:

比如回調模式中,實現了抽象類/接口的實例實現了父類提供的抽象方法後,將該方法交還給父類來處理

  • Listview中的notifyDataSetChanged
  • RxJava中的觀察者模式

**責任鏈模式: **是一個請求有多個對象來處理,這些對象是一條鏈,但具體由哪個對象來處理,根據條件判斷來確定,如果不能處理會傳遞給該鏈條中的下一個對象,直到有對象處理它爲止。

使用場景:

1.有多個對象可以處理同一個請求,具體哪個對象處理該請求待運行時再確定
2.在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。

實際運用:

  • Try...catch語句
  • OrderedBroadcast
  • MotionEvent:actionDwon actionMove actionUp
  • 事件分發機制三個重要方法:dispatchTouchEvent. onInterceptTouchEvent. onTouchEvent

策略模式: 定義一系列的算法,把它們一個個封裝起來,並且使他們可互相替換。本模式使得算法可獨立於使用它的客戶而變化。策略模式的使用場景:一個類定義了多種行爲,並且這些行爲在這個類的方法中以多個條件語句的形式出現,那麼可以使用策略模式避免在類中使用大量的條件語句。

使用場景:

一個類定義了多種行爲,並且這些行爲在這個類的方法中以多個條件語句的形式出現,那麼可以使用策略模式避免在類中使用大量的條件語句。

優點:

1.上下文Context和具體策略ConcreateStrategy是鬆耦合關係
2.策略模式滿足 開-閉原則

具體應用:

  • HttpStack
  • Volley框架

10.字節流和字符流的區別

字節流操作的基本單元爲字節;字符流操作的基本單元爲Unicode碼元(2個字節)。

字節流默認不使用緩衝區;字符流使用緩衝區。

字節流通常用於處理二進制數據,實際上它可以處理任意類型的數據,但它不支持直接寫入或讀取Unicode碼元;字符流通常處理文本數據,它支持寫入及讀取Unicode碼元。

11.OOM異常是否可以被try...catch捕獲(或者可否用try-catch捕獲Out Of Memory Error以避免其發生?)

只有在一種情況下,這樣做是可行的:在try語句中聲明瞭很大的對象,導致OOM,並且可以確認OOM是由try語句中的對象聲明導致的,那麼在catch語句中,可以釋放掉這些對象,解決OOM的問題,繼續執行剩餘語句。

但是這通常不是合適的做法。Java中管理內存除了顯式地catch OOM之外還有更多有效的方法:比如SoftReference, WeakReference, 硬盤緩存等。在JVM用光內存之前,會多次觸發GC,這些GC會降低程序運行的效率。如果OOM的原因不是try語句中的對象(比如內存泄漏),那麼在catch語句中會繼續拋出OOM

12.sqlite可以執行多線程操作嗎?如何保證多線程操作數據庫的安全性

答:

每當你需要使用數據庫時,你需要使用DatabaseManager的openDatabase()方法來取得數據庫,這個方法裏面使用了單例模式,保證了數據庫對象的唯一性,也就是每次操作數據庫時所使用的sqlite對象都是一致得到。其次,我們會使用一個引用計數來判斷是否要創建數據庫對象。如果引用計數爲1,則需要創建一個數據庫,如果不爲1,說明我們已經創建過了。

在closeDatabase()方法中我們同樣通過判斷引用計數的值,如果引用計數降爲0,則說明我們需要close數據庫。

大致的做法就是在多線程訪問的情況下需要自己來封裝一個DatabaseManager來管理Sqlite數據庫的讀寫,需要同步的同步,需要異步的異步,不要直接操作數據庫,這樣很容易出現因爲鎖的問題導致加鎖後的操作失敗。

最後

最後我想說:有些東西你不僅要懂,而且要能夠很好地表達出來,能夠讓面試官認可你的理解,例如Handler機制,這個是面試必問之題。有些晦澀的點,或許它只活在面試當中,實際工作當中你壓根不會用到它,但是你要知道它是什麼東西。

馬上就要到金三銀四得面試旺季,大家都希望趁着這個機會找到一個心儀的工作,但是不管怎麼樣,不論是什麼樣的大小面試,要想不被面試官虐的不要不要的,只有刷爆面試題題做好全面的準備,當然除了這個還需要在平時把自己的基礎打紮實,這樣不論面試官怎麼樣一個知識點裏往死裏鑿,你也能應付如流啊~

面試:如果不準備充分的面試,完全是浪費時間,更是對自己的不負責!

最後這裏是關於我自己的Android 學習,面試文檔,視頻收集大整理,有興趣的夥伴們可以看看~ 

發佈了208 篇原創文章 · 獲贊 780 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章