Retrofit 结合 Lifecycle, 将 Http 生命周期管理到极致
发布日期:2021-10-20 03:26:46 浏览次数:9 分类:技术文章

本文共 14588 字,大约阅读时间需要 48 分钟。

code小生,一个专注 Android 领域的技术平台

公众号回复 Android 加入我的安卓技术群

作者:xcheng_链接:https://www.jianshu.com/p/07fe489a53f2声明:本文已获xcheng_投稿发表,转发等请联系原作者授权

http请求一直是开发中无法避免的存在,生命周期的管理也一直是开发者的痛点,稍不注意就在回调是抛出异常,如NullPointerException,showDialog导致的WindowLeaked等。

Google 最新推荐的 Lifecycle 架构就是可以让你自己的类拥有像 activity 或 fragment 一样生命周期的功能。

于是我决定采用lifecycle结合retrofit 将http请求和Activity或Fragment的生命周期相结合

本文将从以下几个方面一步步实现功能

  • 多线程分发Lifecycle. Event

  • retrofit如何与其关联?自定义 CallAdapter.Factory

  • 异步请求如何关联

  • 同步请求如何关联

如果有不熟悉lifecycle的可以自行学习,这里不做介绍了

lifecycle官方文档地址:

https://developer.android.com/topic/libraries/architecture/lifecycle

为什么要使用lifecycle?

activity 和fragment 是有声明周期的,有时候,我们的很多操作需要写在声明周期的方法中,比如,下载,文件操作等,这样很多情况下回导致,我们在activity中的声明周期方法中写越来越多的代码,activity或者fragment 越来越臃肿,代码维护越来越困难。使用lifecycle就可以很好的解决这类问题。

一 、分发lifecycle event

对于event的分发我们采用观察者模式,需要支持多线程环境,因为http请求可能在任意线程中发起。

首先定义一个 LifecycleProvider类,如下

/** * 统一分发Activity和 Fragment的生命周期时间. */public interface LifecycleProvider {    /**     * Adds an observer to the list. The observer cannot be null and it must not already     * be registered.     *     * @param observer the observer to register     * @throws IllegalArgumentException the observer is null     */    void observe(Observer observer);    /**     * Removes a previously registered observer. The observer must not be null and it     * must already have been registered.     *     * @param observer the observer to unregister     * @throws IllegalArgumentException the observer is null     */    void removeObserver(Observer observer);    /**     * A simple callback that can receive from {@link android.arch.lifecycle.Lifecycle}     */    interface Observer {        /**         * Called when the event is changed.         *         * @param event The new event         */        void onChanged(@NonNull Lifecycle.Event event);    }}

实现类 为AndroidLifecycle 继承了LifecycleObserver接口监听Lifecycle event

public final class AndroidLifecycle implements LifecycleProvider, LifecycleObserver {    private final Object mLock = new Object();    @GuardedBy("mLock")    private final ArrayList
 mObservers = new ArrayList<>();    /**     * 缓存当前的Event事件     */    @GuardedBy("mLock")    @Nullable    private Lifecycle.Event mEvent;    @MainThread    public static LifecycleProvider createLifecycleProvider(LifecycleOwner owner) {        return new AndroidLifecycle(owner);    }    private AndroidLifecycle(LifecycleOwner owner) {        owner.getLifecycle().addObserver(this);    }    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)    void onEvent(LifecycleOwner owner, Lifecycle.Event event) {        synchronized (mLock) {            //保证线程的可见性            mEvent = event;            for (int i = mObservers.size() - 1; i >= 0; i--) {                mObservers.get(i).onChanged(event);            }        }        if (event == Lifecycle.Event.ON_DESTROY) {            owner.getLifecycle().removeObserver(this);        }    }    @Override    public void observe(Observer observer) {        if (observer == null) {            throw new IllegalArgumentException("The observer is null.");        }        synchronized (mLock) {            if (mObservers.contains(observer)) {                return;            }            mObservers.add(observer);            if (mEvent != null) {                observer.onChanged(mEvent);            }        }    }    @Override    public void removeObserver(Observer observer) {        if (observer == null) {            throw new IllegalArgumentException("The observer is null.");        }        synchronized (mLock) {            int index = mObservers.indexOf(observer);            if (index == -1) {                return;            }            mObservers.remove(index);        }    }}

使用时只要在onChanged方法中就可以处理对应的事件,使用如下

    LifecycleProvider provider = AndroidLifecycle.createLifecycleProvider(this);    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        provider.observe(new LifecycleProvider.Observer() {            @Override            public void onChanged(@NonNull Lifecycle.Event event) {                //do...            }        });    }

observe方法不依赖于主线程,可以在任何地方调用。这样二次分发设计的目的有两个

  • owner.getLifecycle().addObserver(LifecycleObserver)方法是线程不安全的,需要依赖主线程

  • 可以缓存最新的Lifecycle.Event

二、retrofit 关联生命周期

retrofit 如何才能关联生命周期呢,通用的做法肯定是自定义CallAdapter.Factory,我们可以返回我们想要的自定义Call,在Call接口添加bindToLifecycle方法于LifecycleProvider相关联

  • 自定义Call接口如下,添加了绑定生命周期的方法,这里只展示核心方法

    public interface Call
     extends Callable
    , Cloneable {          //忽略其他代码  /**   * 绑定生命周期   *   * @param provider LifecycleProvider   * @param event    {@link Lifecycle.Event}, {@link Lifecycle.Event#ON_ANY} is not allowed   * @return LifeCall   */  LifeCall
     bindToLifecycle(LifecycleProvider provider, Lifecycle.Event event);  /**   * default event is {@link Lifecycle.Event#ON_DESTROY}   *   * @param provider LifecycleProvider   * @return LifeCall   * @see Call#bindToLifecycle(LifecycleProvider, Lifecycle.Event)   */  LifeCall
     bindUntilDestroy(LifecycleProvider provider);}
  • 且看如何实现此接口 RealCall

    final class RealCall
     implements Call
     {     //忽略其他代码  @Override  public LifeCall
     bindToLifecycle(LifecycleProvider provider, Lifecycle.Event event) {      Utils.checkNotNull(provider, "provider==null");      Utils.checkNotNull(event, "event==null");      if (event == Lifecycle.Event.ON_ANY) {          throw new IllegalArgumentException("ON_ANY event is not allowed.");      }      return new RealLifeCall<>(clone(), event, provider);  }  @Override  public LifeCall
     bindUntilDestroy(LifecycleProvider provider) {      return bindToLifecycle(provider, Lifecycle.Event.ON_DESTROY);  }}
  • LifeCall 生命周期管理的接口类,它继承了LifecycleProvider.Observer,因此可以在onChanged方法接收分发的Lifecycle.Event

    public interface LifeCall
     extends Callable
    , LifecycleProvider.Observer {  /**   * Returns true if this call has been disposed.   *   * @return true if this call has been disposed   */  boolean isDisposed();  /**   * The method may be called concurrently from multiple   * threads; the method must be thread safe. Calling this method multiple   * times has no effect.   * 

       * like {@code Observable#doOnDispose(Action)},{@code SingleSubject#onSuccess(Object)}   * 

       * you can invoke with {@link Lifecycle.Event#ON_ANY} to dispose from outside immediately.   */  @Override  void onChanged(@NonNull Lifecycle.Event event);}

  • 且看如何实现此接口RealLifeCall

    onChanged中判断,当event参数为指定的event时取消请求,并且标记为disposed,从provider中移除RealLifeCall观察对象。注意的是可以手动调用LifeCall.onChanged(LifeCycle.Event.ON_ANY)取消请求用于你想处理的任何场景,如果isDisposed()返回为true,在异步Callback调用的情况下是不会回调的。

    final class RealLifeCall
     implements LifeCall
     {  private final Call
     delegate;  private final Lifecycle.Event event;  private final LifecycleProvider provider;  private final AtomicBoolean once = new AtomicBoolean();  RealLifeCall(Call
     delegate, Lifecycle.Event event, LifecycleProvider provider) {      this.delegate = delegate;      this.event = event;      this.provider = provider;      provider.observe(this);  }      //忽略其他代码  @Override  public void onChanged(@NonNull Lifecycle.Event event) {      if (this.event == event              || event == Lifecycle.Event.ON_DESTROY              //Activity和Fragment的生命周期是不会传入 {@code Lifecycle.Event.ON_ANY},              //可以手动调用此方法传入 {@code Lifecycle.Event.ON_ANY},用于区分是否为手动调用              || event == Lifecycle.Event.ON_ANY) {          if (once.compareAndSet(false, true)/*保证原子性*/) {              delegate.cancel();              provider.removeObserver(this);          }      }  }  @Override  public boolean isDisposed() {      return once.get();  }}
  • 如何返回Call ?自定义CallAdapter.Factory

    retrofit的解耦灵活我们可以做很多自定义的配置,自定义Factory返回我们的Call接口对象,只需在创建retrofit对象是调用addCallAdapterFactory(CallAdapterFactory.INSTANCE)添加进去即可。

    注:executor默认为Android主线程调度使用,Callback回调函数会在对应线程执行。详情可以看retrofit2.Platform.Android.defaultCallbackExecutor()方法

    public final class CallAdapterFactory extends CallAdapter.Factory {  private static final String RETURN_TYPE = Call.class.getSimpleName();  public static final CallAdapter.Factory INSTANCE = new CallAdapterFactory();  private static final Executor OPTIONAL_NULL_EXECUTOR = new Executor() {      @Override      public void execute(@NonNull Runnable command) {          command.run();      }  };  private CallAdapterFactory() {  }  @Override  public CallAdapter
     get(Type returnType, Annotation[] annotations, Retrofit retrofit) {      if (getRawType(returnType) != Call.class) {          return null;      }      if (!(returnType instanceof ParameterizedType)) {          throw new IllegalArgumentException(                  String.format("%s return type must be parameterized as %s
     or %s
    ", RETURN_TYPE, RETURN_TYPE, RETURN_TYPE));      }      final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);      final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)              ? null              : retrofit.callbackExecutor();      return new CallAdapter
    >() {          @Override          public Type responseType() {              return responseType;          }          @Override          public Call
     adapt(retrofit2.Call call) {              if (executor != null) {                  return new RealCall<>(executor, call);              }              return new RealCall<>(OPTIONAL_NULL_EXECUTOR, call);          }      };  }}
  • 丰富的Callback接口

    支持开始、结束、成功、失败、异常统一解析、简单的数据二次处理操作,HttpError统一包装异常信息

    public interface Callback
     {  /**   * @param call The {@code Call} that was started   */  void onStart(Call
     call);  @NonNull  HttpError parseThrowable(Call
     call, Throwable t);  /**   * 过滤一次数据,如剔除List中的null等,默认可以返回t   */  @NonNull  T transform(Call
     call, T t);  void onError(Call
     call, HttpError error);  void onSuccess(Call
     call, T t);  /**   * @param t 请求失败的错误信息   */  void onCompleted(Call
     call, @Nullable Throwable t);}
  • 异步调用

    定义接口

    @FormUrlEncoded@POST("user/login")Call
     getLogin(@Field("username") String username, @Field("password") String password);

    安全的异步发起请求:

    public class MainActivity extends AppCompatActivity {  LifecycleProvider provider = AndroidLifecycle.createLifecycleProvider(this);  @Override  protected void onCreate(@Nullable Bundle savedInstanceState) {      super.onCreate(savedInstanceState);      RetrofitFactory.create(ApiService.class)              .getLogin("loginName", "password")              //.bindUntilDestroy(provider)              .bindToLifecycle(provider, Lifecycle.Event.ON_STOP)              .enqueue(new DefaultCallback
    () {                  @Override                  public void onStart(Call
     call) {                      showLoading();                  }                  @Override                  public void onError(Call
     call, HttpError error) {                      Toast.makeText(MainActivity.this, error.msg, Toast.LENGTH_SHORT).show();                  }                  @Override                  public void onSuccess(Call
     call, LoginInfo loginInfo) {                      Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();                  }                  @Override                  public void onCompleted(Call
     call, @Nullable Throwable t){                      hideLoading();                  }              });  }}
  • 如何同步调用

    一般同步调用的场景不多,一些连续且相互依赖的请求可以使用同步请求减少逻辑复杂性

    如:注册成功后直接登录,如果采用异步的方式实现,回调接口缠绕在一起,代码不好维护。采用同步的方式实现更为方便。

    @FormUrlEncoded@POST("user/register")Call
     register(@Field("username") String username, @Field("password") String password);@FormUrlEncoded@POST("user/login")Call
     getLogin(@Field("username") String username, @Field("password") String password);

    new Thread(){  @Override  public void run() {      super.run();      try {          RegisterInfo registerInfo=RetrofitFactory.create(ApiService.class)                  .register("loginName", "password")                  .bindToLifecycle(provider, Lifecycle.Event.ON_STOP)                  .execute();          //注册成功开始登录          LoginInfo loginInfo=RetrofitFactory.create(ApiService.class)                  .getLogin("loginName", "password")                  .bindToLifecycle(provider, Lifecycle.Event.ON_STOP)                  .execute();          //登录成功      } catch (Throwable throwable) {          //异常处理          throwable.printStackTrace();      }  }}.start();

    这里涉及二个问题

  • 关于Thread,可以自行用线程池实现,这里制作演示

  • 线程调度,成功和失败的结果需要回调到主线程中,android中回调主线程采用的Handler.post(Runnable)或者postDelayed(Runnable, long)方法实现,当主线程调度执行run方法是可能Activity或者Fragment已经被销毁。那么怎样才能安全的回调到主线程呢?

    调度方法和生命周期关联,在主线程执行时再次做判断。NetTaskExecutor 是做的Handler的封装

    public final class ToMainThread implements LifecycleProvider.Observer {  @Nullable  private volatile Lifecycle.Event mEvent;  private final LifecycleProvider provider;  public ToMainThread(LifecycleProvider provider) {      this.provider = provider;      provider.observe(this);  }  public void to(@NonNull final Runnable runnable, final Lifecycle.Event event) {      NetTaskExecutor.getInstance().postToMainThread(new Runnable() {          @Override          public void run() {              if (mEvent == event || mEvent == Lifecycle.Event.ON_DESTROY)                  return;              runnable.run();          }      });  }  public void toDelayed(@NonNull final Runnable runnable, final Lifecycle.Event event, long delayMillis) {      NetTaskExecutor.getInstance().postToMainThreadDelayed(new Runnable() {          @Override          public void run() {              if (mEvent == event || mEvent == Lifecycle.Event.ON_DESTROY)                  return;              runnable.run();          }      }, delayMillis);  }  @Override  public void onChanged(@NonNull Lifecycle.Event event) {      this.mEvent = event;      if (event == Lifecycle.Event.ON_DESTROY) {          provider.removeObserver(this);      }  }}

    完整的同步执行代码如下,这样处理完全关联了生命周期。不会出任何问题

  • 结束

github 地址:https://github.com/xchengDroid/retrofit-helper

欢迎提出疑问和建议。

640?wx_fmt=jpeg

640?

点个在看,证明你还爱我

转载地址:https://blog.csdn.net/u014400934/article/details/102674931 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:成熟项目的Flutter快速引入以及Flutter、Native混合开发探究
下一篇:512 1024 2048 4096 新里程碑!

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月22日 10时30分33秒