(1-2题资料详见 https://www.jianshu.com/p/8ef1e6e59f66)
- 冷启动如何缩短启动时间
减少应用初始化的时间。
a) 减少在application中的耗时操作(懒加载)
b)减少在onCreate中的耗时操作
居于上面的处理方式,我们可以考虑延迟初始化(比如我们可以延迟几秒再初始化)或者懒加载(这里所谓的懒加载就是非必要的sdk初始化,在使用的时候我们才去初始化,有点像单例里面的懒汉模式)
2.黑白屏问题怎么解决
a)通过修改主题样式来解决,一般用layer-list绘制一个纯色背景加上启动图标,然后在我们的启动SplashActivity中创建主题。
b) 使用背景图片,我们在主题样式中通过修改主题的样式
3.升级数据库 (详见 https://www.jianshu.com/p/f07c6dd8521f)
SQLite数库对ALTER TABLE命令支持非常有限,只能在表末尾添加列,不能修改列定义,不能删除已有的列。我们可以采用临时表的办法。具体来说有四步:
a) 将现有表重命名为临时表
b)创建新表
c)将临时表的数据导入新表
d) 删除临时表
4.加密方式
不可逆加密,如md5,sha1,sha128,sha256
使用场景:如登录注册,账号密码保存到sp中,可以直接保存密码的md5值。
可逆加密,常用对称加密和非对称加密
非对称加密,有一个公钥和一个私钥,通过公钥加密,私钥解密。RSA
使用场景:项目中一些敏感的数据,比如身份证号,银行卡号,支付密码等相关敏感信息可以加密后再传输到服务端,服务端使用私钥进行解密。
对称加密,加密和解密就一个key。AES,将项目中的一些敏感数据保存到本地,需要使用的时候再取出来解密。
5.断点续传和分段下载
断点续传就是信号中断后(掉线或关机等),下次能够从上次的地方接着传送(一般指下载或上传),不支持断点续传就意味着下次下载或上传必须从零开始。http协议中的断点续传是基于Http头Range以及Content-Range。HTTP头中一般断点下载时才用到Range和Content-Range实体头,Range用户请求头中,指定第一个字节的位置和最后一个字节的位置,如( Range:200-300或者Range:200- );Content-Range用于响应头。通俗的来讲就是文件大小为10,这次下载了3,被中断了,下次继续下载时则将指针移到3位置,从3开始下载,最终将整个文件下载下来。
6.apk瘦身 (详见 https://www.cnblogs.com/ryanleee/p/10976545.html)
主要从apk中的包含的这些文件入手,lib,res,assets,resources.arsc,classes.dex。
lib目录的优化:
a) so裁剪、删除,对App引入的so文件进行确认哪些是不需要的,哪些是可以进行裁剪压缩的,哪些是可以避免引入的。
b) 只保留armeabi或者armeabi-v7a,Android系统现在支持很多种CPU架构(如mips、arm、x86等),市面上主流机型都是arm架构,x86和mips类型极少。
res目录优化:
c) res目录优化,res目录一般也是占APK Size大头的一个目录,如下图,这个目录一般都是图片资源占空间比较多,尤其当App为了适配多种分辨率而存放了多套图时,这时候就会导致res目录打下会非常大。可以只保留一套图,非重要图片动态加载,可以使用一些图片压缩软件压缩图片,例如TinyPng、ImageOptim、Zopfli、智图等。使用webp替换png,使用lint删除无用资源,通常都会有漏删无用资源的问题,图片资源也不例外,例如需要删除一个模块的代码时,很容易就会漏删资源文件,所以可以定期使用lint检测出无用的资源文件。打开shrinkResources,shrinkResources是在编译过程中用来检测并删除无用资源文件,也就是没有引用的资源,minifyEnabled 这个是用来开启删除无用代码,比如没有引用到的代码,所以如果需要知道资源是否被引用就要配合minifyEnabled使用,只有两者都为true时才会起到真正的删除无效代码和无引用资源的目的。配置的使用的方法如下:在build.gradle文件里面设置
android {
buildTypes{
minifyEnabled true
shrinkResources true
}
}
assets目录的优化:
d) assests目录优化,assests目录存放的通常是一些通过AssetManager能够检索到的资源,包括MP3、视频、字体、webp的资源.删除无用的字体。动态下载一些MP3、视频、Webp等资源可以在使用到时再进行下载,不需要放在本地。
resources.arsc优化:
e) 使用AndResGuard压缩,AndResGuard是一个帮助你缩小APK大小的工具,他的原理类似Java Proguard,但是只针对资源。他会将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a。
dex文件压缩:
通过Proguard代码混淆,压缩器、优化。
7.性能优化 (详见 https://www.jianshu.com/p/bd29e0327064)
常见 内存优化,UI优化,网络优化,启动优化
内存:单例造成的内存泄漏,内部类造成的内存泄漏,异步线程造成的内存泄漏,Handler造成的内存泄漏,资源未关闭造成内存泄漏
内存泄漏检测方案:
LeakCanary工具,
a) LeakCanary在一个Fragment或者Activity onDestory的时候,创建一个弱引用 KeyedWeakReference 到要被监控的对象。
然后在后台线程检查引用是否被清除,如果没有,调用GC。
b) 如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
c) 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。
d) HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链.
避免内存抖动:内存抖动代表频繁GC,会占用程序执行时间,造成页面卡顿等性能问题。
避免内存抖动的方式:
a)避免在onDraw中创建Paint、Bitmap对象等;
b)避免在循环中创建临时对象;
c)避免在scrollListener中创建对象。
内存抖动检测方法:
使用Android profiler 进行观察,如果发下内存剧增剧减则是GC时间,可查看GC时间段的内存,找出重复创建的大量对象,进行优化(例子:在recycleview中获取距离屏幕的距离时,创建大量对象)。
考虑内存对象的复用来减少内存的占用,如
1)ListView/RecycleView替代scrollview;Bitmap对象的复用
2)使用memory profiler 去检测内存中是否可重复利用的对象;
使用优化的数据结构
UI优化
a) 分析布局,减少布局嵌套或者替换消耗性能少的布局
b) 使用include+merge减少布局层级
c) 使用viewstub提供按需加载
d) 复用系统自带的资源 winow background
网络优化
网络速度的优化
正常一条网络请求需要经过的流程是这样:
(1)、DNS 解析,请求DNS服务器,获取域名对应的 IP 地址;
(2)、与服务端建立连接,包括 tcp 三次握手,安全协议同步流程;
(3)、连接建立完成,发送和接收数据,解码数据。
可以这样来优化:
a) IP直连或者HTTPDNS;
b) 开启 keep-alive进行连接复用;
c) 减少请求和返回数据的大小;(请求和返回数据做Gzip压缩,精简数据格式,图片:使用webp图片格式代替png、jpeg;按需加载图片,图片裁剪,也可按网络状态返回不同分辨率的图片)
d) 数据缓存
e) 断点续传
f) 请求的打包(如埋点统计)
启动优化
Application,Activity中延迟初始化
延迟加载功能:首屏绘制完成之后加载
动态加载布局:主布局文件优化,采用约束布局
8.终止线程的方法
有三种方法可以结束线程:
1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
2. 使用interrupt()方法中断线程
3. 使用stop方法强行终止线程(不推荐使用,可能发生不可预料的结果)
9.service
service的两种启动方式:
StartService
1、定义一个类继承于Service
2、在Manifest.xml文件中配置该Service
3、使用Context的startService(Intent)方法启动该Service
4、不再使用时使用stopService(Intent)方法停止该服务
BindService
1、定义一个类继承Service,创建一个继承与Binder的实例对象,并提供公共方法供客户端调用。
2、实现onBind()方法,返回Binder实例
3、在Manifest.xml文件中配置该Service
4、在客户端中,实现ServiceConnection实例,从onServiceConnected()回调方法接收Binder,并使用bindService绑定服务。注:onServiceDiscounnection方法是在服务崩溃或者服务杀死导致的连接中断时调用
Service是否在main thread中执行, service里面是否能执行耗时的操作?
默认情况,如果没有显示的指 service 所运行的进程, Service 和 activity 是运 行在当前 app 所在进程的 main thread(UI 主线程)里面。service 里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 )特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行
什么情况下会使用Service?
我们可以知道需要长期在后台进行的工作我们需要将其放在Service中去做。说得再通熟易懂一点,就是不能放在Activity中来执行的工作就必须得放到Service中去做。如:音乐播放、下载、上传大文件、定时关闭应用等功能。这些功能如果放到Activity中做的话,那Activity退出被销毁了的话,那这些功能也就停止了,这显然是不符合我们的设计要求的,所以要将他们放在Service中去执行。
Service里面可以弹Toast么?
可以的。弹吐司有个条件就是得有一个Context上下文,而Service本身就是Context的子类,因此在Service里面弹Toast是完全可以的。比如我们在Service中完成下载任务后可以弹一个Toast通知用户。
10.Android中有哪些进程间的通信方式?
Bundle 只能传输Bundle支持的数据类型,适用于四大组件间的进程间通信。
文件共享 不适用高并发场景,并且无法做到进程间即时通信,适用于无关发的情况下,交换简单的数据,对实时性要求不高的场景。
AIDL 功能强大,支持一对多实时并发通信,使用稍复杂,需要处理好线程间的关系,一对多通信且有RPC需求
Messenger 功能一般,支持一对多串行通信,支持实时通信,不能很好地处理高并发的情形,不支持RPC,由于数据通过Message传输,因此只能传输Bundle支持的数据类型,低并发的一对多实时通信,无RPC需求,或者无需要返回结果的RPC需求
ContentProvider 支持一对多的实时并发通信,在数据源共享方面功能强大,可通过Call方法扩展其它操作,可以理解为受约束的AIDL,主要提供对数据源的CRUD操作.
BroadcastReceiver 操作简单,对持一对多实时通信,只支持数据单向传递,效率低且安全性不高,一对多的低频率单向通信
Socket 功能强大,可通过网络传输字节流,支持一对多实时并发通信,实现细节步骤稍繁琐,不支持直接的RPC,网络间的数据交换
由于不同的进程拥有不同的数据空间,所以无论是应用内还是应用间,均无法通过共享内存来实现进程间通信。
应用内使用多进程可能导致哪些问题?
当一个APP启用了多进程后,系统会为不同的进程分配不同的内存空间,因此所有需要通过内存共享的行为都会失败。另外,还会导致以下几个问题:
进程:Application会被多次创建,因为系统会为每一个进程分配独立的内存空间。
线程:线程同步失效,因为不同进程中的线程同步锁不是同一个对象。
内存:单例模式和静态变量失效,因为不同进程间的内存空间不同。
存储:SharedPreferences可靠性下降,因为系统对SharedPreferences有一定的缓存策略,多个进程同时读写可能会造成数据丢失。