EventBus
EventBus是一个为Android和Java平台设计的发布/订阅 事件总线
EventBus有以下特点:
简化组件之间的通信
将事件发送方和事件接收方解耦
很好的使用于Activitis
、Fragments
和后台线程
避免复杂、易出错的依赖
简化你的代码
快
体积小(~50K jar)
有100,000,000+次相关APP安装,证明EventBus的可靠性
还有其他额外的功能
集成到项目
gradle配置:
implementation 'org.greenrobot:eventbus:3.1.1'
使用(只需3步)
定义事件
public static class MessageEvent { /* 如果有需要,可以声明相应的属性 */ }
声明订阅方法
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {/* Do something */};
需要@Subscribe
注解,默认的threadMode
是ThreadMode.MAIN
注册
和反注册
订阅,比如在Activities
和Fragments
中根据生命周期方法去注册和反注册
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
发送事件
EventBus.getDefault().post(new MessageEvent());
源码解析
register
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
*
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);// -> 1
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);// -> 2
}
}
}
注释: 注册subscriber用来接收事件,当不再希望接收到事件发送时必须要调用unregister
取消注册 subscriber必须包含带Subscribe
注解的方法,Subscribe
注解可配置ThreadMode
和priority
根据sbuscriber
的class去查找带有@subscribe
注解的方法,findSubscriberMethods
方法:
List findSubscriberMethods(Class subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);// -> 3
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {// -> 4
subscriberMethods = findUsingReflection(subscriberClass);// -> 5
} else {
subscriberMethods = findUsingInfo(subscriberClass);// -> 6
}
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;
}
}
先从缓存中去查找该subscriber
中订阅的方法,如果查到,直接返回。 METHOD_CACHE
缓存中的数据是在register
后添加的,但是unregister
的时候并不会清掉,这样当一个subscriber
注册完,然后反注册,下次再注册的时候,不需要再去查找其中订阅的方法。
如果上一步没有查到相应的方法,就会走到这一步,先根据ignoreGeneratedIndex
判断是否忽略索引查找,如果忽略,则执行注释5处的代码,不管你是否添加了index
所有,都利用反射去查找subscriber
中订阅的方法列表;如果不忽略,则执行6处的代码,先从索引index
中去查找,如果没有索引或者索引中没有找到,再考虑利用反射的方式去查找。 关于索引index
的使用后面会提到。
调用findUsingReflection
方法:
private List findUsingReflection(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {// -> 7
findUsingReflectionInSingleClass(findState);// -> 8
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
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");
}
}
}
这里之所以使用while
循环而不用if
判断,是因为除了查找当前subscriber
还需要查找其向上的继承链,findState.moveToSuperclass();
就是用来控制向上查找的,EventBus中所有的继承链查找都是用这种方式,不要迷惑就好。
这里就是调用了findUsingReflectionInSingleClass
去用反射的方式查找subscriber
中订阅的方法列表,代码很简单,就是普通的反射使用,看不懂的问我吧。
反射的逻辑就这些
再回到注释4处的代码继续分析另外一种查找方法列表的方式:
调用findUsingInfo
方法:
private List findUsingInfo(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);// -> 9
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);// -> 10
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
调用getSubscriberInfo
方法:
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
这里有两个分支,第一次register
的时候,第一个分支条件是不会成立的,我们看第二个分支:
这里的subscriberInfoIndexes
就是我们通过EventBus的注解生成的index
索引,然后调用EventBus.builder().addIndex(new MyEventBusIndex()).build();
添加的,这个索引的作用就是把本来运行时通过反射查找订阅方法的逻辑换成了编译器自动生成文件,然后运行时在该文件中直接去查找的方式,从而提高了效率,index
索引的方式后面会介绍。
如果我们没有添加index
索引,那么代码9处得到的findState.subscriberInfo
就是null,代码就会走到注释10处,注释10处的逻辑和上面注释8处的逻辑一样,都是通过反射去查找订阅方法。
到这里,不管通过哪种方式,订阅的方法列表已经找到了,我们回到注释1处接着往下看:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取事件类型
Class eventType = subscriberMethod.eventType;
// 将订阅者和订阅者中的订阅方法打包成一个Subscription对象
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();
// 遍历订阅了该event类型的方法,根据priority把新订阅的方法放到合适的位置
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);
}
}
}
至此,register
流程已经走完。
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);// -> 1
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
调用postSingleEvent
方法:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) { // -> 2
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); // -> 3
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); // -> 4
}
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));
}
}
}
eventInheritance
:该属性可以在EventBus的Build中初始化,默认是true。
true的情况举个例子: A和B是两个Event,并且A extends B X和Y是两个方法,X订阅了事件A,Y订阅了事件B post A事件,同时会post B事件,那么X和Y都会收到事件
fasle的情况就是:post哪个事件,就只会发送这一个事件,不会发送它的父类事件,上面这个例子,如果eventInheritance为false,post A的时候Y就不会收到事件
都调用了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);// -> 5
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
这里就是遍历到该event中的所有订阅方法,挨个给他们发送事件,具体的发送逻辑就是代码5处的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:// 没太看懂和MAIN有啥区别
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);
}
}
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);
}
}
这里就是根据不同的ThreadMode
去执行不同的逻辑了,invokeSubscriber
就是通过反射的方式去调用方法,没什么可说的。
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());
}
}
当unregister
的时候,会把该subscriber
中的订阅方法都从集合中移除掉,同时也会把该subscriber
从相应的集合中移除掉。
索引index
EventBus提供一个注解处理器,可以在编译期把你项目中所有regisger
的class和所有的 @Subscribe
方法给整合起来,生成一个特殊的类,类似下面:
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();// key就是register的对象, value就是该对象中@subscribe的方法集合
putIndex(new SimpleSubscriberInfo(cn.jerry.webviewdemo.FirstActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventArrived", cn.jerry.webviewdemo.CustomEvent.class),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
// 根据class对象获取该对象中@Subscribe注解的方法列表
public SubscriberInfo getSubscriberInfo(Class subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
代码运行的时候,再遇到register
逻辑,会直接从这个索引中查找相应的方法列表,从而避免了相应的反射操作,上面提到的register
流程的注释9 处:
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
// 这里就是从刚刚的index处去查找
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
// 根据相应的class对象,去查找该对象中@subscribe的方法列表
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
你可能感兴趣的:(EventBus源码分析)
MediaPlayer 源码分析
踏雪羽翼
music android 音频 java
MediaPlayer有create(),prepare(),prepareAsync(),setDataSource(),start(),pause(),release()等方法,MediaPlayer代码路径是在frameworks/base/media/java/android/media/MediaPlayer.java,MediaPlayer继承PlayerBase,并且实现了Subti
wpa_supplicant源码理解
追梦-北极星
Wifi wifi
目录1、配置文件:wpa_config和wpa_ssid结构2、wpa_supplicant的目录介绍1、配置文件:wpa_config和wpa_ssid结构wpa_supplicant源码分析--conf配置文件|Winddoing'sNotes[OpenWrtWiki]WirelessModes2、wpa_supplicant的目录介绍玩转「Wi-Fi」系列之wpa_supplicant目录介
【OpenHarmony4.1 之 U-Boot 2024.07源码深度解析】006 - Makefile 编译脚本 逐行深度解析
小馋喵星人
鸿蒙OH-v5.0源码分析之 Uboot+Kernel 部分 U-Boot armv8 Makefile
【OpenHarmony4.1之U-Boot2024.07源码深度解析】006-Makefile编译脚本逐行深度解析系列文章汇总:《【鸿蒙OH-v5.0源码分析之Uboot+Kernel部分】000-文章链接汇总》本文链接:《【OpenHarmony4.1之U-Boot2024.07源码深度解析】006-Makefile编译脚本逐行深度解析》本文开始,我们来分析下U-Boot的Makefile编译
QT核心模块源码剖析:信号与槽机制
QT性能优化QT原理源码QT界面美化
qt qt6.3 qt5 QT教程 c++
QT核心模块源码剖析:信号与槽机制使用AI技术辅助生成QT界面美化视频课程QT性能优化视频课程QT原理与源码分析视频课程QTQMLC++扩展开发视频课程免费QT视频课程您可以看免费1000+个QT技术视频免费QT视频课程QT统计图和QT数据可视化视频免费看免费QT视频课程QT性能优化视频免费看免费QT视频课程QT界面美化视频免费看1QT信号与槽机制概述1.1信号与槽的概念1.1.1信号与槽的概念信
【ElasticSearch】Es 启动流程 初始化流程 源码分析
九师兄
源码 es 启动流程
文章目录1.概述1.1核心类2.主要流程2.1主方法2.1.1关闭过程分析2.2execute方法2.3Bootstrap.init2.4INSTANCE.setup方法2.5创建节点2.5.1创建PluginsService2.5.2创建ThreadPool及ThreadContext实例2.5.3初始化ResourceWatcherService2.5.4创建NodeClient2.5.5创建
Flutter全局路由封装及路由栈维护
爬不上树的小松鼠
填坑 flutter 全局路由 路由栈 路由表
目录1.路由选择2.需要解决的问题2.源码分析1.路由选择Flutter路由跳转方式有如下两种:基本路由Navigator.of(context).push(MaterialPageRoute(builder:(context)=>Detail()));命名式路由Navigator.pushNamed(context,"Detail")我选择了后者,主要原因是符合我之前做RN时的路由习惯,并无其他
【Vue.js 中父子组件通信的最佳实践】
程序员远仔
前端 vue.js javascript 前端框架 前端 html5 css
Vue.js中父子组件通信的最佳实践前言在Vue.js应用开发中,组件通信是构建复杂应用的基础。父子组件通信作为最常见的场景,其实现方式直接影响代码的可维护性和可扩展性。本文将深入探讨Vue.js中父子组件通信的最佳实践,涵盖从基础到高级的各种模式。关键词Vue.js、组件通信、Props、自定义事件、v-model、sync修饰符、作用域插槽、Provide/Inject、EventBus、Vu
Spark源码分析
数据年轮
Spark spark源码 spark 大数据 源码分析
过程描述:1.通过Shell脚本启动Master,Master类继承Actor类,通过ActorySystem创建并启动。2.通过Shell脚本启动Worker,Worker类继承Actor类,通过ActorySystem创建并启动。3.Worker通过Akka或者Netty发送消息向Master注册并汇报自己的资源信息(内存以及CPU核数等),以后就是定时汇报,保持心跳。4.Master接受消息
Ubuntu 下 nginx-1.24.0 源码分析 - NGX_HAVE_GETTIMEZONE 宏
若云止水
nginx 运维
表示当前平台支持通过gettimezone()直接获取时区偏移值(以分钟为单位)该宏用于适配不同操作系统对时区信息获取方式的差异。当NGX_HAVE_GETTIMEZONE被定义时,Nginx会调用ngx_gettimezone()获取时区偏移在Ubuntu环境下,Nginx不会使用ngx_gettimezone(),而是通过tm_gmtoff字段(NGX_HAVE_GMTOFF分支)获取时区偏移
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_max_sockets
若云止水
ubuntu nginx linux
在Nginx的源代码中,ngx_max_sockets全局变量的声明位于os/unix/ngx_os.hexternngx_int_tngx_max_sockets;定义在os/unix/ngx_posix_init.cngx_int_tngx_max_sockets;ngx_max_sockets定义了Nginx能够同时使用的最大Socket文件描述符数量。它确保Nginx不会超过系统或配置允许
kmalloc最大能申请多少内存?
lunhui2016
内存管理 linux 内存管理
1.概述本文主要分析kmalloc接口申请内存的大小情况,用于记录kmalloc分配内存的过程。内核版本:Linux4.92.分析记录针对kmalloc最大能申请多少内存,网上众说纷纭,意见各不相同,因此最终决定自己针对源码分析,记录如下:首先看kmalloc()函数实现,在include/linux/slab.h中,代码如下:#ifdefCONFIG_SLAB/**Thelargestkmall
Spring底层源码分析
sdfreregtrh
Java开发 Spring
视频地址:点击跳转如何快速大家一个spring容器:导入spring依赖org.springframeworkspring-context4.3.7.RELEASE初始化Spring环境(有三种方式,我们这里讲的是基于注解的方式,还有ClassPathXmlApplicationContext通过xml文件启动等)publicclassClient{publicstaticvoidmain(Str
【Flink源码分析】6. Flink1.19源码分析-Flink底层的异步通信
ayt007
Flink源码分析 Flink flink 大数据
6.1PekkoInvocationHandler类仅摘取了FlinkRPC进行通信的时候一段代码,也是异步通信的典型代码。//executeanasynchronouscallfinalCompletableFutureresultFuture=//1.ask发起rpc调用的方法,它返回一个CompletableFuture,表示rpc调用的异步结果ask(rpcInvocation,futur
SpringBoot--自动装配之Import注解以及源码分析
我叫叶湘伦
SpringBoot spring boot java spring
上期我们讲解SpringBoot中使用的Conditional注解以及在自动装配中是如何使用的,这期我们来讲一讲,SpringBoot中的Import注解、官方是如何使用的以及自动装配的源码解析。一、Import注解我们先来看一下这个注解的作用:使用@import导入的类都会被加载到ioc容器中@Import的4种用法导入Bean导入配置类导入ImportSelector实现类,一般用于加载配置文
WebKit和Chrome源码分析
bcbobo21cn
转载 VC++ web前端 chrome webkit 浏览器内核
WebKit内核源代码分析http://blog.sina.com.cn/s/blog_53220cef0100ta1i.html摘要:本系列通过分析WebKit的源代码,试图分析WebKit的内核设计架构,模块之间的关系,分析的时候以Qt的移植为参考,涉及移植的东西不多,主要还是以内核为主。在分析内核的时候,Frame是首当其冲的一个类,本文将分析Frame类的代码。1.描述Frame类是Web
RocketMQ中的NameServer主要数据结构
小园子的小菜
MQ rocketmq java-rocketmq java 中间件
1.前言NameServer是RocketMQ中的一个比较重要的组件,我们这篇博客针对NameSever中包含的组件进行分析,分析一下NameServer中包含的组件以及组件的作用。以前我有一篇博客中rocketMq源码分析之搭建本地环境-CSDN博客,在这篇博客中就简单看了下NameSever中会有两个组件:NamesrvConfig和NettyServerConfig。在这里就不在进行介绍。2
【kafka】kafka的动态配置管理使用和分析
石臻臻的杂货铺
Kafka kafka 运维
该文章可能已过期,已不做勘误并更新,请访问原文地址(持续更新)Kafka中的动态配置源码分析kafka知识图谱:Kafka知识图谱大全kafka管控平台推荐使用滴滴开源的Kafka运维管控平台(戳我呀)更符合国人的操作习惯、更强大的管控能力、更高效的问题定位能力、更便捷的集群运维能力、更专业的资源治理、更友好的运维生态、BliBli视频:石臻臻的杂货铺kafka的动态配置文章目录源码分析1.Bro
基于android2.3.5系统:Android中native层C++单例模式
wang-bob
Android系统
***************************************************************************************************************************作者:EasyWave时间:2013.02.16类别:Android系统源码分析声明:转载,请保留链接注意:如有错误,欢迎指正。这些是我学习的日志文章..
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都可以视为一个组件,
node.js的require()
是小傲雨呀^_^
node.js
2009年,Node.js项目诞生,所有模块一律为CommonJS格式。时至今日,Node.js的模块仓库npmjs.com,已经存放了15万个模块,其中绝大部分都是CommonJS格式。这种格式的核心就是require语句,模块通过它加载。学习Node.js,必学如何使用require语句。本文通过源码分析,详细介绍require语句的内部运行机制,帮你理解Node.js的模块机制。一、requ
深入分析React-Scheduler原理
xiaofeng123aazz
reactjs
关键词:reactreact-schedulerscheduler时间切片任务调度workLoop背景本文所有关于React源码的讨论,基于Reactv17.0.2版本。文章背景工作中一直有在用React相关的技术栈,但却一直没有花时间好好思考一下其底层的运行逻辑,碰巧身边的小伙伴们也有类似的打算,所以决定组团卷一波,对React本身探个究竟。本文是基于众多的源码分析文章,加入自己的理解,然后输出
QML音视频架构与设计
QT性能优化QT原理源码QT界面美化
qt qt6.3 qt5 QT教程 c++
QML音视频架构与设计使用AI技术辅助生成QT界面美化视频课程QT性能优化视频课程QT原理与源码分析视频课程QTQMLC++扩展开发视频课程免费QT视频课程您可以看免费1000+个QT技术视频免费QT视频课程QT统计图和QT数据可视化视频免费看免费QT视频课程QT性能优化视频免费看免费QT视频课程QT界面美化视频免费看1QML与音视频处理概述1.1QML音视频处理概念1.1.1QML音视频处理概念
顺序表ArrayList源码分析
汤坤Sunshine
Java基础 面试 java ArrayList
顺序表中的ArrayList源码新增元素add(intindex,Eelement)的时候,用到了位运算右移一位,此时扩容后的数组大小是原来的1.5倍(n+0.5n=1.5n),代码如下:intnewCapacity=oldCapacity+(oldCapacity>>1);然后用到了如下代码来将旧的数组中的元素移动到扩容后的数组中去:elementData=Arrays.copyOf(eleme
20250108慧能科技前端面试
uperficialyu
前端面试实战整理 前端
目录ajax怎么取消请求移动端怎么实现px尺寸vite和webpack的区别设计模式讲一下什么是原型链讲一下什么是闭包实现eventbus事件循环项目发布后,如何对项目进行优化,怎么优化vue2的diff算法和vue3的diff算法的区别1.ajax怎么取消请求原生JavaScript(XMLHttpRequest)创建XMLHttpRequest对象发送请求,通过调用其abort()方法来取消请
ThreadLocal原理和使用场景
Happytoo_
java java 开发语言 ThreadLocal
简介ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的成员变量都是线程不安全的。因为他可能被多个线程同时修改,此变量对于多个线程之间彼此并不独立,是共享变量。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程无法访问和修改。也就是说:将线程公有化变成线程私有化(空间换时间)。核心源码分析注意:ThreadLocal通常都定义为static,ThreadLo
Android Audio基础(20)——AudioTrack音频控制
yyc_audio
音频 android 音视频 驱动开发
AudioTrack是应用用于播放音频数据的类,可以使用set方法设置音频参数,然后使用start来启动播放。而pause和stop则是用于控制播放过程的方法。不管APP使用什么接口播放音频文件,最终都是由AudioTrack实现。一、源码分析1、主要方法构造函数:创建AudioTrack时调用。play():播放音频文件。pause():暂停播放音频文件。stop():停止播放音频文件。flus
VSCode源码分析参考资料
AI时代的程序员
vscode ide 编辑器
VSCodeArchitectureAnalysis-ElectronProjectCross-PlatformBestPractices中文版VSCode架构分析-Electron项目跨平台最佳实践SihanLi博客上的vscode源码分析系列:分析了微服务架构、事件体系、资源管理、配置系统等文召博客上的vscode源码解析系列文章https://github.com/fzxa/VSCode-s
apollo-client源码分析
肥牛火锅
Java Spring java 开发语言
文章目录Apollo-Client1、基础客户端1.1、客户端获取配置流程1.1.1、初始化1.1.2、获取配置1.1.3、感知远程配置更新1.1.4、ConfigRepository监听器更新数据2、远程服务端设置3、Spring集成3.1、Spring初始化3.2、Config监听器更新数据Apollo-Client1、基础客户端Configconfig=ConfigService.getAp
Ubuntu 下 nginx-1.24.0 源码分析 ngx_debug_init();
若云止水
nginx 运维
目录ngx_debug_init()函数:NGX_LINUX的定义:ngx_debug_init()函数:ngx_debug_init()函数定义在src\os\unix目录下的ngx_linux_config.h中#definengx_debug_init()也就是说这个环境下的main函数中的ngx_debug_init()这行代码其实什么都没有做在nginx.c的开头引入了#include而
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_strerror_init()函数
若云止水
nginx 运维
目录ngx_strerror_init()函数声明ngx_int_t类型声明定义intptr_t类型ngx_strerror_init()函数实现NGX_HAVE_STRERRORDESC_NPngx_strerror_init()函数声明在nginx.c的开头引入了:#include在ngx_core.h中引入了#include在ngx_errno.h这个文件中声明了ngx_strerror_i
集合框架
天子之骄
java 数据结构 集合框架
集合框架
集合框架可以理解为一个容器,该容器主要指映射(map)、集合(set)、数组(array)和列表(list)等抽象数据结构。
从本质上来说,Java集合框架的主要组成是用来操作对象的接口。不同接口描述不同的数据类型。
简单介绍:
Collection接口是最基本的接口,它定义了List和Set,List又定义了LinkLi
Table Driven(表驱动)方法实例
bijian1013
java enum Table Driven 表驱动
实例一:
/**
* 驾驶人年龄段
* 保险行业,会对驾驶人的年龄做年龄段的区分判断
* 驾驶人年龄段:01-[18,25);02-[25,30);03-[30-35);04-[35,40);05-[40,45);06-[45,50);07-[50-55);08-[55,+∞)
*/
public class AgePeriodTest {
//if...el
Jquery 总结
cuishikuan
java jquery Ajax Web jquery方法
1.$.trim方法用于移除字符串头部和尾部多余的空格。如:$.trim(' Hello ') // Hello2.$.contains方法返回一个布尔值,表示某个DOM元素(第二个参数)是否为另一个DOM元素(第一个参数)的下级元素。如:$.contains(document.documentElement, document.body); 3.$
面向对象概念的提出
麦田的设计者
java 面向对象 面向过程
面向对象中,一切都是由对象展开的,组织代码,封装数据。
在台湾面向对象被翻译为了面向物件编程,这充分说明了,这种编程强调实体。
下面就结合编程语言的发展史,聊一聊面向过程和面向对象。
c语言由贝尔实
linux网口绑定
被触发
linux
刚在一台IBM Xserver服务器上装了RedHat Linux Enterprise AS 4,为了提高网络的可靠性配置双网卡绑定。
一、环境描述
我的RedHat Linux Enterprise AS 4安装双口的Intel千兆网卡,通过ifconfig -a命令看到eth0和eth1两张网卡。
二、双网卡绑定步骤:
2.1 修改/etc/sysconfig/network
XML基础语法
肆无忌惮_
xml
一、什么是XML?
XML全称是Extensible Markup Language,可扩展标记语言。很类似HTML。XML的目的是传输数据而非显示数据。XML的标签没有被预定义,你需要自行定义标签。XML被设计为具有自我描述性。是W3C的推荐标准。
二、为什么学习XML?
用来解决程序间数据传输的格式问题
做配置文件
充当小型数据库
三、XML与HTM
为网页添加自己喜欢的字体
知了ing
字体 秒表 css
@font-face {
font-family: miaobiao;//定义字体名字
font-style: normal;
font-weight: 400;
src: url('font/DS-DIGI-e.eot');//字体文件
}
使用:
<label style="font-size:18px;font-famil
redis范围查询应用-查找IP所在城市
矮蛋蛋
redis
原文地址:
http://www.tuicool.com/articles/BrURbqV
需求
根据IP找到对应的城市
原来的解决方案
oracle表(ip_country):
查询IP对应的城市:
1.把a.b.c.d这样格式的IP转为一个数字,例如为把210.21.224.34转为3524648994
2. select city from ip_
输入两个整数, 计算百分比
alleni123
java
public static String getPercent(int x, int total){
double result=(x*1.0)/(total*1.0);
System.out.println(result);
DecimalFormat df1=new DecimalFormat("0.0000%");
百合——————>怎么学习计算机语言
百合不是茶
java 移动开发
对于一个从没有接触过计算机语言的人来说,一上来就学面向对象,就算是心里上面接受的了,灵魂我觉得也应该是跟不上的,学不好是很正常的现象,计算机语言老师讲的再多,你在课堂上面跟着老师听的再多,我觉得你应该还是学不会的,最主要的原因是你根本没有想过该怎么来学习计算机编程语言,记得大一的时候金山网络公司在湖大招聘我们学校一个才来大学几天的被金山网络录取,一个刚到大学的就能够去和
linux下tomcat开机自启动
bijian1013
tomcat
方法一:
修改Tomcat/bin/startup.sh 为:
export JAVA_HOME=/home/java1.6.0_27
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:.
export PATH=$JAVA_HOME/bin:$PATH
export CATALINA_H
spring aop实例
bijian1013
java spring AOP
1.AdviceMethods.java
package com.bijian.study.spring.aop.schema;
public class AdviceMethods {
public void preGreeting() {
System.out.println("--how are you!--");
}
}
2.beans.x
[Gson八]GsonBuilder序列化和反序列化选项enableComplexMapKeySerialization
bit1129
serialization
enableComplexMapKeySerialization配置项的含义
Gson在序列化Map时,默认情况下,是调用Key的toString方法得到它的JSON字符串的Key,对于简单类型和字符串类型,这没有问题,但是对于复杂数据对象,如果对象没有覆写toString方法,那么默认的toString方法将得到这个对象的Hash地址。
GsonBuilder用于
【Spark九十一】Spark Streaming整合Kafka一些值得关注的问题
bit1129
Stream
包括Spark Streaming在内的实时计算数据可靠性指的是三种级别:
1. At most once,数据最多只能接受一次,有可能接收不到
2. At least once, 数据至少接受一次,有可能重复接收
3. Exactly once 数据保证被处理并且只被处理一次,
具体的多读几遍http://spark.apache.org/docs/lates
shell脚本批量检测端口是否被占用脚本
ronin47
#!/bin/bash
cat ports |while read line
do#nc -z -w 10 $line
nc -z -w 2 $line 58422>/dev/null2>&1if[ $?-eq 0]then
echo $line:ok
else
echo $line:fail
fi
done
这里的ports 既可以是文件
java-2.设计包含min函数的栈
bylijinnan
java
具体思路参见:http://zhedahht.blog.163.com/blog/static/25411174200712895228171/
import java.util.ArrayList;
import java.util.List;
public class MinStack {
//maybe we can use origin array rathe
Netty源码学习-ChannelHandler
bylijinnan
java netty
一般来说,“有状态”的ChannelHandler不应该是“共享”的,“无状态”的ChannelHandler则可“共享”
例如ObjectEncoder是“共享”的, 但 ObjectDecoder 不是
因为每一次调用decode方法时,可能数据未接收完全(incomplete),
它与上一次decode时接收到的数据“累计”起来才有可能是完整的数据,是“有状态”的
p
java生成随机数
cngolon
java
方法一:
/**
* 生成随机数
* @author cngolon@126.com
* @return
*/
public synchronized static String getChargeSequenceNum(String pre){
StringBuffer sequenceNum = new StringBuffer();
Date dateTime = new D
POI读写海量数据
ctrain
海量数据
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming
mysql 日期格式化date_format详细使用
daizj
mysql date_format 日期格式转换 日期格式化
日期转换函数的详细使用说明
DATE_FORMAT(date,format) Formats the date value according to the format string. The following specifiers may be used in the format string. The&n
一个程序员分享8年的开发经验
dcj3sjt126com
程序员
在中国有很多人都认为IT行为是吃青春饭的,如果过了30岁就很难有机会再发展下去!其实现实并不是这样子的,在下从事.NET及JAVA方面的开发的也有8年的时间了,在这里在下想凭借自己的亲身经历,与大家一起探讨一下。
明确入行的目的
很多人干IT这一行都冲着“收入高”这一点的,因为只要学会一点HTML, DIV+CSS,要做一个页面开发人员并不是一件难事,而且做一个页面开发人员更容
android欢迎界面淡入淡出效果
dcj3sjt126com
android
很多Android应用一开始都会有一个欢迎界面,淡入淡出效果也是用得非常多的,下面来实现一下。
主要代码如下:
package com.myaibang.activity;
import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.os.CountDown
linux 复习笔记之常见压缩命令
eksliang
tar解压 linux系统常见压缩命令 linux压缩命令 tar压缩
转载请出自出处:http://eksliang.iteye.com/blog/2109693
linux中常见压缩文件的拓展名
*.gz gzip程序压缩的文件
*.bz2 bzip程序压缩的文件
*.tar tar程序打包的数据,没有经过压缩
*.tar.gz tar程序打包后,并经过gzip程序压缩
*.tar.bz2 tar程序打包后,并经过bzip程序压缩
*.zi
Android 应用程序发送shell命令
gqdy365
android
项目中需要直接在APP中通过发送shell指令来控制lcd灯,其实按理说应该是方案公司在调好lcd灯驱动之后直接通过service送接口上来给APP,APP调用就可以控制了,这是正规流程,但我们项目的方案商用的mtk方案,方案公司又没人会改,只调好了驱动,让应用程序自己实现灯的控制,这不蛋疼嘛!!!!
发就发吧!
一、关于shell指令:
我们知道,shell指令是Linux里面带的
java 无损读取文本文件
hw1287789687
读取文件 无损读取 读取文本文件 charset
java 如何无损读取文本文件呢?
以下是有损的
@Deprecated
public static String getFullContent(File file, String charset) {
BufferedReader reader = null;
if (!file.exists()) {
System.out.println("getFull
Firebase 相关文章索引
justjavac
firebase
Awesome Firebase
最近谷歌收购Firebase的新闻又将Firebase拉入了人们的视野,于是我做了这个 github 项目。
Firebase 是一个数据同步的云服务,不同于 Dropbox 的「文件」,Firebase 同步的是「数据」,服务对象是网站开发者,帮助他们开发具有「实时」(Real-Time)特性的应用。
开发者只需引用一个 API 库文件就可以使用标准 RE
C++学习重点
lx.asymmetric
C++ 笔记
1.c++面向对象的三个特性:封装性,继承性以及多态性。
2.标识符的命名规则:由字母和下划线开头,同时由字母、数字或下划线组成;不能与系统关键字重名。
3.c++语言常量包括整型常量、浮点型常量、布尔常量、字符型常量和字符串性常量。
4.运算符按其功能开以分为六类:算术运算符、位运算符、关系运算符、逻辑运算符、赋值运算符和条件运算符。
&n
java bean和xml相互转换
q821424508
java bean xml xml和bean转换 java bean和xml转换
这几天在做微信公众号
做的过程中想找个java bean转xml的工具,找了几个用着不知道是配置不好还是怎么回事,都会有一些问题,
然后脑子一热谢了一个javabean和xml的转换的工具里,自己用着还行,虽然有一些约束吧 ,
还是贴出来记录一下
顺便你提一下下,这个转换工具支持属性为集合、数组和非基本属性的对象。
packag
C 语言初级 位运算
1140566087
位运算 c
第十章 位运算 1、位运算对象只能是整形或字符型数据,在VC6.0中int型数据占4个字节 2、位运算符: 运算符 作用 ~ 按位求反 << 左移 >> 右移 & 按位与 ^ 按位异或 | 按位或 他们的优先级从高到低; 3、位运算符的运算功能: a、按位取反: ~01001101 = 101
14点睛Spring4.1-脚本编程
wiselyman
spring4
14.1 Scripting脚本编程
脚本语言和java这类静态的语言的主要区别是:脚本语言无需编译,源码直接可运行;
如果我们经常需要修改的某些代码,每一次我们至少要进行编译,打包,重新部署的操作,步骤相当麻烦;
如果我们的应用不允许重启,这在现实的情况中也是很常见的;
在spring中使用脚本编程给上述的应用场景提供了解决方案,即动态加载bean;
spring支持脚本