Android面試知識點宣鄙、知識體系

自己面試準備的Android知識點毙驯,很多都是簡單描述下留一個印象,僅供參考

Android

Activity

  1. activity的四種狀態(tài):Activie(獲得了焦點)哪自、Paused(失去了焦點丰包、可見)、Stoped(不可見)壤巷、Killed(被銷毀)
  2. 生命周期:
    1. 正常情況下:onCreate( ) —> onStart( ) —> onResume( )邑彪,home鍵:onPause( ) —> onStop( ),再次回到頁面時:onRestart( ) —> onStart( ) —> onResume( )寄症,頁面銷毀時:onPause( ) —> onStop( ) —> onDestroy( )
    2. 異常情況:onSaveInstanceState( ) —> onRestoreInstanceState( )
    3. 優(yōu)先級:前臺(最高)、可見非前臺(中等)矩动、后臺(最低)
    4. 橫豎屏切換:android:configchange有巧;取值一般為orientation、keyboardHidden悲没、screenSize篮迎,會觸發(fā)activity的onConfigChange( )方法
    5. 只走onPause( ),不走onStop( )的情況:打開一個半透明的activity
    6. A啟動B示姿,會先回調(diào)A的onPause( )甜橱,再回調(diào)B的onResume( )
  3. 啟動模式
    1. standard:默認,多實例模式
    2. singleTop:棧頂復用模式栈戳,觸發(fā)onNewIntent( )方法
    3. singleTask:棧內(nèi)復用模式岂傲,觸發(fā)onNewIntent( )方法
    4. singleInstance:單實例模式,會創(chuàng)建一個新的棧用來存放這個activity子檀,且只放它

Broadcast

  1. 種類
    1. 無序廣播 sendBroadcast( )
    2. 有序廣播 sendOrderBroadcast( )
    3. 本地廣播 需要借助LocalBroadcastManager镊掖,LocalBroadcastManager.getInstance(context),然后再sendBroadcast(還有個sync的方式)褂痰、注冊亩进、取消注冊等
  2. 注冊方式
    1. 靜態(tài)注冊 在清單文件中注冊,在android 8.0中已經(jīng)無法接收到大部分的廣播脐恩,除了某些特定的镐侯,比如開機廣播等。
    2. 動態(tài)注冊 在onCreate( )注冊驶冒,在onDestroy( )取消注冊苟翻,生命周期和activity保持一致
    3. 靜態(tài)注冊,8.0以前骗污,在APP不啟動的情況下也能接收到廣播
  3. 系統(tǒng)廣播的原理
    1. 自定義一個廣播接收者崇猫,并且重寫onReceive( )
    2. 通過binder機制,向AMS中注冊
    3. 廣播發(fā)送者需忿,通過binder向AMS發(fā)送消息
    4. AMS找到符合要求的BroadcastReceiver诅炉,將消息發(fā)送到BroadcastReceiver(一般是activity)的消息隊列中
    5. 遍歷消息隊列蜡歹,然后將消息回調(diào)給onReceive( )
  4. 本地廣播原理
    1. 內(nèi)部是通過Handler的方式發(fā)送消息,所以比系統(tǒng)廣播更高效涕烧、安全

ContentProvider

  1. 應用間共享數(shù)據(jù)月而,相對于文件存儲和SharedPreferences的全局可讀可寫,它可以只對部分數(shù)據(jù)進行操作
  2. getContentResolver( ).query(…)议纯,得到一個cursor游標父款,然后循環(huán)遍歷cursor取出數(shù)據(jù)
  3. 自定義contenProvider,寫一個類繼承于ContentProvider瞻凤,重寫全部的方法(6個憨攒,四個增刪改查、一個onCreate阀参、一個getType)
  4. 四大組件都需要注冊肝集,所以需要去清單中注冊,使用provider標簽

Service

  1. 和Thread的區(qū)別:service是ui線程蛛壳,不能執(zhí)行耗時操作杏瞻,thread是子線程,不能更新ui
  2. 開啟service的兩種方式:startService 和 bindService
  3. 生命周期
    1. 沒有綁定activity衙荐,啟動服務 —> onCreate( ) —> onStartCommand( ) —> onDestroy( ) —> 銷毀服務
    2. 綁定activity伐憾,綁定服務 —> onCreate( ) —> onBind( ) —> onUnBind( ) —> onDestroy( ) —> 銷毀服務
  4. 總結(jié):
    1. 如果一個service被某個activity通過startService的方式啟動,那么不管是否有activity使用bindService綁定服務還是unBindService解綁服務赫模,service都會一直在后臺運行,且調(diào)用多次startService蒸矛,只會執(zhí)行一次onCreate( )瀑罗,onStart( )會執(zhí)行多次,service只會被創(chuàng)建一次雏掠,所以只需要調(diào)用一次stopService斩祭,service 的生命周期和activity無關(guān),要停止必須要調(diào)用stopService
    2. 如果一個service被某個activity通過bindService的方式啟動乡话,那么不管執(zhí)行多少次bindService摧玫,都只執(zhí)行一次onCreate方法,不會執(zhí)行onStart方法绑青,通過unBindService方法解綁服務诬像,或者activity被finish、destroy
    3. 如果一個service既被startService也被bindService啟動闸婴,那么調(diào)用unBindService不能解綁service坏挠,需要同時調(diào)用unBindService、stopService來停止服務

Fragment

  1. fragment的加載方式:靜態(tài)加載(在xml中直接聲明fragment的類名)動態(tài)加載(使用FragmentManager)
  2. 與viewPager結(jié)合使用時邪乍,用到的適配器:FragmentPagerAdapter(頁面少)降狠、FragmentStatePagerAdapter(頁面多)
  3. 生命周期
    1. onAttach( ) —> onCreate( ) —> onCreateView( ) —> onActivityCreated( ) —> onStart( ) —> onResume( )
    2. onPause( ) —> onStop( )
    3. onStart( ) —> onResume( )
    4. onPause( ) —> onStop( ) —> onDestroyView( ) —> onDestroy( ) —> onDetach( )
  4. 通信
    1. fragment調(diào)用activity中的方法:getActivity( ) 強轉(zhuǎn)為目標activity对竣,然后直接調(diào)用activity的方法
    2. activity調(diào)用fragment中的方法:通過fragment的實例直接調(diào)用方法
    3. fragment調(diào)用fragment中的方法:結(jié)合第一種和第二種
  5. 顯示方式
    1. add/remove:replace的實際上也是先remove再add
    2. show/hide:顯示和隱藏fragment
    3. attach/detach:detach會將view從viewTree上移除,調(diào)用attach時會觸發(fā)onCreateView重繪view榜配,fragment.isAdd( )會返回false
  6. 懶加載
    1. 重寫fragment中的setUserVisibleHint ( boolean isUserVisibleToUser )否纬,然后根據(jù)isVisibleToUser來判斷是否可見,需要注意的是蛋褥,這個方法在onCreate( )之前執(zhí)行临燃,所以拉取數(shù)據(jù)如果需要更新UI就不行,view還沒初始化壁拉。
    2. 針對上面的情況谬俄,fragment還提供了另外一個方式,可在onStart( )等生命周期方法中通過判斷getUserVisibleHint( )的返回值弃理,返回true則表示可見溃论。

WebView

  1. JS注入的漏洞(解決辦法)
    1. 添加@JavaScriptInterface注解
    2. webSetting.setSavePassword ( false ) 關(guān)閉密碼保存
    3. setAllowFileAccess ( false ) 禁止加載本地文件
  2. 內(nèi)存泄漏
    1. 原因:webView持有activity的實例,類似匿名內(nèi)部類持有外部類的引用
    2. 解決:在布局文件中痘昌,寫入一個viewGroup钥勋,在activity中通過addView的方式添加,在activity銷毀的時候remove
  3. webView常見的三個類:WebSetting(設置)辆苔、WebViewClient(通知和請求事件算灸,pageStart等)、WebChromeClient(網(wǎng)站相關(guān)驻啤,加載進度等)
  4. Chrome在線調(diào)試菲驴,需要webView開啟調(diào)試debug模式
  5. WebView的一些證書、控制臺輸出日志等回調(diào)骑冗,在開發(fā)中很實用
  6. JS注入偶爾會失敗赊瞬,建議在pageFinish中注入對象,因為頁面沒有加載結(jié)束贼涩,JS的方法可能還沒初始化完成

Binder

Handler

  1. 四個類:Handler巧涧、Message、Looper遥倦、MessageQueue
  2. 機制(簡易版):創(chuàng)建一個Handler并且實現(xiàn)它的handleMessage( )方法谤绳,通過sendMessage( )等方法將消息發(fā)送到消息隊列MessageQueue中,然后Looper循環(huán)的從消息隊列中取消息袒哥,通過回調(diào)dispatchMessage( )將消息回調(diào)給handleMessage( )
  3. 源碼細節(jié):
    1. Hadnler中有MessageQueue缩筛、Looper實例,而MessageQueue是在Looper中初始化统诺,所以Handler的MessageQueue實例其實是Looper中的
    2. Looper的初始化是在ActivityThread的main方法中歪脏,Looper.prepareMainLooper,并且這里調(diào)用了Looper.loop( )方法粮呢,開始循環(huán)取消息
    3. 主線程的Looper對象是不能在程序中退出的婿失,會拋異常 throw new RuntimeException("Main thread loop unexpectedly exited")钞艇,退出主線程的循環(huán)是框架在退出應用程序的時候才調(diào)用
    4. Looper中的一些方法:prepare( )、loop( )豪硅、sThreadLocal.set ( new Looper (…) )
    5. loop( )方法哩照,此處為一個死循環(huán),不斷的從MessageQueue中讀取數(shù)據(jù)queue.next( )懒浮,如果沒有數(shù)據(jù)就return飘弧,有數(shù)據(jù)則回調(diào)message.target.dispatchMessage( ),target為handler砚著,在dispatchMessage( )方法中調(diào)用了handleMessage( )

AsyncTask

  1. 是什么:是一個封裝了線程池和handler的異步框架
  2. 核心點:
    1. 三個參數(shù)次伶,AsyncTask接受三個泛型參數(shù) ( params, progress, result ),分別為參數(shù)稽穆、進度值冠王、結(jié)果,也可全取void
    2. 四個方法:doInBackground(String… strings)(必須要重寫)舌镶、onPrepareExcute( )(主線程柱彻,用于初始化,比如顯示進度條)餐胀、onPostExcute( )(主線程哟楷,doInBackground執(zhí)行完畢,更新UI等)否灾、onProgressUpdata( )(主線程卖擅,更新進度條,需要在doInBackground調(diào)用publicProgress)
    3. 原理:線程池中的工作線程執(zhí)行doInBackground中的異步任務墨技,然后通過內(nèi)部的handler發(fā)送消息
    4. 內(nèi)存泄漏:靜態(tài)內(nèi)部類持有外部類的匿名引用磨镶,采用弱引用的方式

HandlerThread

  1. 是什么:本質(zhì)是一個線程類,繼承于thread
  2. 原理:在run方法中初始化了一個Looper對象健提,調(diào)用Looper的loop( )方法,不斷的從MessageQueue中讀取數(shù)據(jù)伟叛,沒有消息則會阻塞私痹;quit( )會清空所有的消息,quitSafely( )只會清空延時消息统刮,無論調(diào)用了哪一個方法紊遵,Looper的循環(huán)就結(jié)束了。quit( )是MessageQueue的方法侥蒙,作用是清空消息并且給變量置空暗膜、釋放資源

IntentService

  1. 是什么:繼承于Service,可以用來執(zhí)行耗時操作鞭衩,當任務執(zhí)行完畢后服務會自動停止学搜,不需要我們手動去停止娃善,每個耗時操作會以一個隊列的形式在IntentService的onHandleIntent( )回調(diào)中執(zhí)行
  2. 和Service的區(qū)別:service運行在主線程,所以不能進行耗時操作瑞佩,而IntentService則為了解決這個問題產(chǎn)生的
  3. 原理:
    1. 繼承于Service聚磺,所以有著和Service一樣的操作方式
    2. 在onCreate( )中,初始化了一個HandlerThread和Handler(創(chuàng)建一個HandlerThread炬丸,并獲得其Looper對象瘫寝,將Looper傳入Handler創(chuàng)建一個Handler),借此構(gòu)建了一個具有消息循環(huán)機制的后臺線程
    3. 非常適合做一次性的后臺任務稠炬,如下載一個文件焕阿,下載完成后自動銷毀

View繪制機制

事件分發(fā)機制

ListView

  1. 優(yōu)化:
    1. 復用convertView
    2. 使用viewHolder,頻繁的findView會消耗內(nèi)存
    3. 分頁加載
    4. 圖片緩存(圖片框架加載)
    5. 滑動過程中不加載圖片

動畫機制

自定義View

Serializable和Parcelable

  1. Serializable是Java提供的一個序列化接口首启,需要提供一個序列化ID用于序列化和反序列化(需要大量的I/O操作暮屡,開銷大,效率低闽坡,但是數(shù)據(jù)持久栽惶,適合序列化到本地的數(shù)據(jù))
  2. Parcelable,Android SDK內(nèi)部提供的一個序列化接口疾嗅,不需要大量的I/O操作外厂,開銷小,效率高代承,適合內(nèi)存序列化

Android各版本特性

  1. 5.0:MD風格汁蝶,推出很多新控件如RecyclerView
  2. 6.0:動態(tài)權(quán)限,對于危險權(quán)限需要用戶授權(quán)
  3. 7.0:文件訪問權(quán)限论悴,fileProvider
  4. 8.0:靜態(tài)注冊的廣播不再適用掖棉;APP logo適配;允許安裝未知來源應用膀估;透明主題的Activity不能設置界面方向
  5. 9.0:前臺service必須要申請 FOREGROUND_SERVICE 權(quán)限
  6. 10.0:暗黑模式幔亥;

Intent

  1. 顯示意圖:常見的就是activity的跳轉(zhuǎn),指明明確的組件名稱
  2. 隱式意圖
    1. 常見的是跳轉(zhuǎn)到通訊錄等察纯,不指明明確的組件名稱
    2. 組成成分:action(動作)帕棉、category(附加信息)、data(數(shù)據(jù))饼记、type(類型)等
  3. 自定義隱式意圖:
    1. 在清單文件中注冊intent-filter標簽中申明action香伴、category等
    2. 如何使用:在代碼中使用intent啟動這個自定義意圖,注意setData( )和setType( )會互相清除具则,如果需要使用即纲,則可以使用setDataAndType( )
  4. bundle和intent傳參的區(qū)別
    1. intent.putExtra(…)實則還是調(diào)用的是bundle方式
    2. bundle是一個封裝類,內(nèi)部采用的是ArrayMap
    3. bundle更適合傳遞對象博肋,intent適合傳遞單個字段

對話框

  1. dialog
    1. 是在window上addView和removeView的操作低斋,window是個抽象概念蜂厅,構(gòu)造方法中創(chuàng)建了一個phoneWindow,windowManager提供了支持
    2. 在show( )方法中拔稳,有個onStart( )的空方法葛峻,可以在顯示dialog前做一些操作,因此常見的一些自定義dialog樣式巴比,會在onStart( )方法中設置window的樣式
    3. dismiss( )方法是線程安全的术奖,當中根據(jù)Looper判斷當前是否在主線程,主線程才去操作dismissDialog( )轻绞,非主線程則通過handler發(fā)送到主線程
  2. popupwindow
  3. toast

Context

  1. Context是一個抽象類采记,有兩個具體的實現(xiàn)子類:ContextImpl、ContextWrapper
  2. Context數(shù)量 = Activity數(shù)量 + Service數(shù)量 + 進程數(shù)
  3. application政勃、service都繼承于ContextWrapper唧龄,activity繼承于ContextThemeWrapper
  4. 不推薦用application去startActivity,因為非activity類型的context沒有任務棧奸远,service同理
  5. getApplication( )和getApplicationContext( )的本質(zhì)是一樣的既棺,都是application實例,區(qū)別在于作用域懒叛,getApplication( )方法只能在activity丸冕、service中調(diào)用到,而getAppcationContext( )可以在其他場景調(diào)用
  6. 關(guān)于context的內(nèi)存泄漏薛窥,主要是要避免context和引用者的生命周期不一致

系統(tǒng)架構(gòu)與系統(tǒng)源碼

冷啟動和熱啟動

  1. 冷啟動:后臺沒有該應用的進程胖烛,會先創(chuàng)建和初始化Application,再創(chuàng)建和初始化MainActivity等
  2. 熱啟動:后臺有該應用的進程诅迷,可以在任務棧中看到佩番,如用戶按back、home等退出app罢杉。再次啟動時趟畏,不會去創(chuàng)建Application,會去創(chuàng)建和初始化MainActivity等滩租。
  3. 冷啟動優(yōu)化:
    1. 減少onCreate( )的工作量
    2. 不在application的初始化中做耗時操作拱镐,可以開啟線程
    3. 布局優(yōu)化
  4. APP啟動白屏解決方案:由于繪制布局資源并不是在窗體繪制的第一步,所以會加載默認的背景色持际,可以在主題中設置默認的背景圖<item name="android:background">@drawable/bg_splash</item>

性能優(yōu)化

  1. 流暢性
    1. 布局優(yōu)化(更簡單的ViewGroup、更少的布局層次哗咆、include蜘欲、viewStup、merge)
    2. 啟動優(yōu)化(異步加載晌柬、分步加載姥份、延遲加載)
  2. 穩(wěn)定性
    1. 崩潰:代碼健壯性郭脂、異常情況的考慮
    2. ANR:合理的處理耗時操作
  3. 節(jié)省
    1. 內(nèi)存泄漏
    2. 減少資源文件的大小,webp
    3. apk大小

進程間通信

AIDL

  1. 定義:接口定義語言
  2. 定義AIDL接口:
    1. 在main目錄定義一個aidl的目錄澈歉,然后寫上和app相同的包名展鸡,在其中申明xxx.aidl
    2. 在aidl中,常見類型可以直接使用埃难,如int莹弊、String、boolean涡尘、List等忍弛,但如果是對象,需要實現(xiàn)Parcelable接口
    3. 非常見類型的參數(shù)考抄,需要申明傳輸方向细疚,如in(輸入)、out(輸出)川梅、inout(可輸入輸出)疯兼,默認為in
    4. 文件名要以“I”開頭(這個說法是舊的,當前版本任意文件名都可以)
    5. 生成java文件贫途,build —> make project
  3. 使用:
    1. 查看生成的java文件吧彪,可以發(fā)現(xiàn)代碼內(nèi)有一個Stub的靜態(tài)抽象類,繼承于Binder潮饱,實現(xiàn)了我們定義的aidl接口
    2. 實現(xiàn)接口:實現(xiàn)一個MyAIDL.Stub( ){ … }来氧,這步就是返回了一個Binder對象
    3. 開放接口,實現(xiàn)一個service香拉,并重寫onBind( )方法啦扬,傳入上述的Binder對象
    4. Activity調(diào)用bindService( )綁定連接此服務,在onServiceConnect( )回調(diào)接收IBinder

Binder

  1. Binder是一個類凫碌,實現(xiàn)了IBinder接口
  2. Binder通信機制
    1. 在Android系統(tǒng)中扑毡,Binder通信機制由四個部分組成,分別是Client盛险、Server瞄摊、Service Manager、Binder Driver
    2. 內(nèi)存空間分為兩部分:用戶空間(Client苦掘、Server换帜、Service Manager)、內(nèi)核空間(Binder Driver)
  3. 在Binder機制中鹤啡,由Binder驅(qū)動負責完成這個中轉(zhuǎn)工作
    1. Client向Server發(fā)起IPC請求時惯驼,Client會先將請求數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間
    2. 數(shù)據(jù)拷貝到內(nèi)核空間后,Binder驅(qū)動再將數(shù)據(jù)拷貝到Server用戶空間的緩存中
  4. Service Manager:主要提供了service的添加和查詢
  5. Client、Server祟牲、Service Manager都處于用戶空間的不同進程中
  6. Binder跨進程通訊的步驟
    1. 初始化Service Manager:應用程序啟動時隙畜,Service Manager和Binder通訊,Binder驅(qū)動新建Service Manager對應的Binder實體
    2. 注冊Server:Server向Binder發(fā)起注冊請求说贝,如果Service Manager中沒有此服務议惰,則添加此服務
    3. Client獲取遠程服務:Client向Binder傳遞要查詢的Server名稱,Binder驅(qū)動將該請求轉(zhuǎn)發(fā)給Service Manager乡恕,然后找到對應的Server并反饋給Client言询,Client收到Server對應的Binder引用后,會創(chuàng)建一個當前Server對應的遠程服務
    4. Client通過代理調(diào)用Server:Client調(diào)用遠程服務几颜,遠程服務和Binder驅(qū)動通訊倍试,因為遠程服務中帶有Server的Binder引用,所以輕而易舉的就能找到蛋哭,進而將Client的請求發(fā)送給Server

Java

異常

  1. 異常通常分為三類:Error县习、編譯時異常、運行時異常
  2. Finally:
    1. 一般情況下都會執(zhí)行谆趾,如果在try躁愿、catch代碼塊中返回了return,則finally代碼塊內(nèi)容會先return執(zhí)行
    2. 如果程序被終止了沪蓬,JVM退出了彤钟,finally代碼塊內(nèi)容不會執(zhí)行
  3. Final關(guān)鍵字
    1. 修飾類:不可被繼承
    2. 修飾變量:常量,只可賦值一次
    3. 修飾方法:不可被重寫

設計模式

  1. 單例

    //懶漢式
    //缺點:多線程同時訪問時會有問題跷叉,缺乏同步
    private static SingletonDemo singletonDemo1;
    
    public static SingletonDemo getInstance1() {
        if (null == singletonDemo1) {
            singletonDemo1 = new SingletonDemo();
        }
        return singletonDemo1;
    }
    
    //線程安全的懶漢式
    //缺點:雖然加了同步鎖逸雹,但是由于大部分情況下,是不需要考慮同步這個情況的云挟,而這里每次請求都加了個同步鎖     就造成了資源的浪費
    private static SingletonDemo singletonDemo2;
    
    public static synchronized SingletonDemo getInstance2() {
        if (null == singletonDemo2) {
            singletonDemo2 = new SingletonDemo();
        }
        return singletonDemo2;
    }
    
    //餓漢式
    //缺點:類加載的時候就初始化了梆砸,沒有起到懶加載的作用
    private static SingletonDemo singletonDemo3 = new SingletonDemo();
    
    public static SingletonDemo getInstance3(){
        return singletonDemo3;
    }
    
    //靜態(tài)內(nèi)部類的形式
    //推薦:實現(xiàn)了懶加載,同樣也是線程安全的
    private static class SingletonDemoInstance{
        private static SingletonDemo INSTANCE = new SingletonDemo();
    }
    
    public static SingletonDemo getInstance4(){
        return SingletonDemoInstance.INSTANCE;
    }
    
    //枚舉
    //推薦:自由實例化园欣、單實例帖世、線程安全
    enum SingleDemo{
        INSTANCE;
        public void doSomething(){
            // TODO: 2019-05-08 do something
        }
    }
    
    //雙重驗證
    //情景分析:
    //線程A進入方法,發(fā)現(xiàn)實例沒有初始化沸枯,進入代碼塊日矫,同時被鎖定
    //線程B進入方法,發(fā)現(xiàn)實例沒有初始化绑榴,進入代碼塊哪轿,發(fā)現(xiàn)已經(jīng)被線程A鎖定,此時阻塞
    //線程A執(zhí)行同步方法翔怎,發(fā)現(xiàn)實例沒有初始化窃诉,則初始化實例,跳出代碼塊,return
    //線程B執(zhí)行同步方法褐奴,發(fā)現(xiàn)實例已經(jīng)被初始化,跳出代碼塊于毙,return敦冬,此時返回的是線程A中初始化的實例
    //邏輯上實現(xiàn)了線程安全,且實現(xiàn)了懶加載
    private static SingletonDemo singletonDemo5;
    
    public static SingletonDemo getInstance5() {
        if (null == singletonDemo5) {
            synchronized (SingletonDemo.class) {
                if (null == singletonDemo5) {
                    singletonDemo5 = new SingletonDemo();
                }
            }
        }
        return singletonDemo5;
    }
    
    //volatile關(guān)鍵字唯沮,會強制將修改的值寫入內(nèi)存脖旱,線程A修改了變量值,線程B/C/D都會立即讀取到修改后的值
    private volatile SingletonDemo getSingletonDemo6;
    
  2. Builder模式

  3. 適配器模式

    1. 現(xiàn)有類介蛉、目標類萌庆、目標接口
    2. 適用場景:現(xiàn)有類實現(xiàn)了某些功能,但是在目標場景下和目標接口定義不一致币旧,則可以通過一個適配器類的方式践险,復用現(xiàn)有類的代碼
    3. 優(yōu)點
      1. 通過適配器,客戶端可以調(diào)用同一接口吹菱,邏輯上更透明
      2. 解決了現(xiàn)有類和復用場景要求不一致的問題
      3. 將目標類和現(xiàn)有類解耦巍虫,引入一個適配器類,復用現(xiàn)有類的代碼
    4. 實現(xiàn)方式
      1. 繼承現(xiàn)有類鳍刷,并且實現(xiàn)目標接口占遥,調(diào)用父類的現(xiàn)有方法
      2. 實現(xiàn)目標接口,但是傳入現(xiàn)有類输瓜,通過現(xiàn)有類的代理方式瓦胎,調(diào)用現(xiàn)有的方法
    //已存在的,具有特殊功能尤揣,但不符合我們既有標準接口的類
    static class Adaptee {
        void specificRequest() {
            System.out.println("被適配器類搔啊,具有特殊功能");
        }
    }
    
    //目標接口
    interface Target {
        void request();
    }
    
    //具體的目標類,只提供普通功能
    static class ConcreteTarget implements Target {
        @Override
        public void request() {
            System.out.println("普通類芹缔,具有普通功能");
        }
    }
    
    /**
     * 繼承了被適配器類坯癣,同時實現(xiàn)了接口
     */
    static class Adapter extends Adaptee implements Target {
        @Override
        public void request() {
            super.specificRequest();
        }
    }
    
    /**
     * 通過委托的方式
     */
    static class Adapter1 implements Target {
        Adaptee adaptee;
        Adapter1(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        @Override
        public void request() {
            this.adaptee.specificRequest();
        }
    }
    
    /**
     * 適用場景:
     * 舊的代碼已經(jīng)實現(xiàn)了某些功能,但是此時我們想以另外一個接口的形式表示最欠,且不改動原有的代碼
     */
    public static void main(String[] args) {
        Target concreteTarget = new ConcreteTarget();
        concreteTarget.request();
    
        Target adapter = new Adapter();
        adapter.request();
    
        Target adapter1 = new Adapter1(new Adaptee());
        adapter1.request();
    }
    
  4. 裝飾模式

    1. 情景:動態(tài)的給一個對象添加額外的職責示罗,比直接繼承的子類更靈活
    2. 優(yōu)點:通過使用不同的具體裝飾類的互相組合,會創(chuàng)建出更多不同行為的組合
    3. 缺點:會產(chǎn)生比繼承關(guān)系更多的對象芝硬,查錯比較難蚜点,而且命名上都比較相似,不易分辨
  5. 外觀設計模式

    1. 概念:提供了一個高層接口拌阴,便于調(diào)用
    2. 場景:在不斷的迭代過程中绍绘,產(chǎn)生了很多小的類,這使得系統(tǒng)更具復用性、靈活陪拘。但是這也給那些不需要定制化的系統(tǒng)的使用帶來了麻煩厂镇,所以可以提供一個高層的類,當然左刽,也可以繞過這個高層類
  6. 觀察者模式

    1. 概念:被觀察者通過注冊觀察者捺信,當被觀察者發(fā)生變化時,通知觀察者

    2. 實現(xiàn)步驟:

      1. 抽象觀察者
      2. 抽象被觀察者
      3. 實現(xiàn)具體的觀察者
      4. 實現(xiàn)具體的被觀察者
    3. 情景:當一方面依賴于另一方面時

      //抽象觀察者
      interface Watcher {
          void update();
      }
      
      //抽象被觀察者
      interface Watched {
          void addWatcher(Watcher watcher);
          void removeWatcher(Watcher watcher);
          void notifyWatcher();
      }
      
      //具體的觀察者
      public static class Police implements Watcher {
          @Override
          public void update() {
              System.out.println("我是警察");
          }
      }
      
      public static class Thief implements Watcher {
          @Override
          public void update() {
              System.out.println("我是小偷");
          }
      }
      
      //具體的被觀察者
      public static class BossMoney implements Watched {
          private ArrayList<Watcher> watchers = new ArrayList<>();
          @Override
          public void addWatcher(Watcher watcher) {
              watchers.add(watcher);
          }
      
          @Override
          public void removeWatcher(Watcher watcher) {
              watchers.remove(watcher);
          }
      
          @Override
          public void notifyWatcher() {
              for (Watcher watcher : watchers) {
                  System.out.println("收到通知 className: " + watcher.getClass().getSimpleName());
                  watcher.update();
              }
          }
      }
      
      public static void main(String[] args) {
          BossMoney bossMoney = new BossMoney();
          Police police = new Police();
          Thief thief = new Thief();
          bossMoney.addWatcher(police);
          bossMoney.addWatcher(thief);
          bossMoney.notifyWatcher();
      }
      

線程欠痴、多線程迄靠、線程池

數(shù)據(jù)結(jié)構(gòu)和算法

鏈表

隊列

排序算法

查找算法

源碼解析

OKHttp v3.11.0

EventBus v3.1.1

  1. 使用
    1. 注冊訂閱者(register)、一般在頁面銷毀時注銷訂閱者(unregister)
    2. 聲明訂閱方法(@Subscribe)
    3. 發(fā)送事件(post喇辽、需要定義一個實體類)
    4. 注解@Subscribe解析:包含ThreadMode(一個枚舉類掌挚、當前線程類型)、sticky(粘性事件)菩咨、priority(優(yōu)先級)
    5. ThreadMode的類型:POSTING(運行在發(fā)送事件線程)吠式、MAIN(UI線程)、BACKGROUND(后臺線程)旦委、ASYNC(訂閱方法和發(fā)送事件始終不在一個線程奇徒,每次都會使用新的線程來運行),默認POSTING
    6. sticky 粘性事件:只會接收到最近一次發(fā)送的粘性事件缨硝,之前的接收不到
  2. 創(chuàng)建
    1. EventBus.getDefault( ):getDefault( )是一個獲取實例的方法(單例摩钙,而且是雙重驗證的方式,保證在不同線程中也只有一個實例)
    2. 也可以通過EventBus.builder( )的形式創(chuàng)建自定義的EventBus查辩,這里的建造者是EventBusBuilder胖笛,使用了建造者模式
  3. 注冊
    1. 創(chuàng)建的時候栅盲,初始化了SubscriberMethodFinder對象 —> findSubscriberMethods( ) —> findUsingInfo( ) —> findUsingReflectionInSingleClass( ) —> checkAdd( ) —> checkAddWithMethodSignature( ) —> subscriberMethods.add( ) 至此獲得了符合條件的SubsciberMethod
    2. findUsingReflectionInSingleClass( ):通過反射的方式芋类,遍歷類中所有的方法,找到符合要求的方法霍弹;比如判斷了修飾符是否是Public萍倡、參數(shù)個數(shù)是否等于1身弊、有沒有Subscribe注解
    3. 總結(jié):注冊的過程就是把訂閱方法和事件綁定起來,放入一個Map中
  4. 注銷
    1. 根據(jù)當前訂閱者獲取它所有的事件列敲,然后遍歷這些事件阱佛,調(diào)用unsubscribeByEventType( ),傳入訂閱者和事件戴而,解除兩者之間的關(guān)系
  5. 發(fā)送事件
    1. 獲取postingState凑术,當中保存了線程信息、訂閱者所意、事件等 —> 將事件添加到隊列中 —> 遍歷這個隊列淮逊,postSingleEvent( ) —> postSingleEventForEventType( ) —> postToSubscription( ) —> 判斷線程類型是否和當前一致催首,是的話調(diào)用invokeSubscriber( ),否則調(diào)用enqueue( )泄鹏,通過handler或者runnable切換線程使得和ThreadMode一致
    2. invokeSubscriber( )郎任,通過反射的方式調(diào)用訂閱方法
  6. 粘性事件的發(fā)送和接收分析
    1. 通過postSticky( )發(fā)送消息
    2. EventBus不知道當前訂閱者對應了哪個粘性事件,所以需要全部遍歷一次备籽,然后找到匹配的粘性事件后調(diào)用postSingleEventForEventType( )涝滴,回到了postToSubscription( )方法中判斷當前ThreadMode的方法中
    3. postToSubscription( ),有三個重要的Poster胶台,分別是mainThreadPoster、backgroundPoster杂抽、asyncPoster诈唬,mainTreadPoster類型是HandlerPoster繼承于Handler,實現(xiàn)了Poster接口缩麸,通過handler的方式將事件發(fā)送到主線程铸磅;backgroundPoster和asyncPoster大致上都差不多,實現(xiàn)了Runnable接口杭朱,區(qū)別在于backgroundPoster會判斷當前線程是否在運行阅仔,而asyncPoster不會判斷,每次都使用一個新線程
  7. 總結(jié):從整個EventBus中可以看出弧械,事件作為被觀察者八酒,訂閱方法是觀察者,當事件發(fā)出或者發(fā)生變更時刃唐,訂閱者都會立馬收到通知羞迷。

Glide 4.x 緩存原理

  1. 簡介:Glide緩存分成了兩個模塊,一個是內(nèi)存緩存画饥,一個是磁盤緩存衔瓮;內(nèi)存緩存是為了避免應用重復的將圖片數(shù)據(jù)加載到內(nèi)存中,磁盤緩存是為了避免應用重復的從網(wǎng)絡或其他地方下載圖片
  2. 使用:Glide默認開啟內(nèi)存緩存和磁盤緩存抖甘,可以通過配置去自定義關(guān)閉热鞍,或者定義緩存類型,如只緩存原始圖衔彻,或者只緩存變換后的圖
  3. 緩存key
    1. Glide的緩存key生成比較復雜薇宠,在3.x版本有數(shù)10個字段,4.x版本由8個字段組成米奸,比較明顯的就是3.x的版本用到了id字段(圖片url)昼接,4.x使用的是Model;決定key生成的字段有很多悴晰,這里比較明顯的是width和height慢睡,也就是說逐工,寬高不一樣,生成的key相應的也不一樣
  4. 內(nèi)存緩存
    1. 內(nèi)存緩存用到了弱引用和LruCache來制定緩存策略
    2. 緩存的讀绕:通過key從緩存中讀取泪喊,并且將緩存移除,然后將緩存加入到一個弱引用的HashMap當中髓涯,如果沒有讀取到緩存袒啼,則再去下載圖片
    3. 緩存的寫入:圖片加載完成后,會通過handler切回到主線程中纬纪,然后在后續(xù)的方法中將資源put到弱引用的HashMap中蚓再,同時還有另外一個操作,通過acquire( )和release( )的變量進行計數(shù)包各,++或者--
    4. 總結(jié):Glide將正在使用的圖片加入到弱引用當中摘仅,不常用的圖片加入到LruCache中
  5. 磁盤緩存
    1. Glide會優(yōu)先讀取內(nèi)存緩存,在讀取不到的時候问畅,再去讀取磁盤緩存
    2. 讀韧奘簟:通過key從緩存中讀取緩存文件,如果文件不為null护姆,則解碼后返回
    3. 寫入:圖片加載完成后矾端,判斷是否允許寫入磁盤,允許的話將圖片寫入磁盤緩存
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卵皂,一起剝皮案震驚了整個濱河市秩铆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灯变,老刑警劉巖豺旬,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異柒凉,居然都是意外死亡族阅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門膝捞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坦刀,“玉大人,你說我怎么就攤上這事蔬咬±鹨#” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵林艘,是天一觀的道長盖奈。 經(jīng)常有香客問我,道長狐援,這世上最難降的妖魔是什么钢坦? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任究孕,我火速辦了婚禮,結(jié)果婚禮上爹凹,老公的妹妹穿的比我還像新娘厨诸。我一直安慰自己,他們只是感情好禾酱,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布微酬。 她就那樣靜靜地躺著,像睡著了一般颤陶。 火紅的嫁衣襯著肌膚如雪颗管。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天滓走,我揣著相機與錄音忙上,去河邊找鬼。 笑死闲坎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的茬斧。 我是一名探鬼主播腰懂,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼项秉!你這毒婦竟也來了绣溜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤娄蔼,失蹤者是張志新(化名)和其女友劉穎怖喻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岁诉,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡锚沸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了涕癣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哗蜈。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖坠韩,靈堂內(nèi)的尸體忽然破棺而出距潘,到底是詐尸還是另有隱情,我是刑警寧澤只搁,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布音比,位于F島的核電站,受9級特大地震影響氢惋,放射性物質(zhì)發(fā)生泄漏洞翩。R本人自食惡果不足惜稽犁,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菱农。 院中可真熱鬧缭付,春花似錦、人聲如沸循未。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽的妖。三九已至绣檬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫂粟,已是汗流浹背娇未。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留星虹,地道東北人零抬。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像宽涌,于是被迫代替她去往敵國和親平夜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容