美文网首页
Android性能优化面试题汇总大纲

Android性能优化面试题汇总大纲

作者: STE北京老徐 | 来源:发表于2019-03-28 09:11 被阅读0次
    image.png

    启动黑白屏问题

    安装app后,启动时会有短暂的白屏,这大大影响整体的美观,姑且在这里也给算在性能优化这一块

    出现白屏原因
    系统加载布局文件是需要时间的,在刚启动时布局文件还没加载完毕(即setContentView(int)之前)显示的是window背景,出现的白屏(或黑屏)是window的背景色
    解决办法
    替换window的背景(可为图片或纯色)
    只需在启动activity界面根标签下设置android:theme="@style/MyTheme"属性
    自定义一个style

      <style name="Mytheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="android:windowBackground">@mipmap/ic_launcher</item>//设置图片
            <item name="ndroid:windowBackground">@color/fireBar</item>//设置纯色
            <item name="android:windowNoTitle">true</item>//去title
        </style>
    

    注意事项

    设置属性只能设置在启动页,不能设置在application标签下,此标签代表全局
    设置图片最好是.9.png类型图片(此种图可适配机型),若使用.png图片需考虑不同机型适配问题(切多种分表率的图)

    布局优化

    减少嵌套的层级(可使用RelativeLayout),减少嵌套层级可加快加载效率,
    使用style提取相同view的公共属性,减少重复代码
    使用include标签
    合理使用ViewStub

    图片的优化

    android中图片的使用是非常占用内存资源的。

    ①:在图片未使用时,及时recycle()回收
    ②:使用三级缓存,内存-sd卡-网络
    内存中再次获取最快,由于内存有限可能被gc回收,sd卡中的图片不会回收,当前面两种都不存在所需图片时,才去网洛下载
    ③:将大图片进行压缩处理再放到内存中,用到BitmapFactory类

    /**创建缩略图方法
    *filepath 图片路径
     i  压缩比例,最终为原图的1/(i^2)
    */
     private Bitmap onCreateThumbnail(String filePath, int i) {
             BitmapFactory.Options options=new BitmapFactory.Options();
            //设置为不读内容,值读取边界值
              options.inJustDecodeBounds=true;
             //通过编辑,得到边界值,并存入到option中
             BitmapFactory.decodeFile(filePath,options);
            //赋值缩放比例
             options.inSampleSize=i;
             //设置显示的图片格式
             options.inPreferredConfig=Config.RGB_565;
             //设置为读取内容,
             options.inJustDecodeBounds=false;
            //得到缩略图
             return BitmapFactory.decodeFile(filePath2, options);
         }
    
    

    ④:尽量不在Button上使用selector来设置点击与正常时背景图,因为在button初始化时会将选中状态与正常状态的两张图都加载都内存中,无疑在无意中加大了内存的占用,可xml中设置正常的背景,在setOnTouchListener监听中通过代码动态改变,在按下时显示选中状态北京,抬起恢复

    final Button button=new Button(this);
            button.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()){
                        case MotionEvent.ACTION_DOWN:
                            //手指按下
                            button.setBackgroundResource(R.mipmap.down);
                            break;
                        case MotionEvent.ACTION_UP:
                            //手指抬起,恢复
                            button.setBackgroundResource(R.mipmap.up);
                            break;
                    }
                    //为了监听事件的分发,返回false
                    return false;
                }
            });
    

    图片解码率也会影响图片所占内存
    常见的png,JPG,webp等格式的图片在设置到UI上之前需要经过解码过程,而图片采用不同的码率,也会造成对内存的占用不同。

    ARGB_4444 格式的解码率,这种格式的图片,看起来质量太差,已经不推荐使用。 而强烈推荐使用ARGB_8888来代替。一个像素占用2个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4个bites 。 共16bytes,即2个字节

    ARGB_8888 格式的解码率,一个像素占用4个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bytes , 共32bytes , 即4个字节。这是一种高质量的图片格式,电脑上普通采用的格式。它也是Android手机上一个BitMap的默认格式。
    RGB_565格式的解码率,一个像素占用2个字节,没有alpha(A)值,即不支持透明和半透明, Red(R)值占5个bytes ,Green(G)值占6个bytes ,Blue(B)值占5个bytes,共16bytes,即2个字节。 对于半透明颜色的图片来说,该格式的图片能够达到比较好的呈现效果,相对于ARGB_8888来说也能减少一半的内存开销,因此它是一个不错的选择。推荐使用

    大量数据优化

    分页加载
    缓存方式

    列表项优化

    listview的优化

    convertview的复用(View的复用)
    viewholder类的使用,减少查找控件的次数(findviewbyid()次数),将holder与view绑定来实现(.setTag()、.getTag())
    数据分页加载

    RecycleView的优化

    其他优化

    网络优化

    同一个页面数据尽量放到一个接口中去处理

    使用Application Context代替Activity Context
    谨慎使用static 关键字

    static使用不当容易造成内存泄漏

    数据库优化
    电量优化
    多线程并发引发的性能等

    anr异常面试问题讲解

    a) 什么是anr?

    应用程序无响应对话框

    b) 造成anr的原因?

    主线程中做了耗时操作

    c) android中那些操作是在主线程呢?

    activity的所有生命周期回调都是执行在主线程的

    Service默认是执行在主线程的

    BroadcastReceiver的onReceiver回调是执行在主线程的
    没有使用子线程的Looper的Handler的handlerMessage,post(Runnable)是执行在主线程的
    AsyncTask的回调中除了doInBackground,其它都是执行在主线程
    d) 如何解决anr
    使用AsyncTask处理耗时IO操作
    使用Thread或者HandlerThread提高优先级
    使用handler来处理工作线程的耗时任务
    activity的onCreate和onResume回调中尽量避免耗时的代码

    oom异常面试问题讲解

    a) 什么是oom ?
    当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出的Out of memory异常 
    b) 一些容易混淆的概念
    内存溢出 / 内存抖动 / 内存泄漏
    内存溢出:就是oom
    内存抖动:短时间内大量对象被创建然后马上被释放
    内存泄漏:当程序不再使用到的内存时,释放内存失败而产生了无用的内存消耗
    d) 如何解决oom
    1.有关bitmap优化
    图片显示(比如监听listview滑动,停止的时候加载大图)
    及时释放内存
    bitmap的够着方法都是私有的,通过BitmapFactory生成Bitmap到内存中都是通过jni实现的,简单点说会有俩部分区域,一部分是java区,一部分是C区,但是Bitmap是通过java分配的,不用的时候也是由java的gc机制回收的。对应C区域不能及时回收的,所以这里说的释放内存就是释放的c的那块区域(通过
    recycle方法,该方法内部调用了jni的方法)。要是不释放只能等进程死了以后就会被释放
    图片压缩
    inBitmap属性(图片复用)
    捕获异常   
    2.其它方法
    listview:convertview / lru(三级缓存)
    避免在onDraw方法里面执行对象的创建
    谨慎使用多进程

    bitmap面试问题讲解

    a) recycle :释放bitmap内存的时候会释放所对应的native的内存,但是不会立即释放,但是调用完就不能使用该bitmap了,是不可逆的。官方不建议主动调用。垃圾回收器主动会清理。

    b) LRU:三级缓存,内部是通过map实现的,里面提供了put、get方法来完成缓存的添加和获取操作。当缓存满的时候,lru算法会提供trimToSize删除最久或者使用最少的缓存对象,添加新的缓存对象
    c) 计算inSampleSize:
    d) 缩略图

    `/**`
    
    `* 获取缩略图`
    
    `* @param imagePath:文件路径`
    
    `* @param width:缩略图宽度`
    
    `* @param height:缩略图高度`
    
    `* @return`
    
    `*/`
    
    `public` `static` `Bitmap getImageThumbnail(String imagePath, ``int` `width, ``int` `height) {`
    
    `BitmapFactory.Options options = ``new` `BitmapFactory.Options();`
    
    `options.inJustDecodeBounds = ``true``; ``//关于inJustDecodeBounds的作用将在下文叙述`
    
    `Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);`
    
    `int` `h = options.outHeight;``//获取图片高度`
    
    `int` `w = options.outWidth;``//获取图片宽度`
    
    `int` `scaleWidth = w / width; ``//计算宽度缩放比`
    
    `int` `scaleHeight = h / height; ``//计算高度缩放比`
    
    `int` `scale = ``1``;``//初始缩放比`
    
    `if` `(scaleWidth < scaleHeight) {``//选择合适的缩放比`
    
    `scale = scaleWidth;`
    
    `} ``else` `{`
    
    `scale = scaleHeight;`
    
    `}`
    
    `if` `(scale <= ``0``) {``//判断缩放比是否符合条件`
    
    `be = ``1``;`
    
    `}`
    
    `options.inSampleSize = scale;`
    
    `// 重新读入图片,读取缩放后的bitmap,注意这次要把inJustDecodeBounds 设为 false`
    
    `options.inJustDecodeBounds = ``false``;`
    
    `bitmap = BitmapFactory.decodeFile(imagePath, options);`
    
    `// 利用ThumbnailUtils来创建缩略图,这里要指定要缩放哪个Bitmap对象`
    
    `bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);`
    
    `return` `bitmap;`
    
    `}`
    

    e) 三级缓存
    网络、本地、内存三级缓存,减少流量的使用

    ui卡顿面试问题讲解

    a) UI卡顿的原理
    60fps -> 16ms
    overdraw过度绘制
    b) UI卡顿的原因分析
    1.人为在UI线程中做轻微耗时操作,导致UI线程卡顿

    2.布局Layout过于复杂,无法在16ms内完成渲染

    3.同一时间动画执行的次数过多,导致CPU、GPU的负载过重

    4.View的过度绘制,导致某些像素在同一帧内被绘制多次,从而使CPU、GPU的负载过重

    5.View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染

    6.内存频繁触发gc过多,导致暂时阻塞渲染操作

    7.冗余资源及逻辑等导致加载和执行的缓慢

    8.ANR

    c) UI卡顿总结

    1.布局优化

    2.列表及adapter优化

    3.背景和图片等内存分配优化

    4.避免ANR

    内存泄漏

    a) java内存泄漏基础知识

    1. java内存的分配策略

    静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。

    栈区:当方法被执行时,方法体内的局部变量(其中包括基础数据类型、对象的引用)都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

    堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存,也就是对象的实例。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。

    1. java是如何管理内存的
    image
    1. java中的内存泄漏
      内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄漏
      b) android内存泄漏
    2. 单例
    public class AppManager {
    
        private Context mContext;
        private static AppManager instance;
     
        private AppManager(Context context){
            //有内存泄漏的问题:传入的是actiivty的context,导致activity没法释放
            //this.mContext = context;
            //所以这里应该传入Application全局的context
            this.mContext = context.getApplicationContext();
        }
     
        public static AppManager getInstance(Context context){
            if(instance ==null){
                instance = new AppManager(context);
            }
            return instance;
        }
     
    }
    

    2.匿名内部类

    public class MainActivity extends AppCompatActivity {
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
    //这样的写法就会造成内存泄漏
    //原因是这样写法虽然避免了重复创建,但是非静态内部类持有外部类的引用,
    //这时候我们又创建了一个静态实例TAG的话就会和应用的生命周期一样长,所以就会使外部的activity没法释放
    
        class TestResource{
            private static final String TAG = "";
        }
         
        //正常的写法,这样的话就不会持有外部类的引用。
        static class TestResource1{
            private static final String TAG = "";
        }
     
    }
    
    1. handler
    public class MainActivity extends AppCompatActivity {
    
        private TextView mTv;
        private MyHandler myHandler;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            myHandler = new MyHandler(this);
        }
    
    //这样的写法会造成内存泄漏
    //mHandler是MainActivity的非静态内部类的实例,它持有外部类的引用,我们知道handler的消息是在一个loop
    //中不断的轮询处理消息,那么当MainActivity退出时,消息队列中还有没处理的消息或正在处理的消息,所以会造成内存泄漏
    
       @SuppressLint("HandlerLeak")
       private Handler mHandler = new Handler(){
           @Override
           public void handleMessage(Message msg) {
               super.handleMessage(msg);
           }
       };
    
    //这样写是正确的写法
    
        static class MyHandler extends Handler{
     
            //创建一个软引用
            private WeakReference<Context> reference;
     
            public MyHandler(Context context){
                reference = new WeakReference<Context>(context);
            }
     
            @Override
            public void handleMessage(Message msg) {
                MainActivity mainActivity = (MainActivity) reference.get();
                if(mainActivity != null){
                    //TODO------
                    mainActivity.mTv.setText("11");
                }
            }
        }
      @Override
      protected void onDestroy() {
          super.onDestroy();
          myHandler.removeCallbacksAndMessages(null);
      }
    }
    

    4.避免使用static静态变量

    如果声明成静态变量,那么它的生命周期就会和app的生命周期一样长,假如你的app是常驻后台的,即使app退到后台,这部分也不会释放

    5.资源未关闭造成的内存泄漏

    6.AsyncTask造成的内存泄漏

    在onDestory调用cancel()方法

    添加弱引用实现

    5、内存管理面试问题

    a)内存管理机制概述

    1.分配机制

    2.回收机制

    b)Android内存管理机制

    1.分配机制

    弹性的分配机制,当发现内存不够用的时候回分配额外的内存。但是额外的内存也不是无限量的。(让更多的进程存活在系统中)

    2.回收机制

    前台进程 可见进程 服务进程 后台进程 空进程

    c)内存管理机制的特点(目标)

    1.更少占用的内存

    2.在合适的时候,合理的释放系统资源

    3.在系统内存紧张的情况下,能释放掉不部分不重要的资源,来为android系统提供可用的内存

    4.能够很合理的在特殊的生命周期中,保存或者还原重要数据,以至于系统能够正确的重新恢复该应用

    d)内存优化方法

    1.当service完成任务后,尽量停止它

    2.在UI不可见的时候,释放掉一些只有UI使用的资源

    3.在系统资源紧张的时候,尽可能多的释放掉一些非重要资源

    4.避免滥用Bitmap导致的内存浪费

    5.使用针对内存优化过的数据容器

    6.避免使用依赖注入的框架

    7.使用ZIP对齐的APK

    8.使用多进程

    d)内存溢出VS内存泄漏

    冷启动优化面试问题讲解

    a)什么是冷启动?

    1.冷启动的定义

    冷启动就是在启动应用前,系统没有该应用的任何进程信息

    2.冷启动 / 热启动的区别

    热启动:用户使用返回键退出应用,然后马上又重新启动应用

    区别:

    1). 定义

    2). 启动特点

    冷启动:Application -> MainActivity ->UI的绘制

    热启动:MainActivity ->UI的绘制

    3.冷启动时间的计算

    这个时间值从应用启动(创建进程)开始计算,到完成视图的第一次绘制(即Activity内容对用户可见)为止

    b)冷启动流程

    Zygote进程中fork创建一个新的进程

    创建和初始化Application类,创建MainActivity类

    inflate布局,当onCreate / onStart / onResume方法都走完

    contentView的measure / layout /draw 显示在界面上

    c)如何对冷启动的时间进行优化

    1.减少onCreate的工作量

    2.不要让Application参与业务的操作

    3.不要在Application进行耗时操作

    4.不要以静态变量的方式在Application中保存数据

    5.布局

    6.mainThread

    d)冷启动流程-----总结

    Application的构造方法-------->attachBaseContext () ---------> onCreate () ----------->Activity的构造方法------------> onCreate () ------------> 配置主题中背景等属性------------->onStart () ---------> onResume() --------> 测量布局绘制显示在界面上

    7、其他优化面试问题讲解

    a)android 不用静态变量存储数据

    1.静态变量等数据由于进程已经被杀死而被初始化

    2.使用其它的数据传输方式:文件 / sp / contentProvider

    b)有关Sharepreference的安全问题

    1.不能跨进程同步数据读写

    2.存储Sharepreference的文件过大问题

    过大的话会读取缓慢,造成UI卡顿

    读取频繁的key和不易变动的key不要放在一起

    c)内存对象序列化

    序列化:将对象的状态信息转化为可以存储或传输的形式的过程

    1.实现Serializable接口(会产生大量的临时变量,频繁的垃圾回收,这样会造成UI卡顿,内存抖动,)

    2.实现Parcelable接口

    总结:1>.Serializable是Java的序列化方式,Parcelable是android特有的序列化方式

    2>.在使用内存的时候,Parcelable比Serializable性能高

    3>.Serializable序列化的时候会产生大量的临时变量,从而引起频繁的GC

    4>.Parcelable不能使用在要数据存储在磁盘上的情况

    d)避免在UI线程中做繁重的操作

    大纲之二

    (一). 布局优化

    1. 就是尽量减少布局文件的层级.
    2. 简单的布局能用1个LinearLayout搞定的,就不要用Relativelayout,因为Framelayout和LinearLayout都是一种简单高效的ViewGroup,Relativelayout功能比较复杂,相对来说,布局渲染要花费更多时间.
      但若是需要嵌套LinearLayout时,能用Relativelayout就用Relativelayout代替.
    3. 使用include,merge标签引入布局,或者用Viewstub延时加载.
      可以通过手机开发者里面的调试GPU过度绘制来检测布局是否需要优化

    (二). 绘制优化

    绘制优化是指在View的onDrwa()方法里避免执行大量的操作,比如

    1. 不要在onDrwa()方法中创建对象,因为onDrwa()频繁被调用,这样就会产生很多临时对象,这样不近耗内存,还会导致系统频繁GC,降低运行效率.
    2. 不要在onDrwa()方法中执行耗时操作.不停的执行onDrwa(),就会有很多耗时任务轮训,造成View绘制不流畅.

    (三). 线程优化

    线程的创建和销毁都比较耗性能,所以线程的优化是采用线程池,线程池的优点:

    1.避免了线程创建销毁带来的消耗

    1. 能够有效控制线程池的最大并发数,避免了大量的线程因互相抢占资源从而导致的阻塞现象.

    (四). 内存泄漏优化

    当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。

    不停的内存泄漏,就会导致内存溢出.所以内存泄漏优化分为2个方面来解决:

    A. 代码中注意,
    B. 上线前通过LeakCanary、MAT等工具来检测

    相关文章

      网友评论

          本文标题:Android性能优化面试题汇总大纲

          本文链接:https://www.haomeiwen.com/subject/qjjfbqtx.html