Android 常見的問題整理 2022

Android 語音TTS 識別全鏈路過程

  • 本地錄音 =》 ASR識別】凇=》請求后臺語義△⒃邸=》語義落域分發(fā)返回 =》 本地仲裁處理落域分發(fā)∠隗稀=》 TTS播報
  • 下面是語音鏈路的一些基本思路
  1. 錄音 :Android基本錄音為48K的采樣率 語音這邊需要做降采樣處理 降采樣為16K。通過Android原生錄音將音頻給到引擎
  2. 喚醒:一般喚醒都是做本地喚醒厉亏,所有wakeup喚醒引擎董习。也可以通過喚醒引擎做一些免喚醒功能
  3. 語音識別,識別分為兩種:
    3.1 離線識別爱只,走本地識別引擎皿淋,
    優(yōu)點:識別快
    缺點:需要精準識別,并不能做太多泛化處理恬试。對音頻要求比較高
    3.2 在線識別窝趣,走云端識別引擎
    優(yōu)點 :可以模糊匹配,多泛化
    缺點 :網(wǎng)路查的情況下識別很慢
  4. 云端與離線云端技能分發(fā)
  5. TTS播報:由云端或本地接收文本進行語音音頻合成训柴。進行播報

Android 屏幕適配相關哑舒,方案

    1. 通過dp加上自適應布局可以基本解決屏幕碎片化的問題。也是Android推薦使用的屏幕兼容性適配方案幻馁。
    1. 根據(jù)ui設計圖的寬度dp值洗鸵,算出當前屏幕每dp占當前屏幕多少像素值(也就是density)。
    1. 根據(jù)ui設計圖的寬度dp值仗嗦,算出當前屏幕分成ui設計圖的寬高度dp份后膘滨,每dp占當前屏幕實際多少dp,然后這個實際dp值再根dpi轉(zhuǎn)換成具體的像素值稀拐。
    1. 自定義像素適配,以美工的設計尺寸為原始尺寸,根據(jù)不同設備的密度 計算出寬和高 參考UIAdapter火邓。如果想顯示屏幕的1/3的話就是360了寬度,是根據(jù)設計師給出來的寬度進行設置
    1. 百分比適配。這是Google 提出來的一個解決適配方案德撬,想要使用必須添加依賴
 implementation 'com.android.support:percent:28.0.0'

主要就是兩個類
PercentRelativeLayout PercentFrameLayout

多線程铲咨,線程池 相關

  1. 線程的創(chuàng)建,線程創(chuàng)建的常用方法
  • 1.繼承Thread重寫run方法
  • 2.實現(xiàn)Runnable重寫run方法
  • 3.實現(xiàn)Callable重寫call方法
    1.3 實現(xiàn)Callable重寫call方法
    實現(xiàn)Callable和實現(xiàn)Runnable類似砰逻,但是功能更強大
    可以在任務結(jié)束后提供一個返回值鸣驱,Runnable不行
    call方法可以拋出異常,Runnable的run方法不行
    可以通過運行Callable得到的Fulture對象監(jiān)聽目標線程調(diào)用call方法的結(jié)果蝠咆,得到返回值,(fulture.get(),調(diào)用后會阻塞,直到獲取到返回值)
  1. Android中的四類線程池
    1. Android中最常見的四類具有不同特性的線程池分別為FixThreadPool刚操、CachedThreadPool闸翅、ScheduleThreadPool和SingleThreadExecutor
    1. FixThreadPool(一堆人排隊上公廁)
public static ExecutorService newFixThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
//使用
Executors.newFixThreadPool(5).execute(r);
  1. 從配置參數(shù)來看,F(xiàn)ixThreadPool只有核心線程菊霜,并且數(shù)量固定的坚冀,也不會被回收,所有線程都活動時鉴逞,因為隊列沒有限制大小记某,新任務會等待執(zhí)行。
  2. FixThreadPool其實就像一堆人排隊上公廁一樣构捡,可以無數(shù)多人排隊液南,但是廁所位置就那么多,而且沒人上時勾徽,廁所也不會被拆遷
  3. 由于線程不會回收滑凉,F(xiàn)ixThreadPool會更快地響應外界請求,這也很容易理解喘帚,就好像有人突然想上廁所畅姊,公廁不是現(xiàn)用現(xiàn)建的
    1. SingleThreadPool(公廁里只有一個坑位)
public static ExecutorService newSingleThreadPool (int nThreads){
    return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor (1, 1, 0, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );
}
//使用
Executors.newSingleThreadPool ().execute(r);
  1. 從配置參數(shù)可以看出,SingleThreadPool只有一個核心線程吹由,確保所有任務都在同一線程中按順序完成若未。因此不需要處理線程同步的問題。
  2. 可以把SingleThreadPool簡單的理解為FixThreadPool的參數(shù)被手動設置為1的情況倾鲫,即Executors.newFixThreadPool(1).execute(r)粗合。所以SingleThreadPool可以理解為公廁里只有一個坑位,先來先上级乍。為什么只有一個坑位呢舌劳,因為這個公廁是收費的,收費的大爺上年紀了玫荣,只能管理一個坑位甚淡,多了就管不過來了(線程同步問題)
    1. CachedThreadPool(一堆人去一家很大的咖啡館喝咖啡)
public static ExecutorService newCachedThreadPool(int nThreads){
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>());
}
//使用
Executors.newCachedThreadPool().execute(r);
  1. CachedThreadPool只有非核心線程,最大線程數(shù)非常大捅厂,所有線程都活動時贯卦,會為新任務創(chuàng)建新線程,否則利用空閑線程(60s空閑時間焙贷,過了就會被回收撵割,所以線程池中有0個線程的可能)處理任務。
  2. 任務隊列SynchronousQueue相當于一個空集合辙芍,導致任何任務都會被立即執(zhí)行啡彬。
  3. CachedThreadPool就像是一堆人去一個很大的咖啡館喝咖啡羹与,里面服務員也很多,隨時去庶灿,隨時都可以喝到咖啡纵搁。但是為了響應國家的“光盤行動”,一個人喝剩下的咖啡會被保留60秒往踢,供新來的客人使用腾誉,哈哈哈哈哈,好惡心啊峻呕。如果你運氣好利职,沒有剩下的咖啡,你會得到一杯新咖啡瘦癌。但是以前客人剩下的咖啡超過60秒猪贪,就變質(zhì)了,會被服務員回收掉佩憾。
  4. 比較適合執(zhí)行大量的耗時較少的任務哮伟。喝咖啡人挺多的,喝的時間也不長
    1. ScheduledThreadPool(4個里面唯一一個有延遲執(zhí)行和周期重復執(zhí)行的線程池)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedQueue ());
}
//使用妄帘,延遲1秒執(zhí)行楞黄,每隔2秒執(zhí)行一次Runnable r
Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS);
  1. 核心線程數(shù)固定,非核心線程(閑著沒活干會被立即回收)數(shù)沒有限制抡驼。
  2. 從上面代碼也可以看出鬼廓,ScheduledThreadPool主要用于執(zhí)行定時任務以及有固定周期的重復任務。

Handler 介紹

  • 由于Android中主線程是不能進行耗時操作的致盟,子線程是不能進行更新UI的碎税。所以就有了handler,它的作用就是實現(xiàn)線程之間的通信馏锡。 handler整個流程中雷蹂,主要有四個對象,handler杯道,Message,MessageQueue, Looper匪煌。當應用創(chuàng)建的時候,就會在主線程中創(chuàng)建handler對象党巾, 我們通過要傳送的消息保存到Message中萎庭,handler.post handler通過調(diào)用sendMessage方法將Message發(fā)送到MessageQueue中,Looper對象就會不斷的調(diào)用loop()方法 不斷的從MessageQueue中取出Message交給handler進行處理齿拂。從而實現(xiàn)線程之間的通信

說下 handler 原理

  • Handler驳规,Message,looper 和 MessageQueue 構(gòu)成了安卓的消息機制署海,handler創(chuàng)建后可以通過 sendMessage 將消息加入消息隊列吗购,然后 looper不斷的將消息從 MessageQueue 中取出來医男,回調(diào)到 Hander 的 handleMessage方法,從而實現(xiàn)線程的通信巩搏。
  1. 在UI線程創(chuàng)建Handler,此時我們不需要手動開啟looper昨登,因為在應用啟動時趾代,在ActivityThread的main方法中就創(chuàng)建了一個當前主線程的looper贯底,并開啟了消息隊列,消息隊列是一個無限循環(huán)撒强,為什么無限循環(huán)不會ANR ? 因為應用的整個生命周期就是運行在這個消息循環(huán)中的禽捆,安卓是由事件驅(qū)動的,Looper.loop不斷的接收處理事件飘哨,每一個點擊觸摸或者Activity每一個生命周期都是在Looper.loop的控制之下的胚想,looper.loop一旦結(jié)束,應用程序的生命周期也就結(jié)束了芽隆。我們可以想想什么情況下會發(fā)生ANR浊服,第一,事件沒有得到處理
  2. 事件正在處理胚吁,但是沒有及時完成牙躺,而對事件進行處理的就是looper,所以只能說事件的處理如果阻塞會導致ANR腕扶,而不能說looper的無限循環(huán)會ANR孽拷。
    另一種情況就是在子線程創(chuàng)建Handler,此時由于這個線程中沒有默認開啟的消息隊列,所以我們需要手動調(diào)用looper.prepare(),并通過looper.loop開啟消息
  3. 主線程Looper從消息隊列讀取消息半抱,當讀完所有消息時脓恕,主線程阻塞。子線程往消息隊列發(fā)送消息窿侈,并且往管道文件寫數(shù)據(jù)炼幔,主線程即被喚醒,從管道文件讀取數(shù)據(jù)史简,主線程被喚醒只是為了讀取消息乃秀,當消息讀取完畢,再次睡眠乘瓤。因此loop的循環(huán)并不會對CPU性能有過多的消耗

Activity A跳轉(zhuǎn)Activity B环形,再按返回鍵,生命周期執(zhí)行的順序衙傀?

  • A.onPause() B.onCreate() B.onStart() B.onResume() A.onStop()
  • 另外 如果Activity B是透明的 或者Activity B 并未完全遮住Activity A抬吟,那么上述操作點擊Activity A 跳轉(zhuǎn) Activity B 生命周期中A.onStop()是不會被調(diào)用的,因為Activity A還可見统抬,所以Activity A不能被停止

View 的繪制流程

  • Activity火本、Window危队、DecorView之間關系
 public void setContentView(@LayoutRes int layoutResID) {
        //  將xml布局傳遞到Window當中
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
  1. 從代碼可以看出,Activity的setContentView實質(zhì)是將View傳遞到Window的setContentView()方法中钙畔,Window的setContenView會在內(nèi)部調(diào)用installDecor()方法創(chuàng)建DecorView
  2. View的繪制是從ViewRootImpl的performTraversals()方法開始蜈敢,從最頂層的View(ViewGroup)開始逐層對每個View進行繪制操作
  • measure:為測量寬高過程差牛,如果是ViewGroup還要在onMeasure中對所有子View進行measure操作。
  • layout:用于擺放View在ViewGroup中的位置,如果是ViewGroup要在onLayout方法中對所有子View進行l(wèi)ayout操作药磺。
  • draw:往View上繪制圖像奥务。
  1. View 的繪制流程是 measure -> layout -> draw

View 事件分發(fā)機制

  1. View 的事件分發(fā)機制主要涉及到以下幾個方法
  • dispatchTouchEvent 卓鹿,這個方法主要是用來分發(fā)事件的
  • onInterceptTouchEvent檩电,這個方法主要是用來攔截事件的(需要注意的是 ViewGroup 才有這個方法,View 沒有 onInterceptTouchEvent 這個方法
  • onTouchEvent 這個方法主要是用來處理事件的
  • requestDisallowInterceptTouchEvent(true)现斋,這個方法能夠影響父View是否攔截事件喜最,true 表示父 View 不攔截事件,false 表示父 View 攔截事件
  1. 當觸摸事件發(fā)生時庄蹋,首先 Activity 將 TouchEvent 傳遞給最頂層的 View瞬内,TouchEvent最先到達最頂層 view 的 dispatchTouchEvent,然后由 dispatchTouchEvent方法進行分發(fā)限书,

如果dispatchTouchEvent返回true 消費事件虫蝶,事件終結(jié)。
如果dispatchTouchEvent返回 false 蔗包,則回傳給父View的onTouchEvent事件處理秉扑;

如果dispatchTouchEvent返回super的話,默認會調(diào)用自己的onInterceptTouchEvent方法调限。

默認的情況下onInterceptTouchEvent回調(diào)用super方法舟陆,super方法默認返回false,所以會交給子View的onDispatchTouchEvent方法處理
如果 interceptTouchEvent 返回 true 耻矮,也就是攔截掉了秦躯,則交給它的 onTouchEvent 來處理,
如果 interceptTouchEvent 返回 false 裆装,那么就傳遞給子 view 踱承,由子 view 的 dispatchTouchEvent 再來開始這個事件的分發(fā)。

Activity 四種啟動模式
  1. standard : 默認啟動模式,每開啟一個activity就在任務棧中創(chuàng)建一個新的實例.Top single 頂部只有一個 不允許存在兩個相同的Activity.使用場景:基本絕大多數(shù)地方都可以用哨免。
  2. singleTop: 如果在任務的棧頂正好存有該 Activity的實例茎活,則會通過調(diào)用 onNewIntent() 方法進行重用,否則就會同 standard 模式一樣琢唾,創(chuàng)建新的實例并放入棧頂载荔。即便棧中已經(jīng)存在了該 Activity 的實例,也會創(chuàng)建新的實例
    當且僅當啟動的 Activity 和上一個 Activity 一致的時候才會通過調(diào)用 onNewIntent()方法重用 Activity 采桃。使用場景:資訊閱讀類 APP 的內(nèi)容界面懒熙。
  3. singleTask : 在同一個任務棧中丘损,如果要啟動的目標Activity已經(jīng)在棧中,則會復用該Activity工扎,并調(diào)用其onNewIntent()方法徘钥,并且該Activity上面的Activity會被清除,如果棧中沒有肢娘,則創(chuàng)建新的實例呈础。使用場景:瀏覽器的主頁面,或者大部分 APP 的主頁面蔬浙。
  4. singleInstance: 指定為singleInstance模式的活動會啟用一個新的返回棧來管理這個活動猪落。在一個新棧中創(chuàng)建該 Activity 的實例,并讓多個應用共享該棧中的該 Activity 實例畴博。一旦該模式的 Activity 實例已經(jīng)存在于某個棧中,任何應用再激活該 Activity 時都會重用該棧中的實例蓝仲,是的俱病,依然是調(diào)用 onNewIntent()方法。其效果相當于多個應用共享一個應用袱结,不管是誰激活亮隙,該 Activity 都會進入同一個應用中。但值得引起注意的是:singleInstance 不要用于中間頁面垢夹,如果用戶中間頁面溢吻,跳轉(zhuǎn)會出現(xiàn)很難受的問題。 這個在實際開發(fā)中我暫未遇到過果元, Android 系統(tǒng)的來電頁面促王,多次來電都是使用的同一個 Activity

Service啟動方式

  1. startService. startService() 啟動一個 Service而晒。一旦啟動蝇狼,Service 將一直運行在后臺,即使啟動這個 Service 的組件已經(jīng)被銷毀倡怎。通常一個被 start 的 Service 會在后臺執(zhí)行單獨的操作迅耘,也并不需要給啟動它的組件返回結(jié)果。只有當 Service 自己調(diào)用 stopSelf() 或者其它組件調(diào)用 stopService() 才會終止监署。
  2. **bindService **. bindService() 來綁定一個 Service颤专。這種方式會讓 Service 和啟動它的組件綁定在一起,當啟動它的組件銷毀的時候钠乏,Service 也會自動進行 unBind 操作栖秕。同一個 Service 可以被多個組件綁定,只有所有綁定它的組件都進行了 unBind 操作缓熟,這個 Service 才會被銷毀
  3. Service 的生命周期
  • 當調(diào)用 startService() 去 start 一個 Service 后累魔,仍然可以 bind 這個 Service摔笤。比如:當播放音樂的時候,需要調(diào)用 startService() 啟動指定的音樂垦写,當需要獲取該音樂的播放進度的時候吕世,又需要調(diào)用 bindService(),在這種情況下梯投,除非 Service 被 unbind命辖,此前調(diào)用 stopService() 和 stopSelf() 都不能停止該 Service

  • 完整生命周期(entire lifetime):從 onCreate() 被調(diào)用,到 onDestroy() 返回分蓖。和 Activity 類似尔艇,一般在 onCreate() 方法中做一些初始化的工作,在 onDestroy() 中做一些資源釋放的工作么鹤。如终娃,若 Service 在后臺播放一個音樂,就需要在 onCreate() 方法中開啟一個線程啟動音樂蒸甜,并在 onDestroy() 中結(jié)束線程棠耕。

  • 活動生命周期(activity lifetime):從 onStartCommand() 或 onBind() 回調(diào)開始,由相應的 startService() 或 bindService() 調(diào)用柠新。start 方式的活動生命周期結(jié)束就意味著完整證明周期的結(jié)束窍荧,而 bind 方式,當 onUnbind() 返回后恨憎,Service 的活動生命周期結(jié)束蕊退。

  • 是 startService() 還是 bindService() 啟動 Service,onCreate() 和 onDestroy() 均會被回調(diào)

Service 的 onCreate() 可以執(zhí)行耗時操作嗎憔恳?

  • Service 運行在主線程中瓤荔,它并不是一個新的線程,也不是新的進程喇嘱,所以并不能執(zhí)行耗時操作茉贡。

如果要在 Service 中執(zhí)行耗時操作,怎么做者铜?

  1. 使用 AysncTask 或 HandlerThread 來替代 Thread 創(chuàng)建線程腔丧。
  2. IntentService 繼承于 Service,若 Service 不需要同時處理多個請求作烟,那么使用 IntentService 將是最好選擇愉粤。只需要重寫 onHandleIntent() 方法,該方法接收一個回調(diào)的 Intent 參數(shù)拿撩, 在方法內(nèi)進行耗時操作衣厘,因為它默認開啟了一個子線程,操作執(zhí)行完成后也無需手動調(diào)用 stopSelf() 方法,onHandleIntent() 將會自動調(diào)用該方法

Service 與 IntentService區(qū)別

  1. Service 不是運行在獨立的線程影暴,所以不建議在Service中編寫耗時的邏輯和操作错邦,否則會引起ANR。
  2. IntentService
  • 可用于執(zhí)行后臺耗時的任務型宙,任務執(zhí)行后會自動停止撬呢。
  • 具有高優(yōu)先級,適合高優(yōu)先級的后臺任務妆兑,且不容易被系統(tǒng)殺死魂拦。
  • 可以多次啟動,每個耗時操作都會以工作隊列的方式在IntentService的onHandleIntent回調(diào)方法中執(zhí)行

Serializable 和Parcelable的區(qū)別

  • 平臺區(qū)別搁嗓。Serializable是屬于 Java 自帶的芯勘,表示一個對象可以轉(zhuǎn)換成可存儲或者可傳輸?shù)臓顟B(tài),序列化后的對象可以在網(wǎng)絡上進行傳輸腺逛,也可以存儲到本地荷愕。
    Parcelable 是屬于 Android 專用。不過不同于Serializable屉来,Parcelable實現(xiàn)的原理是將一個完整的對象進行分解路翻。而分解后的每一部分都是Intent所支持的數(shù)據(jù)類型。
  • 編寫上的區(qū)別茄靠。Serializable代碼量少,寫起來方便
    Parcelable代碼多一些蝶桶,略復雜
  • 選擇的原則
  1. 如果是僅僅在內(nèi)存中使用慨绳,比如activity、service之間進行對象的傳遞真竖,強烈推薦使用Parcelable脐雪,因為Parcelable比Serializable性能高很多。因為Serializable在序列化的時候會產(chǎn)生大量的臨時變量恢共, 從而引起頻繁的GC战秋。
  2. 如果是持久化操作,推薦Serializable讨韭,雖然Serializable效率比較低脂信,但是還是要選擇它,因為在外界有變化的情況下透硝,Parcelable不能很好的保存數(shù)據(jù)的持續(xù)性狰闪。
  • 本質(zhì)的區(qū)別
  1. Serializable的本質(zhì)是使用了反射,序列化的過程比較慢濒生,這種機制在序列化的時候會創(chuàng)建很多臨時的對象埋泵,比引起頻繁的GC、
  2. Parcelable方式的本質(zhì)是將一個完整的對象進行分解,而分解后的每一部分都是Intent所支持的類型丽声,這樣就實現(xiàn)了傳遞對象的功能了

Serializable中為什么要設置UID礁蔗,設置UID與不設置UID值的區(qū)別和影響 ?

  • serialVersionUID 是用來輔助序列化和反序列化的過程。 序列化后的數(shù)據(jù)中的serialVersionUID只有和當前類的serialVersionUID一致才能成功的反序列化
  • serialVersionUID 適用于java序列化機制雁社。簡單來說浴井,JAVA序列化的機制是通過判斷類的serialVersionUID來驗證的版本一致的。在進行反序列化時歧胁,JVM會把傳來的字節(jié)流中的serialVersionUID于本地相應實體類的serialVersionUID進行比較滋饲。如果相同說明是一致的,可以進行反序列化喊巍,否則會出現(xiàn)反序列化版本一致的異常屠缭,即是InvalidCastException。
  • 具體序列化的過程 :序列化操作時會把系統(tǒng)當前類的serialVersionUID寫入到序列化文件中崭参,當反序列化時系統(tǒng)會自動檢測文件中的serialVersionUID呵曹,判斷它是否與當前類中的serialVersionUID一致。如果一致說明序列化文件的版本與當前類的版本是一樣的何暮,可以反序列化成功奄喂,否則就失敗海洼;
  • serialVersionUID有兩種顯示的生成方式:
  1. 默認的1L跨新,比如:private static final long serialVersionUID = 1L;
  2. 根據(jù)包名,類名坏逢,繼承關系域帐,非私有的方法和屬性,以及參數(shù)是整,返回值等諸多因子計算得出的肖揣,極度復雜生成的一個64位的哈希字段「∪耄基本上計算出來的這個值是唯一的龙优。比如:private static final long serialVersionUID = xxxxL;
    注意:顯示聲明serialVersionUID可以避免對象不一致

內(nèi)存泄漏的場景和解決辦法

  1. 非靜態(tài)內(nèi)部類的靜態(tài)實例
    非靜態(tài)內(nèi)部類會持有外部類的引用,如果非靜態(tài)內(nèi)部類的實例是靜態(tài)的事秀,就會長期的維持著外部類的引用彤断,組織被系統(tǒng)回收,解決辦法是使用靜態(tài)內(nèi)部類
  2. 多線程相關的匿名內(nèi)部類和非靜態(tài)內(nèi)部類
    匿名內(nèi)部類同樣會持有外部類的引用秽晚,如果在線程中執(zhí)行耗時操作就有可能發(fā)生內(nèi)存泄漏瓦糟,導致外部類無法被回收,直到耗時任務結(jié)束赴蝇,解決辦法是在頁面退出時結(jié)束線程中的任務
  3. Handler內(nèi)存泄漏
    Handler導致的內(nèi)存泄漏也可以被歸納為非靜態(tài)內(nèi)部類導致的菩浙,Handler內(nèi)部message是被存儲在MessageQueue中的,有些message不能馬上被處理,存在的時間會很長劲蜻,導致handler無法被回收陆淀,如果handler是非靜態(tài)的,就會導致它的外部類無法被回收先嬉,解決辦法是1.使用靜態(tài)handler轧苫,外部類引用使用弱引用處理2.在退出頁面時移除消息隊列中的消息
  4. Context導致內(nèi)存泄漏
    根據(jù)場景確定使用Activity的Context還是Application的Context,因為二者生命周期不同,對于不必須使用Activity的Context的場景(Dialog),一律采用Application的Context,單例模式是最常見的發(fā)生此泄漏的場景疫蔓,比如傳入一個Activity的Context被靜態(tài)類引用含懊,導致無法回收
  5. 靜態(tài)View導致泄漏
    使用靜態(tài)View可以避免每次啟動Activity都去讀取并渲染View,但是靜態(tài)View會持有Activity的引用衅胀,導致無法回收岔乔,解決辦法是在Activity銷毀的時候?qū)㈧o態(tài)View設置為null(View一旦被加載到界面中將會持有一個Context對象的引用,在這個例子中滚躯,這個context對象是我們的Activity雏门,聲明一個靜態(tài)變量引用這個View,也就引用了activity)
  6. WebView導致的內(nèi)存泄漏
    WebView只要使用一次掸掏,內(nèi)存就不會被釋放茁影,所以WebView都存在內(nèi)存泄漏的問題,通常的解決辦法是為WebView單開一個進程丧凤,使用AIDL進行通信募闲,根據(jù)業(yè)務需求在合適的時機釋放掉
  7. 資源對象未關閉導致
    如Cursor,F(xiàn)ile等愿待,內(nèi)部往往都使用了緩沖蝇更,會造成內(nèi)存泄漏,一定要確保關閉它并將引用置為null
  8. 集合中的對象未清理
    集合用于保存對象呼盆,如果集合越來越大,不進行合理的清理蚁廓,尤其是入股集合是靜態(tài)的
  9. Bitmap導致內(nèi)存泄漏
    bitmap是比較占內(nèi)存的访圃,所以一定要在不使用的時候及時進行清理,避免靜態(tài)變量持有大的bitmap對象
  10. 監(jiān)聽器未關閉
    很多需要register和unregister的系統(tǒng)服務要在合適的時候進行unregister,手動添加的listener也需要及時移除

EventBus原理

  1. 主要是維護了幾個數(shù)組相嵌,然后根據(jù)對應的key找到對應的注冊對象腿时,通過放射的方式調(diào)用對應的方法。
  2. EventBus 2.x 是采用反射的方式對整個注冊的類的所有方法進行掃描來完成注冊饭宾,當然會有性能上的影響批糟。EventBus 3.0 中EventBus提供了EventBusAnnotationProcessor注解處理器來在編譯期通過讀取@Subscribe()注解并解析、處理其中所包含的信息看铆,然后生成java類來保存所有訂閱者關于訂閱的信息徽鼎,這樣就比在運行時使用反射來獲得這些訂閱者的信息速度要快
/注冊事件
EventBus.getDefault().register(this);
//注冊方法
@Subscribe
public void event(BaseEventBusBeaan message) {
  LogUtils.d("EventBusActivity event");
}
 
//發(fā)送事件
EventBus.getDefault().post(new BaseEventBusBeaan("123", new Bundle()));
 
//反注冊
EventBus.getDefault().unregister(this);

總結(jié)一下大概的流程

  1. 通過apt在編譯期將所有被 @Subscribe注解的函數(shù)添加到MyEventBusIndex對象中。
  2. register過程中生成subscriptionsByEventType的數(shù)據(jù)。
  3. post過程中通過subscriptionsByEventType數(shù)據(jù)查找對應的函數(shù)否淤,然后再通過反射的方式調(diào)用悄但。

優(yōu)先級的問題

這個問題也十分簡單,只需要在插入數(shù)據(jù)的時候石抡,做下優(yōu)先級判斷即可檐嚣。

Android 熱更新 流程和原理

  • 一個完整的項目應該有如下分支:
    develop分支---- 這個分支中放的是線上的發(fā)布版本。
    bugfix分支---- 這個是熱更新分支啰扛,由develop中遷出嚎京。
    master分支---- 這個是開發(fā)中的分支
  • 熱更新應當按照如下步驟進行:
  1. 線上檢測到嚴重的crash
  2. 從develop中拉出一個線上的最新版,在bugfix分支上進行問題的修復
  3. jenkins構(gòu)建和補丁的生成
  4. app通過推送或主動拉取補丁文件
  5. 將bugfix代碼合到master上隐解,保證以后不會出現(xiàn)該問題
  • 主流熱更新框架介紹
  1. Dexposed :該框架是阿里巴巴開源的一個Android平臺下的無侵入的運行時AOP(面向方向編程)框架鞍帝。基于以前開源的一個Xposed框架實現(xiàn)的厢漩。Dexposed框架基于Hook技術實現(xiàn)功能膜眠,不僅可以hook你自己的程序代碼,也可以hook你的應用程序中調(diào)用的Android框架中的函數(shù)溜嗜∠颍基于動態(tài)類加載技術,運行中的app可以加載一小段經(jīng)過編譯的Java的代碼炸宵,而且在不需要重寫APP的前提下辟躏,就可以實現(xiàn)修改APP
  2. AndFix:該框架同樣出自阿里。與Dexposed不是同一個團隊土全。AndFix也是基于Xposed思想捎琐。而相比第一個,它是一個更純粹的熱修復的框架裹匙。
  3. Nuwa:它其實是基于類加載器ClassLoader加載Dex文件瑞凑。如果多個Dex文件存在相同的類,那么排在前面的Dex文件就將優(yōu)先被選擇概页。這是熱更新最主要的思想籽御,它通過不斷地去輪詢,去遍歷Dex的一個數(shù)組惰匙,然后把我們需要修改的類的dex文件加到最前面技掏,這樣當輪詢遍歷時就不會加載有問題的那個類。
  • 熱更新的原理
  1. Android的類加載機制 :Android的類加載器主要有這樣兩個:PathClassLoader和DexClassLoader项鬼。PathClassLoader主要用于加載系統(tǒng)的類和應用類哑梳,DexClassLoader主要加載Dex文件,Jar文件绘盟,apk文件等鸠真。
  2. 熱修復機制:在BaseClassLoader中會創(chuàng)建一個dexElements數(shù)組悯仙,然后我們會通過ClassLoader遍歷這個數(shù)組,加載這個數(shù)組中的dex文件弧哎。這樣當BaseClassLoader加載到正確的類以后雁比,就不會去加載有Crash的那個類。因此我們就將這個有問題修復后的類放入Dex文件當中撤嫩,讓這個Dex文件排在dexElements前面偎捎。這樣BaseClassLoader就不會加載到處于后面的那個Dex文件。這樣就完成了整個熱修復過程序攘。

Android線程間通信四種方式:

    1. 通過Handler機制
      主線程中定義Handler,子線程發(fā)消息茴她,通知Handler完成UI更新,Handler對象必須定義在主線程中,如果是多個類直接互相調(diào)用程奠,就不是很方便丈牢,需要傳遞content對象或通過接口調(diào)用。另外Handler機制與Activity生命周期不一致的原因瞄沙,容易導致內(nèi)存泄漏己沛,不推薦使用。
    1. runOnUiThread方法,用Activity對象的runOnUiThread方法更新距境,在子線程中通過runOnUiThread()方法更新UI申尼,強烈推薦使用。
  • 3.View.post(Runnable r)這種方法更簡單垫桂,但需要傳遞要更新的View過去,推薦使用
    1. AsyncTask,即異步任務,是Android給我們提供的一個處理異步任務的類.通過此類,可以實現(xiàn)UI線程和后臺線程進行通訊,后臺線程執(zhí)行異步任務,并把結(jié)果返回給UI線程

Android中有哪些進程間通信方式师幕?

  • Binder 簡單易用 只能傳輸Bundle支持的數(shù)據(jù)類型 四大組件間的進程間通信
    文件共享 簡單易用 不適用高并發(fā)場景,并且無法做到進程間即時通信 適用于無關發(fā)的情況下诬滩,交換簡單的數(shù)據(jù)霹粥,對實時性要求不高的場景。
  • AIDL功能強大疼鸟,支持一對多實時并發(fā)通信 使用稍復雜后控,需要處理好線程間的關系 一對多通信且有RPC需求
  • Messenger功能一般,支持一對多串行通信空镜,支持實時通信 不能很好地處理高并發(fā)的情形忆蚀,不支持RPC,由于數(shù)據(jù)通過Message傳輸姑裂,因此只能傳輸Bundle支持的數(shù)據(jù)類型 低并發(fā)的一對多實時通信,無RPC需求男旗,或者無需要返回結(jié)果的RPC需求
    -ContentProvider支持一對多的實時并發(fā)通信舶斧,在數(shù)據(jù)源共享方面功能強大,可通過Call方法擴展其它操作 可以理解為受約束的AIDL察皇,主要提供對數(shù)據(jù)源的CRUD操作 一對多的進程間數(shù)據(jù)共享
  • BroadcastReceiver操作簡單茴厉,對持一對多實時通信 只支持數(shù)據(jù)單向傳遞泽台,效率低且安全性不高 一對多的低頻率單向通信
  • Socket功能強大,可通過網(wǎng)絡傳輸字節(jié)流矾缓,支持一對多實時并發(fā)通信 實現(xiàn)細節(jié)步驟稍繁瑣怀酷,不支持直接的RPC 網(wǎng)絡間的數(shù)據(jù)交換

websocket 和 http相關

    1. 什么是 websocket
      websocketHTML5的一種新協(xié)議嗜闻,允許服務器想客戶端傳遞信息蜕依,實現(xiàn)瀏覽器和客戶端雙工通信。
    1. websocket特點
      (1)與http協(xié)議有良好的兼容性琉雳;
      (2)建立在TCP協(xié)議之上样眠,和http協(xié)議同屬于應用層;
      (3)數(shù)據(jù)格式比較輕量翠肘,性能開銷小檐束,通信高效;
      (4)可以發(fā)送文本束倍,也可以發(fā)送二進制被丧;
      (5)沒有同源限制,可以與任意服務器通信绪妹。
    1. httpwebsocket的區(qū)別
      3.1 http協(xié)議是短鏈接甥桂,因為請求之后,都會關閉連接喂急,下次請求需要重新打開鏈接格嘁。
      3.2 websocket協(xié)議是一種長連接,只需要通過一次請求來初始化連接廊移,然后所有請求和響應都是通過TCP鏈接進行通信糕簿。
    1. 特點
      最大特點就是,服務器可以主動向客戶端推送信息狡孔,客戶端也可以主動向服務器發(fā)送信息懂诗,是真正的雙向平等對話,屬于服務器推送技術的一種苗膝。
      其他特點包括:
      (1)建立在 TCP 協(xié)議之上殃恒,服務器端的實現(xiàn)比較容易。
      (2)與 HTTP 協(xié)議有著良好的兼容性辱揭。默認端口也是80和443离唐,并且握手階段采用 HTTP 協(xié)議,因此握手時不容易屏蔽问窃,能通過各種 HTTP 代理服務器亥鬓。
      (3)數(shù)據(jù)格式比較輕量,性能開銷小域庇,通信高效嵌戈。
      (4)可以發(fā)送文本覆积,也可以發(fā)送二進制數(shù)據(jù)。
      (5)沒有同源限制熟呛,客戶端可以與任意服務器通信宽档。
      (6)協(xié)議標識符是ws(如果加密,則為wss)庵朝,服務器網(wǎng)址就是 URL

持續(xù)更新中 ...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載吗冤,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末偿短,一起剝皮案震驚了整個濱河市欣孤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昔逗,老刑警劉巖降传,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勾怒,居然都是意外死亡婆排,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門笔链,熙熙樓的掌柜王于貴愁眉苦臉地迎上來段只,“玉大人,你說我怎么就攤上這事鉴扫≡拚恚” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵坪创,是天一觀的道長炕婶。 經(jīng)常有香客問我,道長莱预,這世上最難降的妖魔是什么柠掂? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮依沮,結(jié)果婚禮上涯贞,老公的妹妹穿的比我還像新娘。我一直安慰自己危喉,他們只是感情好宋渔,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辜限,像睡著了一般傻谁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上列粪,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天审磁,我揣著相機與錄音,去河邊找鬼岂座。 笑死态蒂,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的费什。 我是一名探鬼主播钾恢,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸳址!你這毒婦竟也來了瘩蚪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤稿黍,失蹤者是張志新(化名)和其女友劉穎疹瘦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巡球,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡言沐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了酣栈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片险胰。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖矿筝,靈堂內(nèi)的尸體忽然破棺而出起便,到底是詐尸還是另有隱情,我是刑警寧澤窖维,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布榆综,位于F島的核電站,受9級特大地震影響陈辱,放射性物質(zhì)發(fā)生泄漏奖年。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一沛贪、第九天 我趴在偏房一處隱蔽的房頂上張望陋守。 院中可真熱鬧,春花似錦利赋、人聲如沸水评。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽中燥。三九已至,卻和暖如春塘偎,著一層夾襖步出監(jiān)牢的瞬間疗涉,已是汗流浹背拿霉。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咱扣,地道東北人绽淘。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像闹伪,于是被迫代替她去往敵國和親沪铭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

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