Jetpack---LifeCycle/LiveData/Vie
Jetpack 是一套库、工具和指南,可帮助开发者更轻松地编写优质应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。Jetpack 包含与平台 API 解除捆绑的 androidx.* 软件包库。这意味着,它可以提供向后兼容性,且比 Android 平台的更新频率更高,以此确保您始终可以获取最新且最好的 Jetpack 组件版本。
前几篇都是用MVP写的demo,下面讲解jetpack的时候可能换成MVC会比较更容易理解,当然了,也可以直接放在MVP里面用。jetpck的使用,可以让你的项目焕然一新,个人觉得对安卓新入门的门槛至少砍掉了百分之三十。及时了解这些技术还是有必要的,哪怕我目前项目里面目前还没用上,说不定哪一天就用上了呢?(新建项目的时候把androidx勾上)
我们这篇主要讲解lifecycle/livedata-viewmodel/livedatabus
一 Lifycycles(管理Activity或者fragment的生命周期)
如果我们在项目中,想监控生命周期实现业务逻辑,我们以前一般常规的做发就是先声明一个接口,新建一个类去实现接口,然后再所对应的Activity或者fragment里面创建对象然后调用其里面的方法这三步。
public class Main implements IMain{
@Override
public void onStart() {
}
@Override
public void onStop() {
}
}
public interface IMain {
public void onStart();
public void onStop();
}
public class MainActivity extends AppCompatActivity {
Main maincycle=new Main();
@Override
protected void onStart(){
super.onStart();
maincycle.onStart();
}
}
这样的话很麻烦,每次都要新创建一个对象。我们完全可以用lifecycleobserver来替代。注意@OnLifecycleEvent(Lifecycle.Event.ON_START)里面有提供的的声明周期方法,自己选择,这样当走到对应的生命周期方法中的时候就会打印log,根据业务需求可以实现对应的逻辑。
/**
* 我们用这个观查者来盯好需要感知生命周期的对象 */
public class MyLifeObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onStartJett(){
Log.i("dasuda","onStartSuda");
}
}
然后再其Activity或者fragement的oncreat初始化里面实现绑定
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//完成绑定
getLifecycle().addObserver(new MyLifeObserver());
}
那么在我们之前写的标准的MVP模式里面怎样加入这个lifecycle呢?(lifecycle在MVC,MVP,MVVM都能加)42_JetPackAndMvp
首先我们在BasePresent里面定义好这些声明周期方法,记得要实现这个lifecycleObserver等接口
public class BasePresenter<T extends IBaseView> implements LifecycleObserver,LifecycleOwner {
//持有左边(VIEW)
// IGirlView iGirlView;
WeakReference<T> iGirlView;
public void attachView(T view){
iGirlView=new WeakReference<>(view);
}
public void detachView(){
if(iGirlView!=null){
iGirlView.clear();
iGirlView=null;
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_ANY)
void onAny(LifecycleOwner owner){
};
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreate(LifecycleOwner owner){
};
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void onStart(LifecycleOwner owner){};
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onStop(LifecycleOwner owner){};
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume(LifecycleOwner owner){};
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void onPause(LifecycleOwner owner){};
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
void onDestory(LifecycleOwner owner){};
@NonNull
@Override
public Lifecycle getLifecycle() {
return null;
}
然后我i们在对应的GirlPresenter(继承至BasePresenter的)里面去重写这些方法,可以自己有需求的重写哪几个,比如我选择两个
@Override
void onDestory(LifecycleOwner owner) {
super.onDestory(owner);
Log.i("dasuda","onDestory");
}
@Override
void onCreate(LifecycleOwner owner) {
super.onCreate(owner);
Log.i("dasuda","onCreate");
}
然后再所对应的Activity或者fragment的初始化里面完成绑定这个Presenter
@Override
protected void init() {
//完成绑定(订阅关系)
getLifecycle().addObserver(presenter);
}
二 viewmodel+livedata
标准写法目前可以再fragment与fragment,或者fragment与Activity之间相互传输数据
一个极好的MVVM框架和jetpack使用的demo https://github.com/KunMinX/Jetpack-MVVM-Best-Practice
需要添加依赖implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
首先我们新建一个类继承Viewmodel。这个就相当于存放数据的仓库’
注意:用static修饰MutableLiveData是livedata用的同一个布局文件,一般来说项目里面99%都不会用同一个文件。这里示例是用的同一个文件,如果不这样用会报错。官方文档上面不是用的static修饰,那是因为他每一个livedata用的布局文件不一样。
public class MyGirl extends ViewModel {
//定义一个对象,相当于一个用来存放数据的仓库
private static MutableLiveData<List<Girl>> liveData;
//用于获取数据
public LiveData<List<Girl>> getDataBean(){
if(liveData==null){
liveData=new MutableLiveData<>();
loadData();
}
return liveData;
}
private void loadData() {
List<Girl> data;
data = new ArrayList<>();
data.add(new Girl(R.drawable.f1, "一星", "****"));
data.add(new Girl(R.drawable.f2, "一星", "****"));
data.add(new Girl(R.drawable.f3, "一星", "****"));
data.add(new Girl(R.drawable.f4, "一星", "****"));
data.add(new Girl(R.drawable.f5, "一星", "****"));
data.add(new Girl(R.drawable.f6, "一星", "****"));
data.add(new Girl(R.drawable.f7, "一星", "****"));
data.add(new Girl(R.drawable.f8, "一星", "****"));
data.add(new Girl(R.drawable.f9, "一星", "****"));
data.add(new Girl(R.drawable.f10, "一星", "****"));
//把这些数据存放到仓库里面
liveData.setValue(data);
}
//提供一个方法来改变数据
public void changeValue(int item,int i){
List<Girl> value = liveData.getValue();
value.get(item).setLike(i+"");
liveData.setValue(value);
}
}
然后我们再Activity的oncreat方法或者fragment里面初始化的时候调用,这样就完成了初始化的操作
//调用系统API初始化这个对象
myGirl= ViewModelProviders.of(this).get(MyGirl.class);
myGirl.getDataBean().observe(this, new Observer<List<Girl>>() {
/**
* 当我们的数据发生变化的时候,我们可以在这个onChanged中进行处理
* @param girls
*/
@Override
public void onChanged(List<Girl> girls) {
listView.setAdapter(new GirlAdapter(MainActivity.this,girls));
}
});
我们写个示例,listview条目上面我们随便把一个item长按,让他立刻改变成我们想要变化的样子,下面的myGirl.changeValue(position,1);是我们再MyGirl这个类里面新增加的一个方法便于理解的。可以随时修改listview所在positon当中内容的变化
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
myGirl.changeValue(position,1);
return false;
}
});
三 事件总线设计---livedatabus(不能跨进程的)(不需要导任何包)
相对比Rxbus,eventbus,内存泄露源码都已经给你做好了,不会造成内存泄漏
我们设想一下,A,B,C三个用户,要在京东淘宝上面买苹果华为手机,我们称ABC为订阅者,苹果华为厂商为发布者,而京东淘宝只是个代理商也称为总线,由代理商统一统计上报给手机厂商,然后手机下来后分布给各个不同的订阅者。总线相当于存放了map来存放订阅者。
首先我们写一个demo看看效果,首先写一个华为的bean类
public class Huawei {
public String type;
public Huawei(String type) {
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
然后我们写一个事件总线LiveDataBus
/**
* 这个类就是我们的总线(天猫,京东)
*/
public class LiveDataBus {
//存放订阅者
private Map<String, BusMutableLiveData<Object>> bus;
//单例
private static LiveDataBus liveDataBus=new LiveDataBus();
private LiveDataBus(){
bus=new HashMap<>();
}
public static LiveDataBus getInstance(){
return liveDataBus;
}
/**
* 用来给用户进行订阅(存入map)
*/
public synchronized<T> BusMutableLiveData<T> with(String key,Class<T> type){
if(!bus.containsKey(key)){
bus.put(key,new BusMutableLiveData<Object>());
}
return (BusMutableLiveData<T>) bus.get(key);
}
public static class BusMutableLiveData<T> extends MutableLiveData<T>{
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
super.observe(owner, observer);
hook(observer);
}
private void hook(Observer<? super T> observer) {
try{
//1.得到mLastVersion
Class<LiveData> liveDataClass=LiveData.class;
Field mObserversField = liveDataClass.getDeclaredField("mObservers");
mObserversField.setAccessible(true);
//获取到这个成员变量对应的对象
Object mObserversObject = mObserversField.get(this);
//得到map
Class<?> mObserversObjectClass = mObserversObject.getClass();
//获取到mObservers对象的get方法
Method get=mObserversObjectClass.getDeclaredMethod("get",Object.class);
get.setAccessible(true);
//执行get方法
Object invokeEntry=get.invoke(mObserversObject,observer);
//取到map中的value
Object observerWraper=null;
if(invokeEntry!=null && invokeEntry instanceof Map.Entry){
observerWraper=((Map.Entry)invokeEntry).getValue();
}
if(observerWraper==null){
throw new NullPointerException("observerWraper is null");
}
//得到ObserverWrapper的类对象
Class<?> superclass=observerWraper.getClass().getSuperclass();
Field mLastVersion = superclass.getDeclaredField("mLastVersion");
mLastVersion.setAccessible(true);
//2.得到mVersion
Field mVersion = liveDataClass.getDeclaredField("mVersion");
mVersion.setAccessible(true);
//3.把mVersion的值填入到mLastVersion中
Object mVersionValue=mVersion.get(this);
mLastVersion.set(observerWraper,mVersionValue);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
注意:以上方法public synchronized<T> BusMutableLiveData<T> with(String key,Class<T> type)。这个Class一定要写,不然系统没办法反射到这个内容的。public static class BusMutableLiveData<T> extends MutableLiveData<T>这个继承类是已经经过优化了的。这是系统里面的一个bug。稍后我们看一下。
我们再主Activity里面去订阅,发布
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//消费者订阅消息
LiveDataBus.getInstance().with("huawei",Huawei.class)
.observe(this,new Observer<Huawei>() {
@Override
public void onChanged(Huawei huawei) {
if(huawei!=null){
Toast.makeText(MainActivity.this, huawei.getType(), Toast.LENGTH_SHORT).show();
}
}
});
}
/**
* 这里就是一个发布者(苹果,华为)
* @param view
*/
public void sendMessage(View view) {
Huawei huawei=new Huawei("META-20");
//厂家发布消息
LiveDataBus.getInstance().with("huawei",Huawei.class).postValue(huawei);
}
public void startSecActivity(View view) {
Intent intent=new Intent(this,SecActivity.class);
startActivity(intent);
}
到了这里我们点击一个sendMessge按钮,对应的上面就有一个吐司了。说明订阅初始化成功了,也成功发布了。
我们假设一下,跳转到另外一个SecondActivity,并且同样订阅,发布.SecondActivity代码如下
public class SecActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//消费者订阅消息
LiveDataBus.getInstance().with("huawei",Huawei.class).observe(
this,
new Observer<Huawei>() {//观查者
@Override
public void onChanged(Huawei huawei) {
if(huawei!=null){
Toast.makeText(SecActivity.this, huawei.getType(), Toast.LENGTH_SHORT).show();
}
}
}
);
}
/**
* 发布者
* @param view
*/
public void sendMessage(View view){
Huawei huawei=new Huawei("META-20");
//厂家发布消息
LiveDataBus.getInstance().with("huawei",Huawei.class).postValue(huawei);
}
此时此刻,第一个Activity跳转到SecondActivity,然后SecondActivity里面的操作效果都是跟Avtivity一样的。这是上面已经优化过的代码。如果LiveDataBus这个类不优化,而是直接当作MutableLiveData类型返回,会出问题(如下图),我们知道订阅是再初始化里面,发布我们是点了按钮才发布,你会发现再第一个Activity里面订阅和发布正常,当跳转到SecondActivity的时候,还没点击发布按钮的时候,他自动就给你订阅消息弹吐司了,因此这是系统的一个bug,我们要通过查找源码反射的方法去修改
我们首先再LiveData里面源码找 image.png
observer.mObserver.onChanged((T) mData);就是造成这种现象的原因,那么从上面代码看,要想某种情况不执行到这里来,肯定要retrun掉。
image.png image.pngmVersion初始值是-1,observer.mLastVersion >= mVersion这里的代码意思是每次执行一次,mVersion都是要+1的,这样就导致observer.mLastVersion >= mVersion这个方法不成立,那么这里就一直走不到return,我们只能通过反射去修改这里。