Android第三方框架学习

Android第三方框架学习

  • Android 浅谈模块化、组件化、插件化、热修复的简单理解
  • Android 组件化开发
  • 组件化和插件化的区别
  • EventBus的使用
    • 参考链接
      • 1.定义EventMessage事件类。
      • 2. 在需要订阅事件的模块中,注册EventBus
      • 3.接收事件
      • 4.发送事件
    • Subscribe 注解介绍
  • OkHttp的使用
    • 参考链接
  • Retrofit的使用
    • 参考链接
  • RxJava的使用
    • 参考链接
  • RxJava 与 Retrofit 的结合
  • Data Binding的使用
  • Navigation的使用
  • ViewModel & LiveData & Lifecycle的使用
  • Room的使用
  • Paging的使用
  • WorkManger的使用


Android 浅谈模块化、组件化、插件化、热修复的简单理解

Android 浅谈模块化、组件化、插件化、热修复的简单理解

Android 组件化开发

Android组件化方案
Android组件化之终极方案

组件化和插件化的区别

不同特点:

  1. 模块化(lib)主要解决问题是重用问题,不强调单独编译
  2. 组件化(lib)主要解决问题是功能拆分,强调单独编译
  3. 插件化(application)是所有组件都为apk的特殊组件化,特点可热更新

通讯方式不同点:

  1. 模块化相互引入,需要引入需要的module
  2. 组件化通讯方式分为隐式和路由。
  3. 插件化本身是不同进程,因此是binder机制进程间通讯。

EventBus的使用

参考链接

EventBus 使用(全面分析,细节提醒)
一文彻底搞懂EventBus 3.0原理

Android第三方框架学习_第1张图片

1.定义EventMessage事件类。

public class EventMessage {
    private String  messageType;
    private String  message;

    public EventMessage(String messageType, String message) {
        this.messageType = messageType;
        this.message = message;
    }

    @Override
    public String toString() {
        return "EventMessage{" +
                "messageType='" + messageType + '\'' +
                ", message='" + message + '\'' +
                '}';
    }

    public String getMessageType() {
        return messageType;
    }

    public void setMessageType(String messageType) {
        this.messageType = messageType;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

2. 在需要订阅事件的模块中,注册EventBus

@Override
    protected void onStart() {
        super.onStart();
        //注册EventBus
        EventBus.getDefault().register(this);
    }


    @Override
    protected void onStop() {
        super.onStop();
        //取消注册
        EventBus.getDefault().unregister(this);
    }

3.接收事件

在需要接受事件的类中进行好register之后,需要在该类中创建一个方法来接收事件消息。

	/* 接收事件
     * 该方法有且只有一个参数。
     * 该方法必须是public修饰符修饰,不能用static关键字修饰,不能是抽象的(abstract)
     * 该方法需要用@Subscribe注解进行修饰。
     */
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
    public void onReceiveMsg(EventMessage eventMessage) {
        Log.i(TAG, "onReceiveMsg: " + eventMessage.toString());
    }

4.发送事件

//发送事件
EventMessage msg = new EventMessage("1","Hello MainActivity");
EventBus.getDefault().post(msg);

Subscribe 注解介绍

@Subscribe是EventBus自定义的一种注解,他可接收三个参数。ThreadMode、boolean sticky、int priority。

ThreadMode

  • ThreadMode.POSTING:默认的线程模式,在哪个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。
  • ThreadMode.MAIN:如在主线程(UI线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过Handler 切换到主线程,依次处理事件。
  • ThreadMode.MAIN_ORDERED:无论在哪个线程发送事件,都将事件加入到队列中,然后通过Handler切换到主线程,依次处理事件。
  • ThreadMode.BACKGROUND:与ThreadMode.MAIN相反,如果在子线程发送事件,则直接在子线程处理事件;如果在主线程上发送事件,则先将事件入队列,然后通过线程池处理事件。
  • ThreadMode.ASYNC:与ThreadMode.MAIN_ORDERED相反,无论在哪个线程发送事件,都将事件加入到队列中,然后通过线程池执行事件

sticky
sticky是一个boolean型的参数,默认值是false,表示不启用sticky特性。那么sticky特性是什么呢?

  • EventBus事件传递的例子的时候,我们都是先对订阅者(Subscriber)进行先注册的,然后再post事件的。那sticky的作用是在先post事件,后对订阅者注册这种开发场景的支持的

priority

  • 该参数是int型优先级。值越高,越先接收到事件

OkHttp的使用

参考链接

android开发网络框架OkHttp
一文了解OKHttp3全
Android第三方框架学习_第2张图片

    final Dispatcher dispatcher;//调度器
    final Proxy proxy;//代理
    final List<Protocol> protocols;//协议
    final List<ConnectionSpec> connectionSpecs;//传输层版本和连接协议
    final List<Interceptor> interceptors;//拦截器
    final List<Interceptor> networkInterceptors;//网络拦截器
    final EventListener.Factory eventListenerFactory;
    final ProxySelector proxySelector;//代理选择器
    final CookieJar cookieJar;//cookie
    final Cache cache;//cache 缓存
    final InternalCache internalCache;//内部缓存
    final SocketFactory socketFactory;//socket 工厂
    final SSLSocketFactory sslSocketFactory;//安全套层socket工厂 用于https
    final CertificateChainCleaner certificateChainCleaner;//验证确认响应书,适用HTTPS 请求连接的主机名
    final HostnameVerifier hostnameVerifier;//主机名字确认
    final CertificatePinner certificatePinner;//证书链
    final Authenticator proxyAuthenticator;//代理身份验证
    final Authenticator authenticator;//本地省份验证
    final ConnectionPool connectionPool;//链接池 复用连接
    final Dns dns; //域名
    final boolean followSslRedirects;//安全套接层重定向
    final boolean followRedirects;//本地重定向
    final boolean retryOnConnectionFailure;//重试连接失败
    final int connectTimeout;//连接超时
    final int readTimeout;//读取超时
    final int writeTimeout;//写入超时


Retrofit的使用

参考链接

Retrofit2详解

Retrofit2 源码解析

Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求


RxJava的使用

参考链接

  1. 给 Android 开发者的 RxJava 详解
  2. 关于RxJava最友好的文章
  3. 关于RxJava最友好的文章——背压
  4. 关于 RxJava 最友好的文章—— RxJava 2.0 全新来袭
  • Rxjava Demo练习源码参考

RxJava 的观察者模式:
Android第三方框架学习_第3张图片
RxJava 有四个基本概念:Observable(可观察者,即被观察者)、 Observer(观察者)、 subscribe (订阅)、事件。ObservableObserver 通过 subscribe()方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer

与传统观察者模式不同, RxJava 的事件回调方法除了普通事件onNext()(相当于 onClick() / onEvent())之外,还定义了两个特殊的事件:onCompleted()onError()

  • onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的 onNext()发出时,需要触发 onCompleted()方法作为标志。
  • onError(): 事件队列异常。在事件处理过程中出异常时,onError()会被触发,同时队列自动终止,不允许再有事件发出。
  • 在一个正确运行的事件序列中,onCompleted()onError()有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted()onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。

我们再来按照观察者模式的运作流程和流式Api的写法复习一遍:

流程图如下:Android第三方框架学习_第4张图片
结合流程图的相应代码实例如下:

//创建被观察者,是事件传递的起点
   Observable.just(getFilePath())
           //指定了被观察者执行的线程环境
          .subscribeOn(Schedulers.newThread())
          //将接下来执行的线程环境指定为io线程
          .observeOn(Schedulers.io())
            //使用map操作来完成类型转换
            .map(new Func1<String, Bitmap>() {
              @Override
              public Bitmap call(String s) {
                //显然自定义的createBitmapFromPath(s)方法,是一个极其耗时的操作
                  return createBitmapFromPath(s);
              }
          })
            //将后面执行的线程环境切换为主线程
            .observeOn(AndroidSchedulers.mainThread())
            //实现订阅
            .subscribe(
                 //创建观察者,作为事件传递的终点处理事件    
                  new Subscriber<Bitmap>() {
                        @Override
                        public void onCompleted() {
                            Log.d("DDDDDD","结束观察...\n");
                        }

                        @Override
                        public void onError(Throwable e) {
                            //出现错误会调用这个方法
                        }
                        @Override
                        public void onNext(Bitmap s) {
                            //处理事件
                            showBitmap(s)
                        }
                    );

-Action1 是RxJava一个接口,它同样只有一个方法 call(T param),这个方法也无返回值,但有一个参数;RxJava 是提供了多个 ActionX 形式的接口 (例如 Action2, Action3) 的,它们可以被用以包装不同的无返回值的方法。

Func1 它和 Action1 非常相似,也是 RxJava 的一个接口,用于包装含有一个参数的方法。 Func1 和 Action 的区别在于, Func1 包装的是有返回值的方法. FuncX 也有多个,用于不同参数个数的方法。FuncX 和 ActionX 的区别在 FuncX 包装的是有返回值的方法

除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:SubscriberSubscriberObserver接口进行了一些扩展,但他们的基本使用方式是完全一样的:

在 RxJava 的subscribe过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。所以如果你只想使用基本功能,选择ObserverSubscriber是完全一样的。它们的区别对于使用者来说主要有两点:

  • onStart(): 这是 Subscriber 增加的方法。它会在 subscribe刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行), onStart()就不适用了,因为它总是在 subscribe所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法,具体可以在后面的文中看到。
  • unsubscribe(): 这是 Subscriber 所实现的另一个接口 Subscription 的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用isUnsubscribed()先判断一下状态。 unsubscribe() 这个方法很重要,因为在subscribe()之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe()来解除引用关系,以避免内存泄露的发生。

延伸:doOnSubscribe()

在前面讲 Subscriber 的时候,提到过 SubscriberonStart() 可以用作流程开始前的初始化。然而 onStart() 由于在 subscribe() 发生时就被调用了,因此不能指定线程,而是只能执行在 subscribe()被调用时的线程。这就导致如果onStart() 中含有对线程有要求的代码(例如在界面上显示一个 ProgressBar,这必须在主线程执行),将会有线程非法的风险,因为有时你无法预测 subscribe()将会在什么线程执行。

而与 Subscriber.onStart() 相对应的,有一个方法 Observable.doOnSubscribe()它和 Subscriber.onStart() 同样是在subscribe()调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe()执行在 subscribe() 发生的线程;而如果在 doOnSubscribe()之后有 subscribeOn()的话,它将执行在离它最近的 subscribeOn()所指定的线程。
示例代码:

Observable.create(onSubscribe)
    .subscribeOn(Schedulers.io())
    .doOnSubscribe(new Action0() {
        @Override
        public void call() {
            progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
        }
    })
    .subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(subscriber);

如上,在 doOnSubscribe()的后面跟一个 subscribeOn() ,就能指定准备工作的线程了。

异步(线程调度)
Scheduler 的 API

  • Schedulers.immediate(): 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。
  • Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。
  • Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
  • Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
  • 另外, Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。

subscribeOn 它指示Observable在一个指定的调度器上创建(只作用于被观察者创建阶段)。只能指定一次,如果指定多次则以第一次为准。

observeOn 指定在事件传递(加工变换)和最终被处理(观察者)的发生在哪一个调度器。可指定多次,每次指定完都在下一步生效。

嗯,基本上我们就把RxJava的骨架就讲完了,总结一下:

  • 创建被观察者,产生事件
  • 设置事件传递过程中的过滤,合并,变换等加工操作。
  • 订阅一个观察者对象,实现事件最终的处理。

map:类型变换

Observable.from(number) 
                .map(new Func1<Integer, Boolean>() {

                    @Override
                    public Boolean call(Integer integer) {
                  
                        return (integer<3); //判断是否小于3
                    }
                })
                .subscribe(new Action1<Boolean>() {
                    @Override
                    public void call(Boolean aBoolean) {
                        mText.append("\n观察到输出结果:\n");
                        mText.append(aBoolean.toString());
                    }
                });

flatmap: flatMap() 和 map() 有一个相同点:它也是把传入的参数转化之后返回另一个对象。但需要注意,和 map() 不同的是, flatMap() 中返回的是个 Observable 对象,并且这个 Observable 对象并不是被直接发送到了 Subscriber 的回调方法中。

 Observable.from(getSchoolClass())
                .flatMap(new Func1<SchoolClass, Observable<Student>>() {
                    @Override
                    public Observable<Student> call(SchoolClass schoolClass) {
                        //将Student列表使用from方法一个一个发出去
                        return Observable.from(schoolClass.getStudents());
                    }
                })
                .subscribe(new Action1<Student>() {
                    @Override
                    public void call(Student student) {
                        mText.append("打印单个学生信息:\n");
                        mText.append("name:"+student.name+"    age: "+student.age+"\n");
                    }
                });

merge:两个任务并发进行,全部处理完毕之后在更新数据

filter: 过滤出满足条件的数据

filter(new Func1<Integer, Boolean>() {
                    @Override
                    public Boolean call(Integer integer) {
                        return integer%2!=0;
                    }
                })

take:截取数据

.take(4) //取前4个
.takeLast(2)//取前四个中的后两个: 5 7

RxJava 与 Retrofit 的结合

Retrofit 除了提供了传统的 Callback 形式的 API,还有 RxJava 版本的 Observable 形式 API。下面我用对比的方式来介绍 Retrofit 的 RxJava 版 API 和传统版本的区别。

以获取一个 User 对象的接口作为例子。使用Retrofit 的传统 API,你可以用这样的方式来定义请求:

@GET("/user")
public void getUser(@Query("userId") String userId, Callback<User> callback);

在程序的构建过程中, Retrofit 会把自动把方法实现并生成代码,然后开发者就可以利用下面的方法来获取特定用户并处理响应:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        userView.setUser(user);
    }
    
    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
};

而使用 RxJava 形式的 API,定义同样的请求是这样的:

@GET("/user")
public Observable<User> getUser(@Query("userId") String userId);

使用的时候是这样的:

getUser(userId)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }
        
        @Override
        public void onCompleted() {
        }
        
        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

看到区别了吗?

当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()

对比来看,Callback 形式和 Observable 形式长得不太一样,但本质都差不多,而且在细节上 Observable 形式似乎还比 Callback 形式要差点。那 Retrofit 为什么还要提供 RxJava 的支持呢?

因为它好用啊!从这个例子看不出来是因为这只是最简单的情况。而一旦情景复杂起来,Callback形式马上就会开始让人头疼。比如:

假设这么一种情况:你的程序取到的User 并不应该直接显示,而是需要先与数据库中的数据进行比对和修正后再显示。使用 Callback 方式大概可以这么写:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        processUser(user); // 尝试修正 User 数据
        userView.setUser(user);
    }
    
    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
};

有问题吗?

很简便,但不要这样做。为什么?因为这样做会影响性能。数据库的操作很重,一次读写操作花费 10~20ms 是很常见的,这样的耗时很容易造成界面的卡顿。所以通常情况下,如果可以的话一定要避免在主线程中处理数据库。所以为了提升性能,这段代码可以优化一下:

getUser(userId, new Callback<User>() {
    @Override
    public void success(User user) {
        new Thread() {
            @Override
            public void run() {
                processUser(user); // 尝试修正 User 数据
                runOnUiThread(new Runnable() { // 切回 UI 线程
                    @Override
                    public void run() {
                        userView.setUser(user);
                    }
                });
            }).start();
    }
    
    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
};

性能问题解决,但……这代码实在是太乱了,迷之缩进啊!杂乱的代码往往不仅仅是美观问题,因为代码越乱往往就越难读懂,而如果项目中充斥着杂乱的代码,无疑会降低代码的可读性,造成团队开发效率的降低和出错率的升高。

这时候,如果用 RxJava 的形式,就好办多了。 RxJava 形式的代码是这样的:

getUser(userId)
    .doOnNext(new Action1<User>() {
        @Override
        public void call(User user) {
            processUser(user);
        })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }
        
        @Override
        public void onCompleted() {
        }
        
        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

后台代码和前台代码全都写在一条链中,明显清晰了很多。

再举一个例子:假设 /user 接口并不能直接访问,而需要填入一个在线获取的token ,代码应该怎么写?

Callback 方式,可以使用嵌套的 Callback

@GET("/token")
public void getToken(Callback<String> callback);

@GET("/user")
public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback);

...

getToken(new Callback<String>() {
    @Override
    public void success(String token) {
        getUser(token, userId, new Callback<User>() {
            @Override
            public void success(User user) {
                userView.setUser(user);
            }
    
            @Override
            public void failure(RetrofitError error) {
                // Error handling
                ...
            }
        };
    }
    
    @Override
    public void failure(RetrofitError error) {
        // Error handling
        ...
    }
});

倒是没有什么性能问题,可是迷之缩进毁一生,你懂我也懂,做过大项目的人应该更懂。

而使用 RxJava 的话,代码是这样的:

@GET("/token")
public Observable<String> getToken();

@GET("/user")
public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);

...

getToken()
    .flatMap(new Func1<String, Observable<User>>() {
        @Override
        public Observable<User> onNext(String token) {
            return getUser(token, userId);
        })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<User>() {
        @Override
        public void onNext(User user) {
            userView.setUser(user);
        }
        
        @Override
        public void onCompleted() {
        }
        
        @Override
        public void onError(Throwable error) {
            // Error handling
            ...
        }
    });

用一个 flatMap() 就搞定了逻辑,依然是一条链。

Data Binding的使用

使用BindingAdapter简化图片加载


Navigation的使用

学习Android Jetpack? 实战和教程这里全都有!
基于Jetpack的全系列加实战 app 教程


ViewModel & LiveData & Lifecycle的使用

Android开发 LiveData与MutableLiveData详解

我们为什么要使用LiveData和ViewModel呢?他们有什么优势呢?

在LiveData出现之前,一般状态分发我们使用EventBus或者RxJava,这些都很容易出现内存泄漏问题,而且需要我们手动管理生命周期。而LiveData则规避了这些问题,LiveData是一个持有Activity、Fragment生命周期的数据容器。当数据源发生变化的时候,通知它的观察者更新UI界面。同时它只会通知处于Active状态的观察者更新界面,如果某个观察者的状态处于Paused或Destroyed时那么它将不会收到通知。所以不用担心内存泄漏问题。

ViewModel将视图和逻辑进行了分离。Activity或者Fragment只负责UI显示部分。具体的网络请求或者数据库操作则有ViewModel负责。这样避免了视图的臃肿和代码的耦合。通过下面这张ViewModel生命周期可以看出,当屏幕发生旋转而导致Activity被销毁并重新创建时,ViewModel并没有被销毁,从而帮助我们在Activity重新创建后获取数据更新UI。它和onSaveInstanceState方法相比更有优势,因为onSaveInstanceState方法只不适合大量数据的恢复操作,只能恢复少量并且被序列化和反序列化的数据,而ViewModel不仅支持大量数据,还不需要序列化、反序列化操作。
Android第三方框架学习_第5张图片


Room的使用


Paging的使用


WorkManger的使用


你可能感兴趣的:(Android学习笔记)