您的位置:首页 > 产品设计 > UI/UE

【Android实战】----Android Retrofit是怎么将回调函数放到UI线程(主线程)中的(源码分析)

2017-01-11 11:27 363 查看
一、简介

集成过Retrofit的猿们都知道,callback是运行在主线程中的,不用再通过异步机制处理。那么是怎么实现的呢,下面从源码角度进行分析,其中涉及到Android异步机制(Handler、Message、Looper、MessageQueue),猿们自行脑补。

二、Retrofit的创建

下面从Retrofit的创建开始,其中的秘密也在其中

Retrofit mRetrofit = new Retrofit.Builder()
.baseUrl(UrlConfig.ROOT_URL)
.client(genericClient())
.addConverterFactory(GsonConverterFactory.create())
.build();

.Builder()

public Builder() {
this(Platform.get());
}

private static final Platform PLATFORM = findPlatform();

static Platform get() {
return PLATFORM;
}

private static Platform findPlatform() {
try {
Class.forName("android.os.Build");
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
return new Java8();
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("org.robovm.apple.foundation.NSObject");
return new IOS();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}

static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}

@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}

static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());

@Override public void execute(Runnable r) {
handler.post(r);
}
}
}

从上到下依次执行,最终到Android中,秘密就在defaultCallbackExecutor()中,从MainThreadExecutor命名就可以看出其作用,下面代码更是进行了验证

private final Handler handler = new Handler(Looper.getMainLooper());


不是别的就是基于主线程中的Looper创建了Handler,那么利用本Handler post进的Runnable就执行在主线程(不懂的猿们可自行脑补)中了。

题外话:还有更常见的一种是异步消息处理,在主线程中采用下列方式创建Handler

private final Handler handler = new Handler()

那么其中的Looper也是主线程中的Looper,Looper是从线程本地变量中获取的。

/**
* Return the Looper object associated with the current thread.  Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

这种使用默认new Handler()构造的只能在主线程中创建,使用new Handler(Looper.getMainLooper())创建跟在哪儿创建无关,Looper都是主线程中的Looper

/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

sMainLooper是应用的主线程looper,是由Android系统为应用创建的,不需要自己创建。

/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself.  See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}


言归正传,看Retrofit的build

/**
* Create the {@link Retrofit} instance using the configured values.
* <p>
* Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
* OkHttpClient} will be created and used.
*/
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}

okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}

Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}

// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}其中callbackExecutor,就是上面创建的MainThreadExecutor,这里要记住,后面callback调用的时候要用

三、callBack的回调

先看使用

/**
* 将call加入队列并实现回调
*
* @param context          应用上下文
* @param call             调入的call
* @param retrofitCallBack 回调
* @param method           调用方法标志,回调用
* @param <T>              泛型参数
*/
public static <T> void addToEnqueue(final Context context, Call<T> call,
final RetrofitCallBack retrofitCallBack, final int method) {
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
LogUtil.d("retrofit back code ====" + response.code());
if (null != response.body()) {
if (response.code() == 200) {
LogUtil.d("retrofit back body ====" + new Gson().toJson(response.body()));
retrofitCallBack.onResponse(response, method);
} else {
LogUtil.d("toEnqueue, onResponse Fail:" + response.code());
ToastUtil.makeShortText(context, "网络连接错误" + response.code());
retrofitCallBack.onFailure(response, method);
}
} else {
LogUtil.d("toEnqueue, onResponse Fail m:" + response.message());
ToastUtil.makeShortText(context, "网络连接错误" + response.message());
retrofitCallBack.onFailure(response, method);
}
}

@Override
public void onFailure(Call<T> call, Throwable t) {
LogUtil.d("toEnqueue, onResponse Fail unKnown:" + t.getMessage());
t.printStackTrace();
ToastUtil.makeShortText(context, "网络连接错误" + t.getMessage());
retrofitCallBack.onFailure(null, method);
}
});
}

将callback加入到call的enqueue中,看下enqueue中都做了什么

@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");

delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}

@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}


其他不管,那么callback的方法就是在callbackExecutor被调用的,而这里的callbackExecutor就是MainThreadExecutor,因此这里的callback方法就运行在主线程中了。

总结:在这方面Retrofit对okhttp的封装,就好比Volley(最新的)对HttpUrlConnection的封装一样,都是采用这种方式将回调方法运行在主线程中的,其中都用到了Android最基本的异步机制(Handler、Looper、Message、MessageQueue)。通过基于主线程中的Looper创建Handler,从而实现通过此Handler post的消息能够被主线程中的Looper获取并运行在主线程中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息