Android平臺手機 5大優點:html
開放性:Android平臺首先就是其開放性,開發的平臺容許任何移動終端廠商加入到Android聯盟中來。顯著的開放性能夠使其擁有更多的開發者;java
掙脫運營商的束縛:在過去很長的一段時間,手機應用每每受到運營商制約,使用什麼功能接入什麼網絡,幾乎都受到運營商的控制,而Android用戶能夠更加方便地鏈接網絡,運營商的制約減小;android
豐富的硬件選擇:因爲Android的開放性,衆多的廠商會推出千奇百怪,功能特點各具的多種產品。功能上的差別和特點,卻不會影響到數據同步、甚至軟件的兼容;git
開發商不受任何限制:Android平臺提供給第三方開發商一個十分寬泛、自由的環境,不會受到各類條條框框的阻擾;github
無縫結合的Google應用: Android平臺手機將無縫結合這些優秀的Google服務如地圖、郵件、搜索等;web
Android平臺手機幾大不足:算法
安全和隱私:因爲手機與互聯網的緊密聯繫,我的隱私很可貴到保守。除了上網過程當中經意或不經意留下的我的足跡,Google這個巨人也時時站在你的身後,洞穿一切;數據庫
過度依賴開發商缺乏標準配置:在Android平臺中,因爲其開放性,軟件更多依賴第三方廠商,好比Android系統的SDK中就沒有內置音樂播放器,所有依賴第三方開發,缺乏了產品的統一性;數組
同類機型用戶不多:在很多手機論壇都會有針對某一型號的子論壇,對一款手機的使用心得交流,並分享軟件資源。而對於Android平臺手機,因爲廠商豐富,產品類型多樣,這樣使用同一款機型的用戶愈來愈少,缺乏統一機型的程序強化。瀏覽器
1:打包資源文件,生成R.java文件 輸入:res文件,Assets文件,AndroidManifest.xml文件,Android基礎類庫(Android.jar文件) 輸出:R.java,resources.arsc 工具:aapt 工具位置:SDK\build-tools\29.0.0\aapt.exe
2:處理aidl文件,生成相應java文件 輸入:源碼文件,aidl文件,framework.aidl文件 輸出:對應的.java文件 工具:aidl工具 工具位置:SDK\build-tools\29.0.0\aidl.exe
3:編譯工程源代碼,生成相應class文件 輸入:源碼文件(包括R.java和AIDL生成的.java文件),庫文件(jar文件) 輸出:.class文件 工具:javac工具 工具位置:Java\jdk1.8.0_201\bin\javac.exe
4:轉換全部class文件,生成classes.dex文件 輸入:.class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),庫文件(.jar文件) 輸出:.dex文件 工具:javac工具 工具位置:Java\jdk1.8.0_201\bin\javac.exe
5:打包生成apk文件 輸入:打包後的資源文件,打包後類文件(.dex文件),libs文件(包括.so文件) 輸出:未簽名的.apk文件 工具:apkbuilder.bat工具已廢棄,改成sdklib.jar工具 工具位置:E:\SDK\tools\lib\sdklib.jar
6:對apk文件進行簽名 輸入:未簽名的.apk文件 輸出:簽名的.apk文件 工具: jarsigner工具 apksigner工具 工具位置: Java\jdk1.8.0_201\bin\jarsigner.exe SDK\build-tools\29.0.0\lib\apksigner.jar
7:對簽名後的apk文件進行對齊處理 輸入:簽名後的.apk文件 輸出:對齊後的.apk文件 工具:zipalign工具 工具位置:SDK\build-tools\29.0.0\zipalign.exe
注:工具位置基於win平臺. 參考鏈接: developer.android.com/studio/buil… blog.csdn.net/jiangwei091…
咱們先看下LruCache算法的構造方法。
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
複製代碼
從構造方法的源碼咱們能夠看到,在這段代碼中咱們主要作了兩件事。第一是判斷下傳遞來的最大分配內存大小是否小於零,若是小於零則拋出異常,由於咱們若是傳入一個小於零的內存大小就沒有意義了。以後在構造方法內存就new了一個LinkHashMap集合,從而得知LruCache內部實現原理果真是基於LinkHashMap來實現的。
以後咱們再來看下存儲緩存的put()方法。
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
複製代碼
從代碼中咱們能夠看到,這個put方法內部其實沒有作什麼很特別的操做,就是對數據進行了一次插入操做。可是咱們注意到最後的倒數第三行有一個trimToSize()方法,那麼這個方法是作什麼用的吶?咱們點進去看下。
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize) {
break;
}
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
複製代碼
咱們能夠看到,這個方法原來就是對內存作了一次判斷,若是發現內存已經滿了,那麼就調用map.eldest()方法獲取到最後的數據,以後調用map.remove(key)方法,將這個最近最少使用的數據給剔除掉,從而達到咱們內存不炸掉的目的。
咱們再來看看get()方法。
public final V get(K key) {
//key爲空拋出異常
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
//獲取對應的緩存對象
//get()方法會實現將訪問的元素更新到隊列頭部的功能
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
複製代碼
get方法看起來就是很常規的操做了,就是經過key來查找value的操做,咱們再來看看LinkHashMap的中get方法。
public V get(Object key) {
LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
if (e == null)
return null;
//實現排序的關鍵方法
e.recordAccess(this);
return e.value;
}
複製代碼
調用recordAccess()方法以下:
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
複製代碼
因而可知LruCache中維護了一個集合LinkedHashMap,該LinkedHashMap是以訪問順序排序的。當調用put()方法時,就會在結合中添加元素,並調用trimToSize()判斷緩存是否已滿,若是滿了就用LinkedHashMap的迭代器刪除隊尾元素,即最近最少訪問的元素。當調用get()方法訪問緩存對象時,就會調用LinkedHashMap的get()方法得到對應集合元素,同時會更新該元素到隊頭。
咱們知道Java虛擬機 —— JVM 是加載類的class文件的,而Android虛擬機——Dalvik/ART VM 是加載類的dex文件, 而他們加載類的時候都須要ClassLoader,ClassLoader有一個子類BaseDexClassLoader,而BaseDexClassLoader下有一個數組——DexPathList,是用來存放dex文件,當BaseDexClassLoader經過調用findClass方法時,實際上就是遍歷數組,找到相應的dex文件,找到,則直接將它return。而熱修復的解決方法就是將新的dex添加到該集合中,而且是在舊的dex的前面,因此就會優先被取出來而且return返回。
App啓動通常分爲兩種:
這裏主要介紹冷啓動,大體分爲5個步驟:
補充知識: Zygote zygote名字翻譯叫受精卵,zygote進程的建立是由Linux系統中init進程建立的,Android中全部的進程都是直接或者間接的由init進程fork出來的,Zygote進程負責其餘進程的建立和啓動,好比建立SystemServer進程。當須要啓動一個新的android應用程序的時候,ActivityManagerService就會經過Socket通知Zygote進程爲這個應用建立一個新的進程。
Launcher 咱們要知道手機的桌面也是一個App咱們叫它launcher,每個手機應用都是在Launcher上顯示,而Launcher的加載是在手機啓動的時候加載Zygote,而後Zygote啓動SystenServer,SystenServer會啓動各類ManageService, 包括ActivityManagerService,並將這些ManageService註冊到ServiceManage 容器中,而後ActivityManagerService就會啓動Home應用程序Launcher.
ActivityManagerService ActivityManagerService咱們簡稱AMS,四大組件都歸它管,四大組件的跨進程通訊都要和它合做。
Binder Binder是Android跨進程通訊(IPC)的一種方式,也是Android系統中最重要的特性之一,android 四大組件以及不一樣的App都運行在不一樣的進程,它則是各個進程的橋樑將不一樣的進程粘合在一塊兒。
ActivityThread 首先ActivityThread並非一個Thread,其做用就是在main方法內作消息循環。那咱們常說的主線程是什麼?主線程就是承載ActivityThread的Zygote fork而建立的進程。 ActivityThread的調用是在ActivityManageService.startProcessLocked()方法裏調用並建立,這個類主要作了這幾個事:
From GavinCui12
一、點擊桌面應用圖標,Launcher進程將啓動Activity(MainActivity)的請求以Binder的方式發送給了AMS。
二、AMS接收到啓動請求後,交付ActivityStarter處理Intent和Flag等信息,而後再交給ActivityStackSupervisior/ActivityStack 處理Activity進棧相關流程。同時以Socket方式請求Zygote進程fork新進程。
三、Zygote接收到新進程建立請求後fork出新進程。
四、在新進程裏建立ActivityThread對象,新建立的進程就是應用的主線程,在主線程裏開啓Looper消息循環,開始處理建立Activity。
五、ActivityThread利用ClassLoader去加載Activity、建立Activity實例,並回調Activity的onCreate()方法,這樣便完成了Activity的啓動。
什麼是Dalvik: Dalvik是Google公司本身設計用於Android平臺的Java虛擬機。 它能夠支持已轉換爲.dex(即Dalvik Executable)格式的Java應用程序的運行。 .dex格式是專爲Dalvik應用設計的一種壓縮格式,適合內存和處理器速度有限的系統。 Dalvik通過優化,容許在有限的內存中同時運行多個虛擬機的實例,而且每個Dalvik應用做爲獨立的Linux進程執行。 獨立的進程能夠防止在虛擬機崩潰的時候全部程序都被關閉。
什麼是ART: 與Dalvik不一樣,ART使用預編譯(AOT,Ahead-Of-Time)。 也就是在APK運行以前,就對其包含的Dex字節碼進行翻譯,獲得對應的本地機器指令,因而就能夠在運行時直接執行了。 ART應用安裝的時候把dex中的字節碼將被編譯成本地機器碼,以後每次打開應用,執行的都是本地機器碼。 去除了運行時的解釋執行,效率更高,啓動更快。
區別:
參考官方文檔: source.android.com/devices/tec…
Serializable(Java自帶): Serializable是序列化的意思,表示將一個對象轉換成可存儲或可傳輸的狀態。序列化後的對象能夠在網絡上進行傳輸,也能夠存儲到本地。
Parcelable(android 專用): 除了Serializable以外,使用Parcelable也能夠實現相同的效果, 不過不一樣於將對象進行序列化,Parcelable方式的實現原理是將一個完整的對象進行分解, 而分解後的每一部分都是Intent所支持的數據類型,這樣也就實現傳遞對象的功能了。
區別:
實現:
參考自簡書:www.jianshu.com/p/a60b609ec…
From Noble_JIE
從出生來來講,Serializable 是java的方法,Parcelable 是android獨有的序列化反序列化方法 從用法上來講,Serializable 比 Parcelable 簡單,全部類實現Serializable便可,Parcelable須要對對全部屬性及成員變量進行Creator 。 從性能上來講,Parcelable 性能要高於Serializable。
Application Not Responding,即應用無響應。避免ANR最核心的一點就是在主線程減小耗時操做。一般須要從那個如下幾個方案下手:
a)使用子線程處理耗時IO操做
b)下降子線程優先級,使用Thread或者HandlerThread時,調用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設置優先級,不然仍然會下降程序響應,由於默認Thread的優先級和主線程相同
c)使用Handler處理子線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程
d)Activity的onCreate和onResume回調中儘可能避免耗時的代碼
e)BroadcastReceiver中onReceiver代碼也要儘可能減小耗時操做,建議使用intentService處理。intentService是一個異步的,會自動中止的服務,很好解決了傳統的Service中處理完耗時操做忘記中止並銷燬Service的問題
Broadcast廣播,註冊方式主要有兩種.
第一種是靜態註冊,也可成爲常駐型廣播,這種廣播須要在Androidmanifest.xml中進行註冊,這中方式註冊的廣播,不受頁面生命週期的影響,即便退出了頁面,也能夠收到廣播這種廣播通常用於想開機自啓動啊等等,因爲這種註冊的方式的廣播是常駐型廣播,因此會佔用CPU的資源。
第二種是動態註冊,而動態註冊的話,是在代碼中註冊的,這種註冊方式也叫很是駐型廣播,收到生命週期的影響,退出頁面後,就不會收到廣播,咱們一般運用在更新UI方面。這種註冊方式優先級較高。最後須要解綁,否會會內存泄露
廣播是分爲有序廣播和無序廣播。
From chenwentong
1、Brodcast註冊方式:
一、靜態註冊:
二、動態註冊:
複製代碼
2、靜態註冊:在清單文件manifest中註冊,當程序退出以後還能夠收到該廣播。不能控制具體某個時間點接收和不接收廣播。
3、動態註冊:經過代碼的方式註冊context.registerReceiver(broadcastReceiver),註冊的同時注意在不須要接受的時候進行反註冊context.unregisterReceiver(broadcastReceiver);避免內存泄漏, 動態註冊能夠很好的控制廣播接受。
4、從Android 8.0(API 26)開始,對於大部分隱式廣播(廣播的對象不是針對你開發的APP),不能在manifest中聲明receiver,若是須要使用隱式廣播,須要使用context.registerReceiver 的方法。
一、使用分頁加載,不要一次性加載全部數據。
二、複用convertView。在getItemView中,判斷converView是否爲空,若是不爲空,可複用。
三、異步加載圖片。Item中若是包含有webimage,那麼最好異步加載。
四、快速滑動時,不顯示圖片。當快速滑動列表(SCROLL_STATE_FLING),item中的圖片或獲取須要消耗資源的view,能夠不顯示出來;而處於其餘兩種狀態(SCROLL_STATE_IDLE和SCROLL_STATE_TOUCH_SCROLL),則將那些view顯示出來
ANR是什麼? ANR全稱:Application Not Responding,也就是應用程序無響應. 簡單來講,就是應用跑着跑着,忽然duang,界面卡住了,沒法響應用戶的操做如觸摸事件等.
緣由 Android系統中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會檢測App的響應時間. 若是App在特定時間沒法相應屏幕觸摸或鍵盤輸入時間,或者特定事件沒有處理完畢,就會出現ANR.
ANR的產生須要同時知足三個條件
解決方案: 總結爲一句話,即不要在主線程(UI線程)裏面作繁重的操做
數據處理和視圖加載分離
從遠端拉取數據確定是要放在異步的,在咱們拉取下來數據以後可能就匆匆把數據丟給了 VH 處理,其實,數據的處理邏輯咱們也應該放在異步處理,這樣 Adapter 在 notify change 後,ViewHolder 就能夠簡單無壓力地作數據與視圖的綁定邏輯,好比:
mTextView.setText(Html.fromHtml(data).toString());
複製代碼
這裏的 Html.fromHtml(data)
方法可能就是比較耗時的,存在多個 TextView
的話耗時會更爲嚴重,這樣便會引起掉幀、卡頓,而若是把這一步與網絡異步線程放在一塊兒,站在用戶角度,最多就是網絡刷新時間稍長一點。
數據優化
分頁拉取遠端數據,對拉取下來的遠端數據進行緩存,提高二次加載速度;對於新增或者刪除數據經過 DiffUtil
來進行局部刷新數據,而不是一味地全局刷新數據。
佈局優化
減小過渡繪製
減小布局層級,能夠考慮使用自定義 View 來減小層級,或者更合理地設置佈局來減小層級,不推薦在 RecyclerView 中使用 ConstraintLayout
,有不少開發者已經反映了使用它效果更差,相關連接有:Is ConstraintLayout that slow?、constraintlayout 1.1.1 not work well in listview。
減小 xml 文件 inflate 時間
這裏的 xml 文件不只包括 layout 的 xml,還包括 drawable 的 xml,xml 文件 inflate 出 ItemView 是經過耗時的 IO 操做,尤爲當 Item 的複用概率很低的狀況下,隨着 Type 的增多,這種 inflate 帶來的損耗是至關大的,此時咱們能夠用代碼去生成佈局,即 new View()
的方式,只要搞清楚 xml 中每一個節點的屬性對應的 API 便可。
減小 View 對象的建立
一個稍微複雜的 Item 會包含大量的 View,而大量的 View 的建立也會消耗大量時間,因此要儘量簡化 ItemView;設計 ItemType 時,對多 ViewType 可以共用的部分儘可能設計成自定義 View,減小 View 的構造和嵌套。
其餘
升級 RecycleView
版本到 25.1.0 及以上使用 Prefetch 功能,可參考 RecyclerView 數據預取。
若是 Item 高度是固定的話,能夠使用 RecyclerView.setHasFixedSize(true);
來避免 requestLayout
浪費資源;
設置 RecyclerView.addOnScrollListener(listener);
來對滑動過程當中中止加載的操做。
若是不要求動畫,能夠經過 ((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false);
把默認動畫關閉來提神效率。
對 TextView
使用 String.toUpperCase
來替代 android:textAllCaps="true"
。
對 TextView
使用 StaticLayout
或者 DynamicLayout
的自定義 View
來代替它。
經過重寫 RecyclerView.onViewRecycled(holder)
來回收資源。
經過 RecycleView.setItemViewCacheSize(size);
來加大 RecyclerView
的緩存,用空間換時間來提升滾動的流暢性。
若是多個 RecycledView
的 Adapter
是同樣的,好比嵌套的 RecyclerView
中存在同樣的 Adapter
,能夠經過設置 RecyclerView.setRecycledViewPool(pool);
來共用一個 RecycledViewPool
。
對 ItemView
設置監聽器,不要對每一個 Item 都調用 addXxListener
,應該你們公用一個 XxListener
,根據 ID
來進行不一樣的操做,優化了對象的頻繁建立帶來的資源消耗。
經過 getExtraLayoutSpace 來增長 RecyclerView 預留的額外空間(顯示範圍以外,應該額外緩存的空間),以下所示:
new LinearLayoutManager(this) {
@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
return size;
}
};
複製代碼
1.代碼混淆 minifyEnabled true
2.資源壓縮 1)shrinkResources true 2)微信的AndResGuard
3.圖片壓縮 1)tinypng 2)svg 3)webp
4.so庫配置 只保留兩個abi平臺,即armeabi和armeabi-v7a
5.dex優化 Facebook的redex
standard 模式 這是默認模式,每次激活Activity時都會建立Activity實例,並放入任務棧中。使用場景:大多數Activity。
singleTop 模式 若是在任務的棧頂正好存在該Activity的實例,就重用該實例( 會調用實例的 onNewIntent() ),不然就會建立新的實例並放入棧頂,即便棧中已經存在該Activity的實例,只要不在棧頂,都會建立新的實例。使用場景如新聞類或者閱讀類App的內容頁面。
singleTask 模式 若是在棧中已經有該Activity的實例,就重用該實例(會調用實例的 onNewIntent() )。重用時,會讓該實例回到棧頂,所以在它上面的實例將會被移出棧。若是棧中不存在該實例,將會建立新的實例放入棧中。使用場景如瀏覽器的主界面。無論從多少個應用啓動瀏覽器,只會啓動主界面一次,其他狀況都會走onNewIntent,而且會清空主界面上面的其餘頁面。
singleInstance 模式 在一個新棧中建立該Activity的實例,並讓多個應用共享該棧中的該Activity實例。一旦該模式的Activity實例已經存在於某個棧中,任何應用再激活該Activity時都會重用該棧中的實例( 會調用實例的 onNewIntent() )。其效果至關於多個應用共享一個應用,無論誰激活該 Activity 都會進入同一個應用中。使用場景如鬧鈴提醒,將鬧鈴提醒與鬧鈴設置分離。singleInstance不要用於中間頁面,若是用於中間頁面,跳轉會有問題,好比:A -> B (singleInstance) -> C,徹底退出後,在此啓動,首先打開的是B。
Android提供了5中存儲數據的方式,分別是如下幾種:
一、使用Shared Preferences存儲數據,用來存儲key-value,pairs格式的數據,它是一個輕量級的鍵值存儲機制,只能夠存儲基本數據類型。
二、使用文件存儲數據,經過FileInputStream和FileOutputStream對文件進行操做。在Android中,文件是一個應用程序私有的,一個應用程序沒法讀寫其餘應用程序的文件。
三、使用SQLite數據庫存儲數據,Android提供的一個標準數據庫,支持SQL語句。
四、使用Content Provider存儲數據,是全部應用程序之間數據存儲和檢索的一個橋樑,它的做用就是使得各個應用程序之間實現數據共享。它是一個特殊的存儲數據的類型,它提供了一套標準的接口用來獲取數據,操做數據。系統也提供了音頻、視頻、圖像和我的信息等幾個經常使用的Content Provider。若是你想公開本身的私有數據,能夠建立本身的Content Provider類,或者當你對這些數據擁有控制寫入的權限時,將這些數據添加到Content Provider中實現共享。外部訪問經過Content Resolver去訪問並操做這些被暴露的數據。
五、使用網絡存儲數據
進程間通訊即IPC,英文全稱Inter-Process Communication,是指進程間數據交互的過程. Android底層是基於Linux,而Linux基於安全考慮,是不容許兩個進程間直接操做對方的數據,這就是進程隔離. 六種經常使用姿式:
參考:Android開發藝術探索 第2章 2.4節
Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖)LayoutInflater像剪刀,Xml配置像窗花圖紙。 1:Activity構造的時候會初始化一個Window,準確的說是PhoneWindow。 2:這個PhoneWindow有一個「ViewRoot」,這個「ViewRoot」是一個View或者說ViewGroup,是最初始的根視圖。 3:「ViewRoot」經過addView方法來一個個的添加View。好比TextView,Button等 4:這些View的事件監聽,是由WindowManagerService來接受消息,而且回調Activity函數。好比onClickListener,onKeyDown等
Android中 SP 的底層是由Xml來實現的,操做SP的過程就是Xml的序列化和解析的過程。Xml是存儲在磁盤上的,所以當咱們頻繁進行SP操做時,就是頻繁進行序列化與解析,這就頻繁進行I/O的操做,因此確定會致使性能消耗。同時序列化Xml是就是將內存中的數據寫到Xml文件中,因爲DVM的內存是頗有限的,所以單個SP文件不建議太大,具體多大是沒有一個具體的要求的,可是咱們知道DVM 堆內存也就是16M,所以數據大小確定不能超過這個數字的。其實 SP 設置的目的就是爲了保存用戶的偏好和配置信息的,所以不要保存太多的數據。
最經常使用的佈局方式爲Absolute Layout、Relative Layout、Linear Layout、FrameLayout、TableLayout。其中Linear Layout和Relative Layout是最經常使用的方式,他們能夠經過在xml配置文件或者代碼中進行佈局。
一、Frame Layout是最簡單的佈局方式,放置的控件都只能羅列到左上角,控件會有重疊,不能進行復雜的佈局。
二、Linear Layout能夠經過orientation屬性設置線性排列的方向是垂直仍是縱向的,每行或每列只有一個元素,能夠進行復雜的佈局。
三、Absolute Layout可讓子元素指定準確的x、y座標值,並顯示在屏幕上。Absolute Layout沒有頁邊框,容許元素之間相互重疊。它是絕對座標,因此在實際中不提倡使用。
四、Relative Layout容許子元素制定他們相對於其餘元素或父元素的位置(經過ID制定)。所以,你能夠以右對齊,或上下,或置於屏幕中央的形式來排列兩個元素。元素按順序排列,所以若是第一個元素在屏幕的中央,那麼相對於這個元素的其餘元素將以屏幕中央的相對位置來排列。這個是相對於Absolute Layout的,採用相對座標,因此在實際中比較經常使用。
五、Table Layout將以子元素的位置分配到行或列。一個Table Layout由許多的Table Row組成,每一個Table Row都會定義一個row。Table Layout容器不會顯示row、column或者cell的邊線框。每一個row擁有0個或多個的cell; 和html中的table差很少。在實際中也常用。
一、線性佈局 (LinearLayout):是一種很是經常使用的佈局,次佈局會將它包含的控件在線性方向上依次排列。經過android:orientation屬性來肯定排列的方向是vertical(垂直)仍是horizontal(水平)。 二、相對佈局(RelativeLayout):也是一種很是經常使用的佈局,經過相對定位的方式讓控件出如今佈局的任何位置。 三、幀佈局(FrameLayout):因爲定位方式的欠缺,全部的控件都會默認擺放在佈局的左上角,應用場景比較少。 四、絕對佈局(AbsoluteLayout):用x、y座標來肯定控件的位置。 五、表格佈局(TableLayout):每個TableLayout裏面有表格行TableRow,TableRow裏面能夠具體定義每個控件。
From Taonce
經過 Handler
來通訊
val handler = @SuppressLint("HandlerLeak")
object : Handler(){
override fun handleMessage(msg: Message?) {
Log.d("taonce","msg arg1: ${msg?.arg1}")
}
}
thread {
val msg: Message = handler.obtainMessage()
msg.arg1 = 1
handler.sendMessage(msg)
}
複製代碼
經過 runOnUiThread()
thread {
val text = "runOnUiThread"
runOnUiThread {
tv.text = text
}
}
複製代碼
經過 View.post()
thread {
val text = "post"
tv.post {
tv.text = text
}
}
複製代碼
經過 AsyncTask
class MyAsyncTask(val name: String) : AsyncTask<String, Int, Any>() {
// 執行任務以前的準備工做,好比將進度條設置爲Visible,工做在主線程
override fun onPreExecute() {
Log.d("async", "onPreExecute")
}
// 在onPreExecute()執行完以後當即在後臺線程中調用
override fun doInBackground(vararg params: String?): Any? {
Log.d("async", "$name execute")
Thread.sleep(1000)
publishProgress(1)
return null
}
// 調用了publishProgress()以後,會在主線程中被調用,用於更新總體進度
override fun onProgressUpdate(vararg values: Int?) {
Log.d("async", "progress is: $values")
}
// 後臺線程執行結束後,會把結果回調到這個方法中,並在主線程中被調用
override fun onPostExecute(result: Any?) {
Log.d("async", "onPostExecute")
}
}
複製代碼
Context是一個抽象基類。在翻譯爲上下文,也能夠理解爲環境,是提供一些程序的運行環境基礎信息。Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。而ContextWrapper又有三個直接的子類, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity,因此Activity和Service以及Application的Context是不同的,只有Activity須要主題,Service不須要主題。Context一共有三種類型,分別是Application、Activity和Service。這三個類雖然分別各類承擔着不一樣的做用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的,所以在絕大多數場景下,Activity、Service和Application這三種類型的Context都是能夠通用的。不過有幾種場景比較特殊,好比啓動Activity,還有彈出Dialog。出於安全緣由的考慮,Android是不容許Activity或Dialog憑空出現的,一個Activity的啓動必需要創建在另外一個Activity的基礎之上,也就是以此造成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),所以在這種場景下,咱們只能使用Activity類型的Context,不然將會出錯。 getApplicationContext()和getApplication()方法獲得的對象都是同一個application對象,只是對象的類型不同。 Context數量 = Activity數量 + Service數量 + 1 (1爲Application)
Activity、Service、ContentProvider 如 果 要 使 用 則 必 須 在AndroidManifest.xml 中 進 行 注 冊 , 而BroadcastReceiver則有兩種註冊方式,靜態註冊和動態註冊。其中靜態註冊就是指在AndroidManifest.xml中進行註冊,而動態註冊時經過代碼註冊。
Activity:一般展示爲一個用戶操做的可視化界面。它爲用戶提供了一個完成操做指令的窗口。 (mp.weixin.qq.com/s/CgfeMT9Yt…) (Activity的來由)
Service:Android系統的服務(不是一個線程,是主程序的一部分),與Activity不一樣,它是不能與用戶交互的,不能本身啓動的,需要調用Context.startService()來啓動,執行後臺,假設咱們退出應用時,Service進程並無結束,它仍然在後臺行。
BroadcastReceiver:廣播接收器是一個專一於接收廣播通知信息,並作出相應處理的組件。
ContentProvider:(內容提供者)主要用於對外共享數據,也就是經過ContentProvider把應用中的數據共享給其它應用訪問,其它應用可以經過ContentProvider對指定應用中的數據進行操做。
這邊介紹三種:AsyncTask,HandlerThread和IntentService
AsyncTask原理:內部是Handler和兩個線程池實現的,Handler用於將線程切換到主線程,兩個線程池一個用於任務的排隊,一個用於執行任務,當AsyncTask執行execute方法時會封裝出一個FutureTask對象,將這個對象加入隊列中,若是此時沒有正在執行的任務,就執行它,執行完成以後繼續執行隊列中下一個任務,執行完成經過Handler將事件發送到主線程。AsyncTask必須在主線程初始化,由於內部的Handler是一個靜態對象,在AsyncTask類加載的時候他就已經被初始化了。在Android3.0開始,execute方法串行執行任務的,一個一個來,3.0以前是並行執行的。若是要在3.0上執行並行任務,能夠調用executeOnExecutor方法
HandlerThread原理:繼承自Thread,start開啓線程後,會在其run方法中會經過Looper建立消息隊列並開啓消息循環,這個消息隊列運行在子線程中,因此能夠將HandlerThread中的Looper實例傳遞給一個Handler,從而保證這個Handler的handleMessage方法運行在子線程中,Android中使用HandlerThread的一個場景就是IntentService
IntentService原理:繼承自Service,它的內部封裝了HandlerThread和Handler,能夠執行耗時任務,同時由於它是一個服務,優先級比普通線程高不少,因此更適合執行一些高優先級的後臺任務,HandlerThread底層經過Looper消息隊列實現的,因此它是順序的執行每個任務。能夠經過Intent的方式開啓IntentService,IntentService經過handler將每個intent加入HandlerThread子線程中的消息隊列,經過looper按順序一個個的取出並執行,執行完成後自動結束本身,不須要開發者手動關閉
ListView採用的是RecyclerBin的回收機制,在一些輕量級的List顯示時效率更高.
自定義ListView 解決:重寫其中的onMeasure()方法
緣由: ScrollView默認把Childview設置爲UNSPEFEIED模式,而該模式下的ListView給本身的測量的高度就是第一個item的高度.原理: int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); 這個方法的做用是根據大小和模式來生成一個int值,這個int值封裝了模式和大小信息. 首先MeasureSpec類是View的一個靜態內部類,MeasureSpec類封裝了從父佈局到子佈局傳遞的佈局需求. 每一個MeasureSpec對象表明了寬度和高度的要求. MeasureSpec用int類型表示,前2位表明模式,後30位表明大小. 第一個參數Integer.MAX_VALUE >> 2:Integer.MAX_VALUE獲取到int的最大值,可是表示大小的值size是int數值的底30位,因此把這個值右移兩位,留出高兩位表示佈局模式. 此時這個值仍舊是一個30位所能表示的最大數,用該數做爲控件的size,應該足夠知足控件大小的需求. 第二個參數MeasureSpec.AT_MOST:表示這個控件適配父控件的最大空間.
(如下三種僅供參考,不推薦使用) 2.手動設置ListView高度 3.使用單個ListView取代ScrollView中全部內容 4.使用LinearLayout取代ListView
參考資料:
juejin.im/entry/5979a…
juejin.im/post/5a322c…
Handler的工做是依賴於Looper的,而Looper(與消息隊列)又是屬於某一個線程(ThreadLocal是線程內部的數據存儲類,經過它能夠在指定線程中存儲數據,其餘線程則沒法獲取到),其餘線程不能訪問。所以Handler就是間接跟線程是綁定在一塊兒了。所以要使用Handler必需要保證Handler所建立的線程中有Looper對象而且啓動循環。由於子線程中默認是沒有Looper的,因此會報錯。正確的使用方法是:
private final class WorkThread extends Thread {
private Handler mHandler;
public Handler getHandler() {
return mHandler;
}
public void quit() {
mHandler.getLooper().quit();
}
@Override
public void run() {
super.run();
//建立該線程對應的Looper,
// 內部實現
// 1。new Looper()
// 2。將1步中的lopper 放在ThreadLocal裏,ThreadLocal是保存數據的,主要應用場景是:線程間數據互不影響的狀況
// 3。在1步中的Looper的構造函數中new MessageQueue();
//其實就是建立了該線程對用的Looper,Looper裏建立MessageQueue來實現消息機制
//對消息機制不懂得同窗能夠查閱資料,網上不少也講的很不錯。
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
}
};
//開啓消息的死循環處理即:dispatchMessage
Looper.loop();
//注意這3個的順序不能顛倒
Log.d("WorkThread", "end");
}
}
複製代碼
事件的傳遞流程: Activity(PhoneWindow)->DecorView->ViewGroup->View。 事件分發過程當中三個重要的方法: dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent(); 事件傳遞規則 通常一次點擊會有一系列的MotionEvent,能夠簡單分爲:down->move->….->move->up,當一次event分發到ViewGroup時,ViewGroup收到事件後調用dispatchTouchEvent,在dispatchTouchEvent中先檢查是否要攔截,若攔截則ViewGroup處理事件,不然交給有處理能力的子容器處理。
內存泄漏指對象再也不使用,本該被回收,卻由於有其餘正在使用的對象持有該對象的引用,而沒法被JVM回收
內存泄漏的影響:
Android開發中常見內存泄漏及解決辦法
內存泄漏排查工具: AS Monitor,MAT,LeakCanary
擴展: Java內存管理,GC
Handler引發的內存泄漏 緣由:該線程持有Handler的引用,而Handler也持有Activity的引用,這就致使了Activity再也不使用時,GC回收不了Activity 解決:Handler持有的引用最好使用弱引用,在Activity被釋放的時候要記得清空Message,取消Handler對象的Runnable
單例模式引發的內存泄漏 緣由:構建該單例的一個實例時須要傳入一個Context,若是此時傳入的是Activity,因爲Context會被建立的實例一直持有,當Activity進入後臺或者開啓設置裏面的不保留活動時,Activity會被銷燬,可是單例持有它的Context引用,Activity無法銷燬 解決:對於生命週期比Activity長的對象,要避免直接引用Activity的context,能夠考慮使用ApplicationContext
非靜態內部類建立靜態實例引發的內存泄漏 緣由:非靜態的內部類會自動持有外部類的引用,建立的靜態實例就會一直持有的引用 解決:能夠考慮把內部類聲明爲靜態的
非靜態匿名內部類引發的內存泄漏 緣由:若是匿名內部類被異步線程使用,可能會引發內存泄漏 解決:能夠考慮把內部類聲明爲靜態的
資源對象沒有關閉引發的內存泄漏 緣由:資源性對象好比Cursor、File、Bitmap、視頻等,系統都用了一些緩衝技術,在使用這些資源以後沒有關閉 解決:處理完資源對象的邏輯記得關閉,最好是造成習慣現寫一開一關
集合對象沒有及時清理引發的內存泄漏 緣由:若是集合是static、不斷的往裏面添加東西、又忘記去清理,確定會引發內存泄漏 解決:集合裏面的東西、有加入就應該對應有相應的刪除
不能經過 GC 來解決內存泄漏問題