主頁 > 移動端開發 > Android 開發藝術探索 看不懂對著書敲慢慢理解,設計模式之禪總結,平時記錄的筆記,3w多次字防止丟失,留存。

Android 開發藝術探索 看不懂對著書敲慢慢理解,設計模式之禪總結,平時記錄的筆記,3w多次字防止丟失,留存。

2020-11-22 12:13:35 移動端開發

知識點1:

1.子執行緒為什么不允許訪問ui因為android中的ui控制元件不是執行緒安全的,

2.為什么不給Ui加上鎖的機制,第一點 會讓ui訪問的邏輯變得復雜,其次降低ui訪問的效率,

3.List轉化成String[]

		public static String[] toStringArray(List<String> strList) {
			String[] array = new String[strList.size()];
			strList.toArray(array);
			return array;
			}

4.webview中的常用方法

(1)goBack():后退,
(2)goForward():前進,
(3)loadUrl(String url):加載指定URL對應的網頁
(4)boolean zoomIn():放大網頁,
(5)boolean zoomOut():縮小網頁,

5.解決ScrollView嵌套listview只顯示一行item得問題
重寫listview

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{    heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

6.activity切換時的overridePendingTransition影片效果:

第一個引數為第一個Activity離開時的影片,第二引數為所進入的Activity的影片效果
淡入淡出效果
Activity.overridePendingTransition(R.anim.fade, R.anim.hold);
放大淡出效果
overridePendingTransition(R.anim.my_scale_action,R.anim.my_alpha_action);
轉動淡出效果
overridePendingTransition(R.anim.scale_rotate,R.anim.my_alpha_action);
轉動淡出效果
overridePendingTransition(R.anim.scale_translate_rotate,R.anim.my_alpha_action);
左上角展開淡出效果
overridePendingTransition(R.anim.scale_translate,R.anim.my_alpha_action);
壓縮變小淡出效果
overridePendingTransition(R.anim.hyperspace_in,R.anim.hyperspace_out);
右往左推出效果
overridePendingTransition(R.anim.push_left_in,R.anim.push_left_out);
下往上推出效果
overridePendingTransition(R.anim.push_up_in,R.anim.push_up_out);
左右交錯效果
overridePendingTransition(R.anim.slide_left,R.anim.slide_right);
放大淡出效果
overridePendingTransition(R.anim.wave_scale,R.anim.my_alpha_action);
縮小效果
overridePendingTransition(R.anim.zoom_enter,R.anim.zoom_exit);
上下交錯效果
overridePendingTransition(R.anim.slide_up_in,R.anim.slide_down_out);

7.app的版本時在build中的defaultConfig中的versionName中設定的,

知識點2:

1.獲取手機螢屏的密度

	float xdpi = getResources().getDisplayMetrics().xdpi;
	float ydpi = getResources().getDisplayMetrics().ydpi;

2.androidstudio中全域替換字串,在包的下面右邊replace in path

3.廣播在不通的包下面,接受要加上Intent.addFlags(0x01000000);

4.通過jks檔案查看簽名

keytool -list -v -keystore D:\companyjks\aaaa-storepass 123456

5.intent在頁面之間跳轉時只可以帶上keyvalue形式的資料 ,可以把物件繼承Serializablera(序列化)然后就可以直接講物件傳輸過去,

6.Onstart和onstop是從Activity是否可見的角度去分析
OnResume和onPauser是從activity是否在前臺的角度去分析的

7.adb shell ps 或者adb shell ps|grep 包名 可以查看當前的行程資訊

行程名以:開頭的行程屬于私有行程 不以:開頭的行程屬于全域行程,其他應用可以通過	ShareUID的方式可以和他跑在同一個行程中,

8.序列化時應該手動指定serialVersionUID的值,如過不指定,系統會自動生成hashcode值,這樣的話如果增加或者洗掉了變數,則反序列化會失敗,指定了serialVersionUID的值之后就不會,但是類的結構大聲改變之后,就算指定了serialVersionUID的值也不會反序列化成功,

9.影片:

(1)ScrollTo/ScrollBy:操作簡單,適合對View內容的話哦的那個
(2)影片:操作簡單,主要適用于沒有互動的view和實作復雜的影片效果
(3)改變布局引數:操作稍微復雜,適用于有互動的view,

10.直接用資料線去安裝apk

adb install -r 應用程式.Apk,

11.使用adb命令去啟動一個Activity

Adb shell am start -n  com.adam.collection.test/com.adam.collection.test.ui.MainActivity,

12.在View提供的方法當中:

getTop():獲取到的是View自身的頂邊到父布局頂邊的距離
getLeft():獲取到的是View自身的左邊到父布局左邊的距離
getRight():獲取到的是View自身的右邊邊到父布局左邊的距離
getBotton():獲取到的是View自身的底邊到父布局頂邊的距離

在MotionEvient提供的方法中:

getX():獲取點擊事件距離控制元件左邊的距離,即視圖坐標
getY():獲取點擊事件距離控制元件頂邊的距離,即視圖坐標
getRawX():獲取點擊事件距離整個螢屏左邊的距離,即絕對坐標
getRawY():獲取點擊事件距離整個螢屏頂邊的距離,即絕對坐標

13:Androuid布局優化:

1.避免重繪ui,性能浪費
2.優化布局層級,view樹不要太高,層級嵌套不要太深
3.使用include復用公共的布局
4.使用viewStub 區別于Gone是他在初始化時不會去加載
5.hierarchyviewer工具去快速找到樹中冗余的布局
6LinearLayout比RelativeLayout性能好,RelativeLayout的功能比較復雜,會花費更多的cpu時間,但是在嵌套的時候,RelativeLayout的性能優于LinearLayout,
7.merge標簽 在嵌套時,如果include里面的布局跟外部一樣,里面的布局可以用merge來替代,來去除多余重復的層級
8.在自定義繪制view的時候  在onDraw方法中避免執行大量的耗時操作,會造成頁面的卡頓,
9.不要用靜態變數去參考當前的 context會造成Activity泄露,記憶體無法釋放,
10.Activity的物件被單例模式所持有,而單例模式的特點是其生命周期和Applicatio保持一致,因此Activity物件無法被及時釋放,
11.屬性影片:從Android3.0開始,Google提供了屬性影片,其中有一類無限回圈的影片,如果在Activity中播放此類影片且沒有在onDestory中停止影片,那么影片會一直播放下去,盡管已經無法在界面上看到影片效果了,并且這個時候Activity的View會被影片持有,而View又持有了Activity,最終Activity無法釋放,下面的影片是無限影片,會泄露當前Activity,解決方法是在Activity的onDestory中嗲用animator.cancel()來停止影片,

14:Android記憶體優化

Bitmap優化:使用適合解析度和大小的圖片,及時回收記憶體,使用圖片快取,

代碼優化:

1.對常量使用static修飾符
2.使用靜態方法,靜態方法會比普通方法提高15%左右的訪問速度
3.減少不必要的成員變數,這點在Androidlint工具上已經集成檢測了,如果一個變數可以定義為區域變數,則會建議你不要定義為成員變數,
4.減少不必要的物件,使用基礎型別會比使用物件更加節省資源,同時更應該避免頻繁創建短作用域的變數
5.盡量不要使用列舉,少用迭代器,列舉占用的記憶體空間要比整型大
6.對Cursor,ReCeiver,Sensor,File等物件,要非常注意對他們的創建,回收與注冊,解注冊
7.避免使用ioc框架,ioc通常使用注解,反射來進行實作,雖然現在java對反射的效率已經進行了很好的優化,但大量使用反射依然會帶來性能的下降
8.使用RenderScript,OpenGl來進行非常復雜的繪圖操作
9.使用SurfaceView來代替view進行大量 頻繁的繪圖操作
//SurfaceView適合頻繁更新,被動更新的試圖,像股票曲線
//view適合自己主動去更新視圖
10.盡量使用視圖快取,而不是每次都執行inflate方法決議視圖,
11.常量請使用static final來修飾
15:記憶體泄露分析工具MAT  

設計模式知識點:

單例模式:
1.在sInstance=new Singleton時有三條原子操作:
(1)給Singleton的實力分配記憶體,
(2)呼叫Singleton()的建構式,初始化成員欄位
(3)講sInstance物件指向分配的記憶體空間(此時sInstance就不是null了),
由于編譯器亂序執行 可能 1,3,2的順序去執行 呼叫3之后被拿走使用了就會出錯
還很難復現,可以給sInstance加上一個Volatile關鍵字,雖然會影響性能,但是程式的正確性也很重要,
單例模式一般沒有介面,擴展很困難,若要擴展,除了修改代碼基本上沒有第二種途徑可以實作,單例物件如何持有Context,那么很容易引發記憶體泄漏,此時需要注意傳遞給單例物件的Context最好時Application Context 參考使用Activity的Context話會造成Activity無法釋放,
工廠方法模式:
產品實作類實作抽象產品方法,工廠實作類實作工廠抽象方法,工廠實作鏟平的生產
抽象工廠模式:
在工廠方法模式的基礎上添加介面,把方法都放在抽象方法中,由子類去實作,他的優點時高層不需要去關心產品時怎么實作的,只需要找到工廠類讓他去創建就可以了,(女媧造人)
模板方法模式
在抽象類中有基本方法跟模板方法,模板方式實作了對基本方法的調度,完成固定的邏輯,上層只需要去呼叫模板方法就可以了 (悍馬模型)
建造者模式
將一個復雜物件的構建與他的表示分離,使得同樣的構建程序可以創建不同的表示(造各種不同功能的車型 能啟動能停止 能啟動能鳴笛) 創建一個產品類,創建一個抽象建造者,具體建造者實作抽象建造者,會實作產品里面的邏輯 并且有個回傳產品的方法,最后導演類 產生了一個建造者拿到里面回傳的產品,
建造者適合相同的方法,不同的執行順序,產生不同的事件結果,
代理模式:
讓其他物件提供一種代理以控制對這個物件的訪問(代練著給張三打游戲,執行者時代練著,升級的時張三)代理者拿著游戲者去執行操作,
普通代理時有一個代理來執行用戶的操作,方法不知道代理后面是誰,而強制代理時方法通過用戶來找找到代理,這個代理時用戶提供的,
原型模式
淺拷貝是指clone方法拷貝了本物件,對是物件內部的陣列,參考物件等都不拷貝,還是指向原生物件的內部元素地址,所以拷貝出來的修改的是原型中的資料,深拷貝對私有的類變數進行獨立的拷貝,比如說物件內部的陣列,所以在拷貝出來物件修改的是拷貝出來的陣列,對原型中的陣列資料并沒有改變,
總結:淺拷貝只拷貝物件,操作的還是原來原型中的資料,深拷貝是把所有的都拷貝一份,是完全獨立出來的,
中介者模式:
用一個中介物件封裝一系列的物件互動,中介者使各物件不需要顯示地相互作用,從而使其耦合松散,而且可以獨立地改變他們之間的互動,在各個不同的類中間形成一個中介類,其他類中需要跟中介類互動就可以,減少類與類之間的耦合程度,
優點:減少類間的依賴,把原來的一對多的依賴變成了一對一的依賴,同事類只依賴中介者,減少依賴,降低耦合,
缺點:會導致中介者會膨脹得很大,而且邏輯復雜,同事類越多,中介者得邏輯就越復雜,
命令模式:
命令模式是一個高內聚得模式,將一個請求封裝成一個物件,有一個接收者角色,一個命令角色,一個呼叫者角色,命令角色給個命令給呼叫者,呼叫者根據命令讓接收者去完成對應得功能,
優點:呼叫者和接收者之間沒有依賴關系,呼叫者實作功能只需要呼叫Command抽象類得execute方法就可以,不需要了解接收者是誰,
缺點:回到直Command的類膨脹問題,
責任鏈模式:
使多個物件都有機會處理請求,從而避免了請求的發送者和接收者之間的耦合關系,將這些物件連成一條鏈,并沿著這條鏈傳遞該請求,知道有物件處理他為止(三從四德)
優點:將請求和處理分開,請求者可以不用知道是誰處理的,處理者可以不用知道請求的全貌,兩者解耦,提高系統的靈活性,
缺點:性能問題,每個請求都要從鏈頭便利到鏈尾,特別是在鏈比較長的時候,性能是一個非常大的問題,二是除錯不方便,環節比較多的時候,由于采用了類似遞回的方式,除錯的時候邏輯可能比較復雜,
裝飾模式:
裝飾模式是動態地給一個物件添加一些額外的職責,就增加功能來說,裝飾及模式相比生成子類更為靈活(成績單)
優點:裝飾類和被裝飾類不會互相耦合,低耦合,裝飾模式是繼承關系的一個替代方案,裝飾模式可以動態地擴展一個實作類的功能,類似于代理模式,擴展性非常好,
缺點:多層的裝飾是很復雜的,如果最里層的裝飾出現了問題,需要一層一層你排查,作業量會很大,系統的復雜度會升高,
使用環境:需要擴展一個類的功能額,或給一個類增加附加功能,
需要動態地給一個物件增加功能,這些功能可以再動態地撤銷
需要為一批的兄弟類進行改裝或加裝功能,首選裝飾模式,
策略模式:
定義一組演算法,將每個演算法都封裝起來,并且使他們之間可以互換,(諸葛亮三個錦囊)
優點:策略模式本身定義的,只要實作抽象策略,他就成為策略家族的一個成員,通過封裝角色對其進行封裝,保障對外可以自由切換,
策略家族對外提供的訪問介面就使封裝類,簡化了操作,同事避免了有條件陳述句判斷
擴展性好,符合OCP原則,(開閉原則)
缺點:每個策略都是一個類,服用的可能性小,類數量增多,
上層模塊必須知道有哪些策略,然后才能決定使用哪一個,這個與迪米特法則相違背,
類膨脹,對外暴露,
配接器模式:
將一個類的介面變成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法再一起作業的兩個類能夠在一起作業,
優點:讓兩個沒有任何關系的類在一起運行,增加了類的透明性,具體的實作都委托給了源角色,而這些對高層次的模塊使透明的,也是它不需要關心的,提高了類的復用度,靈活性特別好,
配接器模式使為了擴張而使用的,不應該在設計的時候考慮用它,
迭代器模式:
迭代器模式目前是一個沒落的模式,基本上沒人會單獨寫一個迭代器,除非是產品性質的開發,(它提供一種方法訪問一個容器物件中各個元素,而又不需暴露該物件的內部細節)
計量不要自己寫迭代器模式,使用java提供的Iterator一般就能滿足你的需求,
組合模式:
將物件組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個物件和組合物件的使用具有一致性,
把一組相似的物件當作一個單一的物件,比如說一個部門 是一個物件,這個物件里面會有很多相似的物件,
觀察者模式:
定義物件間一種一對多的依賴關系,使得每當一個物件改變狀態,則所有依賴與他的物件都會得到通知并被自動更新,(李斯監控韓非子)
優點:觀察者和被觀察者之間是抽象耦合,不管增加觀察者還是被觀察者都非常容易擴展,而且在java中都已經實作的抽象層級的定義,在系統擴展方面更是得心應手,建立一套觸發機制,觀察者可以完美地實作這里的鏈條形式,
缺點:一個被觀察者,多個觀察者,開發效率和運行效率問題需要考慮,在java中訊息的通知默認是順序執行,一個觀察者卡死,會影響整體的執行效率,可以采用異步,
門面模式:
要求一個子系統的外部與其內部的通信必須通過一個統一的物件進行,門面模式提供一個高層次的介面,使得子系統更易于使用,
門面模式注重”統一的物件“也就是提供一個訪問一個子系統的介面,除了這個介面不允許有任何訪問子系統的行為發生,
把子系統屏蔽掉,只暴露一個門面介面,
優點:外界只依賴門面物件,與子系統無關,提高了靈活性,安全性,
缺點:不符合開閉原則,對修改關閉,對擴展開放,
備忘錄模式:
在不破壞封裝性的前提下,捕獲一個物件的內部狀態,并在該物件之外保存這個狀態,這樣以后就可將該物件回復到原先保存的狀態,
訪問者模式:
封裝一些作用于某些資料結構中的個元素的操作,它可以在不改變資料結構的前提下定義作用于這些元素的新的操作,
狀態模式:
當一個物件內在狀態改變時允許其改變行為,這個物件看起來像改變了其類,狀態模式的信心時分裝,狀態的變更引起了行為的變更,
優點:避免了過多的switch…case或者if…else陳述句的使用,避免勞累程式的復雜性,提高系統的可維護性
體現了開閉原則和單一職責原則,每個狀態都是一個子類,你要增加狀態就要增加子類,修改狀態只修改一個子類,封裝性好,符合門面模式
缺點:子類太多造成類膨脹,
解釋器模式:
給定一門語言,定義它得文法得一種表示,并定義一個解釋器,該解釋器使用該表示來解釋語言中的句子,
優點:解釋器是一個簡單語法分析工具,它最顯著的優點就是擴展性,修改語法規則只要修改相應的非終結符運算式就可以了,若擴展語法,則只要增加非終結符類就可以了,
缺點:解釋器模式會引起類膨脹,解釋器模式采用遞回呼叫方法,還有一個效率問題,
享元模式:
享元模式是池技術的重要實作方式,使用共享物件可有效地支持大量的細粒度的物件,沒有的物件直接直接創建,有的物件直接拿出來用 例子中使用的是Hashmap,避免過多的物件造成記憶體溢位,
優點:可以減少應用程式創建的物件,降低程式記憶體占用,增強程式的性能,
缺點:系統的復雜性提高,需要分離出外部狀態和內部狀態,而且外部狀態具有固化特性,不應該隨內部狀態改變而改變,否則導致系統的邏輯混亂,
橋梁模式:
將抽象和實作解耦,使得兩者可以獨立地變化,生產的工廠new 一個不同的類就可以生產不同的東西,
抽象角色參考實作角色,抽象角色的部分實作是由實作角色完成的,
優點:抽象和實作分離,解決繼承的缺點而提出的設計模式,實作不受抽象的約束,不用再系結再一個固定的抽象層次上,
優秀的擴充能力,對外暴露的介面層允許增加實作,增加抽象
客戶不用關心細節的實作,它已經由抽象層通過聚合關系完成了封裝,

深入理解java虛擬機

1.所有的物件實體以及陣列都應當在堆上分配,

2.方法區與java堆一樣,是各個執行緒共享的記憶體區域,他用于存盤已被虛擬機加載的型別資訊、常量、靜態變數、即時編譯器編譯后的代碼快取等資料,

3.運行時常量池時方法區的一部分,class檔案中有一項資訊時常量池表,用于存放編譯期生成的各種字面量與符號參考,這部分講在類加載后存放到方法區的運行時長常量池,

4.參考計數演算法,通過計數器來判斷物件是否失活,主流的java虛擬機里面沒有選用應用計數演算法來管理記憶體
原因:計數演算法有很多例外情況要考慮,必須要配合大量額外處理才能保證正確地作業,譬如單純的參考計數就很難解決物件之間相互回圈參考的問題,

public class ReferenceCountingGC { 
public Object instance = null; 
private static final int _1MB = 1024 * 1024; 
/**
* 這個成員屬性的唯一意義就是占點記憶體,以便能在GC日志中看清楚是否有回收過 
*/ 
private byte[] bigSize = new byte[2 * _1MB]; 
public static void testGC() { 
ReferenceCountingGC objA = new ReferenceCountingGC(); 
ReferenceCountingGC objB = new ReferenceCountingGC(); 
objA.instance = objB; 
objB.instance = objA; 
objA = null; 
objB = null; 
// 假設在這行發生GC,objA和objB是否能被回收? 
System.gc(); 
} 
} 

可達性分析演算法

在主流的Java虛擬機中基本上采用的都是可達性分析演算法來判斷物件的存活,
這個演算法是通過一些“GC Roots”的物件作為起點,從這些節點開始向下搜索,
搜索走過的路徑稱之為參考鏈,當一個物件到“GC Roots”沒有任何參考鏈的時候,
也就是說這個物件到“GC Roots”不可達,證明這個物件是不能再被參考了
,因此就會被垃圾回收器判斷為可以被回收的物件,在虛擬機中可以作為“GC Roots”的物件主要有四種:
       (1)虛擬機堆疊參考的物件
       (3)方法區常量參考的物件
       (4)本地方法堆疊中參考的物件
(有參考之后 GC Roots下面就會有參考鏈)

5.參考分為強參考,軟參考,弱參考和虛參考四種,這4種參考強度依次逐漸減弱,

強參考不會被回收
軟參考在記憶體不夠的時候就會被回收
弱參考只能生存到下一次垃圾收集發生為止無論記憶體是否足夠,都會回收掉弱參考關聯的物件
為一個物件設定虛參考的目的是為了能在這個物件被收集器回收時收到一個系統通知,

6.任何一個物件的finalize()方法只會被系統自動呼叫一次,

7.方法區中的回收
(1)回收常量,只要這個常量并沒有被任何地方參考,就可以回收
(2)回收類

①該類中所有的實體都已經被回收,也就是java堆中不存在該類及其任何派生子類的實體,
②加載該類的類加載器已經被回收,這個條件出給是經過精心設計的可替換類加載器的場景,
如osgi,jsp的重加載,否則通常是很難達成的
③該類對應得java.lang.Class物件沒有在任何地方被參考,無法在任何地方通過反射訪問該類得方法,

Android開發藝術探索

Activity:

1.Activity中的onStart和onStop是從Activity是否可見的角度來回呼的
,而onResume和onPause是從Activity是否位于前臺這個角度來回呼的,
2.在新Activity啟動之前,堆疊頂的Activity需要先onPause,新Activity才能啟動,
3.我們不應該在onPause中做重量級操作,因為下一個Activity要在onPause之后才能oncreate,
onPause太耗時的話,會影響下一個Activity出來的體驗,可以在onStop中做一些資料處理,
4.onSaveInstanceState來保存當前Activity的狀態 這個方法在onStop之前,和onPause沒有關系,
可以在之前,可能在之后,(Activity例外終止),

Bundle是實作Cloneable介面來保存資料,
恢復的是用戶輸入的資料,ListView滾動的位置等,
5.Activity的優先級, 前臺》可見非前臺(Activity彈出對話框)》后臺
6.android:configChanges="orientation|screenSize"
7.問題:為什么設定了android:configChanges="orientation|screenSize"之后旋轉后數			
據依然存在????????
8.在singleTask模式下,多次啟動一個Activity不會生成多個Activity,只是會暫停一下
,onPause -->onNewIntent-->onResume 回圈執行這三個方法,

IPC機制:
1.Messenger通信:
2.AIDL:

View:

1.View中得四個屬性,top\left\right\bottom都是相對于父容器來說得,
2.從Android3.0開始,View增加了額外得幾個引數x,y,translationX和translationY,
其中x,y是view左上角得坐標,而translationX和translationY是view左上角相對于父容器得偏移量,
3.getX/getY回傳得是相當于當前View左上角得x和y坐標
,而getRawX/getRawY但會得是相對于手機螢屏左上角得x和y坐標,
4.Touchslop 是系統能識別出得被認為是滑動得最小距離,
	ViewConfiguration.get(getContext()).getScaledTouchSlop(),
5.監聽滑動相關的,建以在onTouchEvent中實作,如果是監聽雙擊這種行為的話
,那么就使用GestureDetector
6.總結:scrollTo/scrollBy:操作簡單哪,適合對view內容的滑動
		  影片:操作簡單,主要適用于沒有互動的view和實作復雜的影片效果,
		  改變布局引數:操作稍微復雜,適用于有互動的view.

四大組件的作業程序:

1.停止一個Service要靈活采用StopSerrvice和unBindService這兩個方法才能完全停止一個Service組件,
2.FLAG_INCLUDE_STOPPED_PACKAGERS表示已經停止的應用,這時廣播會發送給已經停止的應用,
	FLAG_EXCLUDE_STOPPED_PACKAGES表示不包含已經停止的應用,整個時候廣播不會發送給已經停止的應用,
從Android3.1開始,系統為所有廣播默認添加FLAG_EXCLUDE_STOPPED_PACKAGES屬性
3.補充2,一個應用處于停止狀態分為兩種情況,第一種是應用安裝后未運行,
第二種是應用被手動或者其他應用強停了,Android3.1中廣播的這個特性同樣會影響開機廣播,
從Android3.1開始,處于停止狀態的應用同樣無法接受到開機廣播,而在Android3.1之前,
處于停止狀態的應用是可以收到開機廣播的,

ContentProvider:

1.ContentProvider是一種內容共享型組件,通過Binder向其他應用提供資料,
在ContentProvider所在的行程啟動時,ContentProvider會同時啟動并發布到AMS中,需要注意的是,
這首歌時候ContentProveder的onCreate要先于Application的onCreate而執行,
這是四大組件中的一個少有的現象,原因:原始碼中就是這樣的,
2.當一個應用啟動時,入口方法為ActivityThread的main方法,main方法是一個靜態方法,
在main方法中創建ActivityThread的實體并創建主執行緒的訊息佇列,
然后在ActivityThread的attach方法中遠程呼叫AMS的attachApplication方法并將ApplicationThread物件提供給AMS,ApplicationThread是一個Binder物件,
它的Binder介面時IApplicationThread,它主要用于ActivityThread和AMS之間的通信,
在AMS的attachApplication方法中,會呼叫ApplicAtionThread的bindApplication方法
,這個程序同樣是跨行程完成的,bindApplication的邏輯會經過ActivityThread中
mH Handler切換到ActivityThread中執行,具體的方法是handleBindApplication.
在handleBindApplication方法中,ActivityThread會創建Application物件并加載ContentProvider,
	注意:ActivityThead會先加載ContentProvider,然后再呼叫ApplicAtion的onCreate方法,
3.ContentProvider啟動后,外界就可以通過它所提供的增刪改查這四個介面來操作ContentProvider中的資料源,
即insert,delete,update和query四個方法,這四個方法都是通過Binder來呼叫的,
外界無法直接訪問ContentProvider,它只能通過AMS根據Uri來獲取對應的
ContentProvider的Binder介面IcontentProvider,然后再通過IConentProvider來訪問COn'tentPrOvider中的資料源, 
4.一般來說ContentProvider都應該是單實體的,Contentprovider到底是不是單實體,
這是由它的android:multiprocess屬性來決定的,當android:multiprocess為false時,
ContentProvider為單實體,這也是默認值:當android:multiprocess為true時,
ContentProvider為多實體,這個時候再每個呼叫者的行程中都存在一個ContentProvider物件
,由于在實際的開發中,并未發現多實體的ContentProvider的具體使用場景,
官方檔案中的解釋時這樣可以避免行程間通信的開銷,但是這在實際開發中仍然缺少使用價值,
因此,我們可以簡單地認為ContentProvider都時單實體的,
5.ContentProvider的啟動程序:訪問ContentProvider需要通過ContentResolver
,ContentResolver是一個抽象類,通過Context的getContentResolver方法獲取實際上是
ApplicAtionContentResolver物件,ApplicAtionContenResolver類繼承了ContentResolver
并實作了ContentResolver的抽象方法,當ContentProvider所在的行程未啟動時,
第一次訪問它時機會觸發ContentProvider的創建,當然這也伴隨著ContentProvideru所在行程的啟動,
通過ContentProvider的四個方法的任何一個都可以觸發ContentProvider的啟動程序,
這里選擇query方法,
6.ContentProvider的query方法中,首先會去獲得IContentProvider物件
,不管是通過acquireUnstableProvider方法還是直接通過acquireProvider方法,
他們的本質都時一樣的,最終都時通過acquireProvider方法來獲取ContentProvider
下面是ApplicationContentResolver的acquireProvider方法的具體實作,
首先會從ActivtyThread中查找是否已經存在目標Co'tentProvider了,如果存在就直接回傳,
ActivityThread中通過mProviderMap來存盤已經啟動的ContentProvider物件,
7.如果目前ContentProvider沒有啟動,那么就發送一個行程間請求給AMS
讓其啟動目標ContentProvider,最后再通過installProvider方法來修改參考計數,
那么AMS是如何啟動ContentProvider的尼?我們知道,ContentProvider被啟動時會伴隨著行程的啟動
,再Ams中,首先會啟動ContentPrOvider所在的行程,然后再啟動ContentProvider,啟動行程時由Ams的
startProcessLocked方法來完成的,其內部主要是通過Process的start方法來完成一個新行程的啟動,
新行程啟動后其入口方法為ActivityThread的main方法,
8.可以看到,ActivityThread的mai'n方法是一個靜態方法,在它內部首先會
創建ActivityThread的實體并呼叫attach方法來進行一系列初始化,接著就開始進行訊息回圈了,
ActivityThread的attach方法會將ApplicAtionThread物件通過
Ams的attachApplication方法跨行程傳遞給ams,最終ams會完成ContentProvider的創建程序,
9.Ams的attachApplication方法呼叫了attachApplicationLocked方法,
attachApplicationLocked中又呼叫了ApplicAtionThread的bindApplication,這個程序是行程間呼叫
10.ActivityThread的bindApplication會發送一個BIND_APPLICATION型別的訊息給mH
,mH是以一個Handler,它收到訊息后會呼叫ActivityThread的handleBindApplication方法,
11.ActivityThread的handleBindApplication則完成了Application的創建以及ContentProvider的創建,
可以分為如下四個步驟:
(1)創建ContextImpl和Instrumentation,
(2)創建ApplicAtion物件,
(3)啟動當前行程的ContentProvider并呼叫其onCreate方法,
(4)呼叫ApplicAtion的onCreate方法,
12.經過上面的四個步驟,Co'n'tentProvider已經成功啟動,并且其所在行程的Application也已經啟動,
這意味著COntentProvider所在的行程已經完成了整個的啟動程序,然后與其實行程就可以通過AMS來訪問這個
ContentProvider了,拿到了ContentProvider以后,就可以通過Ams來訪問這個ContentProvider了
,拿到了ContentProvider以后,就可以通過與它所提供的介面方法來訪問它了,
需要注意的是,這里的COntentProvider并不是原始的COntentProvider
,而是ContentProvider的Binder型別的物件IcontenProvider
,IContentProvider的具體實作是ContentProviderNative和ContentProvider.Transport
其中ContentProvider.Transpor繼承了ContentProviderNative,
13.這里仍然選擇query方法,首先其它應用會通過AMS獲取到ContentProvider的Binder物件即IContentProvider
,而IContentProvider的實作者實際上是ContentProvider.Transport
,因此其他應用呼叫IcontentProvider的query方法時最侄訓以行程間通信的方式呼叫到ContentProvider.
Transport的query方法,
14.很顯然,Co'n'tentProvider.Transport的query方法呼叫了ContentProvider的query方法
,query方法的執行結果再通過Bindder發回傳給呼叫者,這樣依賴整個呼叫程序就完成了,
除了query方法,insert,delete和update方法也時類似的,

Android的訊息機制:

1.有時候需要在子執行緒中進行耗時的I/O操作,可能時讀取檔案或者訪問網路,
當耗時操作完成以后可能需要在UI上做一些改變,由于Android開發規范的限制,
我們并不能在子執行緒中訪問UI控制元件,否則就會觸發程式例外,
這個時候通過Handler就可以將更新UI的操作切換到主執行緒中執行,因此,
本質上來說,Handler并不是專門用于更新UI的,它只是常被開發者用來更新UI,

2.Andorid的訊息機制主要是指Handler的運行機制,Handler的運行需要底層的MessageQueue和Looper的支撐,
MessageQueue的中文翻譯是訊息佇列,顧名思義,它的內部存盤了一組訊息,
以佇列的形式對外提供插入和洗掉的作業,雖然叫訊息佇列,
但是它的內部存盤結構并不是真正的佇列,而是采用單鏈表的資料結構來存盤訊息佇列
Looper的中文翻譯為回圈,在這里可以理解為訊息回圈,
由于MessageQueue只是一個訊息得存盤單元,它不能去處理訊息,
而Looper就填補了這個功能,Looper會以無線回圈得形式去查看是否有新訊息
,如果有得話就處理訊息,否則就一直等著,
Looper中還有一個特殊得概念,那就是ThreadLocal,ThreadLocal并不是執行緒,
它的作用是可以在每個執行緒中存盤資料,我們知道Handler創建的時候會采用
當前執行緒的Looper來構造訊息回圈系統,那么Handler內部如何獲取到當前執行緒的Looper尼?
這就要使用ThreadLocal了,ThreadLocal并不是執行緒可以在不同的執行緒中互不干擾地存盤并提供資料,
通過ThreadLocal可以輕松獲取每個執行緒的Looper,當然需要注意的是,執行緒是默認沒有Looper的,
如果需要使用Handler就必須為執行緒創建Looper,我們經常提到的主執行緒,也叫UI執行緒,它就是
ActivityThread,ActivityThread被創建時就會初始化Looper,這也是在主執行緒中默認可以使用Handler的原因,

3.Android的訊息機制主要是指Handler的運行機制預計Handler所附帶的MesssageQueue和Looper的作業程序,
這三者實際上是一個整體,只不過我們在開發程序中比較多地接觸到了Handler而已,
Handler的主要功能是將一個任務切換到某個指定的執行緒中去執行,
那么Andorid為什么要提供這個功能尼?或者說Andorid為什么需要提供在某個具體的執行緒中執行任務這種功能尼?
這是因為Android規定訪問UI只能在主執行緒中進行,如果在子執行緒中訪問UI,那么程式就會拋出例外,
ViewRootImpL對UI操作做了驗證,這個驗證作業是由ViewRootImpl的checkThread方法來完成的,

4.針對checkThread方法中拋出的例外資訊,由于這一點的限制,
導致必須在主執行緒中訪問ui,但是Android又建以不要在主執行緒中進行耗時操作
,否則會導致程式無法回應即ANR,考慮這一種情況,加入我們需要從服務端拉取一些資訊并將其顯示在UI上,
這個時候必須在子執行緒中進行拉取作業,拉取完畢后又不能在子執行緒中直接訪問UI
,如果沒有Handler,那么我們的卻沒有辦法將訪問UI的作業切換到主執行緒中去執行,
因此,系統之所以提供Handler,主要的原因是為了解決在子執行緒中無法訪問UI的矛盾,

5.系統為什么不允許在子執行緒中訪問UI尼?這是因為Android的UI控制元件不是執行緒安全的
,如果在多執行緒中并發訪問可能會導致UI控制元件處于不可預期的狀態,
那為什么系統不對UI控制元件的訪問加上鎖機制尼?缺點有兩個:首先加上鎖機制會讓UI訪問的邏輯變得復雜,
其次鎖機制會降低UI訪問的效率,因為鎖機制會阻塞某些執行緒的執行,鑒于這兩個缺點,
最簡單且搞笑的方法就是采用單執行緒模型來處理UI操作,對于開發者來說也不是很麻煩,
只是需要通過Handler切換一下UI訪問的執行執行緒即可,

6.Handler創建時會采用當前執行緒的Looper來構建內部的訊息回圈系統,
如果當前執行緒沒有Looper,那么就會報錯,如何解決這個問題,只需要為當前執行緒創建Looper即可,
或者在一個有Looper的執行緒中創建Handler,

7.Handler創建完畢后,這個時候其內部的Looper以及MessageQueue就可以和HandLer一起協同作業了
,然后通過Handler的post方法將一個Runnable投遞到Handler內部的Looper中處理,
也可以通過Handler的send方法發送一個訊息,這個訊息同樣會在Looper中處理,
其實post方法最終也是通過Send方法來完成的,當Handler的send方法被呼叫時,
它會呼叫MessageQueue的enqueueMessage方法將這個訊息放入訊息佇列中,
然后Looper發現有新訊息到來的,機會處理這個訊息,最終訊息中的Runnable或者Handler的handleMessage方法
就會被呼叫,注意Looper是運行在創建Handler所在的執行緒中的,
這樣一來Handler中的業務邏輯就被切換到創建Handler所在的執行緒中去執行了,
(handler.post的本質還是在Handler.send中去實作的),

8.當某些資料是以執行緒為作用域并且不同執行緒具有不同的資料副本的時候,
就可以考慮采用ThreadLocal,比如說對于Handler來說,它需要獲取當親前執行緒的Looper,
很顯然Looper的作用域解釋執行緒并且不同的執行緒具有不同的Looper,這個時候
通過ThreadLocal就可以輕松實作Looper在執行緒中的存取,如果不采用ThreadLocal,
那么系統就必須提供一個全域的哈希表供handler查找指定執行緒的Looper,
這樣一來就必須提供一個類似于LooperManager的類了,但是系統并沒有這么做而是選擇了ThreadLocal,
這就是ThreadLocal的好處,

9.ThreadLocal另一個使用場景是復雜邏輯下的物件傳遞,比如監聽器的傳遞,
有些時候一個執行緒中的任務過于復雜,這可能表現為函式呼叫堆疊比較深以及代碼入口的多樣性
,在這種情況下,我們又徐婭監聽器能夠貫穿整個執行緒的而執行程序,整個時候可以怎么做尼
?其實這時就可以采用ThreadLocal,才哦那個ThreadLocal可以讓監聽器作為i執行緒內的全域物件而存在,
在執行緒內部只要通過get方法就可以獲取到監聽器,如果不采用ThreadLocal,
那么我們能想到的可以能是如下兩種方法
(1)將監聽器通過引數的形式在函式呼叫堆疊中進行傳遞
(2)將監聽器作為靜態變數供執行緒訪問
第一種方法是當函式呼叫堆疊很深的時候,通過函式引數來傳遞監聽器物件這幾乎是不可接受的,
這會讓程式的設計看起來很糟糕
第二種方法是可以接受的,但是這種狀態時不具有可擴充性的沒比如同時又兩個執行緒在執行,
那么就需要提供兩個靜態的監聽器物件,
采用ThreadLocal,每個監聽器物件都在自己的執行緒內部儲存,

10.從ThreadLocal的set和get方法可以看出,他們所操作的物件都時當前執行緒的localValues物件的tables陣列,
因此在不同的執行緒中訪問同一個ThreadLocal的set和get方法
,他們對ThreadLocal所作的讀/寫操作僅限于各自執行緒的內部
,這就是為什么ThreadLocal可以在多個執行緒中互不干擾地存盤和修改資料
,理解Threadlocal的實作方式有助于理解Looper的作業原理,

11.訊息佇列的作業原理:訊息佇列在Andorid中指的時MessAgeQueue,MessageQueue主要包含兩個操作:
插入和讀取,讀取操作本身會伴隨著洗掉操作,插入和讀取對應的方法分別為enqueueMessage和next,
其中enqueueMessage的作用時往訊息佇列中插入一條訊息,
而next得作用時從訊息佇列中取出一條訊息并將其從訊息佇列中移除
,盡管MessaheQueue叫訊息佇列,但是它得內部實作并不是用的佇列
,實際上它是通過一個單鏈表的資料結構來維護訊息串列,單鏈表在插入和洗掉上比較有優勢,
(1)enqueueMessage的主要操作其實就是單鏈表的插入操作
(2)next方法是一個無限回圈的方法,如果訊息佇列中沒有訊息,那么next方法會一直阻塞在這里,當有新訊息到來時,next方法會回傳這條訊息并將其從單鏈表中移除,

12.Looper的作業原理:Looper在Android的訊息機制中扮演著訊息回圈的角色,
具體來說就是它會不停地從MessageQueue中查看是否有新訊息,如果有新訊息機會立刻處理,
否則就一直阻塞在那里,在他的構造方法中它會創建一個MessageQueue即訊息佇列
,然后將當前執行緒的物件保存起來,

13.Handler的作業需要Looper,沒有Looper的執行緒就會報錯,那么如何為一個執行緒創建Looper尼?
通過Looper.prepare()即可為當前執行緒創建一個Looper,接著通過Looper.loop()來開啟訊息回圈,

14.Looper除了prepare方法外,還提供了prepareMainLooper方法,
這個方法主要是給主執行緒也就是ActivityThread創建Looper使用的,
其本質也是通過prepare方法來實作的,由于主執行緒的Looper比較特殊
,所以Looper提供了一個getMainLooper方法,
通過他可以在任何地方獲取到主執行緒的Looper,Looper也是可以退出的,
looper提供了quit和quitSafely來退出一個Looper,區別:
(1)Quit會直接退出Looper,
(2)quitSafely只是設定一個退出標記,然后把下次佇列中的已有訊息處理完畢后才安全地退出,
Looper退出后,通過Handler發送的訊息會失敗,這個時候Handler的send方法會回傳false,
在子執行緒中,如果手動為其創建了Looper,那么在所有的事情完成以后應該呼叫quit方法來終止訊息回圈,
否則這個子執行緒就會一直處于等待的狀態,而如果退出Looper以后,這個執行緒就會立刻終止,
因此建議不需要的時候終止Looper,
Looper最重要的一個方法是loop方法,只有呼叫了loop后,訊息回圈系統才會真正地起作用,
Looper的loop方法的作業程序也比較好理解,loop方法是一個死回圈,
唯一跳出回圈的方式是MessageQueue的next方法回傳了null,當Looper的quit方法被呼叫時,
Looper就會呼叫MessageQueue的quit或者quitSafely方法來通知訊息佇列退出,
當訊息佇列被標記為退出狀態時,他的next方法就會回傳null,也就是說
,Looper必須退出,否則loop方法就會無限回圈下去,Loop方法會呼叫MessageQueue的next方法來獲取新訊息,
而next是一個阻塞操作,當沒有訊息時,next方法會一直阻塞在那里,
這也導致loop方法一直阻塞在那里,
如果MessageQueye的next方法回傳了新訊息,Looper就會處理這條訊息:
msg.target.dispatchMessage(msg),這里的msg.target是發送這條訊息的Handler物件,
這樣Handler發送的訊息最終又交給它的disopatchMessage方法來處理了,
但是這里不同的是,Handler的dispachMessage方法是在創建Handler時所使用的Looper中執行的
,這樣就成功地將代碼邏輯切換到指定的執行緒中去執行了,

15.Handler的作業原理:Handler的作業主要包含訊息的發送和接受程序,
訊息的發送可以通過post的一系列方法以及send的一系列方法來實作,
post的一系列方法最終是通過send的一系列方法來實作,

16.可以發現,Handler發送訊息的程序僅僅是向訊息佇列中插入了一條訊息,
MessageQueue的next方法就會但會這條訊息給Looper,Looper收到訊息后就開始處理了
,最終訊息由Looper交由Handler處理,即Handler的dispatchMessage方法會被呼叫,
這時Handler就進入了處理訊息的階段,


17.Handler處理訊息的程序如下:首先,檢查Message的callback是否為null,
不為null就通過handleCallback來處理訊息,Message的callback是一個Runnable物件,
實際上就是Handler的post方法所傳遞的Runnable引數,其次,檢查mCallback是否為null,
不為null就呼叫mCallback的handleMessage方法來處理訊息,

18.通過Callback可以采用如下方式來創建Handler物件:Handler handler=new Handler(callback)
,那么Callback的意義是什么尼?可以用來創建一個Handler的實體但并不需要派生Handler的子類,
在日常開發中,創建Handler最常見的方式就是派生一個Handler的子類并重寫其handleMessage方法來處理具體的訊息,
而Callback給我們提供了另外一種使用Handler的方式,當我們不想派生子類時,就可以通過Callback來實作,


19.Handler的一個默認構造方法public Handler(),這個構造方法會呼叫下面的構造方法,
很明顯,如果當前執行緒沒有Looper
的話,就會拋出“Can not create handler inside thread that has not called Looper.prepare()”這個例外,
這也解釋了在沒有Looper的子執行緒中創建Handler會引發程式例外的原因,

20.主執行緒的訊息回圈:Android的主執行緒解釋ActicityThread,主執行緒的入口方法為main,
在main方法中系統會通過Looper.prepareMainLooper()來創建主執行緒的Looper以及MessaeQueue,
并通過Looper.loop()來開啟主執行緒的訊息回圈,
主執行緒的訊息回圈開始了以后,ActivityThread還需要一個Handler來和訊息佇列進行互動,
這個Handler就ActivirtyThread.H,它內部定義了一組訊息型別,主要包含了四大組件的啟動和停止等程序,

21.ActivityThread通過ApplicationThread和AMS進行行程間通信
,AMS以行程間通信的方式完成ActivityThread的請求后回呼ApplicationThread中的Binder方法,
然后AoolicationThread會向H發送訊息,H收到訊息后會講AppLlicationThread中的邏輯及切換到
ActivityThread中去執行,即切換到主執行緒中去執行,這個程序就是主執行緒的訊息回圈模型,

主執行緒的訊息回圈模型

(1)ActivityThread通過ApplicationThread和AMS進行行程間通信,
(2)AMS以行程間通信的方式完成ActivityThread的請求后回呼ApplicationThread中的Binder方法,
(3)然后ApplicationThread會向H發送訊息,
(4)H收到訊息后會將AppLlicationThread中的邏輯及切換到ActivityThread中去執行,即切換到主執行緒中去執行,

Andorid的執行緒和執行緒池:

1.在Andorid中可以扮演執行緒角色的還有很多,比如AsyncTask和IntentService,同時HandlerThread也是一種特殊的執行緒,
盡管AsyncTask,IntentService以及HandlerThread的表現形式都有別于傳統的執行緒,但是他們的本質仍然是傳統的執行緒,
對于AsyncTask來說,它的底層用到了執行緒池,對于IntentService和HandlerThread來說,他們的底層則直接使用了執行緒,


2.主執行緒也叫UI執行緒,主執行緒的作用是運行四大組件以及處理他們和用戶的互動,而子執行緒的作用則是執行耗時任務,比
如網路請求,I/O操作等,從Andorid3.0開始系統要求網路訪問必須在子執行緒中進行,否則網路訪問會失敗并拋出例外,這
樣做是為了避免主執行緒由于被耗時操作所阻塞從而出現ANR現象,


3.AsyncTask:是一種輕量級的異步任務類,它可以在執行緒池中執行后臺任務,然后把執行的進度和最終結果傳遞給主執行緒
中并在主執行緒中更新UI,從實作上來說,AsyncTask封裝了Thread和Handler,通過AsybcTask可以更加方便地執行后臺任務
以及在主執行緒中訪問UI,但是AsyncTask并不適合進行特別耗時的后臺任務,對于特別耗時的任務來說,建議使用執行緒池,

(***1)AsyncTask是一個抽象的泛型類***,
它提供了Params,Progress和Result這三個泛型引數,其中
			a.Params表示引數的型別,
			b.Progress表示后臺任務的執行進度的型別,
			c.Result則表示后臺任務的回傳結果的型別,
	如果AsyncTask確實不需要傳遞具體的引數,那么三個泛型引數可以用Void來代替,
***(2)AsyncTask提供了4個核心的方法,***
			a.onPreExecute()在主執行緒中執行,在異步任務執行之前,此方法會被呼叫,一般可以用于做一=些準備作業,
			
			b.doInBackground(Params params),在執行緒池中執行,此方法用以執行異步任務,params引數表示異步任務的輸
			入引數,在此方法中可以通過publishProgress方法來更新任務的進度,publishProgress方法會呼叫
			onProgressUpdate方法,另外此方法需要回傳計算結果給onPostRxecute方法,
			
			c.onProgressUpdate(Progress..values),在主執行緒中執行,當后臺任務執行進度發生改變時此方法會被呼叫,
			
			d.onPostExecute(Result result),在主執行緒中執行,在異步任務執行之后,此方法會被呼叫,
			其中result引數是后臺		  任務的回傳值,即doInBackground的回傳值,

上面這幾個方法,onPreExecute先執行,接著是doInBackground,最后才是onPostExecute,
除了上述四個方法以外,AsyncTask還提供了onCancelled()方法,它同樣在主執行緒中執行,
當異步任務被取消時,onCancelled()方法會	被呼叫,這個時候onPosTExecute則不會被呼叫,

(3)AsyncTask的使用限制:

		a.AsyncTask的類必須在主執行緒中加載,這就意味著第一次訪問AsyncTask必須發生在主執行緒,當然這個程序在
		Android4.1及以上版本中已經被系統自動完成,在Android5.0的原始碼中,可以查看ActivityThread的main方法,它會
		呼叫AsyncTask的init方法,這就滿足了AsyncTask的類必須在主執行緒中進行加載這個條件了,
		
		b.AsyncTask的物件必須再主執行緒中創建,
		
		c.execute方法必須在UI執行緒呼叫,
		
		d.不要在程式中直接呼叫onPreExecute(),onPostExecute,doInBackground和onProgressUpdate方法,
		
		e.一個AsyncTask物件只能執行一次,即只能呼叫一次execute方法,否則會報運行時例外,
		
		f.在Andorid1.6之前,AsyncTask是串行執行任務的,Andorid1.6的時候AsyncTask開始采用執行緒池里處理并行任
		務,但是從Andorid3.0開始,為了避免AsyncTAsk所帶來的并發錯誤,AsyncTask又采用一個執行緒來串行執行任
		務,盡管如此,在Android3.0以及后續的版本中,我們仍然可以通過AsyncTask的executeOnExecutor方法來執行任
		務,

(4)AsyncTask的作業原理:

首先系統會把AsyncTAsk的Params引數封裝為FutureTask物件,FutureTask是一個并發類,
在這里它充當了Runnable的作用,接著這個FutureTask會交給SerialExecutor的execute方法去處理,
SerialExecutor的execute方法首先會把FutureTask物件插入到任務佇列mTasks中,
如果這個時候沒有正在活動的AsyncTask任務,
那么就會呼叫SerialExecutor的scheDuleNext方法來執行下一個AsyncTaSk任務,
同時當一個AsyncTask任務執行完后,AsyncTAsk會繼續執行其他任務直到所有的任務都被執行為止,
從這一點可以看出,在默認情況下,AsyncTAsk是串行執行的,

(5)AsyncTask中有兩個執行緒池

(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中執行緒池SerialExecutor用于任務
的排隊,而執行緒池THREAD_POOL_EXECUTOR用于真正地執行任務,InternalHandler用于將執行環境從執行緒池切換到主
執行緒,其本質仍然是執行緒的呼叫程序,

4.HandlerThread

	(1)HandlerThread繼承了Thread,他是一種可以使用Handler的Thread,它的實作也很簡單,就是在run方法中通過
	Looper.prepare()來創建訊息佇列,并通過Looper.loop()來開啟訊息回圈,這樣在實際的使用中就允許在HandlerThread
	中創建Handler了,
		(2)從HandlerThread的實作來看,它和普通的Thread有顯著的不同之處,普通Thread主要用于在run方法中執行一個
		耗時任務,而HandlerThread在內部創建了訊息佇列,外界需要通過Handler的訊息方式來通知Handlethread執行一個
		具體的任務,HandlerThread是一個很有用的類,它在Android中的一個具體的使用場景是IntentServIce,由于
		HandlerThread的run方法是一個無限回圈,因此當明確不需要再使用HandlerThread是,可以通過它的quit或者
		quitSafeLy方法來終止執行緒的執行,
quit是直接退出looper;
quit是設定一個標記,當下次訊息佇列執行完之后再安全地退出,

		(3)IntentService是一種特殊的Service,它繼承了Service并且它是一個抽象類,因此必須創建它的子類才能使用
		IntentService,IntentService可用于執行后臺耗時的任務,當任務執行后它會自動停止,同時由于IntentService是服務
		的原因,這導致它的優先級比單純的執行緒要高很多,所以IntentService比較適合執行一些高優先級的后臺任務,因為
		它優先級高不容易被系統殺死,
		(4)當IntentService被第一次啟動時,它的onCreate方法會被呼叫,onCreate方法會創建一個HandlerThread,然后使用
		它的Looper來構造一個Handler物件mServiceHandler,這樣通過mServiceHandler發送的訊息最終都會在
		HandlerThread中執行,從這個角度來看,IntentService也可以用于執行后臺任務,每次啟動IntentService,它的
		onStartCommand方法就會呼叫一次,IntentService在onStartCommand中處理每個后臺 任務的Intent,
		
		(5)IntentService僅僅是通過mServiceHandler發送了一個訊息,這個訊息會在HandlerThead中被處理,
		mServiceHandler收到訊息后,會將Intent都物件傳遞給onHandlerIntent方法去處理,注意這個Intent物件的內容和外
		界的startService(intent)中的intent的內容是完全一致的,通過這個Intent物件即可決議出外界啟動IntentService時所傳
		遞的引數,通過這些引數就可以區分具體的后臺任務,這樣在onHandleIntent方法中就可以對不同的后臺任務做處理
		了,當onHandleIntent方法執行結束后IntentxService會通過stopSelf(int startId)則會等待所有的訊息都處理完畢后才終
		止服務,一般來說stopSelf(int startId)在嘗試停止服務之前會判斷最近啟動服務的次數是否和startId相等,如果相等就
		立刻停止服務,不相等則不停止服務,
		
		(6)IntentService的onHandlerIntent方法是一個抽象方法,它需要我們在子類中實作,它的作用是從Intent引數中區分
		具體的任務并執行這些任務,如果目前只存在一個后臺任務,那么onHandleIntent方法執行完這個任務后,
		stopSele(int satartId)機會直接停止服務,如果目前存在多個后臺任務,那么當onHandlerIntent方法執行完最后一個任
		務時,stopSelf(int startId)才會直接停止服務,另外,由于每執行一個后臺任務就必須啟動一次IntentService,而
		IntentService內部則通過訊息的方式向HandlerThread請求執行任務,Handler中的Looper是順次處理訊息得,這叫意
		味著IntentService也是順序執行后臺任務的,當有多個后臺任務同時存在時們這些后臺任務會按照外界發起的順序排
		隊執行,

5.Android中的執行緒池

(1)優點:

a.重用執行緒池中的執行緒,避免因為執行緒的創建和銷毀所帶來的性能開銷,
b.能有效控制執行緒池的最大并發數,避免大量的執行緒之間因互相搶占系統資源而導致的阻塞現象,
c.能夠對執行緒進行簡單的管理,并提供定時執行以及指定間隔回圈執行的等功能,

(2)ThreadPoolExecutor是執行緒池的真正實作,它的構造方法提供了一系列引數來配置執行緒池,

a.corePoolSize:執行緒的核心執行緒數,默認情況下,核心執行緒會在執行緒池中一直存活,即使他們處于閑置狀態,如果將
ThreadPoolExecutor的allowCoreThreadTimeOut屬性設定為true,那么閑置的核心執行緒在等待新任務到來時會有超時策略,
這個時間間隔由keepAliveTime所指定,當等待時間超過keepAliveTime所指定的時長后,核心執行緒就會被終止,

b.maximumPoolSize:執行緒池所能容納的最大執行緒數,當活動執行緒數達到這個數值后,后續的新任務將會被阻塞,

c.keepAliveTime:非核心執行緒閑置時的超時時長,超過這個時長,非核心執行緒就會被回收,當ThreadPoolExexutor的
allowCoreThreadTimeOut屬性設定為true時,keepAliveTime同樣會作用于核心執行緒,

d.unit:用于指定keepAliveTime引數的時間單位,這是一個列舉,常用的由TimeUnit.MILLISECONDS(毫秒),
TimeUnit.SECOND(秒)以及TimeUnit.MIMUTES(分鐘)等,

e.workqueue:執行緒池中的任務佇列,通過執行緒池的execute方法提交的Runnable物件會存盤在這個引數中,

f.threadFactory:執行緒工廠,為執行緒池提供新執行緒的功能,ThreadFactory時一個介面,它只有一個方法:Thread new 
Thread(Runnable r),
g.除了上面的這些主要引數外,ThreadPoolExecuTor還有一個不常用的引數RejectedExecutionHandler handler,當執行緒池
無法執行新任務時,這可能時由于任務佇列已滿或者時無法成功執行任務,這個時候ThreadPoolExecuTor會呼叫handler的
rejectedExecution方法來通知呼叫者,默認情況下rejectedExecution方法會直接拋出一個RejectedExecutionException,
ThreadPoolExecutor為RejectedExecutionHandler提供了幾個可選值:CallerRunsPolicy,AbortPolicy,DiscardPolicy和
DiscardOldestPolicy,其中AbortPolicy時默認值,它會直接拋出RejectedExecutionException,,

(3)ThreadPoolExecutor執行任務是大致遵循如下規則

a.如果執行緒池中的執行緒數量未達到核心執行緒的數量,那么會直接啟動一個核心執行緒來執行任務,
b.如果執行緒池中的執行緒數量已經達到或者超過核心執行緒的數量,那么任務會被插入到任務佇列中排隊等待執行,
c.如果在步驟b中無法將任務插入到任務佇列中,這往往是由于任務佇列已滿,這個時候如果執行緒數量未達到執行緒池規定的
最大值,那么會立刻啟動一個非核心執行緒來執行任務,
d.如果步驟c中執行緒數量已經達到執行緒池規定的最大值,那么就拒絕執行此任務,ThreadPoolExecutor會呼叫RejectedExecutionHandler的rejectedExecution方法來通知呼叫者,

***(4)AsyncTask對THREAD_POOL_EXECUTOR這個執行緒池進行了配置,配***置后的執行緒池規格如
下:

a.核心執行緒數等于CPU核心數+1;
b.執行緒池的最大執行緒數為CPU核心數的2倍+1;
c.核心執行緒無超時機制,非核心執行緒在閑置時的超時時間為1秒;
d.任務佇列的容量為128,

***(5)Android中最常見的四類具有不同功能特性的執行緒池,他們都直接或間接地通過配置

***ThreadPoolExecutor來實作自己的功能特性:
			a.FixedThreadPool:通過Executors的newFixedThreadPool方法來創建,它是一種執行緒數量固定的執行緒池,當執行緒
			處于空閑狀態時,他們并不會被回收,除非執行緒池被關閉了,當所有的執行緒都處于活動狀態時,新任務都會處于等
			待狀態,直到有執行緒空閑出來,由于FixedThreadPool只有核心執行緒并且這些核心執行緒不會被回收,這意味著它能夠更加快
			速地回應外界的請求,可以發現FixedThreadPool中只有核心執行緒并且核心執行緒沒有超時機制,另外任務佇列也是
			沒有大小限制,
			b.CachedThreadPool:通過Executors的newCachedThreadPool方法來創建,它是一種執行緒數量不定的執行緒池,它
			只有非核心執行緒,并且其最大執行緒數為Integer.MAX_VALUE,由于Integer.MAX_VALUE是一個很大的數,實際上
			就相當于最大執行緒數可以任意大,當執行緒池的執行緒都處于活動狀態時,執行緒池會創建新的執行緒來處理新任務,否則
			就會利用空閑的執行緒來處理新任務,
執行緒池中的空閑執行緒都有超時機制,這個超時時長為60秒,超過60秒閑置執行緒就會被回收,與FixedThreadPool不同的
是,CachedThreadPool的任務佇列其實相當于一個空集合,這將導致任何任務都會立即被執行,因為在這種場景下
SynchronousQueue是無法插入任務的,SynchronousQueue是一個非常特殊的佇列,在很多情況下可以把它簡單理解為一
個無法存盤元素的佇列,從CachedThreadPool的特性來看,這類執行緒池比較適合執行大量的耗時較少的任務,當整個執行緒
池都處于閑置狀態時,執行緒池中的執行緒都會超時而被停止 ,這個時候CachedThreadPool之中實際上是沒有任何執行緒的,

他幾乎是不占用任何系統資源的,
		c.通過Executors的newScheduledThreadPool方法創建,他的核心執行緒數量是固定的,而非核心執行緒數是沒有限制
		的,并且當非核心執行緒限制時會被立即回收,ScheduledThreadPool這類執行緒池主要用于執行定時任務和具有固定周
		期的重復任務,
		d.SingleThreadExecutor:通過Eexcuors的newSingleThreadExecutor方法來創建,這類執行緒池內部只有一個核心線
		程,它確保所有的任務都是在同一個執行緒中按順序執行,SingleThreadExecutor的意義在于統一所有的外界任務到一
		個執行緒中,這使得在這些任務之間不需要處理執行緒同步的問題


Bitmap的加載和Cache

1.Bitmap在Android中指的是一張圖片,可以是png格式也可以是jpg等其他常見的圖片格式,那么如何加載一個圖片尼?
BitmapFactory類提供了四種方法,decodeFile,decldeResource,decodeStream和decodeByteArray,分別用于支持從檔案系
統,資源,輸入流以及位元組陣列中加載出一個Bitmap物件,其中decodeFile和decodeResource又間接呼叫了
decodeStream方法,這四類方法最終是在Andorid的底層實作的,對應著BitmaopFactory類的幾個native方法,

2.如何高效地加載Bitmap尼?其實核心思想也很簡單,那就是采用BitmapFactoty.Options來加載所需尺寸的圖片,這里假
設通過ImageView來顯示圖片,很多時候ImageViews并沒有圖片的原始尺寸那么大,這個時候把整個圖片加載進來后再設
給ImageView,這顯然是沒必要的,因為ImageView并沒有辦法顯示原始的圖片,通過BitmapFactory.Options就可以按一定
的采樣率來加載縮小后的圖片,將縮小后的圖片在ImageView中顯示,這樣就會降低記憶體占用從而在一定程度上避免OOM,
提高了Bitmap加載時的性能,BitmapFactory提供的加載圖片的四類方法都支持BitmapFactory.Options引數,通過他們就可
以很方便地對一個圖片進行采樣縮放,

3.通過BitmapFactory.options來縮放圖片,主要是用到了它的inSampleSize引數,即采樣率,當inSampleSize為1時,采樣
后的圖片大小為圖片的原始大小;當inSampleSize大于1時,比如為2,那么采樣后的圖片其寬、高均為原圖大小的1/2,而
像素數為原圖的1/4,其占有的記憶體大小也為原圖的1/4,拿一張1024*1024像素的圖片來說,假定采用ARGB8888格式存
儲,那么它占有的記憶體為1024*1024*4,即4MB,如果inSampleSize為2,那么采樣后的圖片其記憶體其記憶體占用只有
512*512*4,即1MB,可以發現采樣率inSampleSize必須是大于1的整數圖片才會有縮小的效果,并且采樣率同時作用于寬/
高,這將導致縮放后的圖片大小以采樣率的2次方形式遞減,即縮放比例為1/(inSampleSize的2次方),比如inSampleSize
為4,那么縮放比例就是1/16,有一種特殊情況,那就是當inSampleSize小于1時,其作用相當于1,即無縮放效果,另外最
新的官方檔案指出,inSampleSizede 取值應該總是為2的指數,比如1,2,4,8,16等等,如果外界傳遞給系統的
inSampleSize不為2的指數,那么系統會向下取整并選擇一個最接近的2的指數來代替,比如3,系統會選擇2來代替,但是
經過驗證發現這個結論并非在所有的Andorid版本上都成立,因此把它當成一個而開發建議即可,

4.通過采樣率即可有效地加載圖片,如何去獲得采樣率:
1)將BitmapFactory.Options的inJustDecodeBounds引數設為true并加載圖片
2)從BitmapFactory.Options中取出圖片的原始寬高資訊,他們對應于outWidth和outHeight引數,
3)根據采樣率的規則并結合目標View的所需大小計算出采樣率inSampleSize,
4)將 BitmapFactory.Options的inJustDecodeBounds引數設為false,然后重新加載圖片,
經過上面4個步驟,加載出的圖片就是最終縮放后的圖片,當然也有可能不需要縮放,這里說明一下inJustDecodeBounds
引數,當此引數設為true時,BitmapFactory只會決議圖片的原始寬/高資訊,并不會去真正地加載圖片,所以這些操作是輕
量級的,另外需要注意的是,這個時候BitmapFactory獲取的圖片寬/高資訊和圖片的位置以及程式運行的設備有關,比如同
一張圖片放在不同的drawable目錄下或者程式運行在不同螢屏密度的設備上,這都可能導致BitmapFactory獲取到不同的結
果,之所以會出現這個現象,這和Android的資源加載機制有關,

Android中的快取策略:

1.LurCache是一個泛型類

它內部采用一個LinkedHashMap以強參考的方式存盤外界快取物件,其提供了get和put方法來完成快取的獲取和添加操
作,當快取滿時,LruCache會移除較早使用的快取物件,然后再添加新的快取物件
強參考:直接的物件參考;
軟參考:當一個物件只有軟參考存在時,系統記憶體不足時此物件會被gc回收;
弱參考:當一個物件只有弱參考存在時,此物件會隨時被gc回收,

2.DiskLruCache用于實作存盤設備快取

即磁盤快取,它通過將快取物件寫入檔案系統從而實作快取的效果,
(1)DiskLruCache的創建:DiskLruCache并不能通過構造方法來創建,它提供了open方法用于創建自身,open方法有四個
引數,其中
第一個引數表示磁盤快取在檔案系統中的存盤路徑,快取路徑可以選擇sd卡上的快取目錄,具體是指 
package_name/cache目錄,建議:如果應用卸載后就希望洗掉快取資料檔案,那么就選擇sd卡上的快取目錄,如果希望

保留快取資料那就應該選擇sd卡上的其他特定目錄,
第二個引數表示應用的版本號,一般設為1即可,當版本號發生改變時DiskLruCache會清空之前所有的快取檔案,而這個
特性在實際開發中作用并不大,很多情況下即使應用的版本號發生了改變快取檔案卻依然是有效地,因此這個引數設為1比
較好,
第三個引數表示單個節點所對應的資料的個數,一般設為1即可,
第四個引數表示快取的總大小,比如50MB,當快取大小超出這個設定值后,DiskLruCache會 清除一些快取從而保證總大小
不大于這個設定值,
(2)DiskLruCache的快取添加:快取添加操作是通過Editor完成的,Editor表示一個快取物件的編輯物件,這里任然以圖片緩
存舉例,首先需要獲取圖片url所對應的key,然后根據key就可以通過edit()來獲取Editor物件,如果這個快取正在被編輯,那
么edit()會回傳null,即DiskLruCache不允許同時編輯一個快取物件,之所以要把url轉換成key,是因為圖片的url中很可能有特
殊字符,這將影響url在Android中直接使用,一般采用url的md5值作為key.
將圖片的url轉成key以后,就可以獲取Editor物件了,對于這個key來說,如果當前不存在其他Editor物件,那么edit()就會返
回一個新的Editor物件,通過它就可以得到一個檔案輸出流,需要注意的時,由于前面在DiskLruCache的open方法中設定
了一個節點只能有一個資料,因此下面的DISK_CACHE_INDEX常量直接設為0即可,
(3)DiskLruCache的快取查找:和快取的添加程序類似,快取查找程序也需要將url轉換為key,然后通過DiskLruCache的get
犯法得到一個Snapshot物件,為了避免在加載圖片程序中導致的OOM問題,一般不建議直接加載原始圖片,
3.ImageLoader的實作:
LISVIEW中的串列錯位問題,因為view的復用,當一個itemA正在從網路記載圖片,它對應的ImageView為A,這個時候用
戶快速向下滑動串列,很可能itemB復用了ImageViewA,然后等了一會之前的圖片下載完畢了,如果直接給imageviewA設
置圖片,由于這個時候imageviewA被itemB所復用,就出現了itemB中顯示了itemA的圖片,

4.圖片的同步加載和異步加載介面的設計

同步加載其作業程序遵循如下幾步:首先嘗試從記憶體快取中讀取圖片,接著嘗試從磁盤快取中讀取圖片,最后才從網路中
拉取圖片,另外,這個方法不能再主執行緒中呼叫否則就拋出例外,
Looper.myLooper()==Looper.getmainLooper判斷是否是主執行緒Looper
異步加載 bindBitmap方法會嘗試從記憶體快取中讀取圖片,如果讀取成功就直接回傳結果,否則會在執行緒池中去呼叫
loadBitmap方法,當圖片加載成功后再將圖片,圖片的地址以及需要系結的imageview封裝成一個LoaderResult物件,然后
再通過mMainHandler向主執行緒發送一個訊息,這樣就可以在主執行緒中給ImageView設定圖片了, 

bindBitmap中用到了執行緒池和Handler,之所以用執行緒池是因為如果采用普通的執行緒去加載圖片,隨著串列的滑動這可能會產
生大量的執行緒,這樣并不利于整體效率的提升,這里沒有使用AsyncTAsk,是因為3.0以上的版本AsyncTask無法實作并發
的效果,

優化串列的卡頓現象:

首先,不要在getView中執行耗時操作,如果在getview方法中加載圖片,肯定會導致卡頓,因為加載圖片是一個耗時的操
作,這種操作必須通過異步的方式來處理,
其次,控制異步任務的執行頻率,如果用戶刻意地頻繁上下滑動,這會在一順便產生上百個異步任務,這些異步任務會造
成執行緒池的擁堵并隨即帶來大量的UI更新操作,解決方法可以考慮在串列滑動的時候停止加載圖片,盡管這個程序是異步
的,等串列停下來以后再加載圖片任然可以或者良好的用戶體驗,具體實作時,可以給Listview或者GridView設定
setOnScrollListener,并在OnScrollListener的onScrollStateChanged方法中判斷串列是否處于滑動狀態,如果是的話就停止
架子按圖片
最后一般前兩個步驟串列都不會又卡頓現象,但是在某些特殊情況下,串列還是會有偶爾的卡頓現象,這個時候還可以開
啟硬體加速,絕大多數情況下,硬體加速都可以解決莫名其妙的卡頓問題可以通過設定android:hardwareAccelerated=”
true”即可未Activity開啟硬體加速,

技術應用

1.方法數過多在地版本的設備上沒有辦法編譯運行
2.一些性能優化建議
(1)避免創建過多的物件
(2)不要過多使用列舉,枚舉占用的記憶體空間要比整型大
(3)常量請使用static final來修飾
(4)使用一些Android特有的資料結構,比如SparseArray和Pair等,他們都具有更好的性能;
(5)適當使用軟參考和弱參考
(6)采用記憶體快取和磁盤快取
(7)盡量采用靜態內部類,這樣可以避免潛在的由于內部類而導致的記憶體泄露

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/226255.html

標籤:其他

上一篇:flutter視頻播放器

下一篇:安卓廣播應用,BroadcastReceiver,詳細解說,看了就會

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more