最后
分享一份NDK基础开发资料
分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
为了避免操作完读写流忘记close,java和kotlin两种编程语言分别给我们提供了以下语法糖:
1. 实现java的AutoCloseable
并搭配try-with-resource
看一段常见的代码:
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream(new File("test.txt"))) {
byte[] data = new byte[1024];
int read = fis.read(data);
//执行其他操纵
} catch (Exception e) {
e.printStackTrace();
}
}
FileInputStream
实现了AutoCloseable
接口,并重写了接口的close()
方法,通过上面的try-with-resource
语法,我们就不需要显示调用close方法关闭io,java会自动帮助我们完成这个操作:
常见的InputStream、OutputStream 、Scanner 、PrintWriter
都实现了AutoCloseable
接口,所以文件读写时可以非常方便的使用上面的语法糖。
2. 使用kotlin中的use()
扩展
kotlin针对Closeable(实现了AutoCloseable)
接口提供了下面的扩展:
我们常见的InputStream、OutputStream 、Scanner 、PrintWriter
等都是支持这个扩展函数的:
override fun create(context: Context) {
FileInputStream("").use {
//执行某些操作
}
}
虽然kotlin和java都从语言层面上帮助尽可能我们读写io流实现安全关闭,但是真正到写代码时忘了是真的忘了;而且项目中还可能存在历史代码也忘记了关闭流,查找起来也是毫无头绪的。
面对上面这中情况,就需要一种io泄漏的检测机制,不管是针对项目的历史代码还是新写的代码,能够检测文件流是否关闭,没有关闭则获取流创建的堆栈并上报帮助开发定位问题,接下来我们来一步步的实现这种能力吧。
二. IO泄漏检测的实现思路
头脑风暴一下,想要检测流有没有关闭,关键就是检测诸如FileInputStream
等操作文件流的类close方法有没有调用;那什么时机才应该去检测呢,当FileInputStream
等流类准备销毁的时候就可以去检测了,而类销毁的时候会调用finalize()
方法(PS:暂时不考虑finalize()特殊场景下的表现,这里认为都会被正常执行),所以检测的最佳时机就是在流类的finalize()
方法执行的时候。
经过上面的分析,我们可以写出下面的代码:
public class FileInputStream {
private Object flag = null;
public void open() {
//打开文件流时赋值
flag = "open";
}
public void close() throws Exception {
//关闭文件流置空
flag = null;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
//flag等于null,说明忘记执行close方法关闭流,io泄漏
if (flag != null) {
Throwable throwable = new Throwable("io leak");
//执行异常日志的打印,或者回调给外部。
//兜底流的关闭
close();
}
}
}
代码中有非常详细的注释,这里就不再一一进行讲述。
所以如果能在我们常见的FileInputStream
、FileOutputStream
、RandomAccessFile
等流类中也增加上面的代码,io泄漏监测这不就成了!!
Android官方自然也能够想到,并且还干了,常见的官方流类FileInputStream
、 FileOutputStream
、 RandomAccessFile
、 CursorWindow
等都增加了上面类似监控逻辑,接下来我们以FileInputStream
为例进行分析。
三 瞅瞅官方FileInputStream源码
这里我们先提前说下,官方监控流类是否泄漏,并不是直接在里面增加逻辑代码,想想也是,那么多流类,一个个增加过去导致模板代码太多,不如封装一个工具类供各个流类使用,这里的工具类就是CloseGuard
。
说清了上面,我们就看下FileInputStream
的源码:
1. 获取工具类CloseGuard
由于CloseGuard
的源码无法直接在AS中查看,这里我们借助 aospxref.com/android-12.…
网站查看下该类的源码:
CloseGuard.get()
方法就是创建了一个CloseGuard
对象。
2. 打开文件流
FileInputStream
构造方法主要干了两件事情:
- 通过传入的文件路径调用
IoBridge.open()
打开文件流(这个底层最终会调用了open(const char *pathname,int flags,mode_t mode)
,做io监控时一般需要hook该方法)。 - 同时还会调用
CloseGuard.open()
方法:
这个方法主要干的事情就是创建了一个Throwable
对象,获取当前流创建的堆栈,并赋值给CloseGuard
的closerNameOrAllocationInfo
字段。
3. 关闭文件流
FileInputStream
的close()
方法主要干了两件事:
- 调用
CloseGuard
的close()
方法:
很简单,就是将上面赋值的closerNameOrAllocationInfo
字段重新置空。
- 关闭文件流;
4. 重写finalize()
监控FileInputStream
的销毁
FileInputStream
的finalize()
方法主要干了两件事:
- 调用
CloseGuard
的warnIfOpen()
方法:
如果closerNameOrAllocationInfo
字段不为空,说明FileInputStream
的close()
关闭文件流的方法漏了调用,发生了io泄漏,调用reporter.report()
方法并传入closerNameOrAllocationInfo
参数(这个参数上面有说:保存了流创建时的堆栈,一旦获取到我们就能很快知道哪个地方创建的流发生了泄漏)。
- 兜底关闭流;
通过上面的分析可以得知,一旦发生io泄漏,就会通过reporter.report()
上报,这就是我们监控应用整体io泄漏的关键。
看下reporter
是个啥:
reporter
是一个静态变量,本质上是一个实现了Reporter
接口的默认实现类DefaultReporter
,默认通过report()
方法打印io泄漏的系统日志。
同时外部可以注入自定义的实现了Reporter
接口的类:
讲到这里大家是不是明白了,如果实现应用层的io泄漏检测,只要我们通过动态代理+反射代理掉reporter
这个静态变量,替换成我们自定义实现的Reporter
接口的类,并在自定义类中实现io泄漏异常上报的逻辑,不就完美实现监听了吗!!
想象很美好,现实很残酷,CloseGuard
是个系统类,且被@hide
隐藏,同时上面的setReporter()
方法被@UnsupportedAppUsage
注解,所以这个是官方非公开的api。在Android P以下自然可以通过反射调用,但是在Android P及以上使用反射就会报错,所以还得探索一种高版本能够成功反射系统非公开api的方法。
四. Android P及以上非公开api访问的实现
想要访问系统非公开api,那就只有系统api才能调用,一般有两种方式:
- 将我们自己的类的classloader转换为系统的classloader去调用系统非公开api;
- 借助于系统类方法去调用系统非公开api,即双反射实现机制;
这里我们采用的是第二种双反射实现方式,并且weishu大佬提供了一个github库方面我们拿来使用:
dependencies {
implementation 'com.github.tiann:FreeReflection:3.1.0'
}
然后在Application.attachBaseContext()
方法中调用;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Reflection.unseal(base);
}
五. 从0到1搭建IO泄露监测框架
上面的准备知识都讲解完毕了,接下来我们从0到1开始我们的io泄漏检测框架搭建之旅吧。
1. 创建名称为ResourceLeakCanary
的一个module,并引入下面两个依赖
dependencies {
implementation 'com.github.tiann:FreeReflection:3.1.0'
implementation("androidx.startup:startup-runtime:1.1.1")
}
2. 通过startup实现SDK的自动初始化,并借助FreeReflection
库解除系统非公开api访问限制
class IOLeakCanaryInstall : Initializer<Unit> {
override fun create(context: Context) {
//android p及以上非公开api允许调用
## 最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
上面分享的**腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题**,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含**知识脉络 + 诸多细节**,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
**【Android思维脑图(技能树)】**
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
![](https://img-blog.csdnimg.cn/img_convert/4467c622cfb1088c143a07e092e6bca4.webp?x-oss-process=image/format,png)
**【Android高级架构视频学习资源】**
**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**