Retrofit结合Lifecycle, 将Http生命周期管理到极致

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代码简洁,我们可以通过实现LifecycleObserver 接口,来监听声明周期,然后我们在activity和fragment中去注册监听。

一 、分发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<Observer> 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<T> extends Callable<T>, Cloneable {
          //忽略其他代码
    /**
     * 绑定生命周期
     *
     * @param provider LifecycleProvider
     * @param event    {@link Lifecycle.Event}, {@link Lifecycle.Event#ON_ANY} is not allowed
     * @return LifeCall
     */
    LifeCall<T> 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<T> bindUntilDestroy(LifecycleProvider provider);
}
  • ​ 且看如何实现此接口 RealCall

    final class RealCall<T> implements Call<T> {
          //忽略其他代码
        @Override
        public LifeCall<T> 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<T> bindUntilDestroy(LifecycleProvider provider) {
            return bindToLifecycle(provider, Lifecycle.Event.ON_DESTROY);
        }
    }
    
  • LifeCall 生命周期管理的接口类,它继承了LifecycleProvider.Observer,因此可以在onChanged方法接收分发的Lifecycle.Event

    public interface LifeCall<T> extends Callable<T>, 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.
         * <p>
         * like {@code Observable#doOnDispose(Action)},{@code SingleSubject#onSuccess(Object)}
         * <p>
         * 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<T> implements LifeCall<T> {
        private final Call<T> delegate;
        private final Lifecycle.Event event;
        private final LifecycleProvider provider;
     
        private final AtomicBoolean once = new AtomicBoolean();
    
        RealLifeCall(Call<T> 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<Foo> or %s<? extends Foo>", 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<Object, Call<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public Call<Object> adapt(retrofit2.Call<Object> call) {
                if (executor != null) {
                    return new RealCall<>(executor, call);
                }
                return new RealCall<>(OPTIONAL_NULL_EXECUTOR, call);
            }
        };
    }
}
  • 丰富的Callback接口

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

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

    定义接口

    @FormUrlEncoded
    @POST("user/login")
    Call<LoginInfo> 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<LoginInfo>() {
                        @Override
                        public void onStart(Call<LoginInfo> call) {
                            showLoading();
                        }
    
                        @Override
                        public void onError(Call<LoginInfo> call, HttpError error) {
                            Toast.makeText(MainActivity.this, error.msg, Toast.LENGTH_SHORT).show();
                        }
    
                        @Override
                        public void onSuccess(Call<LoginInfo> call, LoginInfo loginInfo) {
                            Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                        }
    
                        @Override
                        public void onCompleted(Call<LoginInfo> call, @Nullable Throwable t){
                            hideLoading();
                        }
                    });
        }
    
    }
    
  • 如何同步调用

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

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

    @FormUrlEncoded
    @POST("user/register")
    Call<RegisterInfo> register(@Field("username") String username, @Field("password") String password);
    
    @FormUrlEncoded
    @POST("user/login")
    Call<LoginInfo> 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 SafeOnUiThread implements LifecycleProvider.Observer {
    
        private final LifecycleProvider provider;
        @Nullable
        private volatile Lifecycle.Event mEvent;
    
        public static SafeOnUiThread create(LifecycleProvider provider) {
            return new SafeOnUiThread(provider);
        }
    
        private SafeOnUiThread(LifecycleProvider provider) {
            this.provider = provider;
            provider.observe(this);
        }
    
        public void post(@NonNull Runnable action, Lifecycle.Event event) {
            postDelayed(action, event, 0);
        }
    
        /**
         * @param action the action to run on the UI thread
         * @param event  if {@code mEvent==event}, action will not be invoked
         */
        public void postDelayed(@NonNull final Runnable action, final Lifecycle.Event event, long delayMillis) {
            NetTaskExecutor.getInstance().postToMainThreadDelayed(new Runnable() {
                @Override
                public void run() {
                    if (mEvent == event || mEvent == Lifecycle.Event.ON_DESTROY)
                        return;
                    action.run();
                }
            }, delayMillis);
        }
    
        @Override
        public void onChanged(@NonNull Lifecycle.Event event) {
            this.mEvent = event;
            if (event == Lifecycle.Event.ON_DESTROY) {
                provider.removeObserver(this);
            }
        }
    }
    

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

    public class MainActivity extends AppCompatActivity {
    
        LifecycleProvider provider = AndroidLifecycle.createLifecycleProvider(this);
        SafeOnUiThread safeOnUiThread = SafeOnUiThread.create(provider);
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            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();
                        //登录成功
                        safeOnUiThread.post(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                            }
                        }, Lifecycle.Event.ON_STOP);
    
                    } catch (Throwable throwable) {
                        //异常处理,这里只做简单的演示
                        //你可以对throwable做解析分类
                        safeOnUiThread.post(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.this, "服务异常", Toast.LENGTH_SHORT).show();
                            }
                        }, Lifecycle.Event.ON_STOP);
                    }
                }
            };
        }
    }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容