版本
v3.2.0
源码+注释存放在
参考:
官网
这是一份详细的 EventBus 使用教程
EventBus版本变更图
读前简介
1. EventBus中的各个角色
发布者
订阅者
订阅方法
事件
发布线程
订阅线程
事件Hash表
2.主要类功能及名词介绍
SubscriberMethod:订阅方法的封装
EventBus : EventBus框架的入口,提供 注册,反注册 ,发送消息,配置能力
EventBusBuilder:EventBus的构建器
SubscriberMethodFinder:订阅方法查找器 ,用于获取
Subscription:作为一个订阅的封装 ,拥有订阅者 和 订阅方法
HandlerPoster:一个Handler 如果 传入looper 是主线程 那就是主线程 handler
黏性事件:就是在发送事件之后再订阅该事件也能收到该事件,用于解决异步所带来的一些问题
源码阅读顺序
获取EventBus实例
注册 及 注册订阅方法
反注册
发送消息 及 接受消息
取消事件
黏性事件
线程切换
下面我们就按照这个顺序,依次看一下EventBus 都做了哪些事情
开始阅读
一. 获取EventBus实例
1. EventBus.getDefault()
EventBus.getDefault()
就是通过 DCL 方式 创建单例。通过这种方式获取的消息线路,我们称之为 ==消息总线==
static volatile EventBus defaultInstance;
//使用DCL 方式创建单例
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
2. new EventBus()
除了使用 EventBus.getDefault()
获取单例 以外,EventBus 还支持通过 new
的方式创建新的EventBus 对象,不过不同的EventBus 对象对应着不同的 消息线路,也就是说 ==A== EventBus 不可能 接收到 ==B== EventBus 的 消息。所以如果总线压力过大,我们可以 通过多个EventBus 来分担 总线的压力.
3. new EventBus(EventBusBuilder builder)
除了使用默认构造这种简单的创建方式以外,EventBus 还支持通过 构建器来创建 实例。其实默认构造最终也是调用的构建器模式,只不过它的构建器也是默认的。如下代码所示
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
.....
}
下来我们看看怎么通过构建器创建EventBus 对象
EventBusBuilder builder = EventBus.builder();
builder.eventInheritance=false;
builder. ..
EventBus eventBus = builder.installDefaultEventBus();
eventBus. ..
其实就是通过 构建器 设置一些默认的值 ,然后在构建出一个 EventBus 对象。非常正统的构建器模式。下来我们看一下 EventBus 都给我们预留了那些 接口。可以让我们自行配置。
4. EventBusBuilder 配置
属性---含义---默认值的对应关系(具体功能介绍请看下方)
属性
含义
默认值
logSubscriberExceptions
订阅函数执行有异常时,打印异常信息
true
logNoSubscriberMessages
是否打印没有 订阅函数 的log
true
sendSubscriberExceptionEvent
订阅函数执行出错 是是否发送 一个异常类型为 SubscriberExceptionEvent 的消息 方便统一常
true
sendNoSubscriberEvent
没有对应订阅函数是发送一个类型为 NoSubscriberEvent 的消息 方便统一
true
throwSubscriberException
订阅函数执行出错时抛出 EventBusException 异常,当为true时 logSubscriberExceptions,sendSubscriberExceptionEvent 这俩就没用了
false
eventInheritance
是否触发 订阅函数形参 为消息类型的父类的订阅函数(官方描述 如果关闭会 提升20的速度,具体时间取决于 注册类的继承结构)
true
ignoreGeneratedIndex
忽略订阅者索引(既是存在订阅者索引,也强制使用反射获取订阅方法)
false
strictMethodVerification
是否严格验证订阅函数签名
false
executorService
自定义线程池
DEFAULT_EXECUTOR_SERVICE
skipMethodVerificationForClasses
跳过方法签名验证集合
无
subscriberInfoIndexes
添加订阅者索引(不指定的话EventBus就会通过反射的方式获取 注册方法 列表)
无
logger
EventBus 内部使用的 Logger
无
具体功能解析
logSubscriberExceptions:订阅函数执行有异常时,打印异常信息 当为true是如果订阅函数内部异常的话 会在控制台打印 error 类型的log,如下log,如果为false则不打印。
2020-06-15 11:58:11.380 26367-26367/org.greenrobot.eventbusperf E/EventBus: Could not dispatch event: class java.lang.String to subscribing class class org.greenrobot.debug.BadExceptionSubscriber
java.lang.RuntimeException: Bad
at org.greenrobot.debug.BadExceptionSubscriber.onEvent(BadExceptionSubscriber.java:8)
at java.lang.reflect.Method.invoke(Native Method)
at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:511)
at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:434)
.....
logNoSubscriberMessages : 是否打印没有监听者的log 当为true时:如果没有对应的监听者会在 控制台打印 如下log。如果为false就没有
2020-06-15 11:02:43.656 14516-14516/org.greenrobot.eventbusperf D/EventBus: No subscribers registered for event class java.lang.String
2020-06-15 11:02:43.656 14516-14516/org.greenrobot.eventbusperf D/EventBus: No subscribers registered for event class org.greenrobot.eventbus.NoSubscriberEvent
sendSubscriberExceptionEvent : 订阅函数执行出错 是是否发送 一个异常类型为 SubscriberExceptionEvent 的消息 方便统一处理异常 当为true时遇到 订阅函数执行有异常的时候会发送一个异常类型为 SubscriberExceptionEvent 的消息。这时候我们可以进行降级处理 或者 上报异常 操作如下
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(SubscriberExceptionEvent event) {
Log.e(TAG,"订阅函数有异常 "+event.toString());
}
sendNoSubscriberEvent: 没有对应订阅函数是发送一个类型为 NoSubscriberEvent 的消息 。这时候我们可以进行降级处理 或者 上报异常 操作如下
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(NoSubscriberEvent event) {
Log.e(TAG,"没有订阅函数 "+event.toString());
}
throwSubscriberException:订阅函数执行出错时抛出 EventBusException 异常,当为true时 logSubscriberExceptions,sendSubscriberExceptionEvent 这俩就没用了,因为在 EventBus 中如下代码中 throwSubscriberException
的优先级比 logSubscriberExceptions
,sendSubscriberExceptionEvent
这俩高
private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
if (event instanceof SubscriberExceptionEvent) {
....
} else {
if (throwSubscriberException) {
throw new EventBusException("Invoking subscriber failed", cause);
}
if (logSubscriberExceptions) {
logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class "
+ subscription.subscriber.getClass(), cause);
}
if (sendSubscriberExceptionEvent) {
SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,
subscription.subscriber);
post(exEvent);
}
}
}
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;
}
}
strictMethodVerification: 是否严格验证订阅函数签名,默认为false,当为true时 EventBus 当检测到 注册方法的签名不合法时会抛出异常,代码位于 SubscriberMethodFinder.findUsingReflectionInSingleClass
executorService:可以设置项目自己的线程池 如果有的话 ,避免 项目中有多个箱尺寸在,浪费资源
skipMethodVerificationForClasses :跳过方法签名验证集合,通过 skipMethodVerificationFor(Class clazz) 可以添加
subscriberInfoIndexes:添加订阅者索引,方便我们自定义和扩展
logger:EventBus 内部使用的 Logger ,可以使用 logger()方法进行设置
二. 注册 及 注册订阅方法
主要作用
查找订阅者中的订阅方法并缓存到 EventBus.subscriptionsByEventType
这个map中
如果是黏性订阅方法 订阅以后直接 调用。以实现先 发消息后注册 也能接收到 事件的功能
源码梳理
1. EventBus.register
//注册 订阅者
public void register(Object subscriber) {
//获取接受者的字节码对象
Class subscriberClass = subscriber.getClass();
//查找 subscriber 中的订阅方法 ,这里没有加锁 说明可以并发进行 【详见1.1】
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历所有订阅方法
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//预处理每个 订阅方法 【详见1.2】
subscribe(subscriber, subscriberMethod);
}
}
}
1.1 SubscriberMethodFinder.findSubscriberMethods
主要作用是 在订阅者中查找订阅方法列表 并进行返回,查找方式分为两种
反射
订阅索引
使用订阅索引会提高EventBus的运行速度,具体原理和如何使用参考 ==知识点3,4==
List findSubscriberMethods(Class subscriberClass) {
//从缓存中获取
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
//命中缓存直接返回
return subscriberMethods;
}
//是否强制使用反射获取订阅方法(可在EventBusBuilder 中配置)
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;
}
}
1.2 EventBus.subscribe
主要有两个
将订阅关系缓存到 subscriptionsByEventType 和 typesBySubscriber中
如果是黏性事件 直接调用 订阅方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
....
//添加 订阅方法形参类型 和 一个订阅 的映射
subscriptionsByEventType.put(eventType, subscriptions);
....
//添加 订阅者 和 订阅方法形参类型 的映射
typesBySubscriber.put(subscriber, subscribedEvents);
....
//如果是黏性订阅方法 订阅以后直接 调用,
if (subscriberMethod.sticky) {
//是否触发 订阅函数形参 为消息类型的父类的订阅函数,可在 EventBusBuilder 中配置
if (eventInheritance) {//是
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, Object> entry : entries) {
Class candidateEventType = entry.getKey();
//candidateEventType 是否是 eventType 的子类
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {//否
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
三. 反注册
主要作用
主要作用是 将在订阅过程中形成的订阅关系同 缓存中清除 ,清除的位置有两个
subscriptionsByEventType
typesBySubscriber
源码梳理
1. EventBus.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());
}
}
三. 发送消息 及 接受消息
主要作用
发送事件并触发订阅方法
源码梳理
1. EventBus.post
public void post(Object event) {
//获取到当前线程的 PostingThreadState(用 ThreadLocal 保存 所以每个 线程只有一个)
PostingThreadState postingState = currentPostingThreadState.get();
//获取当前线程的 事件队列
List eventQueue = postingState.eventQueue;
//将事件添加到事件队列中
eventQueue.add(event);
if (!postingState.isPosting) {//没有正在发送事件
....
try {
while (!eventQueue.isEmpty()) {//队列不为空
//发送事件 【详见1.1】
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {//重置状态
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
1.1 EventBus.postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//获取事件类型
Class eventClass = event.getClass();
boolean subscriptionFound = false;
//是否 触发 event 父类的 注册方法
if (eventInheritance) {
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class clazz = eventTypes.get(h);
//【详见1.2】
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//【详见1.2】
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) {
//发送一个 NoSubscriberEvent
post(new NoSubscriberEvent(this, event));
}
}
}
1.2 EventBus.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;
try {
//触发所有订阅方法 【详见1.3】
postToSubscription(subscription, event, postingState.isMainThread);
// 执行 cancelEventDelivery 后 postingState.canceled 会为true
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
//如果执行了 cancelEventDelivery 就会退出,不在 触发其他订阅方法
if (aborted) {
break;
}
}
return true;
}
return false;
}
1.3 EventBus.postToSubscription
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING://直接触发
invokeSubscriber(subscription, event);
break;
case MAIN://如果不是主线程 切换到主线程触发
if (isMainThread) {
//详见【1.4】
invokeSubscriber(subscription, event);
} else {//不是主线程的话 会 通过handle调度到主线程执行
//详见【1.5】
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.4 EventBus.invokeSubscriber
void invokeSubscriber(Subscription subscription, Object event) {
try {
//反射执行 订阅方法
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
1.5 HandlerPoster.enqueue
public void enqueue(Subscription subscription, Object event) {
//将 subscription , event 封装为 PendingPost
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
//加入到队列中
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
//使用handler 发送一个消息 【详见1.6】
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
1.6 HandlerPoster.handleMessage
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
//取出一个 事件
PendingPost pendingPost = queue.poll();
....
//执行 订阅方法 【详见1.4】
eventBus.invokeSubscriber(pendingPost);
....
}
} finally {
handlerActive = rescheduled;
}
}
四. 取消事件
主要作用
取消以后就不会触发 后面的 订阅方法
源码梳理
1 HandlerPoster.cancelEventDelivery
public void cancelEventDelivery(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
//只有 threadMode = POSTING 时有效
if (!postingState.isPosting) {
throw new EventBusException(
"This method may only be called from inside event handling methods on the posting thread");
} else if (event == null) {
throw new EventBusException("Event may not be null");
} else if (postingState.event != event) {
throw new EventBusException("Only the currently handled event may be aborted");
} else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {
throw new EventBusException(" event handlers may only abort the incoming event");
}
//更改 canceled 为true
postingState.canceled = true;
}
其实只是将 postingState.canceled 标志位 设置为 true,生效的地方参见 【==发送消息 及 接受消息 中1.2 postSingleEventForEventType==】
五. 黏性事件
主要作用
添加一个黏性事件,用于解决异步的一些问题
源码梳理
1 HandlerPoster.postSticky
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// 先触发一次
post(event);
}
发送黏性时间后 会先将事件保存到 stickyEvents
中,然后立马触发一次。当以后有黏性 订阅方法订阅后 会立即执行这个黏性事件,这也是为啥 黏性事件可以先发送再注册 也能接收到的原理。==黏性事件触发位置参考 【注册 及 注册订阅方法中1.2 EventBus.subscribe】==
六. 线程切换
主要作用
通过不同的 ThreadMode 指定 线程
源码梳理
【参考 发送消息 及 接受消息 中 1.3 EventBus.postToSubscription】
POSTING :默认值,那个线程发送消息就是那个线程接受消息
MAIN:指定主线程接受消息
MAIN_ORDERED:也是指定主线程接受消息,不过如果前一个也是main_ordered 则需要等前一个执行完成后才执行
BACKGROUND:指定后台线程(只有一个 ),处理如保存到数据库等操作。
ASYNC:接受是始终重启进程进行操作,一般是用于比较消耗时间的任务
总结
流程分析
获取实例步骤 过程中我们可以对EventBus做一些配置
订阅 步骤只是对订阅者及订阅方法映射的缓存,如果是黏性事件则立即触发 订阅方法
反订阅 步骤就是 删除订阅步骤缓存的映射
发送消息 及 接受消息 步骤就是: 通过订阅步骤中生成的缓存 查找到 对应方法并 对于不同的 ThreadMode ,做不同的线程切换,最后都调用对应的 订阅方法。
取消事件就相当于 停止 下一个 订阅方法的执行。
ThreadMode的几种模式
POSTING :默认值,那个线程发送消息就是那个线程接受消息
MAIN:指定主线程接受消息
MAIN_ORDERED:也是指定主线程接受消息,不过如果前一个也是main_ordered 则需要等前一个执行完成后才执行
BACKGROUND:指定后台线程(只有一个 ),处理如保存到数据库等操作。
ASYNC:接受是始终重启进程进行操作,一般是用于比较消耗时间的任务
知识点
==其实 Java中也有观察者模式的实现(Observer,Observable),其工作原理和 EventBus很像,只不过 EventBus 使用起来更加方便而且具有线程切换等优点。==
在某些情况下,比如 Activity中 getDeclaredMethods 比 getMethods 快,因为 getDeclaredMethods 只获取自身的方法(public、protected、private),而getMethods 会向上查找 所有父类的方法(public)。
查找订阅方法重如果我们没有手动设置过 EventBusBuilder 的 subscriberInfoIndexes 那么就会通过反射获取 注册方法 列表, 所以我们再使用EventBus 的时候最好通过builder指定一下 subscriberInfoIndexes,毕竟 反射 是比较耗时的。代码如下
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusIndex())
.installDefaultEventBus();
SubscriberMethodFinder
中 使用反射获取 订阅方法列表和 通过 订阅索引获取 订阅方法列表 本质的区别是前者是在运行期 才组织关系,而 后者是在编译器 就已经确定。所以使用后者会 提高 运行速度。
你可能感兴趣的:(EventBus 3.2.0 源码阅读)
FreeBSD bnxt以太网驱动源码阅读记录三:
酸菜。
linux系统编程与内核编程 linux 驱动开发
FreeBSD里面的mediastatus(2022/8/1)在FreeBSD的Linux系统中,使用ifconfig命令会看到这样的信息:media:Ethernetautoselect(100baseTX)media:Ethernetautoselect(1000baseT)media:Ethernetautoselect(10Gbase-SR)media:Ethernetautoselect
【Vue.js 中父子组件通信的最佳实践】
程序员远仔
前端 vue.js javascript 前端框架 前端 html5 css
Vue.js中父子组件通信的最佳实践前言在Vue.js应用开发中,组件通信是构建复杂应用的基础。父子组件通信作为最常见的场景,其实现方式直接影响代码的可维护性和可扩展性。本文将深入探讨Vue.js中父子组件通信的最佳实践,涵盖从基础到高级的各种模式。关键词Vue.js、组件通信、Props、自定义事件、v-model、sync修饰符、作用域插槽、Provide/Inject、EventBus、Vu
【java】java 定时任务线程池 ScheduledThreadPoolExecutor 源码阅读
九师兄
java 开发语言
文章目录1.概述1.1问题1.2简介1.2数据结构2.源码解析2.1接口和类2.1.1Delayed接口2.1.2ScheduledFuture接口2.1.3DelayedWorkQueue2.1.3.1take2.1.3.2offer2.1内部类ScheduledFutureTask2.1.1属性2.1.2构造方法2.1.3compareTo2.1.4核心方法run()2.1.5cancel方法
[特殊字符] 深入解析Java反射机制:从原理到实战应用(附完整代码示例)
BrightChen666
java java 开发语言 后端
前言**反射是Java开发者进阶的必经之路,**也是各大框架的核心实现基础。本文将通过:✅原理深度解读✅30+个代码示例✅性能对比测试✅企业级最佳实践助你全面掌握反射机制,轻松应对框架源码阅读和高级开发需求!一、反射机制全景认知1.1什么是反射?官方定义:反射(Reflection)是Java语言提供的运行时自省能力,允许程序:动态获取类的元数据(类名/方法/字段/注解等)️动态创建对象实例⚡动态
vue2-组件通信
16年上任的CTO
VUE2 vue.js 前端 javascript 组件通讯 vuex props emit
文章目录vue2-组件通信1.为什么需要组件通信2.props传递数据3.$emit触发自定义事件4.ref5.EventBus6.parent和parent和parent和root7.attrs和attrs和attrs和listeners8.provide和inject9.vuex10.总结vue2-组件通信1.为什么需要组件通信在VUE中,组件是比较核心的功能,每个.vue都可以视为一个组件,
20250108慧能科技前端面试
uperficialyu
前端面试实战整理 前端
目录ajax怎么取消请求移动端怎么实现px尺寸vite和webpack的区别设计模式讲一下什么是原型链讲一下什么是闭包实现eventbus事件循环项目发布后,如何对项目进行优化,怎么优化vue2的diff算法和vue3的diff算法的区别1.ajax怎么取消请求原生JavaScript(XMLHttpRequest)创建XMLHttpRequest对象发送请求,通过调用其abort()方法来取消请
spring源码阅读系列文章目录
master-dragon
# spring spring java 后端
对于spring认识首先要了解spring相关概念术语,然后是如下的几句话牢记并反射出来:Bean怎么来的,通过BeanDefinitionBeanDefinition有Spring框架内置的,有手动定义或者自动配置扫描出来的(写个Demo工程)BeanFactoryPostProcessor可干预BeanDefinition,BeanPostProcessor可干预Bean的生命周期aop怎么实
flutter中常见的跨组件通讯方式
面壁思过程
flutter开发 flutter跨组件通讯
在Flutter开发中,事件管理和组件间通信是非常重要的。EventBus和NotificationListener是两种常用的模式,它们各自有不同的使用场景和优势劣势。本文将对这两者进行比较分析,并提供代码示例。在Flutter开发中,事件管理和组件间通信是非常重要的。EventBus、NotificationListener和观察者模式是常用的三种模式,它们各自有不同的使用场景和优势劣势。本文
skynet 源码阅读 -- 「揭秘 Skynet 网络通讯」
Winston-Tao
skynet 源码阅读 skynet c语言 网络编程 epoll
本文将聚焦Skynet网络通讯的核心线程thread_socket,并深入探讨skynet_socket_poll、forward_message、socket_server_poll等关键函数如何协作,实现高效的网络数据收发与消息分发。1.背景与目标Skynet之所以能轻量高效,网络I/O模块的功劳不可忽视。它利用一个独立线程不断poll网络事件,把事件打包成socketmessage再转交给目
# VueBus 总线传参数
泯泷
javascript vue.js 前端
简介vue组件非常常见的有父子组件通信,兄弟组件通信。而父子组件通信就很简单,父组件会通过props向下传数据给子组件,当子组件有事情要告诉父组件时会通过$emit事件告诉父组件。今天就来说说如果两个页面没有任何引入和被引入关系,该如何通信了?如果咱们的应用程序不需要类似Vuex这样的库来处理组件之间的数据通信,就可以考虑Vue中的事件总线,即**EventBus**来通信。什么是EventBus
Spring Boot 打包报错Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0
旭东怪
Spring Boot spring boot apache maven
问题描述:[ERROR]Failedtoexecutegoalorg.apache.maven.plugins:maven-resources-plugin:3.2.0:resources(default-resources)onprojecthelloworld:Inputlength=1->[Help1]问题分析:1、plugins标签里面缺少maven-resources-plugin依赖。
vue3:mitt
米粒宝的爸爸
vue3 vue.js 前端 javascript
在Vue3中使用mitt进行事件总线的实现非常简单。mitt是一个轻量级的事件库,适用于Vue项目中的组件间通信。实现自定义组件直接相互传值,父到子,子到子,子对子,子对孙,想怎么传就怎么传。和android的Eventbus一个玩法1.下载mittnpmimittmac下载,如果权限不够,加sudo2.创建个公共的utils类-emitter.ts//引入mittimportmittfrom'm
element-tree组件连接线以及懒加载
{{data.fvAreaName}}default-expand-all为节点数默认全部展开,懒加载适用importbusfrom'@/utils/eventBus.js'import{TableAPI}from'@/api/valuation/my_valuation.js'exportdefault{props:{treelist:Array,ShowSave:Boolean,taskInf
NET处理分布式事务的解决方案--CAP
dotNET跨平台
分布式
什么是CAPCAP是一个基于.net标准的库,是处理分布式事务的解决方案,还具有EventBus的功能,它轻量级、好用、高效。CAP(DistributedTransactionFramework)是一个开源的.NET库,用于处理分布式事务。它提供了一种简单而有效的方式来处理微服务架构中的事务问题,特别是在需要保证数据一致性的场景中。CAP通过集成事件驱动架构和消息队列来实现分布式事务。主要特点1
【skynet源码阅读系列】03_skynet_context_new
程序员杨小哥
skynet
上一节看了skynet_start()的一部分代码,那部分代码主要做了下面几件事。如果当前进程收到SIGHUP信号,则调用handle_hup函数,将SIG设为1检查配置文件内的deamon配置,这个配置对应一个文件路径,文件内的记录进程的pid号,防止重复启动skynet进程,第一次启动将自动将进程号写入文件。重定向了文件描述符,把文件描述符0,1,2重定向到了/dev/null,相当于当前进程
skynet 源码阅读 -- timer 的实现原理
Winston-Tao
skynet 源码阅读 c语言 skynet timer 时间轮算法
1.Timer驱动的核心流程1.1Timer线程驱动以下是timer线程的核心流程代码。Skynet的Timer模块是通过一个单独的线程(thread_timer)来定期更新定时器的状态。每隔2500微秒(2.5ms)更新一次定时器的状态。ps:为什么是2500?staticvoid*thread_timer(void*p){structmonitor*m=p;skynet_initthread(
skynet 源码阅读 -- 核心概念服务 skynet_context
Winston-Tao
skynet 源码阅读 skynet 游戏开发 C 语言 游戏服务器框架 lua
本文从Skynet源码层面深入解读服务(Service)的创建流程。从最基础的概念出发,逐步深入skynet_context_new函数、相关数据结构(skynet_context,skynet_module,message_queue等),并通过流程图、结构图、以及源码片段的细节分析,希望能对Skynet服务的创建有一个由浅入深的系统认识。1.前言在Skynet中,“服务(Service)”是最
webrtc 源码阅读 make_ref_counted模板函数用法
wu_qz
webrtc 笔记
目录1.模板参数解析1.1typenameT1.2typename...Args1.3typenamestd::enable_if::value,T>::type*=nullptr2.scoped_refptr3.newRefCountedObject(std::forward(args)...);4.综合说明5.在webrtc中的用法5.1peerConnectionFactory对象的构建过程
skynet 源码阅读 -- 启动主流程
Winston-Tao
skynet 源码阅读 skynet skynet 启动 游戏服务器框架 c语言
Skynet启动主流程分析Skynet是一个轻量级、高并发的服务器框架。它在启动时会进行一系列初始化操作,并启动多个不同功能的线程(Monitor、Timer、Worker、Socket),从而实现消息分发、定时器、网络I/O等核心功能。本文主要从main()函数开始一步步trace,循序渐进地看Skynet的启动过程以及各条线程的分工,为后续深入阅读Skynet源码做铺垫。1.启动入口main函
springboot3.X 无法解析parameter参数问题
m0_74824044
面试 学习路线 阿里巴巴 java
本文参考转载:https://oldmoon.top/post/191简介使用最新版的Springboot3.2.1(我使用3.2.0)搭建开发环境进行开发,调用接口时出现奇怪的错。报错主要信息如下:Nameforargumentoftype[java.lang.String]notspecified,andparameternameinformationnotavailableviareflec
前端面试题-手写篇-万字长文!
前端Jason
面试 前端 面试 前端面试
1.手写实现EventBus实现一个简单的EventBus(事件总线)可以让我们在不同的组件或模块之间进行事件驱动的通信。下面是一个用JavaScript手写实现EventBus的基本例子:classEventBus{constructor(){this.events={};//存储事件名与对应的监听器}//注册事件监听器on(event,listener){if(!this.events[eve
async++源码阅读——parallel部分
哎呦,帅小伙哦
# async++ c++ 异步编程 async++
1、背景async++框架中提供了多种并行计算的工具,其中包括parallel_for、parallel_invoke、parallel_reduce。这3中工具的使用场景略有不同,下面将对它们进行比较详细的介绍。2、parallel_for2.1、核心模板函数//这个函数是一个递归设计//为什么只限制了前半部分任务完成后才可以执行后半部分任务呢?//我理解是因为前半部分任务使用了异步方法,而后半
android-Presentation双屏异显
勘察加熊人
android
最近碰到一个智能硬件,有两块屏幕,主屏幕和小屏幕,主界面执行各种操作流程,然后小屏幕展示数据,然后做一下数据交互主要技术是Presentation+eventbusstep1:清单文件注册权限step2:首页主界面packagecom.example.user.mathgame;importandroid.os.Bundle;importandroid.support.v4.app.Fragmen
Soul网关源码阅读(十六)—— SPI插件
蟹尾巴
后端 soul
SPI概念SPI(ServiceProviderInterface),是一种模块间相互引用的机制,可以用来启用框架和替换组件,一般的流程是服务的提供者在classpath指定配置实现类的全名,由调用方读取和加载使用,调用方无需修改代码,通常以jar包的形式引入需要使用的实现,Dubbo,Soul等项目使用了SPI机制,但给使用者提供了更丰富便捷的选择,可以由用户安优先级,名称等方式选择使用那个实现
@LoadBalanced注解的RestTemplate拥有负载均衡的能力
享学源码
java基础 springcloud
关联阅读(必读)发送http请求(1):发送http请求的几种方式发送http请求(2):RestTemplate发送http请求SpringCloud源码阅读4:Ribbon客户端负载均衡(下)回顾当我在Ribbon的环境下使用RestTemplate发送请求时,通常我们会像下面这样注入一个restTemplate@Autowired@LoadBalancedRestTemplaterestTe
vue3开发:项目添加mitt
项目中遇到一个场景:类似于app.vue页面获取某一个页面组件的数据,因为进入那个组件是通过router-view进入,不是通过组件注入到app.vue,所以使用常规的组件通信获取不到数据,我使用了mitt实现了这个功能。Vue2中我们使用EventBus来实现跨组件之间的一些通信,它依赖于Vue自带的on/on/on/emit/$off等方法,而Vue3中移除了这些相关方法,这意味着EventB
Protobuf学习 - 入门
weixin_30892987
c/c++ python 数据结构与算法
古之立大事者,不惟有超世之才,亦必有坚忍不拔之志--苏轼·《晁错论》从公司的项目源码中看到了这个东西,觉得挺好用的,写篇博客做下小总结。下面的操作以C++为编程语言,protoc的版本为libprotoc3.2.0。一、Protobuf?1.是什么?GoogleProtocolBuffer(简称Protobuf)是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存
source insight4.0中文乱码解决方法
鹿屿二向箔
c语言 stm32 keil mdk
Sourceinsight是强大、顺手的代码编辑器,它几乎支持所有的语言,包括:C,C++,C#,HTML等等,能够自动创建并维护它自己高性能的符号数据库,包括函数、全局变量、结构、类和工程源文件里定义的其它类型的符号,对于大工程的源码阅读非常方便。但是因为是国外软件,对中文支持不是很好,很多电脑安装使用都会出现注释乱码问题,很让人抓狂。网上很多人遇到类似的问题,也有很多解决方式,也有很多“终极解
netty4源码阅读与分析---netty线程模型
红尘之一骑
java NIO netty源码阅读与分析
本文主要说下我自己对netty线程模型的理解,以及这样的线程模型的好处。通俗的来讲,netty的线程模型描述的就是老板和员工的故事。老板(通常情况下是一个老板)负责接活,与客户沟通,协调(netty的accept),谈成后(通道建立),他需要从员工中选出一位员工来负责处理后续具体的事宜(worker线程,这里我们有16位员工,编号1-16),员工做事时按照任务的先后顺序进行处理,这样可以避免错乱,
dolphinscheduler独立集群部署文档(海豚调度)
灰太狼!!
数仓开发 资源调度 scheduler
一、下载解压服务器内下载(华为云镜像站网址):wgethttps://mirrors.huaweicloud.com/apache/dolphinscheduler/3.2.0/apache-dolphinscheduler-3.2.0-bin.tar.gz解压:tar-xvfapache-dolphinscheduler-3.2.0-bin.tar.gz改名:mvapache-dolphinsc
Java常用排序算法/程序员必须掌握的8大排序算法
cugfy
java
分类:
1)插入排序(直接插入排序、希尔排序)
2)交换排序(冒泡排序、快速排序)
3)选择排序(直接选择排序、堆排序)
4)归并排序
5)分配排序(基数排序)
所需辅助空间最多:归并排序
所需辅助空间最少:堆排序
平均速度最快:快速排序
不稳定:快速排序,希尔排序,堆排序。
先来看看8种排序之间的关系:
1.直接插入排序
(1
【Spark102】Spark存储模块BlockManager剖析
bit1129
manager
Spark围绕着BlockManager构建了存储模块,包括RDD,Shuffle,Broadcast的存储都使用了BlockManager。而BlockManager在实现上是一个针对每个应用的Master/Executor结构,即Driver上BlockManager充当了Master角色,而各个Slave上(具体到应用范围,就是Executor)的BlockManager充当了Slave角色
linux 查看端口被占用情况详解
daizj
linux 端口占用 netstat lsof
经常在启动一个程序会碰到端口被占用,这里讲一下怎么查看端口是否被占用,及哪个程序占用,怎么Kill掉已占用端口的程序
1、lsof -i:port
port为端口号
[root@slave /data/spark-1.4.0-bin-cdh4]# lsof -i:8080
COMMAND PID USER FD TY
Hosts文件使用
周凡杨
hosts locahost
一切都要从localhost说起,经常在tomcat容器起动后,访问页面时输入http://localhost:8088/index.jsp,大家都知道localhost代表本机地址,如果本机IP是10.10.134.21,那就相当于http://10.10.134.21:8088/index.jsp,有时候也会看到http: 127.0.0.1:
java excel工具
g21121
Java excel
直接上代码,一看就懂,利用的是jxl:
import java.io.File;
import java.io.IOException;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
import jxl.write.Label;
import
web报表工具finereport常用函数的用法总结(数组函数)
老A不折腾
finereport web报表 函数总结
ADD2ARRAY
ADDARRAY(array,insertArray, start):在数组第start个位置插入insertArray中的所有元素,再返回该数组。
示例:
ADDARRAY([3,4, 1, 5, 7], [23, 43, 22], 3)返回[3, 4, 23, 43, 22, 1, 5, 7].
ADDARRAY([3,4, 1, 5, 7], "测试&q
游戏服务器网络带宽负载计算
墙头上一根草
服务器
家庭所安装的4M,8M宽带。其中M是指,Mbits/S
其中要提前说明的是:
8bits = 1Byte
即8位等于1字节。我们硬盘大小50G。意思是50*1024M字节,约为 50000多字节。但是网宽是以“位”为单位的,所以,8Mbits就是1M字节。是容积体积的单位。
8Mbits/s后面的S是秒。8Mbits/s意思是 每秒8M位,即每秒1M字节。
我是在计算我们网络流量时想到的
我的spring学习笔记2-IoC(反向控制 依赖注入)
aijuans
Spring 3 系列
IoC(反向控制 依赖注入)这是Spring提出来了,这也是Spring一大特色。这里我不用多说,我们看Spring教程就可以了解。当然我们不用Spring也可以用IoC,下面我将介绍不用Spring的IoC。
IoC不是框架,她是java的技术,如今大多数轻量级的容器都会用到IoC技术。这里我就用一个例子来说明:
如:程序中有 Mysql.calss 、Oracle.class 、SqlSe
高性能mysql 之 选择存储引擎(一)
annan211
mysql InnoDB MySQL引擎 存储引擎
1 没有特殊情况,应尽可能使用InnoDB存储引擎。 原因:InnoDB 和 MYIsAM 是mysql 最常用、使用最普遍的存储引擎。其中InnoDB是最重要、最广泛的存储引擎。她 被设计用来处理大量的短期事务。短期事务大部分情况下是正常提交的,很少有回滚的情况。InnoDB的性能和自动崩溃 恢复特性使得她在非事务型存储的需求中也非常流行,除非有非常
UDP网络编程
百合不是茶
UDP编程 局域网组播
UDP是基于无连接的,不可靠的传输 与TCP/IP相反
UDP实现私聊,发送方式客户端,接受方式服务器
package netUDP_sc;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Ine
JQuery对象的val()方法执行结果分析
bijian1013
JavaScript js jquery
JavaScript中,如果id对应的标签不存在(同理JAVA中,如果对象不存在),则调用它的方法会报错或抛异常。在实际开发中,发现JQuery在id对应的标签不存在时,调其val()方法不会报错,结果是undefined。
http请求测试实例(采用json-lib解析)
bijian1013
json http
由于fastjson只支持JDK1.5版本,因些对于JDK1.4的项目,可以采用json-lib来解析JSON数据。如下是http请求的另外一种写法,仅供参考。
package com;
import java.util.HashMap;
import java.util.Map;
import
【RPC框架Hessian四】Hessian与Spring集成
bit1129
hessian
在【RPC框架Hessian二】Hessian 对象序列化和反序列化一文中介绍了基于Hessian的RPC服务的实现步骤,在那里使用Hessian提供的API完成基于Hessian的RPC服务开发和客户端调用,本文使用Spring对Hessian的集成来实现Hessian的RPC调用。
定义模型、接口和服务器端代码
|---Model
&nb
【Mahout三】基于Mahout CBayes算法的20newsgroup流程分析
bit1129
Mahout
1.Mahout环境搭建
1.下载Mahout
http://mirror.bit.edu.cn/apache/mahout/0.10.0/mahout-distribution-0.10.0.tar.gz
2.解压Mahout
3. 配置环境变量
vim /etc/profile
export HADOOP_HOME=/home
nginx负载tomcat遇非80时的转发问题
ronin47
nginx负载后端容器是tomcat(其它容器如WAS,JBOSS暂没发现这个问题)非80端口,遇到跳转异常问题。解决的思路是:$host:port
详细如下:
该问题是最先发现的,由于之前对nginx不是特别的熟悉所以该问题是个入门级别的:
? 1 2 3 4 5
java-17-在一个字符串中找到第一个只出现一次的字符
bylijinnan
java
public class FirstShowOnlyOnceElement {
/**Q17.在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b
* 1.int[] count:count[i]表示i对应字符出现的次数
* 2.将26个英文字母映射:a-z <--> 0-25
* 3.假设全部字母都是小写
*/
pu
mongoDB 复制集
开窍的石头
mongodb
mongo的复制集就像mysql的主从数据库,当你往其中的主复制集(primary)写数据的时候,副复制集(secondary)会自动同步主复制集(Primary)的数据,当主复制集挂掉以后其中的一个副复制集会自动成为主复制集。提供服务器的可用性。和防止当机问题
mo
[宇宙与天文]宇宙时代的经济学
comsci
经济
宇宙尺度的交通工具一般都体型巨大,造价高昂。。。。。
在宇宙中进行航行,近程采用反作用力类型的发动机,需要消耗少量矿石燃料,中远程航行要采用量子或者聚变反应堆发动机,进行超空间跳跃,要消耗大量高纯度水晶体能源
以目前地球上国家的经济发展水平来讲,
Git忽略文件
Cwind
git
有很多文件不必使用git管理。例如Eclipse或其他IDE生成的项目文件,编译生成的各种目标或临时文件等。使用git status时,会在Untracked files里面看到这些文件列表,在一次需要添加的文件比较多时(使用git add . / git add -u),会把这些所有的未跟踪文件添加进索引。
==== ==== ==== 一些牢骚
MySQL连接数据库的必须配置
dashuaifu
mysql 连接数据库配置
MySQL连接数据库的必须配置
1.driverClass:com.mysql.jdbc.Driver
2.jdbcUrl:jdbc:mysql://localhost:3306/dbname
3.user:username
4.password:password
其中1是驱动名;2是url,这里的‘dbna
一生要养成的60个习惯
dcj3sjt126com
习惯
一生要养成的60个习惯
第1篇 让你更受大家欢迎的习惯
1 守时,不准时赴约,让别人等,会失去很多机会。
如何做到:
①该起床时就起床,
②养成任何事情都提前15分钟的习惯。
③带本可以随时阅读的书,如果早了就拿出来读读。
④有条理,生活没条理最容易耽误时间。
⑤提前计划:将重要和不重要的事情岔开。
⑥今天就准备好明天要穿的衣服。
⑦按时睡觉,这会让按时起床更容易。
2 注重
[介绍]Yii 是什么
dcj3sjt126com
PHP yii2
Yii 是一个高性能,基于组件的 PHP 框架,用于快速开发现代 Web 应用程序。名字 Yii (读作 易)在中文里有“极致简单与不断演变”两重含义,也可看作 Yes It Is! 的缩写。
Yii 最适合做什么?
Yii 是一个通用的 Web 编程框架,即可以用于开发各种用 PHP 构建的 Web 应用。因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应
Linux SSH常用总结
eksliang
linux ssh SSHD
转载请出自出处:http://eksliang.iteye.com/blog/2186931 一、连接到远程主机
格式:
ssh name@remoteserver
例如:
ssh ickes@192.168.27.211
二、连接到远程主机指定的端口
格式:
ssh name@remoteserver -p 22
例如:
ssh i
快速上传头像到服务端工具类FaceUtil
gundumw100
android
快速迭代用
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOExceptio
jQuery入门之怎么使用
ini
JavaScript html jquery Web css
jQuery的强大我何问起(个人主页:hovertree.com)就不用多说了,那么怎么使用jQuery呢?
首先,下载jquery。下载地址:http://hovertree.com/hvtart/bjae/b8627323101a4994.htm,一个是压缩版本,一个是未压缩版本,如果在开发测试阶段,可以使用未压缩版本,实际应用一般使用压缩版本(min)。然后就在页面上引用。
带filter的hbase查询优化
kane_xie
查询优化 hbase RandomRowFilter
问题描述
hbase scan数据缓慢,server端出现LeaseException。hbase写入缓慢。
问题原因
直接原因是: hbase client端每次和regionserver交互的时候,都会在服务器端生成一个Lease,Lease的有效期由参数hbase.regionserver.lease.period确定。如果hbase scan需
java设计模式-单例模式
men4661273
java 单例 枚举 反射 IOC
单例模式1,饿汉模式
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
//私有的默认构造函数
private Singleton1() {}
//已经自行实例化
private static final Singleton1 singl
mongodb 查询某一天所有信息的3种方法,根据日期查询
qiaolevip
每天进步一点点 学习永无止境 mongodb 纵观千象
// mongodb的查询真让人难以琢磨,就查询单天信息,都需要花费一番功夫才行。
// 第一种方式:
coll.aggregate([
{$project:{sendDate: {$substr: ['$sendTime', 0, 10]}, sendTime: 1, content:1}},
{$match:{sendDate: '2015-
二维数组转换成JSON
tangqi609567707
java 二维数组 json
原文出处:http://blog.csdn.net/springsen/article/details/7833596
public class Demo {
public static void main(String[] args) { String[][] blogL
erlang supervisor
wudixiaotie
erlang
定义supervisor时,如果是监控celuesimple_one_for_one则删除children的时候就用supervisor:terminate_child (SupModuleName, ChildPid),如果shutdown策略选择的是brutal_kill,那么supervisor会调用exit(ChildPid, kill),这样的话如果Child的behavior是gen_