安卓代碼蔫巩、圖片和布局優(yōu)化

這篇文章分為三個部分代碼優(yōu)化、圖片優(yōu)化、布局優(yōu)化圆仔,盡量每個方法都寫了小的Demo垃瞧!
Picture
代碼優(yōu)化:不要做多余的工作,盡量避免次數(shù)過多的內(nèi)存的分配坪郭,(需要對api有一定的熟悉)

數(shù)據(jù)集合的使用:建議最佳的做法是可能使用ArrayList作為首選个从,只要你需要使用額外的功能的時候,或者當程序性能由于經(jīng)常從表的中間進行插入和刪除而變差的時候歪沃,才會去選擇LinkedList嗦锐。HashMap性能上于HashTable相當,因為HashMap和HashTable在底層的存儲和查找機制是一樣的沪曙,但是TreeMap通常比HashMap要慢奕污。HashSet總體上的性能比TreeSet好,特別實在添加和查詢元素的時候液走,而這兩個操作也是最重要的操作菊值。TreeSet存在的唯一的原因是它可以維持元素的排序的狀態(tài),所以當需要一個排好序的Set育灸,才使用TreeSet腻窒。因為其內(nèi)部的結果歐支持排序,并且因為迭代是我們更有可能執(zhí)行的操作磅崭,所以儿子,用TreeSet迭代通常比用HashSet要快。

  /**
         * 建議最佳的做法是可能使用ArrayList作為首選砸喻,只要你需要使用額外的功能的時候柔逼,或者當程序性能由于經(jīng)常從表的中間進行
         * 插入和刪除而變差的時候,才會去選擇LinkedList
         */
        //數(shù)據(jù)結構的選擇割岛,對于ArrayList愉适,插入的操作特別高昂,并且其代價將隨著列表的尺寸的增加而增加
        ArrayList list=new ArrayList();
        //需要執(zhí)行大量的隨機的訪問癣漆,這個不是一個好的選擇维咸,如果是使用迭代器在列表中插入新的數(shù)據(jù),使用這個惠爽,比較低廉(插入和移除的代價比較低廉)
        LinkedList linkedList=new LinkedList();
        //HashMap性能上于HashTable相當癌蓖,因為HashMap和HashTable在底層的存儲和查找機制是一樣的,但是TreeMap通常比HashMap要慢
        HashMap<String,String> hashMap=new HashMap<>();
        /**
         * HashSet總體上的性能比TreeSet好婚肆,特別實在添加和查詢元素的時候租副,而這兩個操作也是最重要的操作。TreeSet存在的唯一的原因是它
         * 可以維持元素的排序的狀態(tài)较性,所以當需要一個排好序的Set用僧,才使用TreeSet结胀。因為其內(nèi)部的結果歐支持排序,并且因為迭代是我們更有可能
         * 執(zhí)行的操作责循,所以糟港,用TreeSet迭代通常比用HashSet要快
         */
        HashSet<String>  hashSet=new HashSet<>();

        TreeSet<String> treeSet=new TreeSet<>();

SparseArray是Android特有的稀疏數(shù)組的實現(xiàn),他是Integer和Object的為例進行的一個映射用于代替 HsahMap<Integer,<E>>,提高性能沼死。

  • SparseArray

  • 線程不安全(多線程中需要注意)

  • 由于要進行二分查找,(可以是有序的)崔赌,SparseArray會對插入的數(shù)據(jù)按照Key的大小順序插入

  • SparseArray對刪除操作做了優(yōu)化意蛀,它并不會立刻刪除這個元素,而是通過設置標記位(DELETED)的方法健芭,后面嘗試重用县钥。

內(nèi)部核心的實現(xiàn)(二分查找)

 /**
     * 二分查找也稱折半查找(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 <= hi) {
            /**
             * >>>與>>唯一的不同是它無論原來的最左邊是什么數(shù)谴麦,統(tǒng)統(tǒng)都用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) {
                lo = mid + 1;
            } else if (midVal > value) {
                hi = mid - 1;
            } else {
                return mid;  // value found
            }
        }
        //按位取反(~)運算符 ,沒有找到扫步,這個數(shù)據(jù)
        return ~lo;  // value not present
    }

SpareArray 家族有以下的四類

          //SpareArray 家族有以下的四類
        //用于替換    HashMap<Integer,boolean>
        SparseBooleanArray sparseBooleanArray=new SparseBooleanArray();
        sparseBooleanArray.append(1,false);
        //用于替換    HashMap<Integer,Interger>
        SparseIntArray SparseIntArray=new SparseIntArray();
        SparseIntArray.append(1,1);
        //用于替換    HashMap<Integer,boolean>

        @SuppressLint({"NewApi", "LocalSuppress"})
        SparseLongArray SparseLongArray=new SparseLongArray();
        SparseLongArray.append(1,1111000L);
        //用于替換    HashMap<Integer,boolean>
        SparseArray<String> SparseArray11=new SparseArray<String>();
        SparseArray11.append(1,"dd");

SpareArray中的設計模式:原型模式:這里有使用到了的魔策,原型模式內(nèi)存中復制數(shù)據(jù)的,不會調(diào)用到類的構造的方法河胎,而且訪問的權限對原型模式無效

  • 優(yōu)點: 1闯袒、性能提高。 2游岳、逃避構造函數(shù)的約束搁吓。

  • 缺點:
    1、配備克隆方法需要對類的功能進行通盤考慮吭历,這對于全新的類不是很難堕仔,但對于已有的類不一定很容易,特別當一個類引用不支持串行化的間接對象晌区,或者引用含有循環(huán)結構的時候摩骨。

2通贞、必須實現(xiàn) Cloneable 接口。

    SparseArray<String> clone = sparseArray.clone();

Handler正確的使用姿勢
下面的代碼是很多人都會這樣寫恼五,這樣會造成內(nèi)存泄漏
原因:Handler是和Looper以及MessageQueue一起工作的昌罩,在安卓中,一個 應用啟動了灾馒,系統(tǒng)會默認創(chuàng)建一個主線程服務的Looper對象 茎用,該Looper對象處理主線程的所有的Message消息,他的生命周期貫穿整個應用睬罗。在主線程中使用的Handler的都會默認的綁定到這個looper的對象轨功,咋主線程中創(chuàng)建handler的時候,它會立即關聯(lián)主線程Looper對象的MessageQueue容达,這時發(fā)送到的MessageQueue 中的Message對象都會持有這個Handler的對象的引用古涧,這樣Looper處理消息時Handler的handlerMessage的方法,因此花盐,如果Message還沒有處理完成羡滑,那么handler的對象不會立即被垃圾回收

   /*-------------old ide 已經(jīng)告訴我們這里可能內(nèi)存泄露-------------------*/
    @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  用戶即使退出了應用的話,這里也是會執(zhí)行的 算芯,通過日記的觀察
//                //這里有可能用戶退出了Activity
//                System.out.println("shiming mHandler --todo");
//            }
//        },5000);

如何避免柒昏,有兩點的可以嘗試

  • 1、在子線程中使用Handler熙揍,但是Handler不能再子線程中使用昙楚,需要開發(fā)者自己創(chuàng)建一個Looper對象,實現(xiàn)難诈嘿,方法怪

  • 2堪旧、將handler聲明為靜態(tài)的內(nèi)部類,靜態(tài)內(nèi)部類不會持有外部類的引用奖亚,因此淳梦,也不會引起內(nèi)存泄露,


    InnerHandler innerHandler = new InnerHandler(this);
        innerHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //這里這要 退出了 就不會執(zhí)行了
                System.out.println("shiming innerHandler --todo");

            }
        },5000);
  public class InnerHandler extends Handler{
        //弱應用昔字,在另外一個地方會講到
        private final WeakReference<HandlerActivity> mActivityWeakReference;

        public InnerHandler(HandlerActivity activity){
            mActivityWeakReference=new WeakReference<HandlerActivity>(activity);
        }
    }

Context正確的姿勢

   //Context的種類
        //Application 全局唯一的Context實例
        Application application = getApplication();
        Context applicationContext = application.getApplicationContext();
        //不同的Activity爆袍,得到這個Context,是獨立的作郭,不會進行復用
        Context baseContext = this.getBaseContext();

        MyBroadcaseRecriver myBroadcaseRecriver = new MyBroadcaseRecriver();

        //ContentProvider 中的Context

        /**
         *如果創(chuàng)建單利必須需要使用到context對象
         */
        //這樣不會內(nèi)存泄露陨囊,不用改動單利類中代碼
        SingleInstance.getSingleInstance(getApplication().getApplicationContext());
  • 單例模式,如果不得不傳入Context夹攒,由于單例一直存在會導致Activity或者是Service的單例引用蜘醋,從而不會被垃圾回收, Activity中的關聯(lián)的View和數(shù)據(jù)結構也不會被釋放咏尝,正確的方式應該使用Application中的Context
class SingleInstance {
    private static SingleInstance sSingleInstance;
    private final Context mContext;

    private SingleInstance(Context context){
        mContext = context;
    }
//    因為每次調(diào)用實例都需要判斷同步鎖压语,很多項目包括很多人都是用這種的
//    雙重判斷校驗的方法啸罢,這種的方法看似很完美的解決了效率的問題,但是它
//    在并發(fā)量不多胎食,安全性不太高的情況下能完美的運行扰才,但是,
//    在jvm編譯的過程中會出現(xiàn)指令重排的優(yōu)化過程厕怜,這就會導致singleton實際上
//    沒有被初始化衩匣,就分配了內(nèi)存空間,也就是說singleton粥航!=null但是又沒有被初始化琅捏,
//    這就會導致返回的singletonthird返回的是不完整的
    public static SingleInstance getSingleInstance(Context context){
        if (sSingleInstance==null){
            synchronized (SingleInstance.class){
                if (sSingleInstance==null)   {
                    // TODO: 2018/4/28 注意外面?zhèn)魅氲腸onext對象是否,是哪個 
                    sSingleInstance= new SingleInstance(context);
                    //第二種是改動代碼躁锡,使用application 中的context變量
                    sSingleInstance= new SingleInstance(context.getApplicationContext());

                }
            }

        }
        return sSingleInstance;
    }
}
  • java 四種引用方式和引用隊列的解釋
    和java一樣午绳,Android也是基于垃圾回收(GC)機制實現(xiàn)內(nèi)存的自動的回收置侍,垃圾回收的算法“標記-清除(Mark-Sweep)” “標記壓縮(Mark-Compact)“復制算法(Copying)以及引用計數(shù)算法(Reference-Counting)映之,安卓的虛擬機(Dalvik還是Art),都是使用標記清除算法蜡坊。 在Android中杠输,內(nèi)存泄露是指不再使用的對象依然占有內(nèi)存,或者是他們占用的內(nèi)存沒有得到釋放秕衙, 從而導致內(nèi)存空間不斷的減少蠢甲,由于可用的空間比較少,發(fā)生內(nèi)存泄露會使得內(nèi)存更加的緊張据忘,甚至最終由于內(nèi)存耗盡而發(fā)生的OOM鹦牛,導致應用的崩潰。
         * 和java一樣勇吊,Android也是基于垃圾回收(GC)機制實現(xiàn)內(nèi)存的自動的回收曼追,垃圾回收的算法“標記-清除(Mark-Sweep)”
         * “標記壓縮(Mark-Compact)“復制算法(Copying)以及引用計數(shù)算法(Reference-Counting),安卓的虛擬機(Dalvik還是Art)汉规,
         * 都是使用標記清除算法”
         *

        mTextView1.setText(des1);

        /**
         * 在Android中礼殊,內(nèi)存泄露是指不再使用的對象依然占有內(nèi)存,或者是他們占用的內(nèi)存沒有得到釋放针史,
         * 從而導致內(nèi)存空間不斷的減少晶伦,由于可用的空間比較少,發(fā)生內(nèi)存泄露會使得內(nèi)存更加的緊張啄枕,
         * 甚至最終由于內(nèi)存耗盡而發(fā)生的OOM婚陪,導致應用的崩潰
         */

        mTextView2.setText(des2);
  • 強引用:Java中里面最廣泛的使用的一種,也是對象默認的引用類型频祝,如果又一個對象具有強引用近忙,那么垃圾回收器是不會對它進行回收操作的竭业,當內(nèi)存的空間不足的時候,Java虛擬機將會拋OutOfMemoryError錯誤及舍,這時應用將會被終止運行

  • 軟引用:一個對象如果只有一個軟引用未辆,那么當內(nèi)存空間充足是,垃圾回收器不會對他進行回收操作锯玛,只有當內(nèi)存空間不足的時候咐柜,這個對象才會被回收,軟引用可以用來實現(xiàn)內(nèi)存敏感的高速緩存攘残,如果配合引用隊列(ReferenceQueue使用拙友,當軟引用指向?qū)ο蟊焕厥掌骰厥蘸螅琷ava會把這個軟引用加入到與之關聯(lián)的引用隊列中)

  • 弱引用:弱引用是比軟引用更弱的一種的引用的類型歼郭,只有弱引用指向的對象的生命周期更短遗契,當垃圾回收器掃描到只有具有弱引用的對象的時候,不敢當前空間是否不足病曾,都會對弱引用對象進行回收牍蜂,當然弱引用也可以和一個隊列配合著使用

  • 引用隊列:ReferenceQueue一般是作為WeakReference SoftReference 的構造的函數(shù)參數(shù)傳入的,在WeakReference 或者是 softReference 的指向的對象被垃圾回收后泰涂,ReferenceQueue就是用來保存這個已經(jīng)被回收的Reference


        String des3="強引用:Java中里面最廣泛的使用的一種鲫竞,也是對象默認的引用類型,如果又一個對象具有強引用逼蒙,那么垃圾回收器是不會對它進行回收操作的从绘,當內(nèi)存的空間不足的時候,Java虛擬機將會拋出OutOfMemoryError錯誤是牢,這時應用將會被終止運行";

        mTextView3.setText(des3);
        String des4="軟引用:一個對象如果只有一個軟引用僵井,那么當內(nèi)存空間充足是,垃圾回收器不會對他進行回收操作驳棱,只有當內(nèi)存空間不足的時候批什,這個對象才會被回收,軟引用可以用來實現(xiàn)內(nèi)存敏感的高速緩存蹈胡,如果配合引用隊列(ReferenceQueue使用渊季,當軟引用指向?qū)ο蟊焕厥掌骰厥蘸螅琷ava會把這個軟引用加入到與之關聯(lián)的引用隊列中)";

        Object obj=new Object();

        SoftReference<Object> sr = new SoftReference<>(obj);//這里使用了軟引用...
        /*
         *在這個期間罚渐,有可能會出現(xiàn)內(nèi)存不足的情況發(fā)生却汉,那么GC就會直接把所有的軟引用全部清除..并釋放內(nèi)存空間
         *如果內(nèi)存空間足夠的話,那么就GC就不會進行工作...
         *GC的工作取決于內(nèi)存的大小荷并,以及其內(nèi)部的算法,,,,
         */
        if(sr!=null){
            //如果軟引用還存在合砂,那么直接就可以獲取這個對象的相關數(shù)據(jù)...這樣就實現(xiàn)了cache...
            obj = sr.get();

        }else{
            //如果已經(jīng)不存在,表示GC已經(jīng)將其回收源织,我們需要重新實例化對象翩伪,獲取數(shù)據(jù)信息...
            obj = new Object();
            sr = new SoftReference<>(obj);
        }
        mTextView4.setText(des4);
        String des5="弱引用:弱引用是比軟引用更弱的一種的引用的類型微猖,只有弱引用指向的對象的生命周期更短,當垃圾回收器掃描到只有具有弱引用的對象的時候缘屹,不敢當前空間是否不足凛剥,都會對弱引用對象進行回收,當然弱引用也可以和一個隊列配合著使用";

        Object obj1 = new Object();
        WeakReference<Object> weakProductA = new WeakReference<>(obj1);

        mTextView5.setText(des5);
        String des6="虛引用:和軟引用和弱引用不同轻姿,虛引用并不會對所指向的對象生命周期產(chǎn)生任何影響犁珠,也就是對象還是會按照它原來的方式別垃圾回收期回收,虛引用本質(zhì)上只是有一個標記作用互亮,主要用來跟蹤對象被垃圾回收的活動犁享,虛引用必須和引用隊列配合使用,當對象被垃圾回收時豹休,如果存在虛引用炊昆,那么Java虛擬機會將這個虛引用加入到與之關聯(lián)的引用隊列中";

        mTextView6.setText(des6);
        /**
         * 如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣威根,在任何時候都可能被垃圾回收器回收凤巨。
         * 虛引用主要用來跟蹤對象被垃圾回收器回收的活動
         */
        // TODO: 2018/5/2 程序可以通過判斷引用隊列中是否已經(jīng)加入了虛引用,
        // 來了解被引用的對象是否將要被垃圾回收医窿。如果程序發(fā)現(xiàn)某個虛引用已經(jīng)被加入到引用隊列磅甩,
        // 那么就可以在所引用的對象的內(nèi)存被回收之前采取必要的行動炊林。
        ReferenceQueue queue = new ReferenceQueue ();
        PhantomReference pr = new PhantomReference<Object>(obj1, queue);

        String des7="引用隊列:ReferenceQueue一般是作為WeakReference SoftReference 的構造的函數(shù)參數(shù)傳入的姥卢,在WeakReference 或者是 softReference 的指向的對象被垃圾回收后,ReferenceQueue就是用來保存這個已經(jīng)被回收的Reference";
        mTextView7.setText(des7);

  • 將HashMap封裝成一個線程安全的集合渣聚,并且使用軟引用的方式防止OOM(內(nèi)存不足)独榴。由于在ListView中會加載大量的圖片.那么為了有效的防止OOM導致程序終止的情況
  /**
     * list中使用大量的bitmap,這種情況的話奕枝,我自己感覺使用的比較少
     */
    public class MemoryCache {
        //將HashMap封裝成一個線程安全的集合棺榔,并且使用軟引用的方式防止OOM(內(nèi)存不足)...
        //由于在ListView中會加載大量的圖片.那么為了有效的防止OOM導致程序終止的情況...
        private Map<String,SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());

        public Bitmap get(String id){
            if(!cache.containsKey(id))
                return null;
            SoftReference<Bitmap>ref=cache.get(id);
            return ref.get();
        }

        public void put(String id,Bitmap bitmap){
            cache.put(id, new SoftReference<Bitmap>(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 Hashtable<String, EmployeeRef> employeeRefs;// 用于Chche內(nèi)容的存儲
    private ReferenceQueue<Employee> q;// 垃圾Reference的隊列

     // 繼承SoftReference,使得每一個實例都具有可識別的標識隘道。
    // 并且該標識與其在HashMap內(nèi)的key相同症歇。
    public class EmployeeRef extends SoftReference<Employee> {
        private String _key = "";
        public EmployeeRef(Employee em, ReferenceQueue<Employee> q) {
            super(em, q);
            _key = em.getID();
        }
    }

    // 構建一個緩存器實例
    private EmployeeCache() {
        employeeRefs = new Hashtable<String, EmployeeRef>();
        q = new ReferenceQueue<Employee>();
    }

    // 取得緩存器實例
    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);
    }

    // 依據(jù)所指定的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對象已經(jīng)被回收的EmployeeRef對象
    private void cleanCache() {
        EmployeeRef ref = null;
        while ((ref = (EmployeeRef) q.poll()) != null) {
            employeeRefs.remove(ref._key);
        }
    }

    // 清除Cache內(nèi)的全部內(nèi)容
    public void clearCache() {
        cleanCache();
        employeeRefs.clear();
        //告訴垃圾收集器打算進行垃圾收集远舅,而垃圾收集器進不進行收集是不確定的
        System.gc();
        //強制調(diào)用已經(jīng)失去引用的對象的finalize方法
        System.runFinalization();
    }

    /**
     * 當垃圾收集器認為沒有指向?qū)ο髮嵗囊脮r闰蛔,會在銷毀該對象之前調(diào)用finalize()方法痕钢。
     * 該方法最常見的作用是確保釋放實例占用的全部資源猪叙。java并不保證定時為對象實例調(diào)用該方法蚣录,
     * 甚至不保證方法會被調(diào)用,所以該方法不應該用于正常內(nèi)存處理千绪。
     * @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;// 該雇員聯(lián)系電話
    private int salary;// 該雇員薪資
    private String origin;// 該雇員信息的來源

    // 構造方法
    public Employee(String id) {
        this.id = id;
        getDataFromlnfoCenter();
    }
    // 到數(shù)據(jù)庫中取得雇員信息
    private void getDataFromlnfoCenter() {
// 和數(shù)據(jù)庫建立連接井查詢該雇員的信息例诀,將查詢結果賦值
// 給name课梳,department,plone余佃,salary等變量
// 同時將origin賦值為"From DataBase"
    }

    public String getID() {
        return id;
    }
}

其他需要注意到的地方:

  • 1暮刃、不要重復的創(chuàng)建相同的對象,對象的創(chuàng)建都是需要內(nèi)存分配的爆土,對象的銷毀需要垃圾回收椭懊,這些都在一定程度上影響程序的性能

  • 2、對常量使用static final修飾步势,對于基本類型和String類型的常量氧猬,建議使用常量static final 修飾,因為final類型的常量會在靜態(tài)dex文件的域初始化部分坏瘩,這時對基本數(shù)據(jù)類型和String類型常量的調(diào)用不會涉及類的初始化盅抚,而是直接調(diào)用字面量

  • 3、避免內(nèi)部的get set方法的調(diào)用倔矾,get set的作用是對以外屏蔽具體的變量定義妄均,從而達到更好的封裝性,如果在類的內(nèi)部調(diào)用get set的方法訪問變量的話哪自,會降低訪問的速度丰包,根據(jù)在安卓的官方的文檔,在沒有jit編譯器時壤巷,直接訪問變量的速度是調(diào)用get方法的3倍邑彪,在jit編譯器,直接訪問變量是調(diào)用get方法的7倍胧华,當然使用了ProGuard的話寄症,perGuard會對get set 進行內(nèi)聯(lián)的操作,從而達到直接訪問的效果

關于JIT:

  • JIT是”Just In Time Compiler”的縮寫矩动,就是”即時編譯技術”有巧,與Dalvik虛擬機相關,JIT是在2.2版本提出的,目的是為了提高Android的運行速度铅忿,一直存活到4.4版本剪决,因為在4.4之后的ROM中,就不存在Dalvik虛擬機了。

  • 編譯打包APK文件:1柑潦、Java編譯器將應用中所有Java文件編譯為class文件,2享言、dx工具將應用編譯輸出的類文件轉(zhuǎn)換為Dalvik字節(jié)碼,即dex文件

  • Google在2.2版本添加了JIT編譯器渗鬼,當App運行時览露,每當遇到一個新類,JIT編譯器就會對這個類進行編譯譬胎,經(jīng)過編譯后的代碼差牛,會被優(yōu)化成相當精簡的原生型指令碼(即native code),這樣在下次執(zhí)行到相同邏輯的時候堰乔,速度就會更快偏化。

  • dex字節(jié)碼翻譯成本地機器碼是發(fā)生在應用程序的運行過程中的,并且應用程序每一次重新運行的時候镐侯,都要做重做這個翻譯工作侦讨,所以這個工作并不是一勞永逸,每次重新打開App苟翻,都需要JIT編譯韵卤,Dalvik虛擬機從Android一出生一直活到4.4版本,而JIT在Android剛發(fā)布的時候并不存在崇猫,在2.2之后才被添加到Dalvik中沈条。

  • AOT是”Ahead Of Time”的縮寫,指的就是ART(Anroid RunTime)這種運行方式诅炉。

  • JIT是運行時編譯蜡歹,這樣可以對執(zhí)行次數(shù)頻繁的dex代碼進行編譯和優(yōu)化,減少以后使用時的翻譯時間汞扎,雖然可以加快Dalvik運行速度季稳,但是還是有弊病擅这,那就是將dex翻譯為本地機器碼也要占用時間澈魄,所以Google在4.4之后推出了ART,用來替換Dalvik仲翎。

  • ART的策略與Dalvik不同痹扇,在ART 環(huán)境中,應用在第一次安裝的時候溯香,字節(jié)碼就會預先編譯成機器碼鲫构,使其成為真正的本地應用。之后打開App的時候玫坛,不需要額外的翻譯工作结笨,直接使用本地機器碼運行,因此運行速度提高。

  • 當然ART與Dalvik相比炕吸,還是有缺點的伐憾。

  • ART需要應用程序在安裝時,就把程序代碼轉(zhuǎn)換成機器語言赫模,所以這會消耗掉更多的存儲空間树肃,但消耗掉空間的增幅通常不會超過應用代碼包大小的20%

  • 由于有了一個轉(zhuǎn)碼的過程,所以應用安裝時間難免會延長

  • 但是這些與更流暢的Android體驗相比而言瀑罗,不值一提胸嘴。

圖片優(yōu)化

四種圖片格式

  • JPEG

  • 是一種廣泛使用的有損壓縮圖像標準格式,它不支持透明和多幀動畫斩祭,一般攝影的作品是JEPG格式的劣像,通過控制壓縮比,可以調(diào)整圖片的大小

  • PNG

  • 是一種無損壓縮的圖片格式摧玫,他支持完整的透明通道驾讲,從圖片處理的領域來講,JEPG只有RGB三個通道席赂,而PNG有ARGB四個通道吮铭,因此PNG圖片占用空間一般比較大,會無形的增加app的大小颅停,在做app瘦身時一般都要對PNG圖片進行梳理以減小其占用的體積

  • GIF

  • 是一種古老的圖片的格式谓晌,誕生于1987年,隨著初代互聯(lián)網(wǎng)流行開來癞揉,他的特別是支持多幀動畫纸肉,表情圖,

  • Webp

  • google于2010年發(fā)布喊熟,支持有損和無損、支持完整的透明通道芥牌、也支持多幀動畫,目前主流的APP都已經(jīng)使用了Webp壁拉,淘寶,微信弃理,即保證了圖片的大小和質(zhì)量

  • 在安卓應用開發(fā)中能夠使用編解碼格式的只有三種 JEPG PNG WEBP

   /**
     * 在安卓應用開發(fā)中能夠使用編解碼格式的只有三種 JEPG PNG WEBP
     */
    public enum CompressFormat {
        JPEG    (0),
        PNG     (1),
        WEBP    (2);//安卓4.0后開始支持

        CompressFormat(int nativeInt) {
            this.nativeInt = nativeInt;
        }
        final int nativeInt;
    }

推薦幾種圖片處理網(wǎng)站

  • 無損壓縮ImageOptin,在不犧牲圖片質(zhì)量的前提下钥勋,即減下來PNG圖片占用的空間,又提高了圖片的加載速度 https://imageoptim.com/api
Picture
  • 有損壓縮ImageAlpha算灸,圖片大小得到極大的縮小返吻,如果需要使用的話,一定要ui設計師看能否使用 https://pngmini.com/

  • 有損壓縮TinyPNG 比較知名的png壓縮的工具乎婿,也需要ui設計師看能夠使用不https://tinypng.com/

  • PNG/JPEG 轉(zhuǎn)化為 wepb :智圖 :http://zhitu.isux.us/

如果ui設計師工作量不飽和的話测僵,可以推薦, 盡量使用 .9.png 點9圖 小黑點表示 可拉伸區(qū)域谢翎,黑邊表示縱向顯示內(nèi)容的范圍

布局優(yōu)化:如果創(chuàng)建的層級結構比較復雜捍靠,View樹嵌套的層次比較深,那么將會使得頁面的響應的時間變長森逮,導致運行的時候越來越慢
  • merge標簽(對安卓的事件傳遞要達到源碼級的熟悉才可以理解) 在某些場景下可以減少布局的層次,由于所有的Activity的根布局都是FrameLayout Window PhoneWindow DecorView 事件的傳遞榨婆,包括設置setContentView 等的方法---> 我會寫一篇文章獨立解釋安卓事件的源碼解析,會更加清楚的介紹這個類褒侧,(對安卓的事件傳遞要達到源碼級的熟悉才可以理解)todo<-----所以良风,當獨立的一個布局文件最外層是FrameLayout的時候,并且和這個布局不需要設置 background 或者 padding的時候闷供,可以使用<merge>標簽來代替FrameLayout布局烟央。另外一種的情況可以使用《merge》便簽的情況是當前布局作為另外一個布局的子布局
  <include android:layout_height="50dp"
      android:layout_width="match_parent"
      layout="@layout/layout_include_merge"
      />

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView android:gravity="center"
        android:text="merge 標簽 在某些場景下可以減少布局的層次,由于所有的"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</merge>
  • 在安卓中經(jīng)常會使用到相同的布局,比如說title歪脏,最佳的實踐的方法就是把相同的布局抽取出來疑俭,獨立成一個xml文件,需要使用到的時候婿失,就把這個布局include進來钞艇,不僅減少了代碼量,而且修改這個相同的布局豪硅,只需要修改一個地方即可.

  • ViewStub 是一種不可見的并且大小為0的試圖哩照,它可以延遲到運行時才填充inflate 布局資源,當Viewstub設為可見或者是inflate的時候懒浮,就會填充布局資源飘弧,這個布局和普通的試圖就基本上沒有任何區(qū)別,比如說嵌溢,加載網(wǎng)絡失敗眯牧,或者是一個比較消耗性能的功能,需要用戶去點擊才可以加載,參考我的開源的項目 WritingPen

注意事項:如果這個根布局是個View秧骑,比如說是個ImagView乎折,那么找出來的id為null骂澄,得必須注意這一點

修正這個說法,以前我說的是錯誤的磨镶,根本上的原因是ViewStub設置了 inflateid 健提,這才是更本身的原因,對不起私痹!搞錯了紊遵,還是要看源碼

   <ViewStub
        android:padding="10dp"
        android:background="@color/colorPrimary"
        android:layout_gravity="center"
        android:inflatedId="@+id/find_view_stub"
        android:id="@+id/view_stub"
        android:layout="@layout/view_stub_imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:padding="10dp"
    android:src="@drawable/ic_launcher_background"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:text="如果這個根布局是個View蕊爵,比如說是個ImagView桦山,那么找出來的id為null恒水,得必須注意這一點"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <!--如果這個根布局是個View钉凌,比如說是個ImagView御雕,那么找出來的id為null酸纲,得必須注意這一點-->
    <ImageView
        android:layout_marginTop="20dp"
        android:id="@+id/imageview"
        android:padding="10dp"
        android:src="@drawable/ic_launcher_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>

調(diào)用todo: 2018/5/4 為啥為null 原因是布局文件中根布局只有View闽坡,沒有ViewGroup,ViewStub.inflate() 的方法和 setVisibility 方法是差不多外厂,因為 setVisibility方法會(看源碼)走這個inflate的方法

if (null!=mViewStub.getParent()){
                   /*
                   android:inflatedId 的值是Java代碼中調(diào)用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,"已經(jīng)inflate了",Toast.LENGTH_LONG).show();
                   final View viewById = findViewById(R.id.find_view_stub);
                   View iamgeivew = findViewById(R.id.imageview);
                   //已經(jīng)inflate了android.support.v7.widget.AppCompatImageView{4637833 V.ED..... ........ 348,294-732,678 #7f07003e app:id/find_view_stub}
                   System.out.println("shiming l----已經(jīng)inflate了"+viewById);//
                   System.out.println("shiming l----已經(jīng)inflate了iamgeivew"+iamgeivew);//已經(jīng)inflate了iamgeivew==null
                   View iamgeivew11 = viewById.findViewById(R.id.imageview);
                   //已經(jīng)inflate了 iamgeivew11null
                   System.out.println("shiming l----已經(jīng)inflate了 iamgeivew11"+iamgeivew11);
               }
           }
  • 盡量使用CompoundDrawable,如果存在相鄰的ImageView和TextView 的話
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="150dp">
    <TextView
        android:text="我是文字"
        android:drawableBottom="@mipmap/ic_launcher_round"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
   <TextView
       android:text="我是title2"
       android:drawableEnd="@mipmap/ic_launcher_round"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:drawableRight="@mipmap/ic_launcher_round" />
    <TextView
        android:text="我是文字33"
        android:drawableLeft="@mipmap/ic_launcher_round"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableStart="@mipmap/ic_launcher_round" />

    <TextView
        android:drawableTop="@mipmap/ic_launcher_round"
        android:text="我是文字3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    </LinearLayout>

Picture
image

+qq群:853967238术奖。獲取以上高清技術思維圖采记,以及相關技術的免費視頻學習資料

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唧龄,一起剝皮案震驚了整個濱河市选侨,隨后出現(xiàn)的幾起案子然走,更是在濱河造成了極大的恐慌,老刑警劉巖晨仑,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異竟贯,居然都是意外死亡屑那,警方通過查閱死者的電腦和手機持际,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門益眉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來郭脂,“玉大人展鸡,你說我怎么就攤上這事娱颊∠渌叮” “怎么了剧罩?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵惠昔,是天一觀的道長啦鸣。 經(jīng)常有香客問我来氧,道長中狂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任胃榕,我火速辦了婚禮勋又,結果婚禮上泉褐,老公的妹妹穿的比我還像新娘挺邀。我一直安慰自己端铛,他們只是感情好疲眷,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布禾蚕。 她就那樣靜靜地躺著,像睡著了一般狂丝。 火紅的嫁衣襯著肌膚如雪换淆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天几颜,我揣著相機與錄音倍试,去河邊找鬼。 笑死蛋哭,一個胖子當著我的面吹牛县习,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼吠勘,長吁一口氣:“原來是場噩夢啊……” “哼植锉!你這毒婦竟也來了辉饱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤于毙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡垫言,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年担忧,在試婚紗的時候發(fā)現(xiàn)自己被綠了示罗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡脯倒,死狀恐怖悠反,靈堂內(nèi)的尸體忽然破棺而出茵臭,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布功舀,位于F島的核電站酿炸,受9級特大地震影響麦萤,放射性物質(zhì)發(fā)生泄漏姻檀。R本人自食惡果不足惜杂抽,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望济竹。 院中可真熱鬧送浊,春花似錦闭树、人聲如沸与殃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铐料,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豺旬,已是汗流浹背钠惩。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留族阅,地道東北人篓跛。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像坦刀,于是被迫代替她去往敵國和親愧沟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355