- android 触摸事件传递机制
时间传递的三个阶段:
分发(dispatch) ----> 拦截(intercept) ---->消费(consume)
在android 中拥有时间传递处理能力的类有以下三种
Activity: 拥有dispatchTouchEvent 和 onTouchEvent两个方法
ViewGroup: 拥有 dispatchTouchEvent , onInterceptTouchEvent, 和 onTouchEvent
View: 拥有dispatchTouchEvent 和 onTouchEvent两个方法
234190-58b2ae8bc64282fc.png
- android view的绘制流程
android UI 管理系统层级关系
activity ->phoneWindow -> DecorView ->自己的布局
PhoneWindow 是android 系统中最基本的窗口系统, 每个activity 会创建一个, phoneWindow 是 activity 和 view系统交互的接口, decorView 本质上是一个FrameLayout , 是activity 中所有的view的祖先
绘制会从根视图ViewRoot 的 performTraversals()方法开始
视图绘制的过程分为三个步骤:
测量(Measure):
Measure操作用来计算view的实际大小, 测量流程是从performMeasure方法开始的
重点关注三种测量模式:
UNSPECIFIED: 不指定测量模式,父视图没有限制子视图的大小,子视图可以是想要的任何尺寸,通常用于系统内部,应用开发中很少使用
EXACTLY: 精确测量模式,当视图的layout_width 或者 layout_height制动具体的数值或者match_parent时生效,表示父视图已经决定了子视图的大小,这种模式下VIew的测量值就是SpecSIze的值
AT_MOST: 最大值模式,当该视图的layout_width或者layout_height指定wrap_parent时生效,此时子视图的尺寸是不超过父视图允许的最大尺寸的任何尺寸
布局(layout) :
layout 过程用来确定view在父容器中的布局位置,他是由父容器获取子view的位置参数后,调用子view的layout方法并讲位置参数传入实现的
绘制(draw):
draw操作用来将控件绘制出来,绘制的流程是从performDraw 方法开始的,最终会调用到每个View的draw方法绘制每个具体的view,绘制基本上可以分为六个步骤:
步骤一: 绘制view的背景
drawBackground(canvas)
步骤二: 如果需要的话,保存canvas的图层,为fading做准备
canvas.getSaveCount()
步骤三: 绘制view的内容
onDraw(canvas)
步骤四: 绘制view 的子view
dispatchDraw(canvas)
步骤五: 如果需要,绘制view的fading边缘并恢复图层
canvas.drawRect()
...
canvas.restoreToCount()
步骤六: 绘制view的装饰(例如滚动条)
onDrawScrollBars(canvas)
- android 的动画机制
android 3.0版本之前,可以使用逐帧动画和补间动画
3.0发布时,引入了属性动画
4.4的时候带来了android.transition框架
逐帧动画(Drawable Animation):
他是利用人眼的视觉暂留效应-----也就是光对视网膜产生的视觉在光停止作用后,仍会保留一段时间现象
实现逐帧动画,就是由设计师给出一系列状态不断变化的图片,开发者可以指定动画中的每一帧对应的图片和持续时间,然后就可以开始播放动画了,有两种方式可以定义逐帧动画,分别是XML资源文件和代码实现
补间动画(Tween Animation)
补间动画是指开发者无须定义动画过程中的每一帧,只需定义动画的开发和结束这两个关键帧,并指定动画变化的时间和方式等,然后交给系统进行计算,通过两个关键帧之间插入渐变值来实现平滑过度,,从而实现动画效果,主要由四种效果可以动态组合,分别是:
透明度变化Alpha
大小变化Scale
位移变化Translate
旋转变化Rotate
示例:
从上向下无限循环移动
//开始动画
scanAnim = AnimationUtils.loadAnimation(act,R.anim.scan_down)
iv_scan.startAnimation(scanAnim)
//停止动画
iv_scan.clearAnimation(scanAnim)
属性动画(Property Animation)
过度动画(Transition Animation)
- Support Annotation Library 库使用
注解库
- Percent Support Library 库使用
百分比控件库
- Design Support Library 库使用
比较新的控件库
- NDK 开发
ABI -> Application Binary Interface 应用程序二进制接口,android 平台指.so文件
目前android系统支持7种不同的CPU架构,每一种CPU架构对应一个ABI,对应android studio 中的目录文件如下:
ARMv5->armeabi, ARMv7(从2010年起)->armeabi-v7a, x86(从2011年起)->x86 , MIPS(2012)->mips , ARMv8->arm64-v8a , MIPS6-> mips64, x86_64(2014年起)->x86-64
- Gradle 必知必会
共享变量----通用配置---aar函数库引用---签名和混淆配置 - 通过Gradle 打包发布函数库到JCenter 和 Maven Central
- Builder 模式详解
经典的Builder模式
定义: 将一个复杂对象的构建和他的表示分离,使得同样的构建过程可以创建不同的表示
经典的Builder模式主要由四个参与者
1. Product: 被构造的复杂对象, ConcreteBuilder 用来创建该对象的内部表示,并定义他的装配过程
2. Builder: 抽象接口,用来定义创建Product 对象的各个组成部件的操作
3. ConcreteBuilder: Builder接口的具体实现,可以定义多个,是实际构建Product对象的地方,同时会提供一个返回Product的接口
4. Director: Builder 接口的构造者和使用者
示例如下:
/* 定义: 将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建出不同的表示
**/
public class Product {
private String partOne;
private String partTwo;
public String getPartOne() {
return partOne;
}
public void setPartOne(String partOne) {
this.partOne = partOne;
}
public String getPartTwo() {
return partTwo;
}
public void setPartTwo(String partTwo) {
this.partTwo = partTwo;
}
}
interface Builder {
public void buildPartOne();
public void buildPartTwo();
public Product getProduct();
}
public class AConcreteBuilder implements Builder {
private String TAG = "AConcreteBuilder";
private Product product;
@Override
public void buildPartOne() {
System.out.println(TAG+ "buildPartOne: ");
product = new Product();
product.setPartOne("AConcreteBuilder--buildPartOne");
product.setPartTwo("AConcreteBuilder--buildPartTwo");
}
@Override
public void buildPartTwo() {
System.out.println(TAG+ "buildPartTwo: ");
// product = new Product();
// product.setPartTwo("AConcreteBuilder--buildPartTwo");
}
@Override
public Product getProduct() {
return product;
}
}
public class BConcreteBuilder implements Builder {
private Product product;
private String TAG = "BConcreteBuilder";
@Override
public void buildPartOne() {
System.out.println(TAG+"buildPartOne: ");
product = new Product();
product.setPartOne("BConcreteBuilder--buildPartOne");
}
@Override
public void buildPartTwo() {
System.out.println(TAG+"buildPartTwo: ");
product = new Product();
product.setPartOne("BConcreteBuilder--buildPartTwo");
}
@Override
public Product getProduct() {
return product;
}
}
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void builderProduct(){
builder.buildPartOne();
builder.buildPartTwo();
}
public Product getProduct(){
return builder.getProduct();
}
}
@Test
public void builderTest(){
System.out.println("123");
Director director = new Director(new AConcreteBuilder());
director.builderProduct();
Product product = director.getProduct();
System.out.println(product.getPartOne());
System.out.println(product.getPartTwo());
}
变种Builder模式
可网上找个变种例子,一些开源库使用的就是变种builder模式,比如AlertDialog,Okhttp
- 注解
1.编译时注解
2.运行时注解
3.元注解
定义注解
注册注解处理器
android的apt的注解插件
- ANR产生原因及定位分析
可通过 /data/anr/traces.txt 目录文件分析ANR
ANR的监测工具BlockCanary函数库,StructMode
内存监测工具LoakCanary
ANR发生的原因:
1. activity 页面响应超过5s
2.broadcastReceiver 响应超过10s
3.service 响应超过20s
典型的ANR场景
1.UI线程存在耗时操作
2.UI线程等待子线程释放某个锁,无法处理用户的输入
3.动画的绘制需要大量的计算,导致CPU负载过重
- android 异步处理技术
Thread: 线程
在应用层主要分3类线程
1.UI线程,也是主线程
2.后台线程(也是子线程)
3.binder线程(进程之间的通信)
衍生顺序:
Thread->HandleThread ->intentServer(服务线程)
->AsyncQueryHandler(在contentProvider中使用)
->Executor Framework -> AsyncTask ->loader
HandleThread 是一个集成了looper 和 MessageQueue的线程
- android 数据序列化方案研究
1.java中使用的Serializable ,主要应用于磁盘和网络中
原理: 使用java中的反射机制,在序列化过程中会创建多个临时对象,很容易就触发系统的回收机制,性能比较低, 也是对象转换成字节的过程,反序列化就是字节转化成对象的过程.
序列化过程: 先创建某种类型的OutputStream(如 FileOutputSteam),接着将这些Outputstream封装到一个ObjectOutputstream中,然后可以调用ObjectOutputStream 的writeobject方法,写完之后记得关闭各项
2.android 的 parcelable 序列化,android sdk 提供,操作的对象是内存,速度较serialiable快很多,kotlin中可直接使用注解序列化
3.SQLDataBase ,sqllite数据库
4.sharepreference 数据存储
5.json
6.FlatBuffers 是google提供的序列化函数库,注重性能和资源的使用
- android Webview java 和 JS 交互
java调用js直接loadUrl
js调用java通过webSetting类的诸多方法
- MVP,MVVM,MVC模式
MVC
view层,control层,model层,关系是个三角关系
MVP
是MVC模式的延伸和改进,view层-><-presenter层-><-model层
优势:
1.如果界面发生改变,只需要改变view层
2.如果业务逻辑或者数据获取方式改变,只需要修改model层
3.如果控制逻辑发生改变,只需要改变persenter层
4.业务的交互都是通过接口实现的,有利于新成员接触代码,也可根据事先定义好接口,功能交给不同的人开发
劣势
1.接口过多,代码量,方法数增加
2.由于进行了三层划分,函数的调用栈变浅了,这对开发人员的对代码的认识就变高了,如果开发人员没能分清楚那一层具体该负责哪些功能,那么就会造成代码的乱入,mvp也就达不到充分解耦的目的.
MVVM
是MVP模式的延伸和改进: view层->viewModel层-><-model层
关键点在viewmodel,还引入了databinding的概念--以UI驱动数据
- Databinding
UI驱动数据
- 观察者模式扩展,事件总线
观察者模式概念: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并自动更新
原理
几个事件分别是:
1.事件 Event
2.订阅者 Subscrber
3.发布者 Publisher
4.总线 EventBus
publisher ---post Event--->EventBus ---event--->Subscriber1号
---event--->Subscriber2号
开源实现库 EventBus
- 书写简单规范代码
1.严格遵守java编码规范
2.Android的一些命名规范
3.可以使用checkstyle 对项目的规范就行检测
- 搭建自己的技术堆栈
APP整体架构
较高的层次来讲,一个app的整体架构可以分为两层,应用层和基础框架层
1.应用层专注于行业领域的实现,例如金融,支付,地图导航,社交,他直接面向用户,是用户对产品的第一层感知
2.基础框架层专注于技术领域的实现,提供app公有的特性,避免重复制造轮子,他是用户对产品的第二层感知,例如性能,稳定性等
一个理想的App架构,首先应该是支持跨平台开发的,其次应该具有清晰的层次划分,同一层之间模块充分解耦,模块内部符合面向对象设计六大原则,最后应该在功能,性能,稳定性等方面达到综合最优.
1659526289903.jpg
- 64K方法数限制原理和解决方案
APK本质上是一个压缩包,里面包含classes.dex是一个可执行Dalvik字节码文件,这个.dex文件存放的事所有编译后的java代码,dalvik可执行文件规范单个.dex文件最多能引用的方法数是65536个,这其中包含android framework,app引用的第三方函数库以及app自身的方法
解决方案
使用MultiDex解决.俗称分包,就是将一个dex装不下的方法分成多个dex装
其实MultiDex是一个不得已而为之的方案,它本身并不是完美的,他会导致编译非常的慢
在开发阶段优化MultiDex的构建
之所以MultiDex构建如此缓慢,是因为构建系统需要经过复杂的计算决定哪些类要包含在主dex文件中,哪些类可以包含在从dex中.为了解决这个问题,减少开发阶段的构建时间,我们可以在工程的主模块的build.gradle文件中使用productFlavors来创建两个flavor,一个是开发阶段使用的,一个是生产阶段使用的,开发阶段的flavor 中,设置minSDKVersion 为21,这使得构建系统使用ART支持的格式更快的生成MultiDex的输出,生产阶段的flavor中,设置minSdkVersion为应用时机支持的最低版本号
- android 插件框架机制研究
为了解决APK随着开发周期的延长,需求的增多,APK的运行会越来越缓慢,这时插件化框架应运而生,虽然google提出了MultiDex的方案,但我们都知道他并不是很完美.插件化框架的基本形式是将一个APK中的不同功能模块进行拆分,并大儒道不同的dex文件或者apk文件中,主工程只是一个空壳,提供了用来加载模块dex或者apk的框架,使用插件框架的好处也有很多:
1.提升AS工程的构建速度
2.提高应用的启动速度
3.支持多团队开发
4.在线动态加载或者更新模块
5.按需加载不同的模块,实现灵活的功能配置
ClassLoader机制
android中的Classloader机制是用来加载dex文件,系统提供了两个API可供选择
1.PathClassLoader: 只能加载已经安装到android系统的apk文件
2.DexClassLoader: 支持加载外部的apk,jar,或者dex文件,,正好符合插件化的需求,所以有的插件化方案都是使用的DexClassLoader 来加载插件的APK中的.class文件的
开源的插件框架
1.android-pluginmgr
2.dynamic-load-apk
3.DynamicAPK
4.DroidPlugin
5.Small
- 推送机制实现原理详解
开源实现
1.基于XMPP协议
2.基于MQTT协议
第三方平台
百度,小米,极光,友盟等
自己实现推送功能
通过网络套接字socket,他封装了很多tcp的操作,对移动端来说,一个推送的基本框架需要包括:
1.和服务端建立连接的功能
2.发送数据给服务端的功能
3.从服务端接收推送数据的功能
4.心跳包的实现
- APP瘦身经验
1.优化图片资源占用的空间
2.使用Lint工具删除无用的资源
3.利用gradle的配置,减少最后生成的apk包大小
4.重构和优化代码
5.资源混淆
6.插件化
- android Carsh 日志收集原理与实践
定义
java中常用的异常主要有两类,一种是编译时的异常(CheckedException),必须在编译时期进行处理,常用操作就是使用try..catch捕获进行处理.另外一种是运行时异常(UnCheckedException),这类异常在编译时期是检测不出来的,只有在运行阶段特定的情况下触发才能检测出来.
原理
java API提供的全局的异常捕获处理器 Thread.UncaughtExceptionHandler 处理器,通常情况下我们只需要实现这个接口,重写他的uncaughtException放,在该方法就可以读取carsh的堆栈信息,注意一点要在app启动的时候注册哦,不然他还是沿用的系统默认的异常处理
- 函数式编程思想
RxJava,RxAndroid,Agera,这几个函数式框架,都提现了Android的函数式编程
- 依赖注入在android的使用
优势
依赖注入让开发者可以编写低耦合的代码,他解耦了依赖者和被依赖者,代码易于单元测试
依赖注入(简称 DI )也可以称为控制反转(Inversion of Control) 简称IoC,IoC一般分为两种类型,一种就是依赖注入,另一种就是依赖查找
复杂的项目建议直接使用依赖注入框架,常用的依赖注入框架有
1.BufferKnife
2.RoboGuice
3.Dagger
- kotlin 在android中的使用
- react native 在 android中使用
- 在线热修复方案
原理
线上的正式包APK称为宿主APK,补丁包称为补丁APK,线上出现问题后,需在线下载补丁APK,使用补丁APK中的函数替换原来的函数,从而实现修复bug的功能
热修复框架
1.Dexposed 阿里巴巴开源,支持android版本太低
2.AndFix 阿里巴巴开源,纯粹的热修复框架
3.Nuwa QQ空间开发团队,基于classLoader加载dex文件实现热修复
- 面向切面编程
AOP (Aspect Oriented Programming) 即面向切面编程
可以使用AspectJ进行 android 平台的切面编程
1.Hugo 插件
- FaceBook Buck 改造android 构建系统
- 代码优化,图片优化,网络优化,电量优化,布局优化
- android 混淆机制详解
混淆的目的是为了保护自己的知识产权,增加别人的反编译难度
Android的混淆包括三种类型:
1.java代码混淆
2.Native (C 和 C++) 代码混淆
3.资源文件的混淆
主要混淆还是在java代码层,java代码层的混淆一般依赖于proguard 或者 dexguard 工具,
proguard是免费开源的,但功能有限,dexguard是收费的
proguard 特性
1.压缩,优化,混淆,预检验
Android Studio 创建项目的时候默认是使用Proguard 的,其中proguard-Android.text 是Proguard 默认的混淆配置文件,位于 Android SDK 的 tools/proguard目录中 ,项目根目录下的 proguard-rules.pro文件也是编写混淆的文件
- android 反编译机制
借助第三方的反编译工具 APKTool
- 客户敏感信息隐藏技术研究
- android 加固技术研究
加固的目的和混淆的目的其实是一致的,都是为了保护自己的知识产权,防止反编译,一些常用的第三方加固有:
1.梆梆加固
2.爱加固
3.迦娜
4.360加固
5.腾讯加固
6.百度加固等
- android 安全编码
一些保证编写代码安全的建议,如
1.WebVIew 远程代码执行
2.WebView 密码明文保存
3.Android 本地拒绝服务
4.SharedPreference 全局任意读写
5.密码硬编码
6.弱加密
7.PendingIntent 使用不当 等
- android 调试工具 Facebook Stetho
- 内存泄漏检测函数库 LeakCanary
LeakCanary 检测内存泄漏的原理
RefWatcher.watch() 函数会为被监控的对象创建一个 KeyedWeakReference 弱引用对象, KeyedWeakReference 是 WeakReference 的子类,增加了键值对信息,后面会根据指定的键值key 找到弱引用对象
在后台线程AndroidWatchExecutor 中,检查 KeyedWeakReference 弱引用是否已经被清除,如果还存在,则触发一次 垃圾回收机制,垃圾回收之后,如果弱引用对象依然存在,说明发生了内存泄漏
- 基于Facebook Redex 实现 APK的压缩和优化
- android studio 你所需要知道功能
1.Annotate git记录代码作者编写工具
2..ignore 插件
- android 单元测试框架简介
- android UI自动化测试框架简介
- android 静态代码分析
- Jenkins+Gradle 搭建android 持续集成编译环境