安卓程式碼、圖片和佈局最佳化
寫在前面的話,前段時間寫了一篇文章 ,寫的不詳細,因為如果要寫的很詳細,估計一年半載都寫不完,完全都是按照自己理解,每個設計模式就畫了一個簡單的圖,同時完成了一個小Demo,哪知道這篇文章成了我在簡書點贊最高的一篇文章,實在有點受寵若驚,謝謝各位大佬點贊!!!
這篇文章分為三個部分程式碼最佳化、圖片最佳化、佈局最佳化,儘量每個方法都寫了小的Demo!
程式碼最佳化:不要做多餘的工作,儘量避免次數過多的記憶體的分配,(需要對api有一定的熟悉)
資料集合的使用:建議最佳的做法是可能使用ArrayList作為首選,只要你需要使用額外的功能的時候,或者當程式效能由於經常從表的中間進行插入和刪除而變差的時候,才會去選擇LinkedList。HashMap效能上於HashTable相當,因為HashMap和HashTable在底層的儲存和查詢機制是一樣的,但是TreeMap通常比HashMap要慢。HashSet總體上的效能比TreeSet好,特別實在新增和查詢元素的時候,而這兩個操作也是最重要的操作。TreeSet存在的唯一的原因是它可以維持元素的排序的狀態,所以當需要一個排好序的Set,才使用TreeSet。因為其內部的結果歐支援排序,並且因為迭代是我們更有可能執行的操作,所以,用TreeSet迭代通常比用HashSet要快。
/** * 建議最佳的做法是可能使用ArrayList作為首選,只要你需要使用額外的功能的時候,或者當程式效能由於經常從表的中間進行 * 插入和刪除而變差的時候,才會去選擇LinkedList */ //資料結構的選擇,對於ArrayList,插入的操作特別高昂,並且其代價將隨著列表的尺寸的增加而增加 ArrayList list=new ArrayList(); //需要執行大量的隨機的訪問,這個不是一個好的選擇,如果是使用迭代器在列表中插入新的資料,使用這個,比較低廉(插入和移除的代價比較低廉) LinkedList linkedList=new LinkedList(); //HashMap效能上於HashTable相當,因為HashMap和HashTable在底層的儲存和查詢機制是一樣的,但是TreeMap通常比HashMap要慢 HashMaphashMap=new HashMap(); /** * HashSet總體上的效能比TreeSet好,特別實在新增和查詢元素的時候,而這兩個操作也是最重要的操作。TreeSet存在的唯一的原因是它 * 可以維持元素的排序的狀態,所以當需要一個排好序的Set,才使用TreeSet。因為其內部的結果歐支援排序,並且因為迭代是我們更有可能 * 執行的操作,所以,用TreeSet迭代通常比用HashSet要快 */ HashSet hashSet=new HashSet(); TreeSet treeSet=new TreeSet();
SparseArray是Android特有的稀疏陣列的實現,他是Integer和Object的為例進行的一個對映用於代替 HsahMap
SparseArray
執行緒不安全(多執行緒中需要注意)
由於要進行二分查詢,(可以是有序的),SparseArray會對插入的資料按照Key的大小順序插入
SparseArray對刪除操作做了最佳化,它並不會立刻刪除這個元素,而是透過設定標記位(DELETED)的方法,後面嘗試重用。
內部核心的實現(二分查詢)
/** * 二分查詢也稱折半查詢(Binary Search),它是一種效率較高的查詢方法。 * 但是,折半查詢要求線性表必須採用順序儲存結構,而且表中元素按關鍵字有序排列。 * @param array * @param size * @param value * @return */ //二分查詢 static int binarySearch(int[] array, int size, int value) { int lo = 0; int hi = size - 1; while (lo >>與>>唯一的不同是它無論原來的最左邊是什麼數,統統都用0填充。 * —比如你的例子,byte是8位的,-1表示為byte型是11111111(補碼錶示法) * b>>>4就是無符號右移4位,即00001111,這樣結果就是15。 * 這裡相當移動一位,除以二 */ final int mid = (lo + hi) >>> 1; final int midVal = array[mid]; if (midVal value) { hi = mid - 1; } else { return mid; // value found } } //按位取反(~)運算子 ,沒有找到,這個資料 return ~lo; // value not present }
SpareArray 家族有以下的四類
//SpareArray 家族有以下的四類 //用於替換 HashMapSparseBooleanArray sparseBooleanArray=new SparseBooleanArray(); sparseBooleanArray.append(1,false); //用於替換 HashMap SparseIntArray SparseIntArray=new SparseIntArray(); SparseIntArray.append(1,1); //用於替換 HashMap @SuppressLint({"NewApi", "LocalSuppress"}) SparseLongArray SparseLongArray=new SparseLongArray(); SparseLongArray.append(1,1111000L); //用於替換 HashMap SparseArray SparseArray11=new SparseArray (); SparseArray11.append(1,"dd");
SpareArray中的設計模式:原型模式:這裡有使用到了的,原型模式記憶體中複製資料的,不會呼叫到類的構造的方法,而且訪問的許可權對原型模式無效
優點: 1、效能提高。 2、逃避建構函式的約束。
缺點:
1、配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不一定很容易,特別當一個類引用不支援序列化的間接物件,或者引用含有迴圈結構的時候。
2、必須實現 Cloneable 介面。
SparseArrayclone = sparseArray.clone();
Handler正確的使用姿勢
下面的程式碼是很多人都會這樣寫,這樣會造成記憶體洩漏
原因:Handler是和Looper以及MessageQueue一起工作的,在安卓中,一個 應用啟動了,系統會預設建立一個主執行緒服務的Looper物件 ,該Looper物件處理主執行緒的所有的Message訊息,他的生命週期貫穿整個應用。在主執行緒中使用的Handler的都會預設的繫結到這個looper的物件,咋主執行緒中建立handler的時候,它會立即關聯主執行緒Looper物件的MessageQueue,這時傳送到的MessageQueue 中的Message物件都會持有這個Handler的物件的引用,這樣Looper處理訊息時Handler的handlerMessage的方法,因此,如果Message還沒有處理完成,那麼handler的物件不會立即被垃圾回收
/*-------------old ide 已經告訴我們這裡可能記憶體洩露-------------------*/ @SuppressLint("HandlerLeak") private final Handler mHandler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } };// mHandler.postDelayed(new Runnable() {// @Override// public void run() {// // TODO: 2018/4/28 使用者即使退出了應用的話,這裡也是會執行的 ,透過日記的觀察// //這裡有可能使用者退出了Activity// System.out.println("shiming mHandler --todo");// }// },5000);
如何避免,有兩點的可以嘗試
1、在子執行緒中使用Handler,但是Handler不能再子執行緒中使用,需要開發者自己建立一個Looper物件,實現難,方法怪
2、將handler宣告為靜態的內部類,靜態內部類不會持有外部類的引用,因此,也不會引起記憶體洩露,
InnerHandler innerHandler = new InnerHandler(this); innerHandler.postDelayed(new Runnable() { @Override public void run() { //這裡這要 退出了 就不會執行了 System.out.println("shiming innerHandler --todo"); } },5000); public class InnerHandler extends Handler{ //弱應用,在另外一個地方會講到 private final WeakReferencemActivityWeakReference; public InnerHandler(HandlerActivity activity){ mActivityWeakReference=new WeakReference (activity); } }
Context正確的姿勢
//Context的種類 //Application 全域性唯一的Context例項 Application application = getApplication(); Context applicationContext = application.getApplicationContext(); //不同的Activity,得到這個Context,是獨立的,不會進行復用 Context baseContext = this.getBaseContext(); MyBroadcaseRecriver myBroadcaseRecriver = new MyBroadcaseRecriver(); //ContentProvider 中的Context /** *如果建立單利必須需要使用到context物件 */ //這樣不會記憶體洩露,不用改動單利類中程式碼 SingleInstance.getSingleInstance(getApplication().getApplicationContext());
單例模式,如果不得不傳入Context,由於單例一直存在會導致Activity或者是Service的單例引用,從而不會被垃圾回收, Activity中的關聯的View和資料結構也不會被釋放,正確的方式應該使用Application中的Context
class SingleInstance { private static SingleInstance sSingleInstance; private final Context mContext; private SingleInstance(Context context){ mContext = context; }// 因為每次呼叫例項都需要判斷同步鎖,很多專案包括很多人都是用這種的// 雙重判斷校驗的方法,這種的方法看似很完美的解決了效率的問題,但是它// 在併發量不多,安全性不太高的情況下能完美的執行,但是,// 在jvm編譯的過程中會出現指令重排的最佳化過程,這就會導致singleton實際上// 沒有被初始化,就分配了記憶體空間,也就是說singleton!=null但是又沒有被初始化,// 這就會導致返回的singletonthird返回的是不完整的 public static SingleInstance getSingleInstance(Context context){ if (sSingleInstance==null){ synchronized (SingleInstance.class){ if (sSingleInstance==null) { // TODO: 2018/4/28 注意外面傳入的conext物件是否,是哪個 sSingleInstance= new SingleInstance(context); //第二種是改動程式碼,使用application 中的context變數 sSingleInstance= new SingleInstance(context.getApplicationContext()); } } } return sSingleInstance; } }
java 四種引用方式和引用佇列的解釋
和java一樣,Android也是基於垃圾回收(GC)機制實現記憶體的自動的回收,垃圾回收的演算法“標記-清除(Mark-Sweep)” “標記壓縮(Mark-Compact)“複製演算法(Copying)以及引用計數演算法(Reference-Counting),安卓的虛擬機器(Dalvik還是Art),都是使用標記清除演算法。 在Android中,記憶體洩露是指不再使用的物件依然佔有記憶體,或者是他們佔用的記憶體沒有得到釋放, 從而導致記憶體空間不斷的減少,由於可用的空間比較少,發生記憶體洩露會使得記憶體更加的緊張,甚至最終由於記憶體耗盡而發生的OOM,導致應用的崩潰。
* 和java一樣,Android也是基於垃圾回收(GC)機制實現記憶體的自動的回收,垃圾回收的演算法“標記-清除(Mark-Sweep)” * “標記壓縮(Mark-Compact)“複製演算法(Copying)以及引用計數演算法(Reference-Counting),安卓的虛擬機器(Dalvik還是Art), * 都是使用標記清除演算法” * mTextView1.setText(des1); /** * 在Android中,記憶體洩露是指不再使用的物件依然佔有記憶體,或者是他們佔用的記憶體沒有得到釋放, * 從而導致記憶體空間不斷的減少,由於可用的空間比較少,發生記憶體洩露會使得記憶體更加的緊張, * 甚至最終由於記憶體耗盡而發生的OOM,導致應用的崩潰 */ mTextView2.setText(des2);
強引用:Java中裡面最廣泛的使用的一種,也是物件預設的引用型別,如果又一個物件具有強引用,那麼垃圾回收器是不會對它進行回收操作的,當記憶體的空間不足的時候,Java虛擬機器將會拋OutOfMemoryError錯誤,這時應用將會被終止執行
軟引用:一個物件如果只有一個軟引用,那麼當記憶體空間充足是,垃圾回收器不會對他進行回收操作,只有當記憶體空間不足的時候,這個物件才會被回收,軟引用可以用來實現記憶體敏感的快取記憶體,如果配合引用佇列(ReferenceQueue使用,當軟引用指向物件被垃圾回收器回收後,java會把這個軟引用加入到與之關聯的引用佇列中)
弱引用:弱引用是比軟引用更弱的一種的引用的型別,只有弱引用指向的物件的生命週期更短,當垃圾回收器掃描到只有具有弱引用的物件的時候,不敢當前空間是否不足,都會對弱引用物件進行回收,當然弱引用也可以和一個佇列配合著使用
引用佇列:ReferenceQueue一般是作為WeakReference SoftReference 的構造的函式引數傳入的,在WeakReference 或者是 softReference 的指向的物件被垃圾回收後,ReferenceQueue就是用來儲存這個已經被回收的Reference
String des3="強引用:Java中裡面最廣泛的使用的一種,也是物件預設的引用型別,如果又一個物件具有強引用,那麼垃圾回收器是不會對它進行回收操作的,當記憶體的空間不足的時候,Java虛擬機器將會丟擲OutOfMemoryError錯誤,這時應用將會被終止執行"; mTextView3.setText(des3); String des4="軟引用:一個物件如果只有一個軟引用,那麼當記憶體空間充足是,垃圾回收器不會對他進行回收操作,只有當記憶體空間不足的時候,這個物件才會被回收,軟引用可以用來實現記憶體敏感的快取記憶體,如果配合引用佇列(ReferenceQueue使用,當軟引用指向物件被垃圾回收器回收後,java會把這個軟引用加入到與之關聯的引用佇列中)"; Object obj=new Object(); SoftReference
將HashMap封裝成一個執行緒安全的集合,並且使用軟引用的方式防止OOM(記憶體不足)。由於在ListView中會載入大量的圖片.那麼為了有效的防止OOM導致程式終止的情況
/** * list中使用大量的bitmap,這種情況的話,我自己感覺使用的比較少 */ public class MemoryCache { //將HashMap封裝成一個執行緒安全的集合,並且使用軟引用的方式防止OOM(記憶體不足)... //由於在ListView中會載入大量的圖片.那麼為了有效的防止OOM導致程式終止的情況... private Map> cache=Collections.synchronizedMap(new HashMap >()); public Bitmap get(String id){ if(!cache.containsKey(id)) return null; SoftReference ref=cache.get(id); return ref.get(); } public void put(String id,Bitmap bitmap){ cache.put(id, new SoftReference (bitmap)); } public void clear(){ cache.clear(); } }
一個簡單的Demo
/** * author: Created by shiming on 2018/5/2 14:50 * mailbox:lamshiming@sina.com */public class EmployeeCache { static private EmployeeCache cache;// 一個Cache例項 private HashtableemployeeRefs;// 用於Chche內容的儲存 private ReferenceQueue q;// 垃圾Reference的佇列 // 繼承SoftReference,使得每一個例項都具有可識別的標識。 // 並且該標識與其在HashMap內的key相同。 public class EmployeeRef extends SoftReference { private String _key = ""; public EmployeeRef(Employee em, ReferenceQueue q) { super(em, q); _key = em.getID(); } } // 構建一個快取器例項 private EmployeeCache() { employeeRefs = new Hashtable (); q = new ReferenceQueue (); } // 取得快取器例項 public static EmployeeCache getInstance() { if (cache == null) { cache = new EmployeeCache(); } return cache; } // 以軟引用的方式對一個Employee物件的例項進行引用並儲存該引用 private void cacheEmployee(Employee em) { cleanCache();// 清除垃圾引用 EmployeeRef ref = new EmployeeRef(em, q); employeeRefs.put(em.getID(), ref); } // 依據所指定的ID號,重新獲取相應Employee物件的例項 public Employee getEmployee(String ID) { Employee em = null; // 快取中是否有該Employee例項的軟引用,如果有,從軟引用中取得。 if (employeeRefs.containsKey(ID)) { EmployeeRef ref = (EmployeeRef) employeeRefs.get(ID); em = (Employee) ref.get(); } // 如果沒有軟引用,或者從軟引用中得到的例項是null,重新構建一個例項, // 並儲存對這個新建例項的軟引用 if (em == null) { em = new Employee(ID); System.out.println("Retrieve From EmployeeInfoCenter. ID=" + ID); this.cacheEmployee(em); } return em; } // 清除那些所軟引用的Employee物件已經被回收的EmployeeRef物件 private void cleanCache() { EmployeeRef ref = null; while ((ref = (EmployeeRef) q.poll()) != null) { employeeRefs.remove(ref._key); } } // 清除Cache內的全部內容 public void clearCache() { cleanCache(); employeeRefs.clear(); //告訴垃圾收集器打算進行垃圾收集,而垃圾收集器進不進行收集是不確定的 System.gc(); //強制呼叫已經失去引用的物件的finalize方法 System.runFinalization(); } /** * 當垃圾收集器認為沒有指向物件例項的引用時,會在銷燬該物件之前呼叫finalize()方法。 * 該方法最常見的作用是確保釋放例項佔用的全部資源。java並不保證定時為物件例項呼叫該方法, * 甚至不保證方法會被呼叫,所以該方法不應該用於正常記憶體處理。 * @throws Throwable */ @Override protected void finalize() throws Throwable { super.finalize(); } }/** * author: Created by shiming on 2018/5/2 14:49 * mailbox:lamshiming@sina.com */public class Employee { private String id;// 僱員的標識號碼 private String name;// 僱員姓名 private String department;// 該僱員所在部門 private String Phone;// 該僱員聯絡電話 private int salary;// 該僱員薪資 private String origin;// 該僱員資訊的來源 // 構造方法 public Employee(String id) { this.id = id; getDataFromlnfoCenter(); } // 到資料庫中取得僱員資訊 private void getDataFromlnfoCenter() {// 和資料庫建立連線井查詢該僱員的資訊,將查詢結果賦值// 給name,department,plone,salary等變數// 同時將origin賦值為"From DataBase" } public String getID() { return id; } }
其他需要注意到的地方:
1、不要重複的建立相同的物件,物件的建立都是需要記憶體分配的,物件的銷燬需要垃圾回收,這些都在一定程度上影響程式的效能
2、對常量使用static final修飾,對於基本型別和String型別的常量,建議使用常量static final 修飾,因為final型別的常量會在靜態dex檔案的域初始化部分,這時對基本資料型別和String型別常量的呼叫不會涉及類的初始化,而是直接呼叫字面量
3、避免內部的get set方法的呼叫,get set的作用是對以外遮蔽具體的變數定義,從而達到更好的封裝性,如果在類的內部呼叫get set的方法訪問變數的話,會降低訪問的速度,根據在安卓的官方的文件,在沒有jit編譯器時,直接訪問變數的速度是呼叫get方法的3倍,在jit編譯器,直接訪問變數是呼叫get方法的7倍,當然使用了ProGuard的話,perGuard會對get set 進行內聯的操作,從而達到直接訪問的效果
關於JIT:
JIT是”Just In Time Compiler”的縮寫,就是”即時編譯技術”,與Dalvik虛擬機器相關,JIT是在2.2版本提出的,目的是為了提高Android的執行速度,一直存活到4.4版本,因為在4.4之後的ROM中,就不存在Dalvik虛擬機器了。
編譯打包APK檔案:1、Java編譯器將應用中所有Java檔案編譯為class檔案,2、dx工具將應用編譯輸出的類檔案轉換為Dalvik位元組碼,即dex檔案
Google在2.2版本新增了JIT編譯器,當App執行時,每當遇到一個新類,JIT編譯器就會對這個類進行編譯,經過編譯後的程式碼,會被最佳化成相當精簡的原生型指令碼(即native code),這樣在下次執行到相同邏輯的時候,速度就會更快。
dex位元組碼翻譯成本地機器碼是發生在應用程式的執行過程中的,並且應用程式每一次重新執行的時候,都要做重做這個翻譯工作,所以這個工作並不是一勞永逸,每次重新開啟App,都需要JIT編譯,Dalvik虛擬機器從Android一出生一直活到4.4版本,而JIT在Android剛釋出的時候並不存在,在2.2之後才被新增到Dalvik中。
AOT是”Ahead Of Time”的縮寫,指的就是ART(Anroid RunTime)這種執行方式。
JIT是執行時編譯,這樣可以對執行次數頻繁的dex程式碼進行編譯和最佳化,減少以後使用時的翻譯時間,雖然可以加快Dalvik執行速度,但是還是有弊病,那就是將dex翻譯為本地機器碼也要佔用時間,所以Google在4.4之後推出了ART,用來替換Dalvik。
ART的策略與Dalvik不同,在ART 環境中,應用在第一次安裝的時候,位元組碼就會預先編譯成機器碼,使其成為真正的本地應用。之後開啟App的時候,不需要額外的翻譯工作,直接使用本地機器碼執行,因此執行速度提高。
當然ART與Dalvik相比,還是有缺點的。
ART需要應用程式在安裝時,就把程式程式碼轉換成機器語言,所以這會消耗掉更多的儲存空間,但消耗掉空間的增幅通常不會超過應用程式碼包大小的20%
由於有了一個轉碼的過程,所以應用安裝時間難免會延長
但是這些與更流暢的Android體驗相比而言,不值一提。
圖片最佳化
四種圖片格式
JPEG
是一種廣泛使用的有失真壓縮影像標準格式,它不支援透明和多幀動畫,一般攝影的作品是JEPG格式的,透過控制壓縮比,可以調整圖片的大小
PNG
是一種無失真壓縮的圖片格式,他支援完整的透明通道,從圖片處理的領域來講,JEPG只有RGB三個通道,而PNG有ARGB四個通道,因此PNG圖片佔用空間一般比較大,會無形的增加app的大小,在做app瘦身時一般都要對PNG圖片進行梳理以減小其佔用的體積
GIF
是一種古老的圖片的格式,誕生於1987年,隨著初代網際網路流行開來,他的特別是支援多幀動畫,表情圖,
Webp
google於2010年釋出,支援有損和無損、支援完整的透明通道、也支援多幀動畫,目前主流的APP都已經使用了Webp,淘寶,微信,即保證了圖片的大小和質量
在安卓應用開發中能夠使用編解碼格式的只有三種 JEPG PNG WEBP
/** * 在安卓應用開發中能夠使用編解碼格式的只有三種 JEPG PNG WEBP */ public enum CompressFormat { JPEG (0), PNG (1), WEBP (2);//安卓4.0後開始支援 CompressFormat(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; }
推薦幾種圖片處理網站
無失真壓縮ImageOptin,在不犧牲圖片質量的前提下,即減下來PNG圖片佔用的空間,又提高了圖片的載入速度 https://imageoptim.com/api
有失真壓縮ImageAlpha,圖片大小得到極大的縮小,如果需要使用的話,一定要ui設計師看能否使用
有失真壓縮TinyPNG 比較知名的png壓縮的工具,也需要ui設計師看能夠使用不
PNG/JPEG 轉化為 wepb :智圖 :
如果ui設計師工作量不飽和的話,可以推薦, 儘量使用 .9.png 點9圖 小黑點表示 可拉伸區域,黑邊表示縱向顯示內容的範圍
佈局最佳化:如果建立的層級結構比較複雜,View樹巢狀的層次比較深,那麼將會使得頁面的響應的時間變長,導致執行的時候越來越慢
merge標籤(對安卓的事件傳遞要達到原始碼級的熟悉才可以理解) 在某些場景下可以減少佈局的層次,由於所有的Activity的根佈局都是FrameLayout Window PhoneWindow DecorView 事件的傳遞,包括設定setContentView 等的方法---> 我會寫一篇文章獨立解釋安卓事件的原始碼解析,會更加清楚的介紹這個類,(對安卓的事件傳遞要達到原始碼級的熟悉才可以理解)todo標籤來代替FrameLayout佈局。另外一種的情況可以使用《merge》便籤的情況是當前佈局作為另外一個佈局的子佈局
在安卓中經常會使用到相同的佈局,比如說title,最佳的實踐的方法就是把相同的佈局抽取出來,獨立成一個xml檔案,需要使用到的時候,就把這個佈局include進來,不僅減少了程式碼量,而且修改這個相同的佈局,只需要修改一個地方即可.
ViewStub 是一種不可見的並且大小為0的試圖,它可以延遲到執行時才填充inflate 佈局資源,當Viewstub設為可見或者是inflate的時候,就會填充佈局資源,這個佈局和普通的試圖就基本上沒有任何區別,比如說,載入網路失敗,或者是一個比較消耗效能的功能,需要使用者去點選才可以載入,參考我的開源的專案
注意事項:如果這個根佈局是個View,比如說是個ImagView,那麼找出來的id為null,得必須注意這一點
修正這個說法,以前我說的是錯誤的,根本上的原因是ViewStub設定了 inflateid ,這才是更本身的原因,對不起!搞錯了,還是要看原始碼
<!--如果這個根佈局是個View,比如說是個ImagView,那麼找出來的id為null,得必須注意這一點-->
呼叫todo: 2018/5/4 為啥為null 原因是佈局檔案中根佈局只有View,沒有ViewGroup,ViewStub.inflate() 的方法和 setVisibility 方法是差不多,因為 setVisibility方法會(看原始碼)走這個inflate的方法
if (null!=mViewStub.getParent()){ /* android:inflatedId 的值是Java程式碼中呼叫ViewStub的 inflate()或者是serVisibility方法返回的Id,這個id就是被填充的View的Id */ /** * ViewStub.inflate() 的方法和 setVisibility 方法是差不多,因為 setVisibility方法會(看原始碼)走這個inflate的方法 */// View inflate = mViewStub.inflate(); mViewStub.setVisibility(View.VISIBLE); //inflate--->android.support.v7.widget.AppCompatImageView{de7e3a2 V.ED..... ......I. 0,0-0,0 #7f07003e app:id/find_view_stub}// System.out.println("shiming inflate--->"+inflate); final View find_view_stub = findViewById(R.id.find_view_stub); System.out.println("shiming ----"+find_view_stub); View iamgeivew11 = find_view_stub.findViewById(R.id.imageview); //himing ---- iamgeivew11null // TODO: 2018/5/4 為啥為null 原因是佈局檔案中根佈局只有View,沒有ViewGroup System.out.println("shiming ---- iamgeivew11"+iamgeivew11); }else{ Toast.makeText(LayoutOptimizationActivity.this,"已經inflate了",Toast.LENGTH_LONG).show(); final View viewById = findViewById(R.id.find_view_stub); View iamgeivew = findViewById(R.id.imageview); //已經inflate了android.support.v7.widget.AppCompatImageView{4637833 V.ED..... ........ 348,294-732,678 #7f07003e app:id/find_view_stub} System.out.println("shiming l----已經inflate了"+viewById);// System.out.println("shiming l----已經inflate了iamgeivew"+iamgeivew);//已經inflate了iamgeivew==null View iamgeivew11 = viewById.findViewById(R.id.imageview); //已經inflate了 iamgeivew11null System.out.println("shiming l----已經inflate了 iamgeivew11"+iamgeivew11); } }
儘量使用CompoundDrawable,如果存在相鄰的ImageView和TextView 的話
使用Lint 檢查程式碼,和佈局是否可以存在最佳化的地方,我會寫個簡單的經常遇見過的問題,同時完成一篇文件,加以說明,地址
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4301/viewspace-2806493/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 安卓程式碼、圖片、佈局、網路和電量優化安卓優化
- Swift – 一句程式碼搞定button圖片和文字佈局Swift
- CSS 來佈局圖片CSS
- UIbutton 上下圖片文字佈局UI
- 安卓.9圖片製作安卓
- 【圖片版】學習CSS網格佈局CSS
- 【圖片版】CSS網格佈局(Grid)完全教程CSS
- flutter佈局-8-animated_icons動畫圖片Flutter動畫
- Heic格式圖片怎麼開啟 如何在電腦和安卓上開啟蘋果圖片安卓蘋果
- 安卓圖片顯示與網路訪問安卓
- heic格式的圖片安卓怎麼開啟安卓
- 安卓之viewPager簡單用法圖片輪播安卓Viewpager
- 圖片拼接大師v1.0安卓版安卓
- c程式的記憶體佈局圖C程式記憶體
- flex居中佈局程式碼例項Flex
- 浮動佈局 和 flex佈局Flex
- 現代圖片效能最佳化及體驗最佳化指南 - 縮放精細化展示及避免佈局偏移、拉伸
- iOS 中使用 FlexBox 佈局實現圖片九宮格iOSFlex
- flex彈性佈局程式碼例項Flex
- flex聖盃佈局程式碼例項Flex
- Android 相對佈局RelativeLayout 程式碼示例Android
- 響應式佈局程式碼例項
- [譯] 使用 CSS 網格佈局實現響應式圖片CSS
- JavaScript 5/30:Flex Panel Gallery即FLEX佈局的圖片庫JavaScriptFlex
- IOS14桌面佈局圖片怎麼設定iOS
- 安卓6.0以上從相簿選擇圖片,圖片壓縮及動態許可權安卓
- CSS 兩欄佈局和三欄佈局CSS
- 雙飛翼佈局和聖盃佈局
- 聖盃佈局和雙飛翼佈局
- CSS 三欄佈局之聖盃佈局和雙飛翼佈局CSS
- 直播網站程式原始碼,FlowLayoutManager 流式佈局網站原始碼
- 修改驍龍splash.img安卓啟動圖片 - 需要root安卓
- 原生js實現圖片瀑布流佈局,註釋超詳細JS
- Android中常見的佈局和佈局引數Android
- css聖盃佈局和雙飛翼佈局CSS
- CSS佈局–聖盃佈局和雙飛翼佈局以及使用Flex實現聖盃佈局CSSFlex
- 前端效能最佳化——圖片最佳化前端
- flex結合媒體查詢佈局程式碼Flex