Jetpack---LifeCycle/LiveData/Vie

2020-05-06  本文已影响0人  大苏打6815
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,我们要通过查找源码反射的方法去修改

image.png
我们首先再LiveData里面源码找 image.png

observer.mObserver.onChanged((T) mData);就是造成这种现象的原因,那么从上面代码看,要想某种情况不执行到这里来,肯定要retrun掉。

image.png image.png

mVersion初始值是-1,observer.mLastVersion >= mVersion这里的代码意思是每次执行一次,mVersion都是要+1的,这样就导致observer.mLastVersion >= mVersion这个方法不成立,那么这里就一直走不到return,我们只能通过反射去修改这里。

上一篇下一篇

猜你喜欢

热点阅读