lvkang-insist / android-day-issues Goto Github PK
View Code? Open in Web Editor NEWMake progress every day
Make progress every day
明确的优化目标实际上就是一个从定性到定量的转变。首先要确定优化哪些东西,然后在去确定到底要优化多少。
例如耗电优化,定性就是 App 耗电量高,需要优化,而定量就是到底要优化成什么样子,例如从之前的每小时 10% 优化到 6%。也就是需要量化目标。
而优化的方面也有很多,例如下面:
其实我们项目中可优化的地方有很多,首先需要确定的就是哪些问题占比最高,优化了之后的效果最大,然后就从这方面开始进行优化。
所以需要优先解决占比最高的问题。
从开始优化的时间,到现在的时间做一个表,然后进行对比,看看优化的效果到底如何。
线上灰度,优化完一个版本之后,不要直接进行全量更新,因为你无法预知会出现哪些 bug,所以可以进行灰度测试,先 20% 的灰度看一下效果,如果优化的效果还不错就可以加直接更新了。
data 类
data 类不可被继承,不可以实现接口
默认提供了 equals,toString,copy 等方法,如果类中继承了这些方法,则不会自动生成
构造函数至少需要一个参数
所有构造函数的参数必须使用 val 或者 var 标记
密封类
密封类是一种特殊的抽象类,不能被实例化,构造器私有,不能在外部文件中继承密封类。通过反编译就可以看到本质就是个抽象类,并且构造器是私有的
密封类一般用于表示各种状态,不同的状态可以携带不同的参数以及行为来记录各种信息。
观察者模式又称之为发布-订阅模式,发布者负责发送,订阅者负责消费,它定义了对象之间的一种一对多的依赖关系,当一个对象发生改变后,所有依赖他的对象都会收到通知。
优点:解除观察者和发布者的耦合,让耦合的双方都依赖于抽象,而不是依赖于具体
易于扩展,新增观察者非常方便,无需修改原有代码
缺点:依赖关系没有完全解除,双方依然依赖于抽象
观察者如果太多,调试会比较复杂,并且一般通知观察者的时候是顺序执行的,如果一个观察者卡顿,会影响整体的效率,这种情况下一般可以采用异步来实现
可能会引起多余的数据通知
reified 通常和 inline 和 泛型一起出现,总所周知泛型的实现方式是通过类型擦除来实现的,在运行的时候不会带有任何的类型信息。
当调用了内联函数后,该函数就会在调用的地方被执行,如果在被调用的地方执行,会将泛型替换为具体的类型,在配合 reified 关键字,就可以在内联函数中获取的泛型的类型而且不会报错。
LiveData 是一个可被观察的数据持有类,和其他的不同的是,LiveData 具有感知生命周期的能力。
LiveData 通过添加 Observer 来进行观察,添加的时候会被LifecycleBoundObserver 进行包装,这样就可以感知生命周期了,接着判断是否添加过,如果添加过直接退出,最后给包装类注册 Lifecycle,使其可以感知生命周期。
在 LifecycleBoundObserver 中,监听生命周期改变,如果发生改变,则根据条件修改活跃数量,然后进行分发,分发的时候,遍历已注册的所有观察者,如果不是活跃的,或者数据不是最新的(和之前的版本进行对比),则不会分发,否则就进行回调,将数据发送出去。
LiveData 在发送数据的时候有两种方式,直接 set 和 post,分别对应同步和异步发送。异步的话就是扔到主线程在进行发送,在发送的时候首先是版本(version+1),然后同样也是遍历所有的观察者,判断活跃状态和版本,满足条件就会分发出去。
除此之外,LiveData 还有别的扩展方法,例如 map ,switchMap ,和并多个 livedata 等方法。
问题1,粘性数据:在没有观察着的时候发生数据,到添加观察者之后,如果生命周期活跃,这个数据就会重新分发,解决可通过反射来修改版本的值。问题2:数据倒灌,LiveData 在 ViewModel 中,当Activity 重建的时候,观察者会被 remove 掉,重建后会重新添加观察者,此时新的观察着会接收到之前的数据,可以通过反射解决。
使用 LiveData 的好处就是关联的生命周期,可以很大程度避免内存泄露,并且无需手动更新界面,直接在回调中更新即可,还有就是页面始终显示的是最新数据,应为会在活跃的时候接收到最新的数据,最后就是可以在单例中扩展 LiveData,方便在应用**享这个 livedata。
其他问题:
第一次启动:onCreate -> onStart -> onResume
打开新的 Activity / 返回桌面:A : onCreate -> onStart -> onResume -> onPause ; B:onCreate ->onStart - onResume ; A: onStop ; 返回 A时 B:onPause ,A:onRestart -> onStart -> onResume ; B:onStop -> onDestory
横竖屏切换:
onPause -> onStop -> 调用 onSaveInstanceState 保存状态,接着 onDestory
重建时 onCreate -> onStart-> onRestoreInstanceState -> onResulme
可在 配置文件中进行 configChanges 配置,切屏后不会重建 Activity
按下返回键 :onPause -> onStop -> onDestory
[ViewModel 原理](https://github.com/LvKang-insist/note-instance/blob/master/android/%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93%E4%BD%BF%E7%94%A8/JetPack/ViewModel%20%E6%BA%90%E7%A0%81%E6%B5%85%E6%9E%90.md)
ViewModel 是实现 MVVM 不可缺少的一环,其主要就是注重生命周期的方式来管理页面相关数据,当页面因为配置改变而重建时,ViewModel 就可以在重建的过程中恢复数据。
ViewModel 的创建是通过 ViewModeProvider.get() 来完成的,在创建的时候,会传入 ViewModelStoreOwner 和 Frctory ,前者是 ViweModel 的宿主,内部维护了一个 ViewMode 的映射表,当创建 ViewModel 的时候,就会从映射表里面获取,获取到返回,获取不到则根据 Factory 反射创建一个ViewModel。
通过 by viewmodels 创建的流程和上面一样,也是通过 ViewModelProvider 创建的。
至于如何在页面重建时恢复数据,是因为在获取 ViewModelStore 时,会从配置文件中查找是否有 ViewModelStore,如果有,则拿出来直接使用,没有的话就会创建一个新的。
当页面重建的时候,ViewModelStore 会被进行包装,保存在 ActivityClientRecord 中,在重建完成后会被取出。
Fragment 内部也是维护了一个映射表,只不过是由 fragmentManager 管理的。
MVC
M 表示数据层,V 表示 View ,C 表示控制层(Activity 和 Fragment) 的子类
使用的时候,V 和 C 是写在一起的,在页面中直接调用 M 获取数据,然后再刷新 View
这种模式好处就是 model 和 页面彻底解耦,但是 控制层和 VIew 并没有解耦,在页面的代码逻辑中就容易混淆。
MVP
M:数据层,V:即 Activity / Fragment , P:中介(Presenter)
相比于 MVC 多了一个 P 层,数据和View不在进行直接通讯,使用 P 进行分离,获取数据时通过 P 去获取数据,然后 P 层再转交给 V 。
MVP 整个过程都是通过接口的调用来完成的,V 和 P 都必须实现对应的接口。
好处就是实现了 Model 和 View 的完全解耦,彻底解决了 MVC 中 View 和 Controller 分不清楚的问题。
坏处就是无法感知页面的生命周期,可能造成内存泄露等,需要使用弱引用 + 动态代理的方式来判断 V 是否被销毁
还有就是随着业务的逻辑增加,页面就会非常复杂,UI 改变也多,这就会造成接口非常庞大。
MVVM
M :数据层,包括数据,网络数据获取以及耗时操作的处理
V:Activity/Fragment ,负责一些状态的初始化以及 UI 更新
VM:负责完成 View 和 Model 之间的交互,需要继承 ViewModel ,可以保证页面的数据不会丢失
相比于 MVVM ,取消了复杂的接口,取而代之的是 DataBinding,或者是 LiveData,通过 DataBinding 将数据绑定到 ui 上,数据发生变化时会自动更新,也可以采用 LiveData,在页面中监听数据,然后进行刷新
使用 DataBindign 需要数据和视图的双向绑定,就会导致出现问题不好定位,布局很难进行复用等,以及别的问题,所以大多数都是采用 LiveData 来进行操作。
不足之处,在于需要定义 livedata 的模版代码,一个可见,一个不可见,业务太多需要定义大量的 Livedata。
MVI
和 MVVM 很相似,只不过更加强调数据的单向流动和唯一的数据源
M:主要指的是 UI 的状态(State),例如页面加载状态,控件的显示状态等
V:fragment / Activity,通过订阅 Model 的状态变化来进行页面的刷新
I:Intent,用户的任何操作都可以包装为 Intent 发送给 Model 层进行获取。
用户已 Inent 的方式通知 Model,Model 根据对应的 Intent 进行数据处理,然后更新 State,View 接收到 State 的变化后更新 UI
相比于 MVVM ,就是对页面状态进行了统一管理,只需要订阅 ViewState 就可以获取获取所有状态,减少了不少的模版代码
ViewModel 只需要根据 Intent 来进行对应的逻辑处理,然后再通过修改ViewState通知改变即可。
缺点,所有的调用操作最终都活转换成 Intent 和 State,所以页面如果复杂就会导致容易膨胀,state 是不可变的,每次更新的时候都需要用新的对象来代替老的对象,会造成一定的内存开销。
抽象类和接口都是为了抽取共性而存在的
官方停止线程的方法被废弃了,所以不能直接简单的停止线程。
所以就需要设计一个可以随时被中断而取消的任务线程。
栗子:两个线程,线程1去访问一个带锁的方法去读取文件,然后获取到了锁,这个时候线程2也要去读写文件,但是只能被阻塞了。
此时如果停止了线程1,那么就会导致锁无法被释放,从而导致线程2一直在阻塞。甚至说如果线程比较多,就可能会出现死锁的问题。
需要解决的:
线程在设计的时候是和任务进行绑定的,任务执行完了,那么线程自然也就自己停止了。我们想让线程结束本质上就是让任务结束,而不是让线程结束。如果让任务结束了,那么线程自然有机会去释放锁等一系列的操作,自然也就不会引起其他问题了。所以我们需要:
通知目标线程自行结束,而不是强制停止
目标线程应当具备处理中断的能力
中断方式
Interrupt 的原生支持
//目标线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
//清理
}
}
});
//中断宣传
thread.interrupt();
在有些情况下,中断线程也是不好使的,如下:
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
}
});
thread.start();
thread.interrupt();//中断线程
这种情况下中断线程是没有用的,因为 Thread 不支持这种。如果要可以进行中断,就只能是如下写法了:
class InterruptThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(i);
if (interrupted()) {
break;
}
}
}
}
每次去判断一下线程 有没有中断,如果中断了就结束任务,清理资源就行了
interrupted() 与 isInterrupted
boolean 标志位
static class InterruptThread extends Thread {
volatile boolean isStopped = false;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(i);
if (isStopped) {
break;
}
}
}
}
需要注意的是标志位必须要使用 volatile 关键字,否则就会存在可见性的问题。
两者对比
interrupt | boolean 标志位 | |
---|---|---|
系统方法(selep) | 是 | 否 |
使用 JNI | 是 | 否 |
加锁 | 是 | 否 |
触发方式 | 抛异常 | 布尔值判断,也可抛异常 |
为什么线程不应该被直接 stop
因为涉及到线程执行完后的资源清理工作。
线程内置的中断机制的使用和原理
通过 volatile boolean 标志位通知线程停止
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.