目录
一、Java相关
二、Android重要知识模块
1.Android基础
2.网络
3.数据库
- sqlite升级:涉及表和字段。表和字段的新增与删除能直接修改,字段不支持直接修改,需要通过中间文件做转换。
- 数据库框架对比和源码分析:如LiteOrm、GreenDao、Realm等。
- 数据库的优化:a.使用索引;b.语句的拼接使用StringBuilder代替String;c.查询时返回更少的结果集及更少的字段;d.少用cursor.getColumnIndex;e.异步线程;
- 数据库数据迁移问题:创建临时表做中转。
- 如何导入外部数据库:1. 把原数据库包括在项目源码的 res/raw 目录下,2.在进入应用时,若data/data下没有数据库,则用写文件的方式把数据库拷入,若已有则直接打开。
SharedPreferences
sharepreferences 是线程安全的,底层(SharedPreferencesImpl
)用synchronized来保证同步。但不是进程安全的,可通过ContentProvider 实现多进程共同访问。
- 不支持跨进程,
MODE_MULTI_PROCESS
也没用。跨进程频繁读写可能导致数据损坏或丢失。- 初始化的时候会读取 sp 文件,可能导致后续
getXXX()
方法阻塞。建议提前异步初始化 SharedPreferences。- sp 文件的数据会全部保存在内存中,所以不宜存放大数据。
edit()
方法每次都会新建一个EditorImpl
对象。建议一次 edit(),多次 putXXX() 。- 无论是
commit()
还是apply()
,针对任何修改都是全量写入。建议针对高频修改的配置项存在子啊单独的 sp 文件。commit()
同步保存,有返回值。apply()
异步保存,无返回值。onPause()
、onReceive()
等时机会等待异步写操作执行完成,可能造成卡顿或者 ANR。
Android中的SharedPreferences是否支持多进程、多线程
三、架构设计和设计模式
1.常用框架
2.Jetpack
一系列开发组件的统称,方便快速开发应用。
3.注解与事件总线框架
注解与依赖注入
几种注解框架对比,Annotations 和 ButterKnife 为编译时注解,比XUtils(运行时注解)效率高。
事件
- RxJava的功能与原理实现: 使用可观察序列组成的一个异步地,基于事件的响应式编程框架,逻辑简单,流式结构。
- RxJava的作用,与平时使用的异步操作来比的优缺点。
- 说说EventBus作用,实现方式,代替EventBus的方式:观察者模式,依赖反射,订阅后处理。
- 如果一个订阅者需要注册多个事件的时候,Rxjava需要一个个单独的注册,而EventBus则可以实现一个订阅者订阅多个事件,和一个事件对应多个订阅者。所以在Rxjava的基础上有了Rxbus来作为事件总线的库。RxBus在订阅者数量很大的情况下性能下降很明显,比不过EventBus。
- RxAndroid 可理解为专供Android平台的RxJava,针对平台增加了少量类。
3.1RxJava
使用可观察序列组成的一个异步地、基于事件的响应式编程框架。
// RxJava的链式操作
Observable.create(new ObservableOnSubscribe<Integer>() {
// 1\. 创建被观察者 & 生产事件
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
}).subscribe(new Observer<Integer>() {
// 2\. 通过通过订阅(subscribe)连接观察者和被观察者
// 3\. 创建观察者 & 定义响应事件的行为
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "开始采用subscribe连接");
}
// 默认最先调用复写的 onSubscribe()
@Override
public void onNext(Integer value) {
Log.d(TAG, "对Next事件"+ value +"作出响应" );
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "对Error事件作出响应");
}
@Override
public void onComplete() {
Log.d(TAG, "对Complete事件作出响应");
}
});
}
}
注:整体方法调用顺序:观察者.onSubscribe()> 被观察者.subscribe()> 观察者.onNext()>观察者.onComplete()
3.2 协程
kotlin协程是一种并发设计模式,可以用来在Android上简化异步执行的代码。有助于管理长时间运行的任务。可实现类似RxJava效果。
特点
- 轻量:可以在单线程上运行多个协程。
- 内存泄露少;
- 内置取消操作;
- 支持jetpack;
缺点:
- 本质是单线程的,无法利用多核资源;
- 无法避免阻塞:虽然协程可以异步处理I/O操作,但对于一些需要长时间运行的任务(如计算、网络请求等),仍然可能导致整个程序阻塞。
- 难以调试:由于协程的执行流程比较复杂,调试起来相对困难。
用法
3.3 RxJava 和协程优缺点
协程
- 简洁的语法:Kotlin协程的语法简洁明了,易于学习和使用。
- 高性能:由于协程在单线程上运行,避免了线程创建和管理的开销,提高了执行效率。
- 方便的取消和恢复:协程支持取消和恢复,这使得在网络请求中处理异常情况变得简单。
在使用Kotlin协程进行网络请求时,可以考虑使用如Coroutines + Retrofit或Coroutines + OkHttp等组合,它们提供了方便的API来处理异步请求。
RxJava
RxJava是ReactiveX在Java平台上的实现,它提供了一种基于观察者模式的异步编程模型。通过使用可观察序列(Observables)和操作符(Operators),RxJava能够以声明式方式处理异步数据流。
RxJava的主要优点包括:
- 声明式编程:RxJava使用声明式语法,使得异步操作和数据流的处理更加直观。
- 丰富的操作符:RxJava提供了大量操作符,可以组合和变换数据流以满足复杂的需求。
- 社区支持:由于RxJava在Java社区中的广泛应用,有大量的资源和文档可供参考。
- 在使用RxJava进行网络请求时,通常会与Retrofit或OkHttp等库结合使用。通过将网络请求封装为Observable或Flowable,可以轻松地处理异步数据流。
协程适用于处理高并发的IO密集型应用场景,尤其在需要大量并发连接的情况下具有优势。不宜用于对于计算密集型的应用场景或者需要利用多核资源的情况。
4.设计模式
四、性能优化
- 如何对Android 应用进行性能分析以及优化?
- 分类:
- 性能问题:布局与绘制;启动速度; apk启动耗时:am start -W 中的waittime
- 内存问题:内存泄漏,内存浪费。 查看内存使用情况
- 耗电问题:
- 网络优化:
- 存储优化:
- apk大小;
- 稳定性优化;
- 检测工具:Lint代码检查,GPU过度绘制选项查看过渡绘制,
- 分类:
- ddms 和 traceView
- 性能优化如何分析systrace?
- Java多线程引发的性能问题,怎么解决?
- 利用线程池解决频繁创建与销毁问题;
- 减少无意义的同步,与伪共享;
- 启动页白屏及黑屏解决? 设置窗口背景颜色为透明,在布局未加载出来时,显示当前应用背景。
- App启动与发布后崩溃异常捕捉:在Application 启动时初始化Thread.UncaughtExceptionHandler 类,用其去捕捉异常,不能捕获native层。native层使用sigaction捕捉异常信号,或使用Bugly、google-breakpad等第三方工具。 参考crash log收集总结
- 现在下载速度很慢,试从网络协议的角度分析原因,并优化。 网络拥塞控制;ARP;DNS解析;缓存……
五、模块化、组件化、插件化、热修复区别
- 热修复:目的是为了解决线上的bug或者小功能的更新,做到不用为了解决小bug或者小功能而频繁发布版本。核心技术分三类:代码修复、资源修复和动态链接库修复。每个核心技术有不同的方案,每种方案又有不同的实现。
- 代码修复:主要有三个方案:底层替换(直接在Native层修改原有类,不需要重新加载类。阿里系为主。)、类加载(基于Dex分包方案,主要有是Google官方方案、Dex自动拆包和动态加载方案。不能即时生效。)、Instant Run。
- 资源修复:替换系统AssetManger,加入patch的资源
- 动态链接库修复:先加载patch所在的so;
- 插件化原理分析:主要涉及类加载和资源加载。插件化目的是为了解耦业务模块,将一个应用里面不同的业务模块都做成一个apk。可并行开发,动态加载。
- 模块化实现:分模块开发,解耦。
- 项目组件化的理解:一种开发模式,把每个模块使用到的共同的功能抽取出来做成一个Lib去共同依赖,每个业务模块互不依赖、互相解耦,在开发的时候可以安排不同的开发人员去开发不同的模块,开发完毕以后单模块测试,最后整合到一起总体测试上线。
- 对于应用更新这块是如何做的?(解答:灰度,强制更新,分区域更新)?
六、JNI
1.JNI注册方式
- jni中注册native函数方法: 注册JNI函数的两种方式
- 静态注册:每个class都得用javah生成一个头文件,名字长,首次调用慢。类似 android_com_media_Target_test() ,通过函数命令规则查找响应函数。
- 动态注册:要在jni_onload 中注册。有类似 { "hello", "()Ljava/lang/String;", (void*)native_hello} 对应关系。 {"native_start", "()V", (void *)android_media_AudioTrack_start},
2.C调用JAVA
- jni如何调用java层代码? C/C++调用JAVA
- 获取字节码对象;
- 通过字节码对象找到方法对象;
- 通过字节码文件创建object对象;
- 通过对象调用方法。
3.System.loadLibrary原理
Runtime.loadLibrary0 先用findLibrary去查找so是否存在,然后调用nativeLoad去加载库。
nativeLoad 方法参考:System.loadLibrary 加载 .so 原理
七、开发相关问题
1. 系统开发相关
- Android5种签名类型 :platform、media, shared, testkey/releasekey
- App 是如何沙箱化,为什么要这么做? 设置UID,底层是基于Linux内核的权限机制。
- 权限管理系统(底层的权限是如何进行 grant 的)? 参考 Android权限之底层实现概览
- 内存对象的循环引用及避免:内存回收机制的可达性算法。使用弱指针,程序中手动释放。
- 系统启动流程是什么?(init--Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程)
- 一个应用程序安装到手机上时发生了什么
- Android为每个应用程序分配的内存大小是多少?
dalvik.vm.heapstartsize=8m ----起始分配内存 dalvik.vm.heapgrowthlimit=192m ---- 一般情况app申请的最大内存 dalvik.vm.heapsize=512m
- Android中进程内存的分配,能不能自己分配定额内存?
- 进程保活的方式:Android进程分类?前台,可视,服务,后台,空进程。
- 如何保证一个后台服务不被杀死?(相同问题:如何保证service在后台不被kill?)比较省电的方式是什么?
- 什么是协程?:可看做是轻量级的线程,一个线程内能包含多个协程。协程是串行执行的,由用户自己控制调度。
- App中唤醒其他进程的实现方式:通过intent的不同用法,参考 App相互唤醒的几种方式
2.开发遇到的问题
- 工作历史问题技术记录
- 低版本SDK如何实现高版本api:用@SuppressLint 和 @TargetApi 后加入版本判断。
- 为什么不能在子线程更新UI? 在更新UI时都会调用 ViewRootImpl 中的 checkThread()方法来判断当前线程是否是主线程,若不是就会抛异常。在onCreate方法中可以在子线程中更新UI,因为此时ViewRootImpl还没有创建,无法做判断。ViewRootImpl是在handleResumeActivity方法中的makeVisible里面创建的。
- 有什么解决方法可以避免OOM?
- Oom 是否可以try catch?为什么? 可以,但不应该这样做。
- 怎么去除重复代码? 抽象基类,布局文件,style等。
八、调试技巧
- pm, dumpsys 等相关命令
- 编译命令
- trace, systrace 等
- bugreport
- debuggerd
- gdb
- addline