Class.forName(“java.util.Optional”);
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
static class Android extends Platform {
…
@Override public Executor defaultCallbackExecutor() {
//切换线程(子线程 -> 主线程)
return new MainThreadExecutor();
}
// 创建默认的网络请求适配器工厂,如果是Android7.0或Java8上,则使
// 用了并发包中的CompletableFuture保证了回调的同步
// 在Retrofit中提供了四种CallAdapterFactory(策略模式):
// ExecutorCallAdapterFactory(默认)、GuavaCallAdapterFactory、
// va8CallAdapterFactory、RxJavaCallAdapterFactory
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
-
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
- singletonList(executorFactory);
}
…
@Override List<? extends Converter.Factory> defaultConverterFactories() {
return Build.VERSION.SDK_INT >= 24
-
? singletonList(OptionalConverterFactory.INSTANCE)
- Collections.<Converter.Factory>emptyList();
}
…
static class MainThreadExecutor implements Executor {
// 获取Android 主线程的Handler
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
// 在UI线程对网络请求返回数据处理
handler.post®;
}
}
}
3, 添加baseUrl
baseUrl最基本的功能就是将String类型的url转换为OkHttp的HttpUrl的过程,涉及的代码如下。
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, “baseUrl == null”);
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, “baseUrl == null”);
List pathSegments = baseUrl.pathSegments();
if (!“”.equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
4,build过程
build()主要任务是执行Retrofit对象的创建,涉及代码如下。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException(“Base URL required.”);
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
// 默认使用okhttp
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// Android默认的callbackExecutor
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the defaultCall adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
// 添加默认适配器工厂在集合尾部
callAdapterFactories.addAll(platform.defaultCallAdapterFactorisca llbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters thatconsumeall types.
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories();
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
3.2.2 创建网络请求接口实例
Retrofit.create()使用了外观模式和代理模式创建了网络请求的接口实例,创建create()的方法如下。
public T create(final Class service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
// 判断是否需要提前缓存ServiceMethod对象
eagerlyValidateMethods(service);
}
// 使用动态代理拿到请求接口所有注解配置后,创建网络请求接口实例
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
然后,我们再看一下loadServiceMethod()方法。
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 解析注解配置得到了ServiceMethod
result = ServiceMethod.parseAnnotations(this, method);
// 可以看到,最终加入到ConcurrentHashMap缓存中
serviceMethodCache.put(method, result);
}
}
return result;
}
abstract class ServiceMethod {
static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
// 通过RequestFactory解析注解配置(工厂模式、内部使用了建造者模式)
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
“Method return type must not include a type variable or wildcard: %s”, returnType);
}
if (returnType == void.class) {
throw methodError(method, “Service methods cannot return void.”);
}
// 最终是通过HttpServiceMethod构建的请求方法
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract T invoke(Object[] args);
}
3.3 Retrofit流程图
Retrofit最新的版本是2.9.0,已经大半年没有更新了。Retrofit虽然只是一个RESTful 的HTTP 网络请求框架的封装库。但是,它内部通过 大量的设计模式 封装了 OkHttp,让使用者感到它非常简洁、易懂。它内部主要是用动态代理的方式,动态将网络请求接口的注解解析成HTTP请求,最后执行请求的过程。Retrofit完整的流程如下图所示。
4,Glide
=======
4.1 基本使用
作为一个Android图片加载框架,Glide具有功能全、性能高,使用简单等优点。使用下面这一行代码就可以完成图片的加载与展示。
Glide.with(context).load(url).into(iv);
除此之外,我们还可以在图片的加载过程中指定一个占位图。
Glide.with(this)
.load(url)
.placeholder(R.drawable.noimage)
.into(iv);
4.2 源码分析
下面是一个完整的Glide框架的架构图。
4.2.1 with(context)
我们使用Glide,都是从Glide.with()方法开始的,源码如下。
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
可以看到,with()方法有很多,但内容基本一致,都是通过RequestManagerRetriever.get()获取RequestManagerRetriever对象retriever,然后通过retriever.get(context)获取一个RequestManager对象并返回。这些with()方法关键的不同在于传入的参数不同,可以是Context、Activity、Fragment等等。
之所以Glide在加载图片的时候要绑定with(context)方法中传入的context的生命周期,如果传入的是Activity,那么在这个Activity销毁的时候Glide会停止图片的加载。这样做的好处在于:避免了消耗多余的资源,也避免了在Activity销毁之后加载图片从而导致空指针问题。
接下来,我们看一下RequestManagerRetriever类的源码。
public class RequestManagerRetriever implements Handler.Callback {
//饿汉式创建单例
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
//返回单例对象
public static RequestManagerRetriever get() {
return INSTANCE;
}
//根据传入的参数,获取不同的RequestManager
public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException(“You cannot start a load on a null Context”);
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
//省略无关代码…
}
上面的代码是一个饿汉式单例模式,核心是根据传入的参数获取不同的RequestManager。接下来,我们看一下getApplicationManager方法。
private RequestManager getApplicationManager(Context context) {
// 返回一个单例
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
getApplicationManager(Context context)通过双检查单例模式创建并返回applicationManager。如果传入的context是Activity时,操作如下。
public RequestManager get(Activity activity) {
//如果不在主线程或者Android SDK的版本低于HONEYCOMB,传入的还是Application类型的context
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
//判断当前activity是否被销毁
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
//通过fragmentGet(activity, fm)获取RequestManager
return fragmentGet(activity, fm);
}
}
如果传入的context是Activity时,操作如下。
public RequestManager get(Activity activity) {
//如果不在主线程或者Android SDK的版本低于HONEYCOMB,传入的还是Application类型的context
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
//判断当前activity是否被销毁
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
//通过fragmentGet(activity, fm)获取RequestManager
return fragmentGet(activity, fm);
}
}
下面是with()方法的一个简单的总结。
-
通过RequestManagerRetriever.get()获取RequestManagerRetriever单例对象。
-
通过retriever.get(context)获取RequestManager,在get(context)方法中通过对context类型的判断做不同的处理。
-
当context是Application,通过getApplicationManager(Context context)创建并返回一个RequestManager对象。
-
当context是Activity,通过fragmentGet(activity, fm)在当前activity创建并添加一个没有界面的fragment,从而实现图片加载与activity的生命周期相绑定,之后创建并返回一个RequestManager对象。
4.2.2 load(url)
load(url)主要是用于加载网络的图片,如下所示。
Glide.with(context)
.load(url)
.placeholder(R.drawable.place_image)
.error(R.drawable.error_image)
.into(imageView);
load(url)方法的源码如下。
public DrawableTypeRequest load(String string) {
return (DrawableTypeRequest) fromString().load(string);
}
load()方法涉及fromString()、load(string)两个方法,接下来看下这两个方法。
fromString()
public DrawableTypeRequest fromString() {
return loadGeneric(String.class);
}
private DrawableTypeRequest loadGeneric(Class modelClass) {
ModelLoader<T, InputStream> streamModelLoader =
Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + “. You must provide a Model of a type for”
-
" which there is a registered ModelLoader, if you are using a custom model, you must first call"
-
" Glide#register with a ModelLoaderFactory for your custom model class");
}
//这句是核心,本质是创建并返回了一个DrawableTypeRequest
return optionsApplier.apply(
new DrawableTypeRequest(modelClass, streamModelLoader,
fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
loadGeneric()方法本质是创建并返回一个DrawableTypeRequest,Drawable类型的请求。
load(string) load(string)方法的代码如下。
@Override
public DrawableRequestBuilder load(ModelType model) {
super.load(model);
return this;
}
此方法首先调用DrawableRequestBuilder的父类GenericRequestBuilder的load()方法,然后返回自身。接下来,看一下DrawableRequestBuilder父类中的load()方法,如下所示。
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
DrawableRequestBuilder的父类是GenericRequestBuilder,从名字中我们也可以看出来,前者是Drawable请求的构建者,后者是通用的请求构建者,他们是子父关系。
经过上面的分析我们可以知道,在Glide.with(context).load(url)之后会返回一个DrawableTypeRequest的对象,它的父类是DrawableRequestBuilder,DrawableRequestBuilder的父类是GenericRequestBuilder,我们写的placeHolder()、error()等等相关图片请求配置的方法都定义在GenericRequestBuilder中。
4.2.3 into(imageView)
Glide中的前两部是创建了一个Request,这个Request可以理解为对图片加载的配置请求,需要注意的是仅仅是创建了一个请求,而没有去执行。只有当调用into()方法时,这个请求才会真正的被执行。into(imageView)的源码如下。
public Target into(ImageView view) {
return super.into(view);
}
发现它调用的是父类GenericRequestBuilder的into()方法,那我们继续看GenericRequestBuilder中的into()方法。
public Target into(ImageView view) {
//确保在主线程
Util.assertMainThread();
//确保view不为空
if (view == null) {
throw new IllegalArgumentException(“You must pass in a non null View”);
}
//对ScaleType进行配置
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
// C A S E S − O M I T T E D CASES-OMITTED CASES−OMITTED
default:
// Do nothing.
}
}
//核心
return into(glide.buildImageViewTarget(view, transcodeClass));
}
可以看到,上面的方法就是into()的核心代码,它定义在GenericRequestBuilder这个通用的请求构建者中。方法的核心是最后一行:into(glide.buildImageViewTarget(view, transcodeClass)),首先通过glide.buildImageViewTarget(view, transcodeClass)创建出一个Target类型的对象,然后把这个target传入GenericRequestBuilder中的into()方法中。
关于buildImageViewTarget(view, transcodeClass)方法,我们就不再赘述了。
6,EventBus
==========
6.1 基本使用
6.1.1 基本概念
EventBus是一种用于Android的事件发布-订阅的事件总线。它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。
EventBus由三个角色构成:Publisher、Event和Subscriber。
-
Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。
-
Subscriber:事件订阅者,在EventBus 3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe,并且指定线程模型,默认是POSTING。
-
Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
EventBus是一种典型的事件发布-订阅模式,事件由发布者通过EvenentBus传递给订阅者,总体框架如下。
EventBus提供了四种线程模型,分别是:
-
POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
-
MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
-
BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
-
ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
6.1.2 基本使用
EventBus使用流程上分为3步。首先,定义一个事件类。
public class SayHelloEvent {
private String message;
public void sayHellow(String message) {
this.message = message;
}
}
然后,准备一个事件的订阅者,为了防止事件带来的性能消耗问题,还需要在onStop生命周期中注销事件的订阅。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(SayHelloEvent event) {
String message = event.getMessage();
…
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
最后,在需要发送事件的地方调用EventBus.getDefault().post方法发送事件。
EventBus.getDefault().post(new SayHelloEvent(“Hello,EventBus!!!”));
6.2 源码分析
6.2.1 EventBus.getDefault().register(this)
首先,我们从获取EventBus实例的方法getDefault()开发看EventBus。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可以看到,在getDefault()中使用了双重校验并加锁的单例模式来创建EventBus实例,然后我们看一下构造方法。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
在EventBus的默认构造方法中又调用了它的另一个有参构造方法,将一个类型为EventBusBuilder的DEFAULT_BUILDER对象传递进去了。再看一下EventBusBuilder的构造方法。
public class EventBusBuilder {
…
EventBusBuilder() {
}
…
}
里面基本什么也没做,继续查看EventBus的这个有参构造方法。
private final Map<Class<?>, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
EventBus(EventBusBuilder builder) {
…
// 1
subscriptionsByEventType = new HashMap<>();
// 2
typesBySubscriber = new HashMap<>();
// 3
stickyEvents = new ConcurrentHashMap<>();
// 4
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
…
// 5
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
// 从builder取中一些列订阅相关信息进行赋值
…
// 6
executorService = builder.executorService;
}
在注释1处,创建了一个subscriptionsByEventType对象,可以看到它是一个类型为HashMap的subscriptionsByEventType对象,并且其key为 Event 类型,value为 Subscription链表。这里的Subscription是一个订阅信息对象,它里面保存了两个重要的字段,一个是类型为 Object 的 subscriber,该字段即为注册的对象(在 Android 中时通常是 Activity对象);另一个是 类型为SubscriberMethod 的 subscriberMethod,它就是被@Subscribe注解的那个订阅方法,里面保存了一个重要的字段eventType,它是 Class<?> 类型的,代表了 Event 的类型。在注释2处,新建了一个类型为 Map 的typesBySubscriber对象,它的key为subscriber对象,value为subscriber对象中所有的 Event 类型链表,日常使用中仅用于判断某个对象是否注册过。在注释3处新建了一个类型为ConcurrentHashMap的stickyEvents对象,它是专用于粘性事件处理的一个字段,key为事件的Class对象,value为当前的事件。
可能有的同学还不知道粘性事件,所谓粘性事件是相对普通事件来说的。普通事件是先注册,然后发送事件才能收到;而粘性事件,在发送事件之后再订阅该事件也能收到。并且,粘性事件会保存在内存中,每次进入都会去内存中查找获取最新的粘性事件,除非你手动解除注册。
然后,我们看注释5这行代码新建了一个subscriberMethodFinder对象,这是从EventBus中抽离出的订阅方法查询的一个对象。在注释6处,从builder中取出了一个默认的线程池对象,它由Executors的newCachedThreadPool()方法创建,它是一个有则用、无则创建、无数量上限的线程池。
接下来,我们再看一下EventBus的regist()方法。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 1
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 2
subscribe(subscriber, subscriberMethod);
}
}
}
上面代码的主要作用是,根据当前注册类获取 subscriberMethods这个订阅方法列表,然后使用了增强for循环令subsciber对象 对 subscriberMethods 中每个 SubscriberMethod 进行订阅。
更深层次的代码,我们就不做分析了,regist()主要完成了如下一些事情。
-
根据单例设计模式创建一个EventBus对象,同时创建一个EventBus.Builder对象对EventBus进行初始化,其中有三个比较重要的集合和一个SubscriberMethodFinder对象。
-
调用register方法,首先通过反射获取到订阅者的Class对象。
-
通过SubscriberMethodFinder对象获取订阅者中所有订阅的事件集合,它先从缓存中获取,如果缓存中有,直接返回;如果缓存中没有,通过反射的方式去遍历订阅者内部被注解的方法,将这些方法放入到集合中进行返回。
-
遍历第三步获取的集合,将订阅者和事件进行绑定。
-
在绑定之后会判断绑定的事件是否是粘性事件,如果是粘性事件,直接调用postToSubscription方法,将之前发送的粘性事件发送给订阅者。其实这也很好理解,在讲粘性事件时说过,如果在粘性事件发送之前注册的订阅者,当发送粘性事件时,会接收到该事件;如果是粘性事件发送之后注册的订阅者,同样也能接收到事件。
6.2.2 EventBus.getDefault().post()
post()方法的代码如下。
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException(“Internal error. Abort state was not reset”);
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
此方法的作用是,获取当前线程的事件集合,将要发送的事件加入到集合中。然后通过循环,只要事件集合中还有事件,就一直发送。这里的currentPostingThreadState 是一个 ThreadLocal 类型的对象,里面存储了 PostingThreadState,而 PostingThreadState 中包含了一个 eventQueue 和其他一些标志位,相关的源码如下。
private final ThreadLocal currentPostingThreadState = new ThreadLocal () {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
final static class PostingThreadState {
final List eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
然后,我们看一下postSingleEvent() 方法,代码如下。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |=
postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
…
}
}
上面代码的作用是,获取事件的Class对象,找到当前的event的所有父类和实现的接口的class集合。遍历这个集合,调用发送单个事件的方法进行发送。
可以看到,上面的代码首先取出 Event 的 class 类型,接着会对 eventInheritance 标志位 判断,它默认为true,如果设为 true 的话,它会在发射事件的时候判断是否需要发射父类事件,设为 false,能够提高一些性能。接着,调用lookupAllEventTypes() 方法,它的作用就是取出 Event 及其父类和接口的 class 列表,当然重复取的话会影响性能,所以它也做了一个 eventTypesCache 的缓存,这样就不用重复调用 getSuperclass() 方法。最后,调用postSingleEventForEventType()方法。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class <?> eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized(this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription: subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
根据事件获取所有订阅它的订阅者集合,遍历集合,将事件发送给订阅者。方法最后又调用了postToSubscription()方法。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknow thread mode: " + subscription.subscriberMethod.threadMode);
}
}
可以看出,通过threadMode 来判断在哪个线程中去执行订阅消息。
-
POSTING:执行 invokeSubscriber() 方法,内部直接采用反射调用。
-
MAIN:首先去判断当前是否在 UI 线程,如果是的话则直接反射调用,否则调用mainThreadPoster的enqueue()方法,即把当前的方法加入到队列之中,然后通过 handler 去发送一个消息,在 handler 的 handleMessage 中去执行方法。
-
MAIN_ORDERED:与MAIN类似,不过是确保是顺序执行的。
-
BACKGROUND:判断当前是否在 UI 线程,如果不是的话则直接反射调用,是的话通过backgroundPoster的enqueue()方法 将方法加入到后台的一个队列,最后通过线程池去执行。注意,backgroundPoster在 Executor的execute()方法 上添加了 synchronized关键字 并设立 了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行。
-
ASYNC:逻辑实现类似于BACKGROUND,将任务加入到后台的一个队列,最终由Eventbus 中的一个线程池去调用,这里的线程池与 BACKGROUND 逻辑中的线程池用的是同一个,即使用Executors的newCachedThreadPool()方法创建的线程池,它是一个有则用、无则创建、无数量上限的线程池。不同于backgroundPoster的保证任一时间只且仅能有一个任务会被线程池执行的特性,这里asyncPoster则是异步运行的,可以同时接收多个任务。
可以看到,EventBus.getDefault().post()主要做了如下一些事情:
-
获取当前线程的事件集合,将要发送的事件加入到集合中。
-
通过循环,只要事件集合中还有事件,就一直发送。
-
获取事件的Class对象,找到当前的event的所有父类和实现的接口的class集合。遍历这个集合,调用发送单个事件的方法进行发送。
-
根据事件获取所有订阅它的订阅者集合,遍历集合,将事件发送给订阅者。
-
发送给订阅者时,根据订阅方法的线程模式调用订阅方法,如果需要线程切换,则切换线程进行调用;否则,直接调用。
6.2.3 EventBus.getDefault().unregister(this)
前面已经介绍了订阅者注册和消息的发送,接下来我们再来看一下消息的解绑。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
总结
算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。
这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。
部分资料展示:
有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。
efault().post()主要做了如下一些事情:
-
获取当前线程的事件集合,将要发送的事件加入到集合中。
-
通过循环,只要事件集合中还有事件,就一直发送。
-
获取事件的Class对象,找到当前的event的所有父类和实现的接口的class集合。遍历这个集合,调用发送单个事件的方法进行发送。
-
根据事件获取所有订阅它的订阅者集合,遍历集合,将事件发送给订阅者。
-
发送给订阅者时,根据订阅方法的线程模式调用订阅方法,如果需要线程切换,则切换线程进行调用;否则,直接调用。
6.2.3 EventBus.getDefault().unregister(this)
前面已经介绍了订阅者注册和消息的发送,接下来我们再来看一下消息的解绑。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-RL9cvCIN-1711545056661)]
[外链图片转存中…(img-AFpUWLbc-1711545056662)]
[外链图片转存中…(img-OTi6thK8-1711545056662)]
[外链图片转存中…(img-F7fZwXHz-1711545056662)]
[外链图片转存中…(img-vXNRemXw-1711545056663)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
[外链图片转存中…(img-d96PplC9-1711545056663)]
[外链图片转存中…(img-x5ZvyW16-1711545056664)]
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-r9WvzbUy-1711545056664)]
总结
算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。
这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。
部分资料展示:
[外链图片转存中…(img-GcPbdGSD-1711545056664)]
[外链图片转存中…(img-ERtoBWJw-1711545056665)]
[外链图片转存中…(img-iLeHECIv-1711545056665)]
[外链图片转存中…(img-xsKdYIYg-1711545056665)]
有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。