Android知识点总结(二)

1.ANR application not responding

在主线程这做了耗时操作。
主要原因是:应用程序的响应性是Activity Manager和WindowManager系统服务监视的。

(1)主线程被IO操作阻塞;
(2)主线程中存在耗时的计算;
以下这些操作是发生在主线程中:

  • Acitivity的所有生命周期回调都是执行在主线程
  • Service默认是执行在主线程。
  • BroadcastReceivew的onReceive回调是执行在主线程的。
  • 没有使用子线程的looper的handler的HandleMessage.Post(Runnable)执行在主线程。
  • AnsyncTask的回调中除了doinbackgroun,其他都是执行在主线程。
解决:
  • Asynctask处理耗时IO操作
  • 使用Thread或HandlerThread提高优先级;
  • 使用handler来处理工作线程耗时任务;

2 OOM

当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出out of mermory异常。

容易混淆的概念:
  • 内存溢出 OOM 严重会造成程序崩溃
  • 内存抖动 在短时间内大量对象被创建,会被马上释放,瞬间产生的对象会占用大量的内存区域,叠加在一起就会触发内存压力,触发gc。
  • 内存泄漏 进程中的某些对象,没有被用的,直接间接应用到其他没有回收的对象,导致gc不能起作用。
解决
  • bitmap:图片显示,listview滑动的时候不进行网络请求;及时的释放内存;图片压缩;inBitmap属性;捕获异常;
  • listview:convertview /LRU最近最少使用;避免在onDraw方法里面创建对象;谨慎使用多进程;

3.bitmap

  • recycle
  • LRU 最近最少使用三级缓存,LinkedhashMap,缓存满的时候就移除对象,并添加新的对象trimToSize(maxsize)方法;put(),同步代码;removew(key),同步代码;<LRU泛型通过一些方法进行添加删除>
 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);
        }
    }
  • 计算InSampleSize
  • 缩略图 根据InSampleSize值,thumnail(),inJustDecodeBounds=false 不加载到内存中,然后计算出合适的inSampleSize,再设置inJustDecodeBounds=true;
  • 三级缓存 :网络缓存 内存缓存 本地缓存(SD)

4.UI卡顿

60fps 每秒60帧—>16ms ;overdraw过度绘制,大量的重叠背景,减少红色;

原因分析:
  • 人为在UI线程中做轻微耗时操作,导致UI线程卡顿;
  • 布局layout过于复杂,无法在16ms内完成渲染;
  • 同一时间动画执行次数太多,导致CPU或者GPU负载过重;
  • VIew过度绘制;
  • view频繁的measure layout,导致measure、layout累计耗时过多,整个View频繁的重新渲染;
  • 内存频繁触发GC过多,导致暂时阻塞渲染操作;
  • 冗余资源逻辑等导致加载或者执行缓慢;
  • anr
优化
  1. 布局优化,尽量不嵌套,gone就不绘制;
  2. 列表adapter优化,使用converView,滑动停止在加载图片;
  3. 背景和图片的内存分配优化;
  4. 避免ANR

5 内存泄漏

内存分配策略
(1)静态存储区:方法区 ,静态数据,成员变量。整个运行期都存在
(2)栈区:方法变量。存放函数内的局部变量,形参和函数返回值。方法结束之后会自动释放,不需要手动管理。容量有限。系统自动分配回收。
(3)堆区:new 出来的。动态的。不使用垃圾回收机制会回收。全局得,(完全二叉树)

如何管理内存
java中内存泄漏:

无用的对象持续占有内存,或者无用的内存得不到及时释放,造成内存空间的泄漏;

android中的内存泄漏

单例;匿名内部类;handler;避免使用static变量;资源没有关闭造成的;AsyncTask造成的;

6 内存管理

分配机制;回收机制;

特点

更少的占用内存;在合适的时候,合适的释放系统资源;在系统内存紧张情况下,释放大部分不重要的资源,为将来内存提供可用的内存空间;合理的生命周期中保存或者还原重要数据。

内存优化方法
  • 当service完成任务后,尽量停止它。intentService自动释放
  • UI不可见时候,释放一些只有UI使用的资源;
  • 内存紧张尽可能多的释放掉一些非重要资源
  • 避免滥用Bitmap导致的内存浪费,recycle();软引用,LRU
  • 使用针对内存优化过的数据容器。少使用枚举。正常的2倍多
  • 避免使用依赖注入的框架
  • 使用ZIP对齐的APK
  • 使用多进程

7.冷启动

启动应用之前,系统中没有该应用的任何进程信息。

热启动:

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

冷启动时间计算:

从创建进程开始计算,到完成视图的第一次绘制(activyt内容对用户可见)为止。

冷启动流程:

Zygote进程中fork创建一个新的进程,创建和初始化Applicatin、创建MainActiviy.inflate布局、当onCreate() onStart() onResume()方法都走完。contentView的measure onlayout ondraw走完。

冷启动优化:
  • 减少onCreate()方法的工作量;
  • 不要让Application参与业务的操作;
  • 不要在Application进行耗时操作;
  • 不要用静态变量的方式在Application中保存数据;
  • 减少布局复杂性 mainThread不做初始化,初始化放在子线程中

8.其他优化

  • 不要用静态变量存储数据;
  • sharepreference问题:不能跨进程同步;文件过大;
  • 内存对象的序列化,Serializeble Java会产生大量的临时变量,Parcelable Android <要保存在磁盘上的数据不能序列化>
  • 避免UI中做繁重的操作

9.架构设计

  • MVC 耦合性好;可扩展性好;
  • MVP View <–> Presenter–> Model;
  • MVVM View<–同步—>ViewModel<---->Model。View:不做任何业务逻辑数据处理,对应Activity和XML,负责View的绘制以及用户交互;Model:实体模型;ViewModel:负责完成View与Model间的交互,负责业务逻辑。

10. Android 插件化

来源:65536/64K

解决的问题:动态加载apk(DexClassLoader 加载jar文件的字节码,PathClassLoader加载文件目录下的apk),资源加载(AssetManager,Method),代码加载(绑定activity生命周期,通过反射);

11.Android 热更新

  • Dexposed AndFix Nuwa
  • DexClassLoader
  • PathClassLoader
    热修复机制:dexElements,ClassLoader会遍历这个数组

12.进程保活

进程优先级 高到低
  • Foreground process 前台进程
  • Visible process 可见进程
  • Service process 服务进程 数据计算,音乐播放…
  • Background process 后台进程
  • Empty process 缓存作用
回收策略

Low memory killer:评分机制。分数高的进程判定为bad进程,杀死并释放内存。
oom_ODJ:判别进程优先级,越大,优先级越低就越容易被回收。

进程保活方案:
  • 利用系统广播拉活
  • 利用系统service拉活
  • 利用Native进程拉活<Android5.0以后失效> fork进行监控主进程,利用native拉活
  • 利用JobScheduler机制拉活<Android5.0以后>
  • 利用账号同步机制拉活<新版本应该也失效了>

13.final finally finalize区别和用法

  • final 关键字,属性不可改变,方法不可以覆盖(只能使用),类不可以继承;
  • finally 异常处理语句中的一部分,表示总是执行;(不管异常是否执行)
  • finalize Object类中的一个方法,在垃圾收集器执行的时候调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件。

14.retrofit的优势

15.view和surfaceview区别

(1)view在UI线程中去更新自己,在非UI线程更新会报错,当在主线程更新View时如果耗时过长也会出错;
surfaceview在一个子线程中更新画面;

(2)surfaceview不支持平移,缩放,旋转等动画,Android7.0及以上的支持;

(3)View适用于主动更新。SurfaceView适用于被动更新,比如频繁的刷新。

(4)SurfaceView可以控制刷新频率。SurfaceView底层利用双缓存机制,绘图时候不会出现闪烁问题;(双缓冲技术把要处理的图片在内存中处理好之后,把要画的东西画到一个内存区域,然后整体的一次性画出来,将其显示在屏幕上)

SurfaceView 使用步骤:
  • 首先要继承SurfaceView,实现SurfaceHolder.Callback接口
  • 重写方法:
    surfaceChangeed:surface大小或者格式发生变化的时候,在surfaceCreated调用后该函数至少会被调用一次;
    surfaceCreated:Surface创建时触发,一般在这个函数开启绘图线程(新的线程,不要在这个线程中绘制Surface);
  • 利用getHolder()获取SurfaceHolder对象,调用SurfaceHolder.addCallback添加回调
  • SurfaceHolder.lockCanvas获取Canvas对象并锁定画布,调用Canvas绘图,SurfaceHolder.unlockCanvasAndPost结束锁定画布,提交改变;

16.hashMap

https://note.youdao.com/web/#/file/recent/note/WEB5aa71cf3a9799350357e3fc77ea56748/

public class HashMapTestTest {
    HashMap<String, String> mStringStringHashMap = new HashMap<>();
    HashMap<Integer, String> mIntegerStringHashMap = new HashMap<>();

    @Test
    public void test() {
        mStringStringHashMap.put(null, "A");
        System.out.println("获取:" + mStringStringHashMap.get(null));//A key可以为空

        mStringStringHashMap.put(null, "B");
        System.out.println("获取:" + mStringStringHashMap.get(null));//B  value会变,不是覆盖,next值改变了

        mStringStringHashMap.put("A", "C");
        mStringStringHashMap.put("A", "C");
        mStringStringHashMap.put("A", "D");
        System.out.println("获取:" + mStringStringHashMap.get("A"));//D

        mIntegerStringHashMap.put(null, "A");
        mIntegerStringHashMap.put(null, null);
        System.out.println("获取:" + mIntegerStringHashMap.get(null));//null key value都可以为空
    }
}

元素越多碰撞机率就越高。
hashmap底层是数组加链表实现的,加载因子0.75f容量的0.75就开始扩容,扩大一倍,(数组大小是16 hashmap中的元素超过16*0.75=12个的时候就开始扩容到2 * 16=32 )
HashMap和ArrayMap各自优势:

  • (1)查找效率
    hashmap因为是根据其hashcode计算出index。所以其查找效率是随着数组增大而增加的。ArrayMap使用的是二分查找,所以,当数组长度每增加一倍时候,就需要多进行一次判断,效率下降。
  • (2)扩容数量
  • hashmap是双倍扩容;ArrayMap每次扩容的时候,如果size长度大于8时申请size*1.5倍,大于4小于8申请8个,小于4时申请4个;
  • (3)扩容效率
    hashmap每次扩容的时候重新计算每个数组成员的位置,然后放到新的位置;
    ArrayMap则是直接使用System.arraycopy;(直接调用的是c层代码)
    所以效率上arrayMap更占优势。
  • (4)内存耗费
  • arrayMap能够重复利用因为数据扩容而遗留下来的数组空间,方便下一个ArrayMap的使用
总结:数据量小的时候使用arrayMap数据量大的时候使用hashMap;

16.抽象类和接口的区别

抽象类要被子类继承不能被实例化,接口要被类实现;
接口只能做方法声明,抽象类可以做方法声明,也可以做方法实现;

参数 抽象类 接口
默认方法的实现 他可以有默认的方法实现 接口完全抽象,不存在方法的实现
实现 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 子类使用关键字implements来实现接口,它需要提供接口中所有声明的方法的实现。
构造器 抽象类可以有构造器 接口不能有构造器
与正常的Java类的区别 不能实例化抽象类 完全不能的类型
访问修饰符 抽象方法可以有public protected default这些修饰符 接口默认修饰符是public,不可以使用其他修饰符
main方法 可以有main方法并且可以运行它 接口没有main方法,不能运行它
多继承 抽象方法可以继承一个类和实现多个接口 接口只可以继承一个或者多个其他接口
速度 比接口速度快 稍微慢一些,需要去寻找在类中实现的方法;
添加新方法 如果你往抽象类中增加新的方法,可以给他提供默认的实现。不需要改变现有的代码 往接口中添加方法,必须改变实现该接口的类。
  • 1.android内部是怎么实现发送延时消息
  • 2.怎么减小so包的体积
  • 3.怎么给一个应用加固
  • 4.怎么从trace.log中找到ANR的原因及出现的地方
  • 5.什么是NDK库?
  • 6.如何在jni中注册native函数,有几种注册方式?
  • 7.Binder怎么实现进程间通信的
  • 8.怎么检测内存泄漏的
  • 9.现在下载速度很慢,试从网络协议的角度分析原因并优化
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章