Android优化系列——代码、图片和布局优化

这篇文章分为三个部分代码优化、图片优化、布局优化,尽量每个方法都写了小的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要慢        HashMap<String,String> hashMap=new HashMap<>();        /**         * HashSet总体上的性能比TreeSet好,特别实在添加和查询元素的时候,而这两个操作也是最重要的操作。TreeSet存在的唯一的原因是它         * 可以维持元素的排序的状态,所以当需要一个排好序的Set,才使用TreeSet。因为其内部的结果欧支持排序,并且因为迭代是我们更有可能         * 执行的操作,所以,用TreeSet迭代通常比用HashSet要快         */        HashSet<String>  hashSet=new HashSet<>();        TreeSet<String> treeSet=new TreeSet<>();

SparseArray是Android特有的稀疏数组的实现,他是Integer和Object的为例进行的一个映射用于代替 HsahMap<Integer,<E>>,提高性能。

  • 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 <= hi) {            /**             * >>>与>>唯一的不同是它无论原来的最左边是什么数,统统都用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            }        }        //按位取反(~)运算符 ,没有找到,这个数据        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中的设计模式:原型模式:这里有使用到了的,原型模式内存中复制数据的,不会调用到类的构造的方法,而且访问的权限对原型模式无效

  • 优点: 1、性能提高。 2、逃避构造函数的约束。

  • 缺点:
    1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
    2、必须实现 Cloneable 接口。

    SparseArray<String> clone = 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 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        /**         *如果创建单利必须需要使用到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<Object> sr = new SoftReference<>(obj);//这里使用了软引用...        /*         *在这个期间,有可能会出现内存不足的情况发生,那么GC就会直接把所有的软引用全部清除..并释放内存空间         *如果内存空间足够的话,那么就GC就不会进行工作...         *GC的工作取决于内存的大小,以及其内部的算法,,,,         */        if(sr!=null){            //如果软引用还存在,那么直接就可以获取这个对象的相关数据...这样就实现了cache...            obj = sr.get();        }else{            //如果已经不存在,表示GC已经将其回收,我们需要重新实例化对象,获取数据信息...            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="虚引用:和软引用和弱引用不同,虚引用并不会对所指向的对象生命周期产生任何影响,也就是对象还是会按照它原来的方式别垃圾回收期回收,虚引用本质上只是有一个标记作用,主要用来跟踪对象被垃圾回收的活动,虚引用必须和引用队列配合使用,当对象被垃圾回收时,如果存在虚引用,那么Java虚拟机会将这个虚引用加入到与之关联的引用队列中";        mTextView6.setText(des6);        /**         * 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。         * 虚引用主要用来跟踪对象被垃圾回收器回收的活动         */        // TODO: 2018/5/2 程序可以通过判断引用队列中是否已经加入了虚引用,        // 来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,        // 那么就可以在所引用的对象的内存被回收之前采取必要的行动。        ReferenceQueue queue = new ReferenceQueue ();        PhantomReference pr = new PhantomReference<Object>(obj1, queue);        String des7="引用队列:ReferenceQueue一般是作为WeakReference SoftReference 的构造的函数参数传入的,在WeakReference 或者是 softReference 的指向的对象被垃圾回收后,ReferenceQueue就是用来保存这个已经被回收的Reference";        mTextView7.setText(des7);
  • 将HashMap封装成一个线程安全的集合,并且使用软引用的方式防止OOM(内存不足)。由于在ListView中会加载大量的图片.那么为了有效的防止OOM导致程序终止的情况

  /**     * list中使用大量的bitmap,这种情况的话,我自己感觉使用的比较少     */    public class MemoryCache {        //将HashMap封装成一个线程安全的集合,并且使用软引用的方式防止OOM(内存不足)...        //由于在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内容的存储    private ReferenceQueue<Employee> q;// 垃圾Reference的队列     // 继承SoftReference,使得每一个实例都具有可识别的标识。    // 并且该标识与其在HashMap内的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);    }    // 依据所指定的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设计师看能否使用  https://pngmini.com/

  • 有损压缩TinyPNG 比较知名的png压缩的工具,也需要ui设计师看能够使用不  https://tinypng.com/

  • PNG/JPEG 转化为 wepb :http://zhitu.isux.us/

如果ui设计师工作量不饱和的话,可以推荐, 尽量使用 .9.png 点9图   小黑点表示 可拉伸区域,黑边表示纵向显示内容的范围

布局优化:如果创建的层级结构比较复杂,View树嵌套的层次比较深,那么将会使得页面的响应的时间变长,导致运行的时候越来越慢
  • merge标签(对安卓的事件传递要达到源码级的熟悉才可以理解) 在某些场景下可以减少布局的层次,由于所有的Activity的根布局都是FrameLayout    Window   PhoneWindow  DecorView   事件的传递,包括设置setContentView 等的方法---> 我会写一篇文章独立解释安卓事件的源码解析,会更加清楚的介绍这个类,(对安卓的事件传递要达到源码级的熟悉才可以理解)todo<-----所以,当独立的一个布局文件最外层是FrameLayout的时候,并且和这个布局不需要设置 background 或者 padding的时候,可以使用

    标签来代替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>
  • 在安卓中经常会使用到相同的布局,比如说title,最佳的实践的方法就是把相同的布局抽取出来,独立成一个xml文件,需要使用到的时候,就把这个布局include进来,不仅减少了代码量,而且修改这个相同的布局,只需要修改一个地方即可.

  • ViewStub 是一种不可见的并且大小为0的试图,它可以延迟到运行时才填充inflate 布局资源,当Viewstub设为可见或者是inflate的时候,就会填充布局资源,这个布局和普通的试图就基本上没有任何区别,比如说,加载网络失败,或者是一个比较消耗性能的功能,需要用户去点击才可以加载,参考我的开源的项目 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>

调用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 的话

<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>
  • 使用Lint 检查代码,和布局是否可以存在优化的地方,我会写个简单的经常遇见过的问题,同时完成一篇文档,加以说明,地址Lint的使用(安卓性能提升必备掌握的工具)

Android系统性能优化(59)----代码、图片和布局优化相关推荐

  1. 【Android】自定义FlowLayout,支持多种布局优化--android-flowlayout

    前言 flow layout, 流式布局, 这个概念在移动端或者前端开发中很常见,特别是在多标签的展示中, 往往起到了关键的作用.然而Android 官方, 并没有为开发者提供这样一个布局, 于是有很 ...

  2. Android --- 使用纯java代码实现相对布局(通俗易懂)

    java布局 java代码布局和xml布局的区别 1.Java纯布局更加的灵活,比如自定义控件或一些特殊要求时,使用java代码布局 2.常用的xml布局是所见即所得的编写方式,以及xml本身拥有一些 ...

  3. Android——使用纯java代码实现线性布局(通俗易懂)

    https://blog.csdn.net/sinat_31998357/article/details/49363707

  4. 一篇文章搞定《Android布局优化》

    ------<一篇文章搞定Android布局优化> 前言 为什么要进行布局优化? Android绘制原理 双缓冲机制 布局加载原理 布局加载优化的一些方法介绍 AsyncLayoutInf ...

  5. Android超越iOS用户体验改进之安卓优化趋势

    根据权威智能手机操作系统的排名,Android.iOS是当今最受欢迎的智能手机操作系统.苹果公司的iOS是Android的最大竞争对手,从有用.易用.友好.视觉设计和品牌这五个用户体验设计目标来看,i ...

  6. matlab代做mhslogic,MATLAB代做|FPGA代做|simulink代做——基于遗传算法的车间布局优化MATLAB源码...

    MATLAB代做|FPGA代做|simulink代做--基于遗传算法的车间布局优化MATLAB源码 添加时间:2019-12-8 来源:本站整理 基于遗传算法的车间布局优化MATLAB源码 车间布局优 ...

  7. Android代码、图片、布局、网络和电量优化

    这篇文章分为五个部分代码优化.图片优化.布局优化.网络优化.电量优化,尽量每个方法都写了小的Demo! 代码优化:不要做多余的工作,尽量避免次数过多的内存的分配,(需要对api有一定的熟悉) 数据集合 ...

  8. 2.4万字长文!Android代码、图片、布局、网络和电量优化

    点击上方 "程序员小乐"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 关注订阅号「程序员小乐」,收看更多精彩内容 每日英文 https://wei ...

  9. Android系统性能优化(69)---含内存优化、布局优化

    Android性能优化:含内存优化.布局优化 前言 在 Android开发中,性能优化策略十分重要 因为其决定了应用程序的开发质量:可用性.流畅性.稳定性等,是提高用户留存率的关键 本文全面讲解性能优 ...

最新文章

  1. protel DXP的类矢量图功能
  2. Ajax Control Toolkit Animation 想说爱你不容易
  3. XamarinAndroid组件教程设置自定义子元素动画(二)
  4. Spring-Cloud 整合Nacos
  5. FluentAspects -- 基于 Fluent API 的 Aop
  6. java.util.Properties类的介绍-配置文件的读写【-Z-】
  7. 转: DH密钥交换和ECDH原理
  8. 基于CSS class的事件监听管理机制 (转)
  9. 【系列三之CentOS系列】Shell编程入门(3)
  10. NetAssist使用
  11. 打开英伟达控制面板超时打不开解决办法
  12. 计算机无法识别打印机usb,Windows7打印机usb无法识别如何解决
  13. Windows实例通过IIS如何搭建多个FTP站点
  14. Kubernetes基础:Pod中的Pause容器
  15. 简单PHP会话(session)说明
  16. 西门子免授权CNC数控系统数据采集c#、C、python都支持,可支持再各种操作系统上运行,无须西门子OPC,支持828D 840dsl 808 802dsl 840d 810d 西门子数控DNC程序
  17. 【APP本地化】提高APP下载量的 5 个技巧
  18. python:pyqt5+mysql=学生信息管理系统(图文并茂,超详细)——登录,注册及找回密码篇
  19. 【小技巧】Linux安装matlab教程
  20. android内嵌套cocos2dx,Cocos2dx项目嵌入到Android原生项目中

热门文章

  1. squid 的配置详解 (转)--SeriesII
  2. SQL server USE GO语句学习总结
  3. ROS 教程之 navigation : 用 move_base 控制自己的机器人(1)
  4. c 语言输入n个数求和,c++---天梯赛---N个数求和
  5. koa2 从入门到进阶之路 (四)
  6. [数据结构与算法] : 栈的链式实现
  7. Eclipse配置jstl标准标签库详解
  8. [转载] Docker网络原则入门:EXPOSE,-p,-P,-link
  9. 利用apache限制IP并发数和下载流量控制
  10. Gitorious基本配置流程