Hey, do have a minute for a quick survey on how we are doing with EventBus?
EventBus是一个开源库,
https://github.com/greenrobot/EventBus
主要通过发布者和订阅者来简化组件之间的通信,并解耦。
EventBus的优点?
1、简化组件之间的通信代码
2、分离事件发送者和接收者(解耦)
3、可以指定工作线程,可调整优先级
4、避免复杂且易出错的依赖关系和生命周期问题
5、速度快等
在使用的过程中会存在多个订阅者的情况,需要发送事件的同时打个标签,明确谁是谁的事件,使用场景过多后,如果标注不明确可能会导致后期维护增加阅读代码的难度。
使用方法直接进开源网址即可查看,文章主要是看看源码怎么写的。
EventBus分为三个部分:
1、Publisher发送者,通过post或postSticky 来发送消息。
2、Subscriber订阅者,register 或 unregister。
3、@Subscribe 注解 接收事件。
一、注册
EventBus.getDefault().register(this);
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
getDefault()进来之后,可以发现EventBus对象是个单例模式,这样就方便可以全局使用。保证了使用的是同一个对象。
- 接着看下new EventBus,在初始EventBus的时候都初始了什么
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
构造方式是通过builder建造模式,除了builder模式之外,初始了很多的成员变量。
1、subscriptionsByEventType 这个hashmap贯穿整个EventBus,在subscriber方法中 是将订阅的事件都保存一份, 在post发送消息需要从它取,最后unregister的时候遍历移除一个个事件。
2、typesBySubscriber 这个hashmap 我觉得是跟subscriptionsByEventType差不多的,除了不涉及发送外。也是subscriber方法中保存订阅的事件,unregiste移除订阅事件。
3、stickyEvents 粘性事件处理的hashmap
4、SubscriberMethodFinder 主要是检验订阅事件的方法符合不符合EventBus的标准,不符合的都在内部throw做处理了。除了检验还加了双层效验,缓存等。但最终返回成一个List。
5、参数尾部为Poster的,都是跟threadMode有关系的。
大致上就这些,先简单了解下作用,后面看源码的时候会很明确,至于没提到的后面可以看着理解。
- 接着看register(),传入了一个Object对象。
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
- 先看看 findSubscriberMethods
private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
···
List findSubscriberMethods(Class> subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
···
static void clearCaches() {
METHOD_CACHE.clear();
}
1、METHOD_CACHE就是一个缓存的map,首先从缓存里面找,缓存有直接就返回了,没有的话通过反射找,找完了缓存里面put存一份。
2、ignoreGeneratedIndex 在初始化SubscriberMethodFinder的时候,就传入这个参数了,全局代码没有地方修改,也没有默认设置为true,所以它如果在不手动设置的前提下,就一直是false。
- 直接看false调用的方法
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
1、prepareFindState 会返回一个FindState对象, 这个类可以理解为辅助类,也可以理解为过渡类, 通过它过渡生成一个新的List
2、findState.subscriberInfo返回为null的,走else。
*接着看findUsingReflectionInSingleClass方法
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
1、前面的代码就是 通过反射获取method[] ,遍历数组
2、getModifiers()获取这个类修饰符的整数,通过这个整数判定接收事件的方法,只能是public,且不能是abstract、static关键字。并且该方法也只能是一个参数。
3、method.getAnnotation(Subscribe.class);获取方法中 是否有Subscribe注解,有的话返回注解对象,没有的话返回的Null。
- 这块有二层检查,看方法
boolean checkAdd(Method method, Class> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
private boolean checkAddWithMethodSignature(Method method, Class> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class> methodClass = method.getDeclaringClass();
Class> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
1、这两个check方法都是findstate这个类的。
2、第一层检查,anyMethodByEventType.put()这个方法返回的是 key映射的旧值,如果没有则返回为null,也就是说之前没有别的方法订阅这个事件。没有直接返回true。
3、第二层检查,拼接methodKey、获取methodClass,传值给subscriberClassByMethodKey,获取methodClassOld,如果methodClassOld为null直接返回true,如果不为空,判定methodClassOld是否是methodClass的父类或者说是否是同一个类,如果都不满足,则当前方法不会被添加。
- 检查通过后
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
1、获取注解上标注的类型, threadMode、sticky、priority跟着一起传入SubscriberMethod对象,并由findState.subscriberMethods.add填入。
- 接着看下一步
private List getMethodsAndRelease(FindState findState) {
List subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
到这里就是获取了订阅者中的所有符合标准的订阅方法 的List, findState也是在这里进行回收,所以就更明确了findState 就是一个辅助类。
- 回到register方法, 看subscribe
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
1、通过subscriber 和 subscriberMethod生成一个新的对象Subscription,subscriptionsByEventType通过eventType获取之前有没有value,没有的话就新建value,放到Key里。有的话就看看是不是已经被注册过事件。之后的代码逻辑都差不多,先判定符合后添加到对应的map里。
2、typesBySubscriber 和 subscriptionsByEventType我从最开始就简单的介绍了穿插的方法,现在可以发现确实如此。
3、粘性事件后面说,这块光知道粘性事件的开启是 获取注解时sticky = true
到此订阅的部分源码就讲完了,总结一下
1.内部有缓存
2.通过反射的方式 配合FindState辅助类,来判定订阅方法是否符合EventBus标准
3.被@Subscribe标注的订阅方法必须是public,不能有abstract、static 且 必须只能有一个参数
4.二层检查,检查订阅事件是否有方法监听,检查底层声明类与旧值底层声明类是否是父类关系,是否是同一个对象,旧值是否为空。
二、Post
注册完成后,订阅者与订阅方法都已存在,现在我们看发送者。
看这块的原因是想知道发送者怎么把事件发送给订阅方法的。
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
1、通过ThreadLocal获取一个PostingThreadState类的对象,接着就是对这个类里面的声明的一些成员变量进行赋值,核心还是看postSingleEvent方法。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List> 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) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
private static List> lookupAllEventTypes(Class> eventClass) {
synchronized (eventTypesCache) {
List> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
1、eventInheritance参数是个开关,默认为true,开启后会通过lookupAllEventTypes,找到所有父类的事件并且存到List中,最后在通过postSingleEventForEventType来对事件进行处理。
- 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;
}
1、在这个方法中通过subscriptionsByEventType取出之前赋值的所有subscriptions,通过postToSubscription对事件进行分发。这块加synchronized是因为hashmap线程是不安全的,此处分发需要线程同步。
- 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 {
// temporary: technically not correct as poster not decoupled from subscriber
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("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
1、这里的swtich很明确了,就是最开始订阅注解里的 threadMode。
2、invokeSubscriber是通过反射把事件传给订阅方法。
3、mainThreadPoster就是把线程切换成主线程,在调用invokeSubscriber。
4、backgroundPoster就是把线程切换成子线程,在子线程中调用invokeSubscriber。
5、asyncPoster就是把线程切换成子线程,在子线程中调用invokeSubscriber。
有了上面的5点基础理论,大概就清楚这几种模式都是什么情况?
- POSTING
case POSTING:
invokeSubscriber(subscription, event);
break;
直接调用的invokeSubscriber,很明显就是post是什么线程,订阅事件处理就在什么线程。
- MAIN
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
如果在主线程,就在主线程处理订阅事件,如果不在主线程,就把事件对象传到队列中,发送一个message,由handlermessage来处理。
这块可以先看最开始EventBus初始化的那一堆成员变量里,其中有个MainThreadSupport,下面我摘出了核心代码。
EventBus(EventBusBuilder builder) {
mainThreadSupport = builder.getMainThreadSupport();
}
···
MainThreadSupport getMainThreadSupport() {
if (mainThreadSupport != null) {
return mainThreadSupport;
} else if (Logger.AndroidLogger.isAndroidLogAvailable()) {
Object looperOrNull = getAndroidMainLooperOrNull();
return looperOrNull == null ? null :
new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
} else {
return null;
}
}
···
Object getAndroidMainLooperOrNull() {
try {
return Looper.getMainLooper();
} catch (RuntimeException e) {
// Not really a functional Android (e.g. "Stub!" maven dependencies)
return null;
}
}
···
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
this.looper = looper;
}
@Override
public boolean isMainThread() {
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper, 10);
}
}
···
public class HandlerPoster extends Handler implements Poster {
这很明显了,从AndroidHandlerMainThreadSupport方法传入的时候,就传入的是主线的loop。知道这点之后,case main也没别的什么了,就是handler接收消息后进行处理,处理就是invokeSubscriber。
- MAIN_ORDERED
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
post在什么线程就在什么线程处理。
- BACKGROUND
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
final class BackgroundPoster implements Runnable, Poster {
···
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
···
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
我把主要的代码抠了出来,可以明确的看到backgroundPoster就是个Runnable,通过cache线程池调度,最后在run方法里进行事件处理。
- ASYNC
这个同理 跟BACKGROUND一样,也是由cache线程池调度,最后在run方法里进行事件处理。
unregister
public synchronized void unregister(Object subscriber) {
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
List subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
这块没什么好说的,就是把之前添加过的事件,订阅都移除掉。
可以发现subscriptionsByEventType 与 typesBySubscriber 都是最开始subscribe的时候就存值了。
粘性事件
EventBus.getDefault().postSticky(this);
粘性事件从开始register->subscribe的时候,就开始post了
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
···
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
很直观就知道它实现也是通过postToSubscription,与post一模一样,改变了它的调用顺序后,就使得它具有了粘性。在register的时候会post一次,在postSticky的时候也会调用一次。所以才会使得 先postSticky之后在注册,能接收到事件。
结束,
上述代码都是个人的理解,有问题可以留言。