Handler是用來結合線程的消息隊列來發送、處理「Message對象」和「Runnable對象」的工具。每個Handler實例以後會關聯一個線程和該線程的消息隊列。當你建立一個Handler的時候,從這時開始,它就會自動關聯到所在的線程/消息隊列,而後它就會陸續把Message/Runnalbe分發到消息隊列,並在它們出隊的時候處理掉。
Looper:
每個線程只有一個Looper,每一個線程在初始化Looper以後,而後Looper會維護好該線程的消息隊列,用來存放Handler發送的Message,並處理消息隊列出隊的Message。它的特色是它跟它的線程是綁定的,處理消息也是在Looper所在的線程去處理,因此當咱們在主線程建立Handler時,它就會跟主線程惟一的Looper綁定,從而咱們使用Handler在子線程發消息時,最終也是在主線程處理,達到了異步的效果。
MessageQueue:
MessageQueue是一個消息隊列,用來存放Handler發送的消息。每一個線程最多隻有一個MessageQueue。MessageQueue一般都是由Looper來管理,而主線程建立時,會建立一個默認的Looper對象,而Looper對象的建立,將自動建立一個MessageQueue。其餘非主線程,不會自動建立Looper。
Message: 消息對象,就是MessageQueue裏面存放的對象,一個MessageQueu能夠包括多個Message。當咱們須要發送一個Message時,咱們通常不建議使用new Message()的形式來建立,更推薦使用Message.obtain()來獲取Message實例,由於在Message類裏面定義了一個消息池,當消息池裏存在未使用的消息時,便返回,若是沒有未使用的消息,則經過new的方式建立返回,因此使用Message.obtain()的方式來獲取實例能夠大大減小當有大量Message對象而產生的垃圾回收問題。android
緣由:
(1)非靜態內部類是會隱式持有外部類的引用,因此當其餘線程持有了該Handler,線程沒有被銷燬,則意味着Activity會一直被Handler持有引用而沒法致使回收。
(2)MessageQueue中若是存在未處理完的Message,Message的target也是對Activity等的持有引用,也會形成內存泄漏。
解決辦法:
(1)使用靜態內部類+弱引用的方式
(2)在外部類對象被銷燬時,將MessageQueue中的消息清空。例如,在Activity的onDestroy時將消息清空算法
當上一個消息存在耗時任務的時候,會佔用延時任務執行的時機,此時是不許確的。因爲Loop.loop裏面消息是串行取出併發給handler.dispatchMessage的,那麼輪處處理第二個延時runnable的時候,MessageQueue類的next方法再執行到if(now < msg.when)的時候,就馬上return了該msg,而後由handler.dispatchMessage處理,執行到該runnable的run方法編程
//ActivityThread.main()
public static void main(String[] args) {
....
//建立Looper和MessageQueue對象,用於處理主線程的消息
Looper.prepareMainLooper();
//建立ActivityThread對象
ActivityThread thread = new ActivityThread();
//創建Binder通道 (建立新線程)
thread.attach(false);
Looper.loop(); //消息循環運行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
ActivityThread的main方法主要就是作消息循環,一旦退出消息循環,那麼你的應用也就退出了。Activity的生命週期都是依靠主線程的Looper.loop,當收到不一樣Message時則採用相應措施。咱們的代碼其實就是在這個循環裏面去執行的,固然不會阻塞了。並且主線程Looper從消息隊列讀取消息,當讀完全部消息時,主線程阻塞。子線程往消息隊列發送消息,而且往管道文件寫數據,主線程即被喚醒,從管道文件讀取數據,主線程被喚醒只是爲了讀取消息,當消息讀取完畢,再次睡眠。所以loop的循環並不會對CPU性能有過多的消耗。緩存
當使用內部類(包括匿名類)來建立Handler的時候,Handler對象會隱式地持有一個外部類對象(一般是一個Activity)的引用(否則你怎麼可能經過Handler來操做Activity中的View?)。而Handler一般會伴隨着一個耗時的後臺線程(例如從網絡拉取圖片)一塊兒出現,這個後臺線程在任務執行完畢(例如圖片下載完畢)以後,經過消息機制通知Handler,而後Handler把圖片更新到界面。然而,若是用戶在網絡請求過程當中關閉了Activity,正常狀況下,Activity再也不被使用,它就有可能在GC檢查時被回收掉,但因爲這時線程還沒有執行完,而該線程持有Handler的引用(否則它怎麼發消息給Handler?),這個Handler又持有Activity的引用,就致使該Activity沒法被回收(即內存泄露),直到網絡請求結束(例如圖片下載完畢)。另外,若是你執行了Handler的postDelayed()方法,該方法會將你的Handler裝入一個Message,並把這條Message推到MessageQueue中,那麼在你設定的delay到達以前,會有一條MessageQueue -> Message -> Handler -> Activity的鏈,致使你的Activity被持有引用而沒法被回收。處理方法:靜態內部類+弱引用,還有一個就是handler.removeCallbacksAndMessages(null);,就是移除全部的消息和回調,簡單一句話就是清空了消息隊列。安全
LRU(Least Recently Used)是近期最少使用的算法,它的核心思想是當緩存滿時,會優先淘汰那些近期最少使用的緩存對象。LruCache中維護了一個集合LinkedHashMap,該LinkedHashMap是以訪問順序排序的。當調用put()方法時,就會在結合中添加元素,並調用trimToSize()判斷緩存是否已滿,若是滿了就用LinkedHashMap的迭代器刪除隊尾元素,即近期最少訪問的元素。當調用get()方法訪問緩存對象時,就會調用LinkedHashMap的get()方法得到對應集合元素,同時會更新該元素到隊頭。bash
內存泄露服務器
圖片Bitmap相關網絡
內存抖動 大量臨時小對象頻繁建立會致使內存碎片,程序頻繁分配內存&垃圾收集器頻繁回收內存。致使卡頓甚至內存溢出。數據結構
代碼質量 & 數量
代碼自己的質量(如 數據結構、數據類型等) & 數量(代碼量的大小)可能會致使大量的內存問題,如佔用內存大、內存利用率低等 平常不正確使用多線程
避免複雜的View層級。佈局越複雜就越臃腫,就越容易出現性能問題,尋找最節省資源的方式去展現嵌套的內容;
儘可能避免在視圖層級的頂層使用相對佈局 RelativeLayout 。相對佈局 RelativeLayout 比較耗資源,由於一個相對佈局 RelativeLayout 須要兩次度量來確保本身處理了全部的佈局關係
佈局層級同樣的狀況建議使用線性佈局 LinearLayout 代替相對佈局 RelativeLayout,由於線性佈局 LinearLayout 性能要更高一些
不要使用絕對佈局 AbsoluteLayout
將可重複使用的組件抽取出來並用include標籤進行重用。若是應用多個地方的 UI用到某個佈局,就將其寫成一個佈局部件,便於各個 UI 重用
使用 merge 標籤減小布局的嵌套層次
幫助include標籤排除多餘的一層ViewGroup容器,減小view hierarchy的結構,提高UI渲染的性能
使用 ViewStub 標籤來加載一些不經常使用的佈局
ViewStub也能夠用來加載佈局文件,但與include標籤徹底不一樣。ViewStub是一個不可見的View類,用於在運行時按需懶加載資源,只有在代碼中調用了viewStub.inflate()或者viewStub.setVisible(View.visible)方法時才內容才變得可見。
優化應用的啓動速度。當應用啓動一個應用時,界面的儘快反饋顯示能夠給用戶一個良好的體驗。爲了啓動更快,能夠延遲加載一些 UI 以及避免在應用 Application 層級初始化代碼。
使用include標籤提升佈局重用性
將一些通用的視圖提取到一個單獨的layout文件中,而後使用標籤在須要使用的其餘layout佈局文件中加載進來,好比咱們本身App導航欄等。這樣,便於對相同視圖內容進行統一的控制管理,提升佈局重用性。
*標籤當中,能夠重寫全部layout屬性的,如上面include中指定的layout屬性將會覆蓋掉titlebar中指定的layout屬性。 而非layout屬性則沒法在標籤當中進行覆寫。另外須要注意的是,若是咱們想要在標籤當中覆寫layout屬性, 必需要將layout_width和layout_height這兩個屬性也進行覆寫,不然覆寫效果將不會生效
外部攔截法 重寫父容器的onInterceptTouchEvent方法
內部攔截法
//重寫這個方法,而且在方法裏面請求全部的父控件都不要攔截他的事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
複製代碼
SurfaceView TexttureView
層級 單獨 普通view
可否動畫 否 能
硬件加速 必須
可否覆蓋 否
消耗內存 少 多
使用版本 4.0之後
複製代碼
onTouchListener的onTouch方法優先級比onTouchEvent高,會先觸發。假如onTouch方法返回false會接着觸發onTouchEvent,反之onTouchEvent方法不會被調用。只要view的CLICKABLE和LONG_CLICKABLE有一個爲true,那麼他就會消耗這個事件,即onTouchEvent方法返回true,無論它是否是DISABLE狀態。而後就是當ACTION_UP事件發生時,會出發performClick方法,若是View設置了OnClickListener,那麼performClick方法內部會調用它的onClick方法。總結:onTouch—–>onTouchEvent—>onclick
關於VSYNC
Vertical Synchronization,就是所謂的「垂直同步」。咱們也能夠把它理解爲「幀同步」。就是爲了保證 CPU、GPU 生成幀的速度和 Display 刷新的速度保持一致。在 VSYNC 開始發出信號時,CPU和GPU已經就開始準備下一幀的數據了,趕在下個 VSYNC 信號到來時,GPU 渲染完成,及時傳送數據給屏幕,Display 繪製顯示完成。
雙緩衝機制
雙緩衝技術一直貫穿整個 Android 系統。由於實際上幀的數據就是保存在兩個 Buffer 緩衝區中,A 緩衝用來顯示當前幀,那麼 B 緩衝就用來緩存下一幀的數據,同理,B顯示時,A就用來緩衝!這樣就能夠作到一邊顯示一邊處理下一幀的數據。
丟幀
因爲某些緣由,好比咱們應用代碼上邏輯處理過於負責或者過於複雜的佈局,過分繪製(Overdraw),UI線程的複雜運算,頻繁的GC等,致使下一幀繪製的時間超過了16ms,用戶很明顯感知到了卡頓的出現,也就是所謂的丟幀狀況。
一、當 Display 顯示第 0 幀數據時,此時 CPU 和 GPU 已經開始渲染第 1 幀畫面,並將數據緩存在緩衝 B 中。可是因爲某些緣由,就好像上面說的,致使系統處理該幀數據耗時過長或者未能及時處理該幀數據。
二、當 VSYNC 信號來時,Display 向 B 緩衝要數據,由於緩衝 B 的數據還沒準備好,B緩衝區這時候是被鎖定的,Display 表示你沒準備好,只能繼續顯示以前緩衝 A 的那一幀,此時緩衝 A 的數據也不能被清空和交換數據。這種狀況就是所謂的「丟幀」,也被稱做「廢幀」;當第 1 幀數據(即緩衝 B 數據)準備完成後,它並不會立刻被顯示,而是要等待下一個 VSYNC,Display 刷新後,這時用戶纔看到畫面的更新。
三、當某一處丟幀後,大機率會影響後面的繪製也出現丟幀,最後給用戶感受就是卡頓了。最嚴重的直接形成ANR。
卡頓處理
在up事件裏相應判斷
public class RotateYAnimation extends Animation {
int centerX, centerY;
Camera camera = new Camera();
/**
* 獲取座標,定義動畫時間
* @param width
* @param height
* @param parentWidth
* @param parentHeight
*/
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
//得到中心點座標
centerX = width / 2;
centerY = width / 2;
//動畫執行時間 自行定義
setInterpolator(new OvershootInterpolator());
}
/**
* 旋轉的角度設置
* @param interpolatedTime
* @param t
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
camera.save();
//設置camera的位置
camera.setLocation(0,0,180);
camera.rotateY(180 * interpolatedTime);
//把咱們的攝像頭加在變換矩陣上
camera.getMatrix(matrix);
//設置翻轉中心點
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX,centerY);
camera.restore();
}
}
複製代碼
數據處理
不少時候從咱們要對服務器上獲取下來的列表數據進行一次二次加工,以便轉化爲咱們界面上要顯示的數據,這些操做可能會比較耗時。好比字符串拼接、時間格式化等操做都是比較耗時的操做。比較好的實踐是將列表數據的加工在notifyDataSetChanged()以前在後臺線程作好,在Adapter中只作數據的綁定。
界面優化 優化佈局層級減小過渡繪製、優化佈局方式提升佈局效率。
避免非必要的刷新
如列表控件,在滑動時不要去加載圖片,能夠在滑動監聽裏中止圖片的加載。
onMeasure
測量本質就是測量自己有多大,也就是給mMeasuredWidth和mMeasuredHeight這兩個屬性賦值,也就是調用setMeasuredDimension這個方法。另外父view測量子view的時候調用的measure方法,還有一些衍生方法如measureChildWithMargins。
onLayout
做用是子view應該怎樣放置,也就是設置子view的mLeft、mTop、mRight、mBottom屬性。該方法在View中是空實現,很顯然主要用於ViewGroup。父view放置子view的時候調用layout方法。
onDraw
具體長什麼樣。
contentprovider是運行在哪一個進程裏面的
contentprovider的oncreate方法,運行在ui線程。可是其餘的方法,運行在非ui線程,例如call、query、delete、insert、upate等方法
別的主線程調用它的時候會被阻塞嗎?
別的主線程調contentprovider裏面方法的時候,雖然他的call、query、delete、insert、upate等方法運行在非ui線程,可是其餘調用方法是會被阻塞的。好比你在activity的oncreate方法中調用contentprovider的query等方法,oncreate方法會被阻塞
若是不一樣的其餘應用,同時調用了這個contentprovider的同一個方法,它們會相互阻塞嗎?好比有三個應用同時都在調用這個provider的插入方法,它們會相互阻塞仍是併發運行
無論Provider訪問者是同一個進程的不一樣線程,仍是不一樣的進程,Provider方其實是同一個Provider對象實例,當併發訪問CP的同一個方法的時候,這個方法運行在不一樣的線程中,不會相互影響
兩種服務啓動區別
同時用兩種方式啓動同一個service 會產生幾個service實例?
同時調用兩種方法啓動同一個方法,只會啓動一個服務,可是其生命週期有所不一樣,取決於兩種方法啓動服務的前後順序。
綁定的service 頁面關閉後未解除綁定 如何銷燬
bindService時LoadApk將ServiceConnection用map保存了起來,當Activity被destroy時會執行removeContextRegistrations來清除 該context的相關注冊。因此Activity退出時服務也被解綁。
Intentservice原理
介紹:
IntentService是繼承於Service並處理異步請求的一個類,在IntentService內有一個工做線程來處理耗時操做,啓動IntentService的方式和啓動傳統Service同樣,同時,當任務執行完後,IntentService會自動中止,而不須要咱們去手動控制。另外,能夠啓動IntentService屢次,而每個耗時操做會以工做隊列的方式在IntentService的onHandleIntent回調方法中執行,而且,每次只會執行一個工做線程,執行完第一個再執行第二個,以此類推。
原理:
IntentService第一次啓動時會調用onCreate方法,建立一個HandlerThread對象,並開啓HandlerThread線程,而後使用HandlerThread的Looper對象初始化mServiceHandler對象,經過mServiceHandler發送消息在HandlerThread中執行操做。每次啓動IntentService都會調用onStartCommand()方法,onStartCommand方法會調用onStart方法。onStart方法中只是調用mServiceHandler發送了一個消息,交給HandlerThread處理,HandlerThread在handleMsg方法裏調用handleIntent處理消息,完成後調用stop方法中止任務。
* Bundle
四大組件中的三大組件(Activity,BroadcaseReceiver,Service)都是支持在Intent中傳遞Bundle數據的,因爲Bundle實現了Parcelable接口,因此能夠很方便的在不一樣進程間傳輸。
* 使用文件共享
享文件也是一種不錯的進程間通訊方式,兩個進程經過讀/寫同一個文件來交換數據。但有一點要注意:android系統是基於Linux的,使得其併發讀/寫文件能夠沒限制地進行,甚至兩線程同時對同一文件進行寫操做都是容許的,儘管這可能出問題。So,重點:文件共享方式適合在對數據同步要求不高的進程間進行通訊,而且要妥善處理併發讀/寫問題。
* Messenger
Messenger譯爲信使,顧名思義,主要做用就是傳遞消息。經過它可在不一樣進程中傳遞Message對象,在Message中放入要傳遞的數據,便可輕鬆地實現數據的進程間傳遞了。Messenger是一種輕量級的IPC方案,底層實現是AIDL。
* AIDL
上面說到Messenger,其是以串行的方式處理客戶端發來的消息,若是有大量的併發請求,那麼使用Messenger就不太合適了。同時Messenger主要做用就是傳遞消息,不少時候咱們可能須要跨進程調用服務端的方法,這種情形Messenger就沒法作到了,可是咱們可使用AIDL來實現跨進程的方法調用。
* ContentProvider
ContentProvider是Android中提供的專門用於不一樣應用間進行數據共享的方式,從這一點,它天生就適合進程間通訊。和Messenger同樣,contentProvider的底層實現一樣是Binder,因而可知,Binder在Android中是何等的重要。雖然ContentProvider底層是用Binder,但它的使用過程要比AIDL 簡單許多,由於系統已經作了封裝。系統預置了許多ContentProvider,好比通信錄信息,日程變信息等,要跨進程訪問這些信息,只須要經過ContentResolver的query、update、insert和delete方法便可。
* Socket
Socket 也稱爲「套接字」。是網絡通訊中的概念,它分爲流式套接字和用戶數據報套接字兩種,分別對應於網絡傳輸控制層中的TCP和UDP協議。TCP是面向鏈接的協議,提供穩定的雙向通訊功能,TCP鏈接的創建須要通過「三次握手」才能完成,爲了提供穩定的數據傳輸功能,其自己提供了超時重傳機制,所以具備很高的穩定性。而UDP是無鏈接的,提供不穩定的單向通訊功能,固然UDP也能實現雙向通訊功能。在性能上,UDP 具備更高的效率,缺點是不保證數據必定可以正確傳輸,尤爲是在網絡阻塞的狀況下。
複製代碼
* 服務端
服務端首先要建立一個Service用來監聽客戶端的鏈接請求,而後建立一個AIDL文件,將暴露給客戶端的接口在這個AIDL文件中聲明,最後在Service中實現這個接口便可。    
* 客戶端
客戶端所要作的事情就稍微簡單一些,首先須要綁定服務Service,綁定成功後,將服務端的Binder對象轉成AIDL藉口所屬的類型,接着就能夠調用AIDL中的方法了。
* AIDL接口建立
建立一個後綴爲AIDL的文件,裏面聲明接口和方法。
* 遠程服務端Service的實現
建立一個Binder對象並在onBind中返回它,這個對象繼承自.Stub並實現了它內部的AIDL方法
* 客戶端的實現
首先綁定遠程服務,綁定成功後將服務端返回的Binder對象轉換成AIDL接口,而後就能夠經過這個接口去調用服務端的遠程方法
複製代碼
Android系統是基於Linux系統的,理論上應該使用Linux內置的IPC方式。Linux中的IPC方式有管道、信號量、共享內存、消息隊列、Socket,Android使用的Binder機制不屬於Linux。Android不繼承Linux中原有的IPC方式,而選擇使用Binder,說明Binder具備必定的優點。 Android系統爲了嚮應用開發者提供豐富的功能,普遍的使用了Client-Server通訊方式,如媒體播放、各類傳感器等,Client-Server的通訊方式是Android IPC的核心,應用程序只須要做爲Client端,與這些Server創建鏈接,便可使用這些功能服務。
下面經過一些功能點,一一排除Linux系統中五種IPC方式,解釋爲何Android選擇使用Binder:
從通訊方式上說,咱們但願獲得的是一種Client-Server的通訊方式,但在Linux的五種IPC機制中,只有Socket支持這種通訊方式。雖然咱們能夠經過在另外四種方式的基礎上架設一些協議來實現Client-Server通訊,但這樣增長了系統的複雜性,在手機這種條件複雜、資源稀缺的環境下,也難以保證可靠性;
從傳輸性能上說,Socket做爲一款通用接口,其傳輸效率低,開銷大,主要用在跨網絡的進程間通訊和本機上進程間的低速通訊;消息隊列和管道採用存儲-轉發方式,即數據先從發送方拷貝到內存開闢的緩存區中,而後再從內核緩存區拷貝到接收方緩存區,至少有兩次拷貝過程;共享內存雖然無需拷貝,但控制複雜,難以使用;而Binder只須要拷貝一次;
從安全性上說,Android做爲一個開放式的平臺,應用程序的來源普遍,所以確保只能終端的安全是很是重要的。Linux傳統的IPC沒有任何安全措施,徹底依賴上層協議來確保,具體有如下兩點表現:
綜上,Binder是一種基於Client-Server通訊模式的通訊方式,傳輸過程只須要一次拷貝,能夠爲發送方添加UID/PID身份,支持實名Binder和匿名Binder,安全性高。
Singletask 生命週期 launchMode爲singleTask的時候,經過Intent啓到一個Activity,若是系統已經存在一個實例,系統就會將請求發送到這個實例上,但這個時候,系統就不會再調用一般狀況下咱們處理請求數據的onCreate方法,而是調用onNewIntent方法。
onNewIntent->onRestart->onStart->onResume
Android onPause和onStop的比較
Fragmentmanager和supportfm 區別
3.0如下:getSupportFragmentManager()
3.0以上:getFragmentManager()
嵌套fragment獲取manager getChildFragmentManager()是fragment中的方法, 返回的是管理當前fragment內部子fragments的manage
@Override
public void onResume() {
super.onResume();
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if(keyEvent.getAction() == KeyEvent.ACTION_DOWN && i == KeyEvent.KEYCODE_BACK){
Toast.makeText(getActivity(), "按了返回鍵", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
}
複製代碼
// ①先定義接口BackHandleInterface
public interface BackHandleInterface {
void onSelectedFragment(BackHandleFragment backHandleFragment);
}
複製代碼
// ②定義公用的Fragment
public abstract class BackHandleFragment extends Fragment {
private BackHandleInterface backHandleInterface;
/**
* 全部繼承BackHandledFragment的子類都將在這個方法中實現物理Back鍵按下後的邏輯
* FragmentActivity捕捉到物理返回鍵點擊事件後會首先詢問Fragment是否消費該事件
* 若是沒有Fragment消息時FragmentActivity本身才會消費該事件
*/
public abstract boolean onBackPressed();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getActivity() instanceof BackHandleInterface){
this.backHandleInterface = (BackHandleInterface)getActivity();
}else{
throw new ClassCastException("Hosting Activity must implement BackHandledInterface");
}
}
@Override
public void onStart() {
super.onStart();
backHandleInterface.onSelectedFragment(this);
}
}
複製代碼
//須要實現監聽的Fragment的Activity實現接口
//主要的是onSelectedFragment()和onBackPressed()其餘方法能夠忽略
public class EdittextActivity extends AppCompatActivity implements BackHandleInterface {
private BackHandleFragment backHandleFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
addFragment(R.id.fragmentContainer, new EdittextFragment());
}
public void addFragment(int containerViewId, Fragment fragment){
final FragmentTransaction transaction = this.getSupportFragmentManager().beginTransaction();
transaction.add(containerViewId, fragment);
transaction.commit();
}
@Override
public void onSelectedFragment(BackHandleFragment backHandleFragment) {
this.backHandleFragment = backHandleFragment;
}
@Override
public void onBackPressed() {
//if判斷裏面就調用了來自Fragment的onBackPressed()
//同樣!!,若是onBackPressed是返回false,就會進入條件內進行默認的操做
if(backHandleFragment == null || !backHandleFragment.onBackPressed()){
if(getSupportFragmentManager().getBackStackEntryCount() == 0){
super.onBackPressed();
}else{
getSupportFragmentManager().popBackStack();
}
}
}
}
複製代碼
//須要監聽的Fragment
public class EdittextFragment extends BackHandleFragment {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_edittext, container, false);
return view;
}
@Override
public boolean onBackPressed() {
Toast.makeText(getActivity(), "按了返回鍵", Toast.LENGTH_SHORT).show();
return true;//由於這裏return true 因此不會返回上一個頁面,方便我截圖
}
}
複製代碼
alter爲AlertDialog類型對象,注意要在alter.show()語句前加入
alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
複製代碼
而後在AndroidManifest.xml中加入權限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"></uses-permission>
複製代碼
優勢:
缺點:
最明顯的建立一個Activity須要配合建立多個接口類和實現類,每一個操做都須要經過接口回調的方式進行,雖然邏輯清晰代碼,同時也形成了類的增多和代碼量的加大。
官方文檔明確指出,SharedPreferences不支持多線程,進程也是不安全的 若是想要實現線程安全需從新實現其接口,以下:
private static final class SharedPreferencesImpl implements SharedPreferences {
...
public String getString(String key, String defValue) {
synchronized (this) {
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
...
public final class EditorImpl implements Editor {
public Editor putString(String key, String value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
...
}
}
複製代碼
PathClassLoader和DexClassLoader都是繼承與BaseDexClassLoader,BaseDexClassLoader繼承與ClassLoader。DexClassLoader:可以加載自定義的jar/apk/dex PathClassLoader:只能加載系統中已經安裝過的apk 因此Android系統默認的類加載器爲PathClassLoader,而DexClassLoader能夠像JVM的ClassLoader同樣提供動態加載。