日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 面试题笔记(一)

發布時間:2023/12/20 Android 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 面试题笔记(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

每日更新每日學習面試筆記,來自https://github.com/Moosphan/Android-Daily-Interview/

  • 1、自定義 Handler 時如何有效地避免內存泄漏問題?
    問題原因:一般非靜態內部類持有外部類的引用的情況下,造成外部類在使用完成后不能被系統回收內存,從而造成內存泄漏。這里 Handler 持有外部類 Activity 的引用,一旦 Activity 被銷毀,而此時 Handler 依然持有 Activity 引用,就會造成內存泄漏。

解決方案:將 Handler 以靜態內部類的形式聲明,然后通過弱引用的方式讓 Handler 持有外部類 Activity 的引用,這樣就可以避免內存泄漏問題了:
1.自定義的靜態handler
2.可以加一個弱引用
3.還有一個主意的就是當你activity被銷毀的時候如果還有消息沒有發出去 就remove掉吧
4.removecallbacksandmessages去清除Message和Runnable 加null 寫在生命周的ondestroy()就行

private WeakHandler weakHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);weakHandler = new WeakHandler(this);}static class WeakHandler extends Handler {private final WeakReference<MainActivity> mActivity;WeakHandler(MainActivity activity) {mActivity = new WeakReference<>(activity); // 弱引用}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);MainActivity activity = mActivity.get();switch (msg.what) {case 1:if (activity != null) {activity.btnDemoOne.setText("ceshi");}break;default:break;}}}@Overrideprotected void onDestroy() {super.onDestroy();weakHandler.removeCallbacksAndMessages(null);}
  • 2、Activity 與 Fragment 之間常見的幾種通信方式?

通常,Fragment 與 Activity 通信存在三種情形:

  • Activity 操作內嵌的 Fragment
  • Fragment 操作宿主 Activity
  • Fragment 操作同屬 Activity中的其他 Fragment

在Android中我們可以通過以下幾種方式優雅地實現Activity和fragment之間的通信:

  • Handler
  • 廣播
  • EventBus
  • 接口回調
    見Activity 與 Fragment 之間的通信
    - 3、一般什么情況下會導致內存泄漏問題?
  • 內部類&匿名內部類實例無法釋放(有延遲時間等等),而內部類又持有外部類的強引用,導致外部類無法釋放,這種匿名內部類常見于監聽器、Handler、Thread、TimerTask
  • 持有靜態的Context(Avtivity)和View
  • 資源使用完成后沒有關閉File,Cursor,Stream,Bitmap(調用recycle())等相關流的操作
  • 接收器、監聽器注冊沒取消,BraodcastReceiver,ContentObserver
  • 集合類內存泄漏,如果一個集合類是靜態的(緩存HashMap),只有添加方法,沒有對應的刪除方法,會導致引用無法被釋放,引發內存泄漏。

順便說一下內存泄漏和內存溢出的區別

  • 內存溢出(Out of memory):系統會給每個APP分配內存也就是Heap size值,當APP所需要的內存大于了系統分配的內存,就會造成內存溢出;通俗點就是10L桶只能裝10L水,但是你卻用來裝11L的水,那就有1L的水就會溢出
  • 內存泄漏(Memory leak):當一個對象不在使用了,本應該被垃圾回收器(JVM)回收,但是這個對象由于被其他正在使用的對象所持有,造成無法被回收的結果,通俗點就是系統把一定的內存值A借給程序,但是系統卻收不回完整的A值,那就是內存泄漏
    - 4、LaunchMode 的應用場景?
    LaunchMode 有四種,分別為 Standard,SingleTop,SingleTask 和 SingleInstance,每種模式的實現原理
  • android:launchMode="standard"可以存在多個實例,這是默認的啟動模式,系統總是會在目標棧中創建新的activity實例。Standard 模式是系統默認的啟動模式,一般我們 app 中大部分頁面都是由該模式的頁面構成的,比較常見的場景是:社交應用中,點擊查看用戶A信息->查看用戶A粉絲->在粉絲中挑選查看用戶B信息->查看用戶A粉絲… 這種情況下一般我們需要保留用戶操作 Activity 棧的頁面所有執行順序。
  • android:launchMode=“singleTop” 如果這個 activity 實例已經存在目標棧的棧頂,系統會調用這個 activity 中的 onNewIntent() 方法,并傳遞 intent,而不會創建新的 activity 實例;如果不存在這個 activity 實例或者 activity 實例不在棧頂,則 SingleTop 和Standard 作用是一樣的。SingleTop 模式一般常見于社交應用中的通知欄行為功能,例如:App 用戶收到幾條好友請求的推送消息,需要用戶點擊推送通知進入到請求者個人信息頁,將信息頁設置為 SingleTop 模式就可以增強復用性。
  • android:launchMode=“singleTask” 不會存在多個實例,如果棧中不存在 activity 實例,系統會在新棧的根部創建一個新的 activity;如果這個 activity 實例已經存在,系統會調用這個 activity的 onNewIntent() 方法而不會創建新的 activity 實例。SingleTask 模式一般用作應用的首頁,例如瀏覽器主頁,用戶可能從多個應用啟動瀏覽器,但主界面僅僅啟動一次,其余情況都會走onNewIntent,并且會清空主界面上面的其他頁面。
  • android:launchMode=“singleInstance” 這種啟動模式比較特殊,因為它會啟用一個新的棧結構,將 Acitvity 放置于這個新的棧結構中,并保證不再有其他 Activity 實例進入,除此之外,SingleInstance模式和 SingleTask 模式是一樣的。SingleInstance 模式常應用于獨立棧操作的應用,如鬧鐘的提醒頁面,當你在A應用中看視頻時,鬧鐘響了,你點擊鬧鐘提醒通知后進入提醒詳情頁面,然后點擊返回就再次回到A的視頻頁面,這樣就不會過多干擾到用戶先前的操作了。
  • 5、如何實現多線程中的同步?
    線程間的同步問題一般借助于同步鎖 Synchronized 和 volatile 關鍵字實現:
public class Singleton{private volatile static Singleton mSingleton;private Singleton(){}public static Singleton getInstance(){if(mSingleton == null){synchronized(Singleton.class){if(mSingleton == null)mSingleton = new Singleton();}}return mSingleton;} }
  • 6、Android 補間動畫和屬性動畫的區別?

  • 補間動畫
    補間動畫,主要是向View對象設置動畫效果,包括AlphaAnimation 、RotateAnimation 、ScaleAnimation 、TranslateAnimation 這4種效果,對應的xml標簽分別是alpha、rotate、scale、translate。通過為動畫設置初始和終止對應的值,根據插值器和duration計算動畫過程中間相應的值實現平滑運動,即設置初始和終止狀態,插值器來計算填補初始狀態到終止狀態間的動畫

  • 屬性動畫
    屬性動畫可以對任何對象的屬性做動畫而不僅僅是View,甚至可以沒有對象。除了作用對象進行擴展外,屬性動畫的效果也加強了,不僅能實現View動畫的4中效果,還能實現其它多種效果,這些效果都是通過ValuAnimator或ObjectAnimator、AnimatorSet等來實現的。

  • 7、ANR出現的場景及解決方案?
    在Android中,應用的響應性被活動管理器(Activity Manager)和窗口管理器(Window Manager)這兩個系統服務所監視。當用戶觸發了輸入事件(如鍵盤輸入,點擊按鈕等),如果應用5秒內沒有響應用戶的輸入事件,那么,Android會認為該應用無響應,便彈出ANR對話框。而彈出ANR異常,也主要是為了提升用戶體驗。
    解決方案是對于耗時的操作,比如訪問網絡、訪問數據庫等操作,需要開辟子線程,在子線程處理耗時的操作,主線程主要實現UI的操作

  • 8、談談 Handler 機制和原理?
    首先在UI線程我們創建了一個Handler實例對象,無論是匿名內部類還是自定義類生成的Handler實例對象,我們都需要對handleMessage方法進行重寫,在handleMessage方法中我們可以通過參數msg來寫接受消息過后UIi線程的邏輯處理,接著我們創建子線程,在子線程中需要更新UI的時候,新建一個Message對象,并且將消息的數據記錄在這個消息對象Message的內部,比如arg1,arg2,obj等,然后通過前面的Handler實例對象調用sendMessge方法把這個Message實例對象發送出去,之后這個消息會被存放于MessageQueue中等待被處理,此時MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出來,通過回調dispatchMessage方法將消息傳遞給Handler的handleMessage方法,最終前面提到的消息會被Looper從MessageQueue中取出來傳遞給handleMessage方法。
    問題擴展
    A. messageQueue.next 是阻塞式的取消息, 如果有 delay 會調用 nativeWake;
    那么問題來了, 線程掛起了, 是掛起的 UI線程嗎? 答案是 YES, 為什么我沒有察覺呢?
    還有就是 nativeWake 和 nativePollOnce 的實現原理;

B. looper.loop 既然是 while-true 為什么不會卡死?

C. MessageQueue 是隊列嗎? 他是什么數據結構呢?

D. handler 的postDelay, 時間準嗎? 答案是不準, 為什么呢?

E. handler 的 postDelay 的時間是 system.currentTime 嗎? 答案是 NO, 你知道是什么嗎?

F. 子線程run方法使用 handler 要先 looper.prepare(); 再 handler.post; 再 looper.loop();
那么問題來了, looper.loop(); 之后 在 handler.post 消息, 還能收到嗎? 答案是 NO, 為什么?

G. handler 這么做到的 一個線程對應一個 looper, 答案是threadLocal, 你對ThreadLocal 有什么了解嗎?

H. 假設先 postDelay 10ms, 再postDelay 1ms, 你簡單描述一下, 怎么處理這2條消息?

I. 你知道主線程的Looper, 第一次被調用loop方法, 在什么時候嗎? 哪一個類

J. 你對 IdleHandler 有多少了解?

K. 你了解 HandlerThread 嗎?

L. 你對 Message.obtain() 了解嗎, 或者你知道 怎么維護消息池嗎;

  • 9、什么是Sticky事件?
    在Android開發中,Sticky事件只指事件消費者在事件發布之后才注冊的也能接收到該事件的特殊類型。Android中就有這樣的實例,也就是Sticky Broadcast,即粘性廣播。正常情況下如果發送者發送了某個廣播,而接收者在這個廣播發送后才注冊自己的Receiver,這時接收者便無法接收到剛才的廣播,為此Android引入了StickyBroadcast,在廣播發送結束后會保存剛剛發送的廣播(Intent),這樣當接收者注冊完Receiver后就可以接收到剛才已經發布的廣播。這就使得我們可以預先處理一些事件,讓有消費者時再把這些事件投遞給消費者。
  • 10、抽象類與接口的區別?
    1.抽象類是用來捕捉子類的通用特性的 。它不能被實例化,只能被用作子類的超類。抽象類是被用來創建繼承層級里子類的模板。
    2.接口是抽象方法的集合。如果一個類實現了某個接口,那么它就繼承了這個接口的抽象方法。這就像契約模式,如果實現了這個接口,那么就必須確保使用這些方法。接口只是一種形式,接口自身不能做任何事情。
    大體區別如下:
  • 抽象類可以提供成員方法的實現細節,而接口中只能存在 public 抽象方法;
  • 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是 public static final 類型的;
  • 接口中不能含有構造器、靜態代碼塊以及靜態方法,而抽象類可以有構造器、靜態代碼塊和靜態方法;
  • 一個類只能繼承一個抽象類,而一個類卻可以實現多個接口;
  • 抽象類訪問速度比接口速度要快,因為接口需要時間去尋找在類中具體實現的方法;
  • 如果你往抽象類中添加新的方法,你可以給它提供默認的實現。因此你不需要改變你現在的代碼。如果你往接口中添加方法,那么你必須改變實現該接口的類。
    補充詳細見抽象類與接口的區別
  • 11、BroadcastReceiver 與 LocalBroadcastReceiver 有什么區別?
  • BroadcastReceiver 是跨應用廣播,利用Binder機制實現,支持動態和靜態兩種方式注冊方式。
  • LocalBroadcastReceiver是應用內廣播,利用Handler實現,利用了IntentFilter的match功能,提供消息的發布與接收功能,實現應用內通信,效率和安全性比較高,僅支持動態注冊。
/*** 自定義廣播*/ public static final String LOGIN_ACTION = "com.archie.action.LOGIN_ACTION"; //廣播接收器private LoginBroadcastReceiver mReceiver = new LoginBroadcastReceiver();//注冊廣播方法private void registerLoginBroadcast(){IntentFilter intentFilter = new IntentFilter(LoginActivity.LOGIN_ACTION);LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver,intentFilter);}//取消注冊private void unRegisterLoginBroadcast(){LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);} /*** 自定義廣播接受器,用來處理登錄廣播*/private class LoginBroadcastReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {//處理我們具體的邏輯,更新UI}}/*** 發送我們的局部廣播*/private void sendBroadcast(){LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(LOGIN_ACTION));}

小結:
1、LocalBroadcastManager在創建單例傳參時,不用糾結context是取activity的還是Application的,它自己會取到tApplicationContext。
2、LocalBroadcastManager只適用于代碼間的,因為它就是保存接口BroadcastReceiver的對象,然后直接調用其onReceive方法。
3、LocalBroadcastManager注冊廣播后,當該其Activity或者Fragment不需要監聽時,記得要取消注冊,注意一點:注冊與取消注冊在activity或者fragment的生命周期中要保持一致,例如onResume,onPause。
4、LocalBroadcastManager雖然 支持對同一個BroadcastReceiver可以注冊多個IntentFilter,但還是應該將所需要的action都放進一個 IntentFilter,即只注冊一個IntentFilter,這只是我個人的建議。
5、LocalBroadcastManager所發 送的廣播action,只能與注冊到LocalBroadcastManager中BroadcastReceiver產生互動。如果你遇到了通過 LocalBroadcastManager發送的廣播,對面的BroadcastReceiver沒響應,很可能就是這個原因造成的。

  • 12、請簡要談一談單例模式?
    單例分為懶漢模式和惡漢模式,主要是雙檢查、靜態內部類、枚舉等
    懶漢模式有線程安全和非線程安全的區別
    實現線程安全的懶漢模式有多重 其中一種是加double check,一種是靜態內部類
/*** 雙重檢查*/ public class SingletonDoubleCheck {private SingletonDoubleCheck() { }private static volatile SingletonDoubleCheck instance;//代碼1public static SingletonDoubleCheck getInc() {if (null == instance) {//代碼2synchronized (SingletonDoubleCheck.class) {if (null == instance) {//代碼3instance = new SingletonDoubleCheck();//代碼4}}}return instance;} }/*** 靜態內部類實現單例* */ public class SingleDemo4 {private static SingleDemo4 instance;private static class SingleDemo4Holder {private static final SingleDemo4 instance = new SingleDemo4();}private SingleDemo4() {if (instance != null) {throw new RuntimeException();}}/*** 調用這個方法的時候,JVM才加載靜態內部類,才初始化靜態內部類的類變量。由于由JVM初始化,保證了線程安全性,* 同時又實現了懶加載* @return*/public static SingleDemo4 getInstance() {return SingleDemo4Holder.instance;} }

更多詳細單例見單例的五種實現方式,及其性能分析
在代碼 在多線程中 兩個線程可能同時進入代碼2, synchronize保證只有一個線程能進入下面的代碼,
此時一個線程A進入一個線程B在外等待, 當線程A完成代碼3 和代碼4之后,
線程B進入synchronized下面的方法, 線程B在代碼3的時候判斷不過,從而保證了多線程下 單例模式的線程安全,
另外要慎用單例模式,因為單例模式一旦初始化后 只有進程退出才有可能被回收,如果一個對象不經常被使用,盡量不要使用單例,否則為了幾次使用,一直讓單例存在占用內存。
接著上一篇Android 面試題筆記(一)

13、Window和DecorView是什么?DecorView又是如何和Window建立聯系的?
DecorView的作用
DecorView是頂級View,本質就是一個FrameLayout
包含了兩個部分,標題欄和內容欄
內容欄id是content,也就是activity中setContentView所設置的部分,最終將布局添加到id為content的FrameLayout中
獲取content:ViewGroup content = findViewById(R.android.id.content)
獲取設置的View:content.getChidlAt(0)
Window是什么?

表示一個窗口的概念,是所有View的直接管理者,任何視圖都通過Window呈現(單擊事件由Window->DecorView->View; Activity的setContentView底層通過Window完成)
Window是一個抽象類,具體實現是PhoneWindow
創建Window需要通過WindowManager創建
WindowManager是外界訪問Window的入口
Window具體實現位于WindowManagerService中
WindowManager和WindowManagerService的交互是通過IPC完成
DecorView又是如何和Window建立聯系的?

在Activity的啟動流程中,處理onResume()的相關方法中,將DecorView作為Window的成員變量保存到Window內部
DecorView與Window建立聯系又有什么用呢?例如Activity的onSaveInstanceState()進行數據保存時,就通過window內部的DecorView觸發整個View樹進行狀態保存
//ActivityThread.java
final void handleResumeActivity(IBinder token, …) {
//1. 創建DecorView,設置為不可見INVISIBLE
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//2. 獲取到WindowManager, addView方法將DecorView添加到Window中
ViewManager wm = a.getWindowManager();
wm.addView(decor, l);
//3. 將DecorView設置為visible
r.activity.makeVisible();
}

  • 14、對于 Context,你了解多少?
    Context 宏觀來說是一個描述應用程序全局信息的場景,當然,本質上來說,這個“場景”其實是一個抽象類詳細見Android Context 上下文 你必須知道的一切
  • 15、SharedPreferences 是線程安全的嗎?它的 commit 和 apply 方法有什么區別?
    SharedPreferences 是線程安全的 進程不安全的, commit 是同步寫入有返回值,apply是異步寫入。
    apply沒有返回值而commit返回boolean表明修改是否提交成功
    apply是將修改數據原子提交到內存, 而后異步真正提交到硬件磁盤, 而commit是同步的提交到硬件磁盤,因此,在多個并發的提交commit的時候,他們會等待正在處理的commit保存到磁盤后在操作,從而降低了效率。而apply只是原子的提交到內容,后面有調用apply的函數的將會直接覆蓋前面的內存數據,這樣從一定程度上提高了很多效率。
    由于在一個進程中,sharedPreference是單實例,一般不會出現并發沖突,如果對提交的結果不關心的話,建議使用apply,當然需要確保提交成功且有后續操作的話,還是需要用commit的。
    - 16、HashMap 的實現原理?
  • 數組:存儲區間連續,占用內存嚴重,尋址容易,插入刪除困難;
  • 鏈表:存儲區間離散,占用內存比較寬松,尋址困難,插入刪除容易;
  • Hashmap: 綜合應用了這兩種數據結構,實現了尋址容易,插入刪除也容易。
    更多可參考以下文章:
    HashMap 原理以及源碼解析
    HashMap 碰撞問題
    HashMap 中的負載因子
  • 17、簡述一下 Android 中 UI 的刷新機制?
    應用層的:
  • 界面刷新的本質流程
    通過ViewRootImpl的scheduleTraversals()進行界面的三大流程。
    調用到scheduleTraversals()時不會立即執行,而是將該操作保存到待執行隊列中。并給底層的刷新信號注冊監聽。
    當VSYNC信號到來時,會從待執行隊列中取出對應的scheduleTraversals()操作,并將其加入到主線程的消息隊列中。
    主線程從消息隊列中取出并執行三大流程: onMeasure()-onLayout()-onDraw()
  • 同步屏障的作用
    同步屏障用于阻塞住所有的同步消息(底層VSYNC的回調onVsync方法提交的消息是異步消息)
    用于保證界面刷新功能的performTraversals()的優先執行。
  • 同步屏障的原理?
    主線程的Looper會一直循環調用MessageQueue的next方法并且取出隊列頭部的Message執行,遇到同步屏障(一種特殊消息)后會去尋找異步消息執行。如果沒有找到異步消息就會一直阻塞下去,除非將同步屏障取出,否則永遠不會執行同步消息。
    界面刷新操作是異步消息,具有最高優先級
    我們發送的消息是同步消息,再多耗時操作也不會影響UI的刷新操作

系統層的:
首先屏幕是 大約16.6ms刷新一次(固定的),當界面需要改變時, CPU開始計算,將計算結果 賦予 GPU 的buffer緩存起來,等待刷新時間的到來,然后根據buffer的數據刷新界面。如果當前界面沒有變化,CPU不用計算,也不會給GPU的buffer賦值啥的,這個buffer也就沒變化,等到刷新時間的到來,會依舊根據buffer刷新屏幕
結論是:界面改不改變都會刷新界面,只是在于CPU是否計算這點區別
UI刷新卡頓,基本都在于卡在CPU計算這一環節,對于根據GPU 的buffer刷新這一環節,在系統里有很高的優先級

  • 18、Serializable和Parcelable的區別?
    Serializable是屬于Java自帶的,本質是使用了反射。序列化的過程比較慢,這種機制在序列化的時候會創建很多臨時的對象,比引起頻繁的GC。Parcelable 是屬于 Android 專用。不過不同于Serializable,Parcelable實現的原理是將一個完整的對象進行分解。而分解后的每一部分都是Intent所支持的數據類型。 如果在內存中使用建議Parcelable。持久化操作建議Serializable;目前AS安裝android parcelable code generator插件可直接生成Parcelable
  • 19、Android進程間的通信方式
    1、Bundle的使用
    可以看到Bundle實現了Parcelable 接口。
    優點:簡單易用
    缺點:只能傳遞Bundle支持的數據類型
    使用場景:四大組件間的進程通訊

2.文件共享
優點:簡單易用
缺點:不適合高并發的場景,不能做到即時通訊。
使用場景:無并發訪問的情景,簡單的交換數據,實時性要求不高。

3.AIDI
優點:功能強大,支持一對多并發通信,支持實時通信。
缺點:一定要處理好線程同步的問題
使用場景:一對多進行通訊,有RPC(遠程過程調用協議)的需求

4.Messenger(信使)
優點:功能一般,支持一對多串行通信,支持實時通信。
缺點:不能很好的處理高并發場景,不支持RPC,數據通過Message進行傳輸,因此只能支持Bundle支持的數據類型。
使用場景:低并發的一對多的實時通訊,沒有RPC的需求或者說沒有返回結果的RPC(不調用服務端的相關方法)

5.ContentProvider
優點:主要用于數據訪問,支持一對多的并發數據共享。
缺點:受約束,主要針對數據源的增刪改查。
使用場景:一對多的數據共享。

6.Socket(套接字)
優點:功能強大,通過讀寫網絡傳輸字節流,支持一對多的并發的實時通訊。
缺點:不支持直接的RPC(這里我也不是很明白,間接的怎么實現?)
使用場景:網絡的數據交換
20、請簡述一下String、StringBuffer和StringBuilder的區別?

  • String 為字符串常量,一旦創建不可以被修改,是線程安全的;String 類使用 final
    修飾符,不可以被繼承;String 的長度是不變的。適用于少量操作的字符串。
  • StringBuffer 為字符串變量,長度是可變的,線程安全。適用于多線程下在字符緩沖區進行大量字符串操作
  • StringBuilder 為字符串變量,長度是可變的,線程不安全。適用于單線程下在字符緩沖區進行大量字符串操作。
  • 字符串操作在執行速度:StringBuilder > StringBuffer > String
    21、請簡述從點擊圖標開始app的啟動流程?
    ①點擊桌面App圖標,Launcher進程采用Binder IPC向system_server進程發起startActivity請求;
    ②system_server進程接收到請求后,向zygote進程發送創建進程的請求;
    ③Zygote進程fork出新的子進程,即App進程;
    ④App進程,通過Binder IPC向sytem_server進程發起attachApplication請求;
    ⑤system_server進程在收到請求后,進行一系列準備工作后,再通過binder IPC向App進程發送scheduleLaunchActivity請求;
    ⑥App進程的binder線程(ApplicationThread)在收到請求后,通過handler向主線程發送LAUNCH_ACTIVITY消息;
    ⑦主線程在收到Message后,通過發射機制創建目標Activity,并回調Activity.onCreate()等方法。
    ⑧到此,App便正式啟動,開始進入Activity生命周期,執行完onCreate/onStart/onResume方法,UI渲染結束后便可以看到App的主界面。
  • 22、IntentService 的應用場景和使用姿勢?
    IntentService是Service的子類,比普通的Service增加了額外的功能。先看Service本身存在兩個問題:Service不會專門啟動一條單獨的進程,Service與他所在應用位于同一個進程中。
    Service也不是專門一條新進程,因此不應該在Service中直接處理耗時的任務。
    特點:
    IntentService會創建獨立的worker線程來處理所有的Intent請求;
    會創建獨立的worker線程來處理onHandleIntent()方法實現的代碼,無需處理多線程的問題;
    所有請求處理完成后,IntentService會自動停止,無需調用stopSelf()方法停止Service;
    為Service的onBind()提供默認實現,返回null;
    為Service的onStartCommand提供默認實現,將請求Intent添加到隊列中;
    接著上一篇面試題Android 面試題筆記(二)
    23、IntentFilter是什么?有哪些使用場景?

(1)IntentFilter是和intent相匹配的,其中action,category,組成了匹配規則。同時intentFilter還可以設置優先級,其中默認是0,范圍是【-1000,1000】,值越大優先級越高。并且IntentFilter多被通過AndroidManifest.xml的形式使用。
(2) 使用場景
activity的隱式啟動和廣播的匹配
(3)IntentFilter的匹配規則
IntentFilter的過濾信息有action,category,data.一個組件可以包含多個intent-filter,一個intent只要能完全匹配一組intent-filter即可成功的啟動對應的組件。
24、回答一下什么是強、軟、弱、虛引用以及它們之間的區別?

  • 強引用(StrongReference)

強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧愿拋出 OutOfMemoryError 錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。

  • 軟引用(SoftReference)

如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存(下文給出示例)。
軟引用可以和一個引用隊列 ReferenceQueue 聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。

  • 弱引用(WeakReference)

弱引用與軟引用的區別在于:只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由于垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。
弱引用可以和一個引用隊列 ReferenceQueue 聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。

  • 虛引用(PhantomReference)

“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在于:虛引用必須和引用隊列 ReferenceQueue 聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之 關聯的引用隊列中。

- 25、AsyncTask的優點和缺點?
優點:使用方便,既可以執行串行任務,也可以執行并行任務
缺點:默認使用串行任務執行效率低,不能充分利用多線程加快執行速度;如果使用并行任務執行,在任務特別多的時候會阻塞UI線程獲得CPU時間片,后續做線程收斂需要自定義AsynTask,將其設置為全局統一的線程池,改動量比較大

  • 26、對于面向對象的六大基本原則了解多少?
  • 單一職責(Single Responsibility Principle):一個類只做一件事,可讀性提高
  • 里式替換原則( Liskov Substitution Principle):依賴繼承和多態,就是能用父類的地方就可以用子類替換,用子類的但不能用父類。
  • 依賴倒置原則(Dependence Inversion Principle):依賴抽象,就是模塊之間的依賴通過抽象發生。
  • 開閉原則(Open-Close Principle):不管是實體類,模塊還是函數都應該遵循對擴展開放對修改關閉。還是要依賴封裝和繼承
  • 接口隔離原則(Interface Segregation Principle):一個類對另一個類的依賴應該建立在最小的接口上,如果接口太大,我們需要把它分割成一些更細小的接口,也是為了降低耦合性
  • 迪米特原則(Law of Demeter ):也稱最少知識原則,也就是說一個類應該對自己需要耦合或者調用的類知道的最少,只需知道該方法即可,實現細節不必知道。
  • 27、LinearLayout, FrameLayout, RelativeLayout 哪個效率高, 為什么?
    對于比較三者的效率那肯定是要在相同布局條件下比較繪制的流暢度及繪制過程,在這里流暢度不好表達,并且受其他外部因素干擾比較多,比如CPU、GPU等等,我說下在繪制過程中的比較,1、Fragment是從上到下的一個堆疊的方式布局的,那當然是繪制速度最快,只需要將本身繪制出來即可,但是由于它的繪制方式導致在復雜場景中直接是不能使用的,所以工作效率來說Fragment僅使用于單一場景,2、LinearLayout 在兩個方向上繪制的布局,在工作中使用頁比較多,繪制的時候只需要按照指定的方向繪制,繪制效率比Fragment要慢,但使用場景比較多,3、RelativeLayout 它的沒個子控件都是需要相對的其他控件來計算,按照View樹的繪制流程、在不同的分支上要進行計算相對應的位置,繪制效率最低,但是一般工作中的布局使用較多,所以說這三者之間效率分開來講個有優勢、不足,那一起來講也是有優勢、不足,所以不能絕對的區分三者的效率
  • 28、請簡述一下 Android 7.0 的新特性?
    1.低電耗功能改進
    2.引入畫中畫功能
    3.引入“長按快捷方式”,即App Shortcuts
    4.引入混合模式,同時存在解釋執行/AOT/JIT,安裝應用時默認不全量編譯,使得安裝應用時間大大縮短
    5.引入了對私有平臺庫限制,然而用一個叫做Nougat_dlfunctions的庫就行
    6.不推薦使用file:// URI傳遞數據,轉而推薦使用FileProvider
    7.快速回復通知
  • 29、 Android 8.0 的新特性

1、通知渠道 — Notification Channels
2、畫中畫模式 — PIP
3、自適應圖標 — Adaptive Icons
4、定時作業調度
5、后臺限制
6、廣播限制
7、后臺位置限制
8、WebView API
9、多顯示器支持
10、 統一的布局外邊距和內邊距
11、指針捕獲
12、輸入和導航
13、新的 StrictMode 檢測程序
14、指紋手勢
15、更新的 ICU4J Android Framework API

  • 30、Android9.0新特性?
    1、室內WIFI定位
    2、“劉海”屏幕支持
    3、通知
    4、增強體驗
    5、通道設置、廣播以及免打擾
    6、多相機支持和相機更新
    7、新的圖片解碼
    8、動畫
    9、HDR VP9視頻,HEIF圖像壓縮和媒體API
    10、JobScheduler中的數據成本敏感度
    11、神經網絡API 1.1
    12、改進表單自動填充
    13、安全增強
    14、Android 備份加密

  • 31、請談談你對 MVC 和 MVP 的理解?

1.MVC
用戶首先通過View發起交互,View調用Controller執行業務邏輯,Controller修改Model,然后View通過觀察者模式檢測到Model的變化(具體表現形式可以是Pub/Sub或者是觸發Events),刷新界面顯示。
從這里可以看出,主要業務邏輯都在Controller中,Controller會變得很重。MVC比較明顯的缺點:
View依賴特定的Model,無法組件化
View和Controller緊耦合,如果脫離Controller,View難以獨立應用(功能太少)
2.MVP
為了克服MVC的上述缺點,MVP應運而生。在MVP中,View和Model是沒有直接聯系的,所有操作都必須通過Presenter進行中轉。View向Presenter發起調用請求,Presenter修改Model,Model修改完成后通知Presenter,Presenter再調用View的相關接口刷新界面。這樣,View就不需要監聽具體Model的變化了,只需要提供接口給Presenter調用就可以了。MVP具有以下優點:

View可以組件化,不需要了解業務邏輯,只需提供接口給Presenter
便于測試:只需要給Presenter mock一個View,實現View的接口即可

- 32、談談Android的事件分發機制?

  • 會經過Activity->ViewGroup->view,一次往下傳遞事件,如果一直不攔截再回調回來。

  • 主要經過三個方法,dispatchTouchEvent(分發事件),oninterceptTouchEvent(是否攔截View中不存在),onTouchEvent(處理)。

  • 三個方法的用法是,先用dispatchTouchEvent來分發事件,然后用oninterceptTouchEvent來判斷是否攔截該任務(此方法在dispatchTouchEvent內部),如果不攔截直接dispatch向下回調,如果攔截就調用自己的onTouchEvent來處理事件。

  • 如果由setOnClickListener方法會先執行onClick.
    更多事件分發機制見講講 Android 的事件分發機制

  • 33、談談ArrayList和LinkedList的區別?
    ArrayList和LinkedList的大致區別:
    1.ArrayList是實現了基于動態數組的數據結構,LinkedList是基于鏈表結構。
    2.對于隨機訪問的get和set方法,ArrayList要優于LinkedList,因為LinkedList要移動指針。
    3.對于新增和刪除操作add和remove,LinkedList比較占優勢,因為ArrayList要移動數據。
    性能上的缺點:
    1.對ArrayList和LinkedList而言,在列表末尾增加一個元素所花的開銷都是固定的。對 ArrayList而言,主要是在內部數組中增加一項,指向所添加的元素,偶爾可能會導致對數組重新進行分配;而對LinkedList而言,這個開銷是 統一的,分配一個內部Entry對象。
    2.在ArrayList集合中添加或者刪除一個元素時,當前的列表所所有的元素都會被移動。而LinkedList集合中添加或者刪除一個元素的開銷是固定的。
    3.LinkedList集合不支持 高效的隨機隨機訪問(RandomAccess),因為可能產生二次項的行為。
    4.ArrayList的空間浪費主要體現在在list列表的結尾預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗相當的空間

  • 34、handlerThread使用場景分析及原理?

當我們需要向子線程發送消息處理耗時操作時可使用handlerThread,詳細使用介紹見
handlerThread使用場景分析及源碼解析

  • 35、針對RecyclerView你做了哪些優化?
    1,減少view type的種類,如果樣式差別不大,可以公用一個布局。因為inflate調用比公用布局的繪制占用更多的性能。
    2,可以使用DiffUtil去刷新數據,notifyDataSetChanged性能太低而且不會出發增刪動畫。(子線程計算新舊數據,主線程刷新recylerView)
    3,分頁加載
    4,有大量圖片時,滾動停止加載圖片,停止才通知adapter去加載
    5,設置合理的RecycledViewPool
    6,item的高度固定時setHasFixedSize(true)
    7,在ViewHolder中設置點擊事件而不是在onBindViewHolder
  • 36,請說一下HashMap與HashTable的區別?
    HashMap和Hashtable的比較是Java面試中的常見問題,用來考驗程序員是否能夠正確使用集合類以及是否可以隨機應變使用多種思路解決問題。HashMap的工作原理、ArrayList與Vector的比較以及這個問題是有關Java 集合框架的最經典的問題。Hashtable是個過時的集合類,存在于Java API中很久了。在Java 4中被重寫了,實現了Map接口,所以自此以后也成了Java集合框架中的一部分。Hashtable和HashMap在Java面試中相當容易被問到,甚至成為了集合框架面試題中最常被考的問題,所以在參加任何Java面試之前,都不要忘了準備這一題。
  • 1父類不同

第一個不同主要是歷史原因。Hashtable是基于陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現。
public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable {…}
public class Hashtable<K, V> extends Dictionary<K, V> implements Map<K, V>, Cloneable, Serializable {…}
而HashMap繼承的抽象類AbstractMap實現了Map接口:
public abstract class AbstractMap<K, V> implements Map<K, V> {…}

  • 2 線程安全不一樣

Hashtable 中的方法是同步的,而HashMap中的方法在默認情況下是非同步的。在多線程并發的環境下,可以直接使用Hashtable,但是要使用HashMap的話就要自己增加同步處理了。

  • 3允不允許null值

Hashtable中,key和value都不允許出現null值,否則會拋出NullPointerException異常。
而在HashMap中,null可以作為鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應的值為null。當get()方法返回null值時,即可以表示 HashMap中沒有該鍵,也可以表示該鍵所對應的值為null。因此,在HashMap中不能由get()方法來判斷HashMap中是否存在某個鍵, 而應該用containsKey()方法來判斷。

  • 4遍歷方式的內部實現上不同

Hashtable、HashMap都使用了 Iterator。而由于歷史原因,Hashtable還使用了Enumeration的方式 。

  • 5哈希值的使用不同

HashTable直接使用對象的hashCode。而HashMap重新計算hash值。

  • 6 內部實現方式的數組的初始大小和擴容的方式不一樣

HashTable中的hash數組初始大小是11,增加的方式是 old*2+1。HashMap中hash數組的默認大小是16,而且一定是2的指數。

  • 37,簡述一下自定義View的流程?
    自定義屬性;
    選擇和設置構造方法;
    重寫onMeasure()方法;
    重寫onDraw()方法;
    重寫onLayout()方法;
    重寫其他事件的方法(滑動監聽等);
    更多見自定義view的三種實現方式Android自定義View的三種實現方式
    38、談談線程死鎖,如何有效的避免線程死鎖?
    死鎖產生的條件
    一般來說,出現死鎖問題需要滿足以下條件
  • 互斥條件:一個資源每次只能被一個線程使用
  • 請求與保證條件:一個線程因請求資源而阻塞時,對已獲得的資源保持不放
  • 不剝奪條件:線程已獲得的資源,在未使用完成之前,不能強行剝奪
  • 循環等待條件:若干線程之間形成一種頭尾相接的循環等待資源關系

在JAVA編程中,有3中典型的死鎖類型:

  • 靜態的鎖順序死鎖

  • 動態的鎖順序死鎖

  • 協作對象之間發生的死鎖

  • 典型死鎖例子

  • 注意以下代碼都是錯誤代碼

1.靜態的鎖順序死鎖

class Test{final Object objA = new Object();final Object objB = new Object();public void a(){//注意這里 先A后Bsynchronized(objA){synchronized(objB){//sth....}}}public void b(){//注意這里 先B后Asynchronized(objB){synchronized(objA){//sth....}}} }

2.動態的鎖順序死鎖
動態的鎖順序死鎖是指兩個線程調用同一個方法時,傳入的參數顛倒造成的死鎖。如下情景,一個線程調用了transferMoney(轉賬)方法并傳入參數accountA,accountB;另一個線程調用了transferMoney方法并傳入參數accountB,accountA。此時就可能發生在靜態的鎖順序死鎖中存在的問題,即:第一個線程獲得了accountA鎖并等待accountB鎖,第二個線程獲得了accountB鎖并等待accountA鎖。

3.協作對象之間發生的死鎖
有時,死鎖并不會那么明顯,比如兩個相互協作的類之間的死鎖,比如:一個線程調用了A對象的a方法,另一個線程調用了B對象的b方法。此時可能會發生,第一個線程持有A對象鎖并等待B對象鎖,另一個線程持有B對象鎖并等待A對象鎖。

  • 39、“equals”與“==”、“hashCode”的區別和使用場景?
    我們一般這么理解
    equal比較的是內容
    == 比較的存儲地址或基本數據類型的數值比較(數學意義)
    hashCode 對內存分配的位置確定

使用場景

equal一般比較內容相等 比如字符串相等
==一般比較數值 或者null判斷
hashcode我們一般用來判斷來兩個對象是否相等,但這里需要注意的是 兩個對象的hashcode相等,兩個對象不一定相等,兩個相等的對象hashcode一定相等。

我們為什么要這樣判斷呢?

因為判斷兩個對象相等重寫equal的重載方法比較多,需要判斷 傳遞性、非空性、自反性、一致性、對稱性

  • 40、談一談startService和bindService的區別,生命周期以及使用場景?
    1、生命周期上的區別

執行startService時,Service會經歷onCreate->onStartCommand。當執行stopService時,直接調用onDestroy方法。調用者如果沒有stopService,Service會一直在后臺運行,下次調用者再起來仍然可以stopService。

執行bindService時,Service會經歷onCreate->onBind。這個時候調用者和Service綁定在一起。調用者調用unbindService方法或者調用者Context不存在了(如Activity被finish了),Service就會調用onUnbind->onDestroy。這里所謂的綁定在一起就是說兩者共存亡了。

多次調用startService,該Service只能被創建一次,即該Service的onCreate方法只會被調用一次。但是每次調用startService,onStartCommand方法都會被調用。Service的onStart方法在API 5時被廢棄,替代它的是onStartCommand方法。

第一次執行bindService時,onCreate和onBind方法會被調用,但是多次執行bindService時,onCreate和onBind方法并不會被多次調用,即并不會多次創建服務和綁定服務。
2、調用者如何獲取綁定后的Service的方法

onBind回調方法將返回給客戶端一個IBinder接口實例,IBinder允許客戶端回調服務的方法,比如得到Service運行的狀態或其他操作。我們需要IBinder對象返回具體的Service對象才能操作,所以說具體的Service對象必須首先實現Binder對象。
3、既使用startService又使用bindService的情況

如果一個Service又被啟動又被綁定,則該Service會一直在后臺運行。首先不管如何調用,onCreate始終只會調用一次。對應startService調用多少次,Service的onStart方法便會調用多少次。Service的終止,需要unbindService和stopService同時調用才行。不管startService與bindService的調用順序,如果先調用unbindService,此時服務不會自動終止,再調用stopService之后,服務才會終止;如果先調用stopService,此時服務也不會終止,而再調用unbindService或者之前調用bindService的Context不存在了(如Activity被finish的時候)之后,服務才會自動停止。

那么,什么情況下既使用startService,又使用bindService呢?

如果你只是想要啟動一個后臺服務長期進行某項任務,那么使用startService便可以了。如果你還想要與正在運行的Service取得聯系,那么有兩種方法:一種是使用broadcast,另一種是使用bindService。前者的缺點是如果交流較為頻繁,容易造成性能上的問題,而后者則沒有這些問題。因此,這種情況就需要startService和bindService一起使用了。

另外,如果你的服務只是公開一個遠程接口,供連接上的客戶端(Android的Service是C/S架構)遠程調用執行方法,這個時候你可以不讓服務一開始就運行,而只是bindService,這樣在第一次bindService的時候才會創建服務的實例運行它,這會節約很多系統資源,特別是如果你的服務是遠程服務,那么效果會越明顯(當然在Servcie創建的是偶會花去一定時間,這點需要注意)。
4、本地服務與遠程服務

本地服務依附在主進程上,在一定程度上節約了資源。本地服務因為是在同一進程,因此不需要IPC,也不需要AIDL。相應bindService會方便很多。缺點是主進程被kill后,服務變會終止。

遠程服務是獨立的進程,對應進程名格式為所在包名加上你指定的android:process字符串。由于是獨立的進程,因此在Activity所在進程被kill的是偶,該服務依然在運行。缺點是該服務是獨立的進程,會占用一定資源,并且使用AIDL進行IPC稍微麻煩一點。

對于startService來說,不管是本地服務還是遠程服務,我們需要做的工作都一樣簡單。

  • 41、synchronized和volatile關鍵字的區別?
    1.volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取; synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。
    2.volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的
    volatile僅能實現變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性
    3.volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
    4.volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化

synchronized 可以保證原子性。他可以保證 在同一時刻,只有一個線程可以訪問被 synchronized 修飾的方法,或者代碼塊。
volatile 不能保證原子性。當時在使用這個關鍵字后。當被Volatitle 修飾字段的值發生改變后,其他線程會立刻知道這個值已經發生變化了。volatitle 可以保證可見性和有序性。

  • 42、什么是冒泡排序?如何優化?
    冒泡排序算法原理:(從小到大排序)
    1.比較相鄰的元素。如果第一個比第二個大,就交換他們兩個
    2.對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對,交換一趟后,最后的元素會是最大的數
    3.針對所有的元素重復以上的步驟,除了最后一個
    4.持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較

優化方案1(定義一個變量l來保存一趟交換中兩兩交換的次數,如果l==0,則說明排序已經完成,退出for循環)
優化方案2(假如有一個長度為50的數組,在一趟交換后,最后發生交換的位置是10,那么這個位置之后的40個數必定已經有序了,記錄下這位置,下一趟交換只要從數組頭部到這個位置就可以了)
定義一個變量n來保存一趟交換中最后一次發生交換的位置,并把它傳遞給下一趟交換

/*** 排序思想:* 對一組數字進行從小到大或者從大到小的進行排序。* 它是通過讓相鄰的兩個元素進行比較,大的元素向下沉,小的元素向上冒* arr[0]與arr[1]進行比較,如果前者大于后者,則交換位置* 然后arr[1]與arr[2]進行比較,以此類推。當進行到n-1輪后,排序完成。*/ import java.util.Arrays; public class Sort {public static void main(String[] args){int arr[]= {100,90,101,23,13,75};int temp=0;for(int i=0;i<arr.length-1;i++) {for(int j=0;j<arr.length-1-i;j++) {if(arr[j]>arr[j+1]) {temp=arr[j+1];arr[j+1]=arr[j];arr[j]=temp;}}System.out.println("第["+(i+1)+"]輪,排序結果:"+ Arrays.toString(arr));}System.out.print("================================");int arr2[]= {100,90,101,23,13,75};sort2(arr2);}/*** 優化思路:* 假如在第1輪比較當中,發現所有的元素都沒有進行交換,則說明此原數據就是有序的,不需要再進行排序* @param arr*/public static void sort2(int arr[]){int temp=0;int flag=0;for(int i=0;i<arr.length-1;i++) {flag=0;for(int j=0;j<arr.length-1-i;j++) {if(arr[j]>arr[j+1]) {temp=arr[j+1];arr[j+1]=arr[j];arr[j]=temp;//如果有交換的行為,則flag=1flag=1;}}//說明上面 內for循環中,沒有交換任何元素。if(flag==0) {break;}System.out.println("第["+(i+1)+"]輪,排序結果:"+Arrays.toString(arr));}} }
  • 43、分別講講 final,static,synchronized 關鍵字可以修飾什么,以及修飾后的作用?
    static
    static 方法
    static 方法一般稱作靜態方法,由于靜態方法不依賴于任何對象就可以進行訪問,因此對于靜態方法來說,是沒有 this 的,因為它不依附于任何對象,既然都沒有對象,就談不上 this 了。
    public class StaticTest {
    public static void a(){
    }
    public static void main(String[]args){
    StaticTest.a();
    }
    }
    static 變量
    static 變量也稱作靜態變量,靜態變量和非靜態變量的區別是:靜態變量被所有的對象所共享,在內存中只有一個副本,它當且僅當在類初次加載時會被初始化。而非靜態變量是對象所擁有的,在創建對象的時候被初始化,存在多個副本,各個對象擁有的副本互不影響。
    static 代碼塊
    static 關鍵字還有一個比較關鍵的作用就是 用來形成靜態代碼塊以優化程序性能。static 塊可以置于類中的任何地方,類中可以有多個 static 塊。在類初次被加載的時候,會按照 static 塊的順序來執行每個 static 塊,并且只會執行一次。
    public class StaticTest {
    private static int a ;
    private static int b;
    static {
    a = 1;
    b = 2;
    }
    final
    final 變量
    凡是對成員變量或者本地變量(在方法中的或者代碼塊中的變量稱為本地變量)聲明為 final 的都叫作 final 變量。final 變量經常和 static 關鍵字一起使用,作為常量。
    private final int aa = 1;
    static {
    a = 1;
    b = 2;
    }
    private void init(){
    aa = 2;//報錯編譯器會提示 不能賦值。。
    }
    final 方法
    final 也可以聲明方法。方法前面加上 final 關鍵字,代表這個方法不可以被子類的方法重寫。如果你認為一個方法的功能已經足夠完整了,子類中不需要改變的話,你可以聲明此方法為 final。final 方法比非 final 方法要快,因為在編譯的時候已經靜態綁定了,不需要在運行時再動態綁定。
    public static void main(String[]args){
    StaticTest.a();
    }
    class StaticTest2 extends StaticTest{
    public final void a(){ //這邊就會編譯器提示不能重寫
    }
    }
    **final 類 **
    其實更上面同個道理,使用 final 來修飾的類叫作 final 類。final 類通常功能是完整的,它們不能被繼承。Java 中有許多類是 final 的,譬如 String,Interger 以及其他包裝類。
    synchronized
    synchronized 是 Java 中解決并發問題的一種最常用的方法,也是最簡單的一種方法。synchronized 的作用主要有三個:

確保線程互斥的訪問同步代碼
保證共享變量的修改能夠及時可見
有效解決重排序問題。
synchronized 方法
有效避免了類成員變量的訪問沖突:
private synchronized void init(){
aa = 2;
}
synchronized 代碼塊
這時鎖就是對象,誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以這樣寫程序,但當沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創建一個特殊的 instance 變量(它得是一個對象)來充當鎖。
public final void a(){
synchronized (lock){
//代碼
}
}
@Override
public void run() {
}

  • 44、什么是 RemoteViews?使用場景有哪些?
    RemoteViews
    RemoteViews翻譯過來就是遠程視圖.顧名思義,RemoteViews不是當前進程的View,是屬于SystemServer進程.應用程序與RemoteViews之間依賴Binder實現了進程間通信.
    用法
    通常是在通知欄
//1.創建RemoteViews實例RemoteViews mRemoteViews=new RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout);//2.構建一個打開Activity的PendingIntentIntent intent=new Intent(MainActivity.this,MainActivity.class);PendingIntent mPendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);//3.創建一個NotificationmNotification = new Notification.Builder(this).setSmallIcon(R.drawable.ic_launcher).setContentIntent(mPendingIntent).setContent(mRemoteViews).build();//4.獲取NotificationManagermanager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);Button button1 = (Button) findViewById(R.id.button1);button1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {//彈出通知manager.notify(1, mNotification);}});
  • 45、什么是反射機制?反射機制的應用場景有哪些?
    Java 反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類中的所有屬性和方法,對于任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為 Java 語言的反射機制。
    應用場景:

逆向代碼,例如反編譯
與注解相結合的框架,如 Retrofit
單純的反射機制應用框架,例如 EventBus(事件總線)
動態生成類框架 例如Gson

import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;/*** 對于任何一個類,我們都能夠知道這個類有哪些方法和屬性。對于任何一個對象,* 我們都能夠對它的方法和屬性進行調用。* 我們把這種動態獲取對象信息和調用對象方法的功能稱之為 反射機制*/ /*** 所謂反射其實是獲取類的字節碼文件,* 也就是.class文件,那么我們就可以通過Class這個對象進行獲取*/ public class HookTest {public static void main(String[] args) {//第一種方式LoopTest loopTest = new LoopTest();Class aClass = loopTest.getClass();System.out.println(aClass.getName());//第二種方式Class aclass2 = LoopTest.class;System.out.println(aclass2.getName());//第三種方式try {Class aclass3 = Class.forName("LoopTest");System.out.println(aclass3.getName());}catch (ClassNotFoundException ex){ex.printStackTrace();}/*** 那么這3中方式我們一般選用哪種方式呢?第一種已經創建了對象,那么這個時候就不需要去進行反射了,* 顯得有點多此一舉。第二種需要導入類的包,依賴性太強。所以我們一般選中第三種方式。*//*** 三、通過反射獲取類的構造方法、方法以及屬性*//*** 1、獲取構造方法*/Constructor[]constructors = aclass2.getConstructors();System.out.println("獲取構造方法:");for (Constructor constructor1 : constructors){System.out.println(constructor1.getName());}System.out.println("獲取類的屬性:");Field[] fields = aclass2.getFields();//88888System.out.println("獲取類的方法:");Method[]methods = aclass2.getMethods();for (Method method : methods){System.out.println(method.getName());}/*** 反射執行方法*/try { Class aclass4 = Class.forName("LoopTest");Method method = aclass4.getDeclaredMethod("method",String.class);Constructor ct = aclass4.getConstructor(null);Object obj = ct.newInstance(null);method.invoke(obj,"反射調用");} catch (Exception e) {e.printStackTrace();}/*** Android中使用場景:其實很多用過的EventBus 、Retrofit 都有涉獵 可以去看看源碼*/}}

java反射機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
靜態編譯:在編譯時確定類型,綁定對象。
動態編譯:在運行時確定類型,綁定對象。
反射機制的優缺點:
優點:運行期類型的判斷,動態加載類,提高代碼靈活度。
缺點:性能瓶頸:反射相當于一系列解釋操作,通知 JVM 要做的事情,性能比直接的java代碼要慢很多。

  • 46、Java 中使用多線程的方式有哪些?
    1、繼承Thread類創建線程
    Thread類本質上是實現了Runnable接口的一個實例,代表一個線程的實例。啟動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啟動一個新線程,并執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend Thread,并復寫run()方法,就可以啟動新線程并執行自己定義的run()方法。
    2、實現Runnable接口創建線程
    如果自己的類已經extends另一個類,就無法直接extends Thread,此時,可以實現一個Runnable接口
    3、實現Callable接口通過FutureTask包裝器來創建Thread線程
    Callable接口(也只有一個方法
    4、4、使用ExecutorService、Callable、Future實現有返回結果的線程

ExecutorService、Callable、Future三個接口實際上都是屬于Executor框架。返回結果的線程是在JDK1.5中引入的新特征,有了這種特征就不需要再為了得到返回值而大費周折了。
可返回值的任務必須實現Callable接口。類似的,無返回值的任務必須實現Runnable接口

  • 47、請簡述一下什么是 Kotlin?它有哪些特性?
    設計理念
    1、創建一種兼容Java的語言
    2、讓它比Java更安全,能夠靜態檢測常見的陷阱。如:引用空指針
    3、讓它比Java更簡潔,通過支持variable type inference,higher-order functions (closures),extension functions,mixins and first-class delegation等實現。
    4、讓它比最成熟的競爭對手Scala語言更加簡單。
    Kotlin優勢
    1、簡潔: 大大減少樣板代碼的數量。
    2、安全: 避免空指針異常等整個類的錯誤。
    3、互操作性: 充分利用 JVM、Android 和瀏覽器的現有庫。
    4、工具友好: 可用任何 Java IDE 或者使用命令行構建。

kotlin和java都是運行在java虛擬機的語言。編譯后都會生成.class文件。而虛擬機運行的正是.class文件。所以兩者都可以用來寫Android。再說說個人的一些看法。java作為一門相對時間長一點的語言。相對來說更萬能一些。基本上能完成所有的開發場景。而且,因為時間夠久,相對來說問題也很少,雖然大家都吐槽分號,類型轉換,空指針這些傻瓜操作,但是我并沒有覺得不寫這些就能對我的開發有質的的提升,唯一讓我想學kt的動力就是google的Android實例將來要用kt寫。而kotlin作為一門新語言,有他自己的優點,也有一些缺點。具體什么缺點大家看下面的文章吧。

從java到kotlin,再從kotlin回歸java

  • 48.談談Error和Exception的區別?
    Exception是java程序運行中可預料的異常情況,咱們可以獲取到這種異常,并且對這種異常進行業務外的處理。

Error是java程序運行中不可預料的異常情況,這種異常發生以后,會直接導致JVM不可處理或者不可恢復的情況。所以這種異常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。

其中的Exception又分為檢查性異常和非檢查性異常。兩個根本的區別在于,檢查性異常 必須在編寫代碼時,使用try catch捕獲(比如:IOException異常)。非檢查性異常 在代碼編寫使,可以忽略捕獲操作(比如:ArrayIndexOutOfBoundsException),這種異常是在代碼編寫或者使用過程中通過規范可以避免發生的。 切記,Error是Throw不是Exception 。

  • 49.請簡述 Http 與 Https 的區別?

HTTP協議傳輸的數據都是未加密的,也就是明文的,因此使用HTTP協議傳輸隱私信息非常不安全,為了保證這些隱私數據能加密傳輸,于是網景公司設計了SSL(Secure Sockets Layer)協議用于對HTTP協議傳輸的數據進行加密,從而就誕生了HTTPS。
1、https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。

2、http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。

3、http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。

4、http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。
最后一點在Android 9.0 如果用http進行傳輸,需要在application節點下設置 android:usesCleartextTraffic=“true”

  • 50、說說項目中用到的設計模式和使用場景?

單例模式

常見應用場景:網絡請求的工具類、sp存儲的工具類、彈窗的工具類等

工廠模式

常見應用場景:activity的基類等

責任鏈模式

常見應用場景:OKhttp的攔截器封裝

觀察者模式

常見應用場景:Rxjava的運用

代理模式

常見應用場景:AIDL的使用

建造者模式

常見應用場景:Dialog的創建

  • 51、簡述下熱修復的原理?

代碼修復主要有三個方案,分別是底層替換方案、類加載方案和Instant Run方案。3.1 類加載方案類加載方案基于Dex分包方案,什么是Dex分包方案呢?這個得先從65536限制和LinearAlloc限制說起。
65536限制
隨著應用功能越來越復雜,代碼量不斷地增大,引入的庫也越來越多,可能會在編譯時提示如下異常:com.android.dex.DexIndexOverflowException:methodIDnotin[0, 0xffff]: 65536

這說明應用中引用的方法數超過了最大數65536個。產生這一問題的原因就是系統的65536限制,65536限制的主要原因是DVM Bytecode的限制,DVM指令集的方法調用指令invoke-kind索引為16bits,最多能引用 65535個方法。
LinearAlloc限制
在安裝時可能會提示INSTALL_FAILED_DEXOPT。產生的原因就是LinearAlloc限制,DVM中的LinearAlloc是一個固定的緩存區,當方法數過多超出了緩存區的大小時會報錯。為了解決65536限制和LinearAlloc限制,從而產生了Dex分包方案。Dex分包方案主要做的是在打包時將應用代碼分成多個Dex,將應用啟動時必須用到的類和這些類的直接引用類放到主Dex中,其他代碼放到次Dex中。當應用啟動時先加載主Dex,等到應用啟動后再動態的加載次Dex,從而緩解了主Dex的65536限制和LinearAlloc限制。Dex分包方案主要有兩種,分別是Google官方方案、Dex自動拆包和動態加載方案;

熱更新 / 熱修復
不安裝新版本的軟件直接從網絡下載新功能模塊對軟件進行局部更新
熱更新和插件化的區別
區別有兩點:
1.插件化的內容在原來的App中沒有,而熱更新在原來 App 做了 改動
2. 插件化在代碼中有固定的入口,而熱更新則可能改變任何一個位置的代碼
熱更新的原理

classsLoder 的 dex 文件替換
直接修改字節碼文件
了解熱更新就必須了解 loadeClass() 的類加載過程

宏觀上: loadClass() 是一個帶緩存,自上而下的加載過程(即網上說的[雙親委托機制])

對于一個具體的 ClassLoader

先從自己緩存中取
自己緩存沒有,就在 父 ClassLoader 要 (parent.loadClass())
父 View 沒有,就自加載(findClass)
BaseDexClassLoader 或者 它的子類(DexClassLoader,PathClassLoader) 的 findClass()

通過它的 pathList.findClass()
它的 pathList .loadClass() 通過 pathClassLoader 等
所以熱更新關鍵在于,把補丁 dex 文件加載到 Element 對象插入到 dexElement 前面才行.插入后面會被忽閱掉
正確的做法是:反射

自己用補丁創建一個PathClassLoader
2.把補丁 PathClassLoader 里面的 elments 替換到舊的里面去
注意:
1.盡早加載熱更新(通用手段把加載過程放到Application.attachBaseContext())
把 補丁 PathClassLoader 里面的 elements 替換到舊的里面去
熱更新下載完成后在需要時先殺死進程才能補丁生效
優化:熱更新沒必要把所有的內容都打過來,只要把類拿過來就行了
d8 把 指定 的 class 打包 進 dex
5.完整化: 從網上加載
再優化: 把打包過程寫一個task

  • 52、談談對于ConcurrentHashMap的理解?
    從JDK1.2起,就有了HashMap,正如前一篇文章所說,HashMap不是線程安全的,因此多線程操作時需要格外小心。
    在JDK1.5中,偉大的Doug Lea給我們帶來了concurrent包,從此Map也有安全的了。
    ConcurrentHashMap具體是怎么實現線程安全的呢,肯定不可能是每個方法加synchronized,那樣就變成了HashTable。

從ConcurrentHashMap代碼中可以看出,它引入了一個“分段鎖”的概念,具體可以理解為把一個大的Map拆分成N個小的HashTable,根據key.hashCode()來決定把key放到哪個HashTable中。

在ConcurrentHashMap中,就是把Map分成了N個Segment,put和get的時候,都是現根據key.hashCode()算出放到哪個Segment中:
ConcurrentHashMap中默認是把segments初始化為長度為16的數組。
總結:以上就是ConcurrentHashMap的工作機制,通過把整個Map分為N個Segment(類似HashTable),可以提供相同的線程安全,但是效率提升N倍,默認提升16倍。

  • 53、Java 中深拷貝與淺拷貝的區別?
    首先需要明白,淺拷貝和深拷貝都是針對一個已有對象的操作。那先來看看淺拷貝和深拷貝的概念。在 Java 中,除了基本數據類型(元類型)之外,還存在 類的實例對象 這個引用數據類型。而一般使用 『 = 』號做賦值操作的時候。對于基本數據類型,實際上是拷貝的它的值,但是對于對象而言,其實賦值的只是這個對象的引用,將原對象的引用傳遞過去,他們實際上還是指向的同一個對象。而淺拷貝和深拷貝就是在這個基礎之上做的區分,如果在拷貝這個對象的時候,只對基本數據類型進行了拷貝,而對引用數據類型只是進行了引用的傳遞,而沒有真實的創建一個新的對象,則認為是淺拷貝。反之,在對引用數據類型進行拷貝的時候,創建了一個新的對象,并且復制其內的成員變量,則認為是深拷貝。所以到現在,就應該了解了,所謂的淺拷貝和深拷貝,只是在拷貝對象的時候,對 類的實例對象 這種引用數據類型的不同操作而已。總結來說:1、淺拷貝:對基本數據類型進行值傳遞,對引用數據類型進行引用傳遞般的拷貝,此為淺拷貝。2、深拷貝:對基本數據類型進行值傳遞,對引用數據類型,創建一個新的對象,并復制其內容,此為深拷貝。
  • 54.談談如何重寫equals()方法?為什么還要重寫hashCode()?
    hashcode()
    (1)hashCode 的存在主要用于查找的快捷性,如 Hashtable, HashMap 等,hashCode 是用來在三列存儲結構中確定對象的存儲地址的。
    (2)如果兩個對象相同,就是適用于 euqals(java.lang.Object) 方法,那么這兩個對象的 hashCode一定相同。
    (3)如果對象的euqals 方法被重寫,那么對象的 hashCode 也盡量重寫,并且產生 hashCode 使用的對象,一定要和 equals 方法中使用的一致,否則就會違反上面提到的第二點。
    (4)兩個對象的 hashCode 相同,并不一定表示這兩個對象就相同,也就是不一定適用于equals() 方法,只能夠說明這兩個對象在三列存儲結構中,如 Hashtable.,他們存在同一個籃子里。
  • 55,說一說https,udp,socket區別?
    (1)TCP(Transmission Control Protocol,傳輸控制協議)與UDP(User Data Protocol,用戶數據協議)是互聯網傳輸數據較為常用的協議,我們熟知的HTTP就是基于TCP的.而HTTPS就是HTTP 加上SSL的加密方式:
    (2)UDP是非面向連接的協議,發送數據時不管對方狀態直接發送,無需建立連接,如同微信發送一個消息或者語音信息,對面在不在線無所謂.
    (3)Socket不屬于協議范疇,別名套接字通過調用Socket,才能使用TCP/IP協議,Socket連接是長連接,理論上客戶端和服務器端一旦建立連接將不會主動斷開此連接。Socket連接屬于請求-響應形式,服務端可主動將消息推送給客戶端。
  • 56,簡要說說 LruCache 的原理?
    LruCache 非常適合用于 緩存圖片,他的主要算法原理是包最近使用的對象 存儲在 LinckHashMap中,并且把最近使用的最少的對象在 緩存值達到預設值之前從內存中移除
`public class LruCachePhoto { /** * 圖片 緩存技術的核心類,用于緩存下載好的所有圖片, * 在程序內存達到設定值后會將最少最近使用的圖片移除掉 */ private LruCache<String, Bitmap> mMenoryCache;public LruCachePhoto() {//獲取應用最大可用內存int maxMemory = (int) Runtime.getRuntime().maxMemory();//設置 緩存文件大小為 程序最大可用內存的 1/8int cacheSize = maxMemory / 8;mMenoryCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getByteCount();}}; }/*** 從 LruCache 中獲取一張圖片,如果不存在 就返回 null** @param key LurCache 的鍵,這里是 圖片的地址* @return 返回對應的 Bitmap對象,找不到則為 null*/ public Bitmap getBitmapFromMemoryCache(String key) {return mMenoryCache.get(key); }/*** 添加一張圖片* * @param key key* @param bitmap bitmap*/ public void addBitmapToCache(String key, Bitmap bitmap) {if (getBitmapFromMemoryCache(key) == null) {mMenoryCache.put(key, bitmap);} } }`
  • 57、談談怎么給apk瘦身?
    (1)res目錄優化:將png格式轉webp或svg格式,
    保真壓縮圖片:可以使用一些圖片壓縮網站或者工具壓縮你的資源文件吧,例如TinyPng、ImageOptim、Zopfli、智圖等。
    (2)使用lint刪除無用資源:在多人開發過程中,通常都會有漏刪無用資源的問題,圖片資源也不例外,例如需要刪除一個模塊的代碼時,很容易就會漏刪資源文件,所以可以定期使用lint檢測出無用的資源文件,原理這里不作介紹,使用方法非常簡單,可以直接在AS里面使用,如下圖所示。注意:lint檢查出來的資源都是無直接引用的,所以如果我們通過getIdentifier()方法引用文件時,lint也會標記為無引用,所以刪除時注意不要刪除通過getIdentifier()引用的資源。
    (3)方法:Analyze -> Run Inspection by Name -> 輸入:Unused resources -> 跳出彈框選擇范圍即可
    (4)去掉無用資源:打開shrinkResources
    shrinkResources是在編譯過程中用來檢測并刪除無用資源文件,也就是沒有引用的資源,minifyEnabled 這個是用來開啟刪除無用代碼,比如沒有引用到的代碼,所以如果需要知道資源是否被引用就要配合minifyEnabled使用,只有兩者都為true時才會起到真正的刪除無效代碼和無引用資源的目的。打開方式也是非常簡單,在build.gralde文件里面打開即可:
    android {
    buildTypes{
    minifyEnabled true
    shrinkResources true
    }
    }
    (5)Proguard代碼混淆:
    Proguard是一款免費的Java類文件壓縮器、優化器和混淆器,Android Studio已經集成了這個工具,只要經過簡單的配置,即可完成,如下代碼所示,在build.gradle里面設置minifyEnabled為ture,同時在proguardFiles指向proguard的規則文件即可。
    android {
    buildTypes{
    minifyEnabled true
    proguardFiles ‘proguard.cfg’
    }
    }
  • 58、JVM、Dalvik、ART三者的原理和區別?
    JVM:是Java Virtual Machine的縮寫,其并不是指某個特定的虛擬機實現,而指任何能夠運行Java字節碼(class文件)的虛擬機實現,比如oracle的Hotspot VM

Dalvik:是Google寫的一個用于Android的虛擬機,但其嚴格來說并不算JVM(沒有遵守Java虛擬機規范,比如其字節碼格式是dex而非class)
該虛擬機在5.0時被ART替代

ART:是Android Runtime的縮寫,嚴格來說并不是一個虛擬機,在4.4~6.0時采用安裝時全部編譯為機器碼的方式實現,7.0時默認不全部編譯,采用解釋執行+JIT+空閑時AOT以改善安裝耗時
ART在安卓4.4時加入,5.0取代dalvik作為唯一實現直到現在。

  • 59、談談你是如何優化App啟動過程的?
    (1)盡量不要在Application里做耗時操作,能放子線程的放子線程,能延后初始化的延后
    (2)啟動頁可以做成一個view在主頁面加載,同時主頁面的一些操作可以在這個過程中開始初始化
    (3)啟動頁的view層級盡量簡單

  • 60、談一談單例模式,建造者模式,工廠模式的使用場景?如何合理選擇?
    (1)單例模式,一般是指將消耗內存、屬性和對象支持全局公用的對象,應該設置為單例模式,如持久化處理(網絡、文件等)
    (2)建造者模式,一般見于開發的框架或者屬性時可以直接鏈式設置屬性,比如我們看到的AlertDialog,一般用在某些輔助類(如BRVAH的BaseViewHolder)或者開發的框架的時候方便連續設置多個屬性和調用多個方法。
    (3)工廠模式,一般用于業務的實體創建,在創建的過程中考慮到后期的擴展。在Android源碼中比較常見的有BitmapFactoryLayoutInflater.Factory,在實體編碼的過程中,比如BRVAH的多布局,如果數據類型比較多或者后期需要擴展,則可以通過工廠布局的方式,將實現MultiItemEntity接口的實體通過工廠模式創建:

  • 61、談談布局優化的技巧?
    1、降低Overdraw(過度繪制),減少不必要的背景繪制
    2、減少嵌套層次及控件個數
    3、使用Canvas的clipRect和clipPath方法限制View的繪制區域
    4、通過imageDrawable方法進行設置避免ImageView的background和imageDrawable重疊
    5、借助ViewStub按需延遲加載
    6、選擇合適的布局類型
    7、熟悉API盡量借助系統現有的屬性來實現一些UI效果
    8、盡量減少控件個數,對 TextView 左邊或者右邊有圖片可是試用 drawableLeft,drawableRight

  • 61、說一下線程的幾種狀態?
    1.初始(NEW) ,創建線程對象
    2.運行(RUNNABLE),此時就緒且正在運行一起稱為運行
    3.阻塞(BLOCKED),線程阻塞
    4.等待(WAITING),等待中斷等操作
    5.超時等待(TIMED_WAITING),可以指定時間返回,不一定需要操作
    6.終止(TERMINATED),線程執行完畢

  • 62、簡單介紹下ContentProvider是如何實現數據共享的?
    使用 ContentProvider 可以將數據共享給其他應用,讓除本應用之外的應用也可以訪問本應用的數據。它的底層是用 SQLite 數據庫實現的,所以其對數據做的各種操作都是以 Sql 實現,只是在上層提供的是 Uri,用戶只需要關心操作數據的 uri 就可以了,ContentProvider 可以實現不同 app 之間共享。詳細使用見ContentProvider跨程序共享數據(一)

  • 63、談談App的電量優化?
    (1)GPS

——使用要謹慎,如精確度不高可用WiFi定位或者基站定位,可用;非要用的話,注意定位數據的復用和定位頻率的閾值

(2)Process和Service

——按需啟動,用完就退出

(3)網絡數據交互

——減少網絡網絡請求次數和數據量;WiFi比手機網絡省電

(4)CPU

——減少I/O操作(包括數據庫操作),減少大量的計算

(5)減少手機硬件交互

——使用頻率優化和選擇低功耗模式
(6)避免輪循。可以利用推送。如果非要輪循,合理的設置頻率。
應用處于后臺時,避免某些數據的傳輸,比如感應器,定位,視頻緩存。
頁面銷毀時,取消掉網絡請求。
限制訪問頻率,失敗后不要無限的重連。
合理的選擇定位精度和頻率。
使用緩存。如果數據變化周期比較長,可以出一個配置接口,用于記錄那些接口有變化。沒變化的直接用緩存。
減少廣播的使用頻率。可以用觀察者,startActivityForResult等代替。

  • 64、Java 線程中notify 和 notifyAll有什么區別?
    當線程狀態為等待、超時等待會調用notify 和 notifyAll方法通知線程更改狀態,此時
    當線程數量為1時,notify 和 notifyAll的效果一樣,會喚醒一個線程,并獲取鎖
    當線程數量大于1時,notify會喚醒一個線程,并獲取鎖,notifyAll會喚醒所有線程并根據算法選取其中一個線程獲取鎖,區別在于此時使用notify可能會出現死鎖的情況

  • 65、談一談你對binder的機制的理解?
    Binder機制:
    1.為了保證進程空間不被其他進程破壞或干擾,Linux中的進程是相互獨立或相互隔離的。
    2.進程空間分為用戶空間和內核空間。用戶空間不可以進行數據交互;內核空間可以進行數據交互,所有進程共用一個內核空間。
    3.Binder機制相對于Linux內傳統的進程間通信方式:(1)性能更好;Binder機制只需要拷貝數據一次,管道、消息隊列、Socket等都需要拷貝數據兩次;而共享內存雖然不需要拷貝,但實現復雜度高。(2)安全性更高;Binder機制通過UID/PID在內核空間添加了身份標識,安全性更高。
    4.Binder跨進程通信機制:基于C/S架構,由Client、Server、Server Manager和Binder驅動組成。
    5.Binder驅動實現的原理:通過內存映射,即系統調用了mmap()函數。
    6.Server Manager的作用:管理Service的注冊和查詢。
    7.Binder驅動的作用:(1)傳遞進程間的數據,通過系統調用mmap()函數;(2)實現線程的控制,通過Binder驅動的線程池,并由Binder驅動自身進行管理。
    8.Server進程會創建很多線程處理Binder請求,這些線程采用Binder驅動的線程池,由Binder驅動自身進行管理。一個進程的Binder線程池默認最大是16個,超過的請求會阻塞等待空閑的線程。
    9.Android中進行進程間通信主要通過Binder類(已經實現了IBinder接口),即具備了跨進程通信的能力。

  • 66、什么是線程池?如何創建一個線程池?
    線程池:
    1.線程池:創建多個線程,并管理線程,為線程分配任務并執行。
    2.使用線程池的好處:多個線程的創建會占用過多的系統資源,造成死鎖或OOM
    3.線程池的作用:(1)可以復用創建好的線程,減少線程的創建或銷毀的開銷;(2)提高響應速度,當任務到達時,不需要等待就可以立即執行;(3)可有效控制最大并發的線程數,提高系統資源的利用率。防止死鎖或OOM;(4)可以提供定時和定期的執行方式。
    4.線程池參數:corePoolSize(核心線程數)、maximumPoolSize(最大線程數)、workQueue(阻塞隊列)、keepAliveTime(保活時間)、threadFactory(線程工廠,用于生成線程)。
    5.線程池提交任務:有兩個方法可以向線程池提交任務,分別是execute()和submit()。
    execute():用于提交不需要返回值的任務,無法判斷任務是否被線程執行成功。
    submit():用于提交需要返回值的任務,會返回一個future類型的對象,來判斷任務是否執行成功,還可以通過future的get()方法獲取返回值,get()方法會阻塞當前線程直到任務完成。
    5.線程池的工作流程:
    (1)有新任務時,判斷當前線程數是否超過corePoolSize,如果小于corePoolSize,即使有空閑線程可以執行任務,也會創建一個新的線程用來執行該任務;
    (2)如果超過corePoolSize,就把任務放在workQueue(阻塞隊列)中,等待被執行,前提是workQueue是有界隊列;
    (3)如果workQueue滿了,判斷當前線程數是否小于maximumPoolSize,如果小于maximumPoolSize就創建一個線程用來執行任務。
    (4)如果當前線程數大于maximumPoolSize,就會執行線程池的飽和策略。
    6.線程池的飽和策略:(1)默認策略:直接拋出異常;(2)用調用者所在的線程(提交任務的那個線程)執行任務;(3)丟棄阻塞隊列中最靠前的任務,執行當前任務;(4)直接丟棄任務。
    7.線程池的狀態:
    (1)RUNNING:接收提交的任務。
    (2)SHUTDOWN:不再接收新提交的任務,繼續處理阻塞隊列中的任務。
    (3)STOP:不再接收新的任務,也不會處理阻塞隊列中的任務,并會終止正在執行的任務。
    (4)TIDYING:所有的任務已終止,ctl記錄的任務數量為0,會執行鉤子函數terminated()。
    (5)TERMINATED:線程池徹底終止。
    8.關閉線程池的方法:ThreadPoolExecutor提供了兩個方法,用于線程池的關閉,分別是shutdown()和shutdownNow()。
    原理:都是循環遍歷線程池的工作線程,然后依次調用線程的intercept()方法來中斷線程。
    shutdown():將線程池狀態設置為SHUTDOWN。
    shutdownNow():將線程池狀態設置為STOP。

  • 67、給View設置的透明度的三種方法
    1,java代碼實現

text = (TextView) findViewById(R.id.text); text.getBackground().setAlpha(12);

setAlpha()的括號中可以填0–255之間的數字。數字越大,越不透明。

注意點:在5.0以上系統時,有些機型會出現莫名其妙的顏色值不起作用,變成透明了,也就是用此方法會導致其他共用一個資源的布局(例如:@color/white)透明度也跟著改變。
比如text用上述方法設置成透明后,項目中,其他用到text顏色值的控件,都變成透明了。
原因:在布局中多個控件同時使用一個資源的時候,這些控件會共用一個狀態,例如ColorState,如果你改變了一個控件的狀態,其他的控件都會接收到相同的通知。這時我們可以使用mutate()方法使該控件狀態不定,這樣不定狀態的控件就不會共享自己的狀態了。

text.getBackground().mutate().setAlpha(12);

2,在xml布局中進行設置

<TextViewandroid:id="@ id/text"android:text="Hello World!"android:background="#FFFFFF"android:layout_width="match_parent"android:alpha="0.6"android:layout_height="100dp" />

android:alpha的值為0~1之間的數。數字越大,越不透明。1表示完全不透明,0表示完全透明。

3,在xml布局中通過android:background設置

<TextViewandroid:id="@ id/text"android:text="Hello World!"android:background="#52FFFFFF"android:layout_width="match_parent"android:layout_height="100dp" />

顏色和不透明度 (alpha) 值以十六進制表示法表示。任何一種顏色的值范圍都是 0 到 255(00 到 ff)。對于 alpha,00 表示完全透明,ff 表示完全不透明。android:background的值的格式為”#AARRGGBB”。AA即透明度,R、G、B是紅綠藍三色。每一位均為0–F的十六位數。其中透明度的數值越大,越不透明
java代碼

//java代碼生成的對應表 for (int i = 100; i>=0; i--) {double j = (i / 100.0d);int alpha = (int) Math.round(255-j * 255);String hex = Integer.toHexString(alpha).toUpperCase();if (hex.length() == 1) hex = "0" + hex;int percent = (int) (j*100);System.out.println(String.format("%d%% — %s", percent, hex)); }

透明度對照表
透明度 16進制表示
100% 00(全透明)
99% 03
98% 05
97% 07
96% 0A
95% 0D
94% 0F
93% 12
92% 14
91% 17
90% 1A
89% 1C
88% 1E
87% 21
86% 24
85% 26
84% 29
83% 2B
82% 2E
81% 30
80% 33
79% 36
78% 38
77% 3B
76% 3D
75% 40
74% 42
73% 45
72% 47
71% 4A
70% 4D
69% 4F
68% 52
67% 54
66% 57
65% 59
64% 5C
63% 5E
62% 61
61% 63
60% 66
59% 69
58% 6B
57% 6E
56% 70
55% 73
54% 75
53% 78
52% 7A
51% 7D
50% 80
49% 82
48% 85
47% 87
46% 8A
45% 8C
44% 8F
43% 91
42% 94
41% 96
40% 99
39% 9C
38% 9E
37% A1
36% A3
35% A6
34% A8
33% AB
32% AD
31% B0
30% B3
29% B5
28% B8
27% BA
26% BD
25% BF
24% C2
23% C4
22% C7
21% C9
20% CC
19% CF
18% D1
17% D4
16% D6
15% D9
14% DB
13% DE
12% E0
11% E3
10% E6
9% E8
8% EB
7% ED
6% F0
5% F2
4% F5
3% F7
2% FA
1% FC
0% FF(完全不透明)

不透明度對照表
不透明度—十六進制值
100% — FF(完全不透明)
99% — FC
98% — FA
97% — F7
96% — F5
95% — F2
94% — F0
93% — ED
92% — EB
91% — E8
90% — E6
89% — E3
88% — E0
87% — DE
86% — DB
85% — D9
84% — D6
83% — D4
82% — D1
81% — CF
80% — CC
79% — C9
78% — C7
77% — C4
76% — C2
75% — BF
74% — BD
73% — BA
72% — B8
71% — B5
70% — B3
69% — B0
68% — AD
67% — AB
66% — A8
65% — A6
64% — A3
63% — A1
62% — 9E
61% — 9C
60% — 99
59% — 96
58% — 94
57% — 91
56% — 8F
55% — 8C
54% — 8A
53% — 87
52% — 85
51% — 82
50% — 80
49% — 7D
48% — 7A
47% — 78
46% — 75
45% — 73
44% — 70
43% — 6E
42% — 6B
41% — 69
40% — 66
39% — 63
38% — 61
37% — 5E
36% — 5C
35% — 59
34% — 57
33% — 54
32% — 52
31% — 4F
30% — 4D
29% — 4A
28% — 47
27% — 45
26% — 42
25% — 40
24% — 3D
23% — 3B
22% — 38
21% — 36
20% — 33
19% — 30
18% — 2E
17% — 2B
16% — 29
15% — 26
14% — 24
13% — 21
12% — 1F
11% — 1C
10% — 1A
9% — 17
8% — 14
7% — 12
6% — 0F
5% — 0D
4% — 0A
3% — 08
2% — 05
1% — 03
0% — 00(全透明)

總結

以上是生活随笔為你收集整理的Android 面试题笔记(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。