设计缓存算法的时候需要考虑
- 哪些应该保存
- 哪些应该丢弃
- 什么时候丢弃
- 获取数据的成本和缓存数据的成本
- 缓存价值(命中率)
LRU(Least Recently Used)算法的实现
- LRU内部包含一个LinkedHashMap
- 统计监控(额外的开销,可以监控算法的有效性)
1 | private int putCount;//放入个数 |
1 | private int putCount;//放入个数 |
每一个枚举就是一个对象,对象至少占24字节。所以使用静态常量:
1 | @Retention(value = RetentionPolicy.SOURCE) |
Kotlin 可以使用内联类,编译时转化为int,节省内存。
在RxJava中,Observable被订阅后,可以得到一个Disposable对象。为了防止内存泄露,一般情况下,都会在Activity结束的时候调用Disposable对象的dispose()方法,解除订阅。
RxJava2中,提供了一个CompositeDisposable类作为Disposable的容器。
1 | public final class CompositeDisposable implements Disposable, DisposableContainer { |
可以看出,CompositeDisposable实现了Disposable接口(void dispose();方法);实现了DisposableContainer接口,可以add、remove、delete操作Disposable。
重点要关注两个方法:
1、add(@NonNull Disposable d)
这个方法是将一个dispoable加入CompositeDisposable中,当Activity或者Fragment的生命周期结束的时候,通过
dispose()方法将Observable的订阅事件取消关联,防止内存泄露。
2、public void dispose()
这个方法就是CompositeDisposable中将所有的Observable的订阅事件取消关联使用。
之前遇到过一个问题:
使用MVP模式开发的时候,Activity A的Presenter中包含CompositeDisposable对象,Activity A的onStart()方法中关联了A的Presenter,当Activity A 跳转到Activity B的时候,我在A的onStop()方法中调用了CompositeDisposable.dispose()方法。当Activity B 调用finish()方法结束时候,回到Activity A页面,Activity A上的所有关于RxJava的调用都失效。
我通过Debug的方式一层层的查看方法的调用栈对比正常的情况,最终发现:
CompositeDisposable的dispose()方法在被调用之后,所有的add()进来的Disposable都会直接调用自身的dispose()方法,也就是说在事件刚被绑定监听的时候就被解除绑定了。
查看源码:
1 | @Override |
在dispose() 方法调用的时候,会将全局变量disposed
置为true。
再看add方法:
1 | @Override |
在add一个disposable的时候,如果全局变量disposed为true,那么不会走if条件,而是直接走下面的d.dispose();
这句,直接将监听的事件解除绑定。
所有,CompositeDisposable的dispose()方法一定要放到Activity和Fragment生命周期结束的地方——onDestroy()中。
sendMessageDelayed(Message msg, long delay)
postDelayed(Runnable r, long delay)
loop()
方法,在死循环中获取消息队列中的消息,分发给handler处理使用DelayQueue
仿造Android的message
实现BlockingQueue接口(实现阻塞功能)
有PriorityQueue对象(可以优先排序)
DelayQueue的阻塞机制:通过condition.await方法
没有实现remove的方法
Zygote fork出的线程就是UI线程。
zygote创建App的过程中调用ActivityThread类的main()方法,main中会自行Looper.loop();
。
那么需要对控件加锁,哪一个线程拿到锁才能设置UI。
加锁可以变成线程安全,但是加锁是有性能开销的,会使UI的整体性能下降
SurfaceView可以在非UI线程更新UI:
lockCanvas -> draw -> unLockCanvasAndPost
处理好window的控制
修改activity的style属性:
1 | <style name="AppTranslucentTheme" parent="AppTheme"> |
Activity联动(多个activity栈)
Activity A和Activity C在Task#0
Activity B在Task#1
当Activity A跳转Activity B时没有问题(有动画效果)
再当Activity B跳转Activity C时,会先出现Activity A,然后动画效果到Activity C
处理方法:再Activity B跳转Activity C的时候将B页面整体截屏放到Activity C的下面。
获取Activity的栈:
1 | Activity.java |
1 | graph TB |
A、B、C、D四个Activity依次跳转,此时Activity栈中:A->B->C->D。(默认都不透明)
setResult()
写法不够直观,结果数据没有类型安全保障intent.putExtra()
时和getIntent.getXxxExtra()
时需要人工去保证类型安全
注解处理器生成Builder
1 | @Builder |
通过注解处理器生成注入逻辑
1 | graph LR |
在AndroidManifest中:manifest
标签中添加android:sharedUserId="xxxx"
启动时:startActivity(new Intent().setComponent(new ComponentName("com.example.zhu","com.example.zhu.XxxActivity")));
在Manifest中添加exported属性<activity android:name=".BActivity" android:exported="true"/>
启动时:startActivity(new Intent().setComponent(new ComponentName("com.example.zhu","com.example.zhu.XxxActivity")));
在Manifest的Activity标签中添加:
1 | <intent-filter> |
启动时:startActivity(new Intent("android.intent.action.TEST"))
给AppA的manifest中添加权限:
<uses-permission android:name="com.example.zhu"/>
gei AppB中需要启动的Activity添加permission属性:
android:permission="com.example.zhu"
启动时:使用隐式跳转
这种添加权限的方法必须要AppB先安装,否则AppA无法获取权限
在被启动的Activity中,getIntent().getExtras()
获取的bundle对象中如果有的序列化数据,无法被反序列化成对象,程序就会崩溃(报找不到反序列化对象的exception),此时需要对bundle的操作加try-catch。
AMP -> AMS 以及 ATP -> ApplicationThread都是通过binder进行通信的
1 | graph LR |
解决办法:
1 | graph LR |
Activity之间的数据传递不能太大
通过反射newInstance实现,所以不能对Activity添加构造方法。
同理,fragment也一样。
静态绑定:通过命名规则映射
1 | package com.example.nativec; |
1 | extern "C" JNIEXPORT void JNICALL |
动态绑定:通过JNI函数注册
1 | int registerMethods(JNUEnv *env, const char *className, const JNINativeMethod *methods, int methodsLength){ |
动态绑定 | 静态绑定 | |
---|---|---|
Native函数名 | 无需求 | 按照固有规则编写且采用C的名称修饰规则 |
Native函数可见性 | 无需求 | 可见 |
动态更换 | 可以 | 不可以 |
调用性能 | 无需查找 | 有额外的查找开销 |
开发体验 | 几乎无副作用 | 重构代码时较为繁琐 |
Android Studio支持 | 不能自动关联跳转 | 自动关联JNI函数可跳转 |