推薦:《2020年Android面試題大匯總【收藏】》
先簡單說說我這四年期間的面試經(jīng)歷吧。面試的公司很多,其中有讓我心血沸騰的經(jīng)歷,也有讓我感到失望到無助的經(jīng)歷,我將這些體會都記錄下來,細(xì)想之后很值得,面了這么多公司,要是最后什么也沒有留下來,那就太浪費了。至少對于我來說有些東西在整理總結(jié)之后才能得到一個肯定的答案。希望這些能對即將換工作或者打算看看機會的你有一些幫助。
以下問題的答案均是個人四年來多次面試實踐中整理的,如有不同意見,歡迎斧正。
1.自定義Handler時如何避免內(nèi)存泄漏
答案:
一般非靜態(tài)內(nèi)部類持有外部類的引用的情況下,造成外部類在使用完成后不能被系統(tǒng)回收內(nèi)存,從而造成內(nèi)存泄漏。為了避免這個問題,我們可以自定義的Handler聲明為靜態(tài)內(nèi)部類形式,然后通過弱引用的方式,讓Handler持有外部類的引用,從而可避免內(nèi)存泄漏問題。
以下是代碼實現(xiàn)
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private TextView mTextView; 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); } }復(fù)制代碼
參考博文blog.csdn.net/ucxiii/arti…
2.onNewIntent()的調(diào)用時機
解析:
在Android應(yīng)用程序開發(fā)的時候,從一個Activity啟動另一個Activity并傳遞一些數(shù)據(jù)到新的Activity上非常簡單,但是當(dāng)您需要讓后臺運行的Activity回到前臺并傳遞一些數(shù)據(jù)可能就會存在一點點小問題。
首先,在默認(rèn)情況下,當(dāng)您通過Intent啟到一個Activity的時候,就算已經(jīng)存在一個相同的正在運行的Activity,系統(tǒng)都會創(chuàng)建一個新的Activity實例并顯示出來。為了不讓Activity實例化多次,我們需要通過在AndroidManifest.xml配置activity的加載方式(launchMode)以實現(xiàn)單任務(wù)模式,如下所示:
<activity android:label="@string/app_name"android:launchmode="singleTask"android:name="Activity1"> </activity>復(fù)制代碼
launchMode為singleTask的時候,通過Intent啟到一個Activity,如果系統(tǒng)已經(jīng)存在一個實例,系統(tǒng)就會將請求發(fā)送到這個實例上,但這個時候,系統(tǒng)就不會再調(diào)用通常情況下我們處理請求數(shù)據(jù)的onCreate方法,而是調(diào)用onNewIntent方法
答案:
前提:ActivityA已經(jīng)啟動過,處于當(dāng)前應(yīng)用的Activity堆棧中; 當(dāng)ActivityA的LaunchMode為SingleTop時,如果ActivityA在棧頂,且現(xiàn)在要再啟動ActivityA,這時會調(diào)用onNewIntent()方法 當(dāng)ActivityA的LaunchMode為SingleInstance,SingleTask時,如果已經(jīng)ActivityA已經(jīng)在堆棧中,那么此時會調(diào)用onNewIntent()方法
當(dāng)ActivityA的LaunchMode為Standard時,由于每次啟動ActivityA都是啟動新的實例,和原來啟動的沒關(guān)系,所以不會調(diào)用原來ActivityA的onNewIntent方法,仍然調(diào)用的是onCreate方法
以下是代碼實例
1.設(shè)置MainActivity的啟動模式為SingleTask(棧內(nèi)復(fù)用)
<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>復(fù)制代碼
2.MainActivity中重寫onNewIntent方法
package code.xzy.com.handlerdemo; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; 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...."); } }復(fù)制代碼
3.Main2Actvity執(zhí)行點擊跳轉(zhuǎn),MainActivity被復(fù)用,執(zhí)行onNewIntent方法
package code.xzy.com.handlerdemo; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; 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(); } }); } }
打印截圖
**這里分享一份全套體系化高級架構(gòu)視頻;**七大主流技術(shù)模塊,視頻+源碼+筆記(文末有詳細(xì)面試資料專題整理包分享)
3.RecyclerView相比ListView有哪些優(yōu)勢
解析:
首先需要解釋下RecyclerView的這個名字了,從它類名上看,RecyclerView代表的意義是,我只管Recycler View,也就是說RecyclerView只管回收與復(fù)用View,其他的你可以自己去設(shè)置。可以看出其高度的解耦,給予你充分的定制自由(所以你才可以輕松的通過這個控件實現(xiàn)ListView,GirdView,瀑布流等效果)
其次RecyclerView提供了添加、刪除item的動畫 效果,而且可以自定義
RecyclerView相比ListView優(yōu)勢在于可以輕松實現(xiàn):
- ListView的功能
- GridView的功能
- 橫向ListView的功能
- 橫向ScrollView的功能
- 瀑布流效果
- 便于添加Item增加和移除動畫
不過一個挺郁悶的地方就是,系統(tǒng)沒有提供ClickListener和LongClickListener。 不過我們也可以自己去添加,只是會多了些代碼而已。 實現(xiàn)的方式比較多,你可以通過mRecyclerView.addOnItemTouchListener去監(jiān)聽然后去判斷手勢,
當(dāng)然你也可以通過adapter中自己去提供回調(diào)
參考
jcodecraeer.com/a/anzhuokai… blog.csdn.net/lmj62356579… www.360doc.com/content/16/…
4.談一談Proguard混淆技術(shù)
答案:
Proguard技術(shù)有如下功能:
壓縮 –檢查并移除代碼中無用的類 優(yōu)化–對字節(jié)碼的優(yōu)化,移除無用的字節(jié)碼 混淆–混淆定義的名稱,避免反編譯
預(yù)監(jiān)測–在java平臺對處理后的代碼再次進(jìn)行檢測
代碼混淆只在上線時才會用到,debug模式下會關(guān)閉,是一種可選的技術(shù)。
那么為什么要使用代碼混淆呢?
因為Java是一種跨平臺的解釋性開發(fā)語言,而java的源代碼會被編譯成字節(jié)碼文件,存儲在.class文件中,由于跨平臺的需要,java的字節(jié)碼中包含了很多源代碼信息,諸如變量名、方法名等等。并且通過這些名稱來訪問變量和方法,這些變量很多是無意義的,但是又很容易反編譯成java源代碼,為了防止這種現(xiàn)象,我們就需要通過proguard來對java的字節(jié)碼進(jìn)行混淆,混淆就是對發(fā)布的程序進(jìn)行重新組織和處理,使得處理后的代碼與處理前的代碼有相同的功能,和不同的代碼展示,即使被反編譯也很難讀懂代碼的含義,哪些混淆過的代碼仍能按照之前的邏輯執(zhí)行得到一樣的結(jié)果。
但是,某些java類是不能被混淆的,比如實現(xiàn)了序列化的java類是不能被混淆的,否則反序列化時會出問題。
下面這類代碼混淆的時候要注意保留,不能混淆。
- Android系統(tǒng)組件,系統(tǒng)組件有固定的方法被系統(tǒng)調(diào)用。
- 被Android Resource 文件引用到的。名字已經(jīng)固定,也不能混淆,比如自定義的View 。
- Android Parcelable ,需要使用android 序列化的。
其他Anroid 官方建議 不混淆的,如
- android.app.backup.BackupAgentHelper?
- android.preference.Preference?
- com.android.vending.licensing.ILicensingService?
- Java序列化方法,系統(tǒng)序列化需要固定的方法。?
- 枚舉 ,系統(tǒng)需要處理枚舉的固定方法。?
- 本地方法,不能修改本地方法名?
- annotations 注釋?
- 數(shù)據(jù)庫驅(qū)動?
- 有些resource 文件用到反射的地方
5.ANR出現(xiàn)的場景及解決方案
在Android中,應(yīng)用的響應(yīng)性被活動管理器(Activity Manager)和窗口管理器(Window Manager)這兩個系統(tǒng)服務(wù)所監(jiān)視。當(dāng)用戶觸發(fā)了輸入事件(如鍵盤輸入,點擊按鈕等),如果應(yīng)用5秒內(nèi)沒有響應(yīng)用戶的輸入事件,那么,Android會認(rèn)為該應(yīng)用無響應(yīng),便彈出ANR對話框。而彈出ANR異常,也主要是為了提升用戶體驗。
解決方案是對于耗時的操作,比如訪問網(wǎng)絡(luò)、訪問數(shù)據(jù)庫等操作,需要開辟子線程,在子線程處理耗時的操作,主線程主要實現(xiàn)UI的操作
6.HTTPS中SSL證書認(rèn)證的過程
7.簡述Android的Activity的內(nèi)部機制
8.對Android Framework層的某一個模塊(或者System App)做簡要介紹
9.Android Handler的機制和原理
主線程使用Handler的過程
首先在主線程創(chuàng)建一個Handler對象 ,并重寫handleMessage()方法。然后當(dāng)在子線程中需要進(jìn)行更新UI的操作,我們就創(chuàng)建一個Message對象,并通過handler發(fā)送這條消息出去。之后這條消息被加入到MessageQueue隊列中等待被處理,通過Looper對象會一直嘗試從Message Queue中取出待處理的消息,最后分發(fā)會Handler的handler Message()方法中。
參考 blog.csdn.net/u012827296/…
10.線程間通信和進(jìn)程間通信有什么不同,Android開發(fā)過程中是怎么實現(xiàn)的
www.cnblogs.com/yangtao1995…
11.簡述項目中對于內(nèi)存優(yōu)化的幾個細(xì)節(jié)點
答案:
- 當(dāng)查詢完數(shù)據(jù)庫之后,及時關(guān)閉Cursor對象。
- 記得在Activity的onPause方法中調(diào)用unregisterReceiver()方法,反注冊廣播
- 避免Content內(nèi)存泄漏,比如在4.0.1之前的版本上不要講Drawer對象置為static。當(dāng)一個Drawable綁定到了View上,實際上這個View對象就會成為這個Drawable的一個callback成員變量,上面的例子中靜態(tài)的sBackground持有TextView對象lable的引用,而lable只有Activity的引用,而Activity會持有其他