差不多两年没写博客了,最近想着要找工作了,打算复习下一些常用的开源库,也是这篇博客的由来~
EventBus使用非常简单 参考:github 再贴一张官网的图
一、示例代码
示例代码是为了便于理解后面注解处理器生成代码的处理流程
public class TestRunnerActivity extends Activity {
private EventBus eventBus;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_runtests);
eventBus = new EventBus();
eventBus.register(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(TestFinishedEvent event) {
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onEventBackgroundThread(TestEvent event) {
}
public void onDestroy() {
eventBus.unregister(this);
super.onDestroy();
}
}
二、EventBus#register流程
调用register进行订阅 ,参数this代表订阅者
public void register(Object subscriber) {
//获取订阅者的class类型
Class subscriberClass = subscriber.getClass();
//获取subscriber所有的订阅方法
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//对订阅类中的每个订阅方法进行订阅
subscribe(subscriber, subscriberMethod);
}
}
}
首先调用了findSubscriberMethods获取了当前订阅者类里的所有订阅方法。
List findSubscriberMethods(Class subscriberClass) {
//如果缓存有,直接返回。 METHOD_CACHE类型是Map, List>
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex默认是false
if (ignoreGeneratedIndex) {
//方式1:通过反射获取到所有的订阅方法
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//方式2:通过注解处理器生成的代码来获取所有的订阅方法
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;
}
}
可以看到有两种方式获取订阅方法,一种是利用反射,另一种是利用注解处理器生成的代码。很多开源库都利用了注解生成器,通过它在编译时生成代码可以很好的帮助我们减少反射代码的调用。
先看看如何通过反射获取到所有的订阅方法
private List findUsingReflection(Class subscriberClass) {
//从对象缓存池获取一个FindState,这只是一个工具对象
FindState findState = prepareFindState();
//给findState成员变量subscriberClass 、class赋值为subscriberClass
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//通过反射去找
findUsingReflectionInSingleClass(findState);
//继续找父类中的所有方法
findState.moveToSuperclass();
}
//返回找到的List,回收FindState
return getMethodsAndRelease(findState);
}
反射的核心逻辑在findUsingReflectionInSingleClass方法,该方法通过反射查找class的所有方法,然后遍历这些方法找到带@Subscribe的方法,然后将查询的结果保存到findState.subscriberMethods中
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
//getDeclaredMethod()获取的是类自身声明的所有方法,包含public、protected和private方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
...
}
//遍历所有方法,找到带Subscribe注解的方法 比如:@Subscribe(threadMode = ThreadMode.MAIN)
for (Method method : methods) {
int modifiers = method.getModifiers();
//方法是public的且不是 abstract | static 类的
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取方法的参数类型,对于订阅方法来说 参数就是事件类型
Class[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
//获取到方法上的Subscribe注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//获取事件类型
Class eventType = parameterTypes[0];
//检查method&eventType对于的订阅方法是否已经添加过了
if (findState.checkAdd(method, eventType)) {
//获取方法的线程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
//最终将找到的结果 封装到SubscriberMethod里面,然后保存到findState.subscriberMethods中
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
}
}
}
}
前面的findUsingReflection方法先通过反射找到订阅类里的所有订阅方法,然后保存到findState.subscriberMethods中,接着调用getMethodsAndRelease(findState),将 List< SubscriberMethod>返回,然后回收FindState到对象池
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;
}
Ok,现在我们已经通过反射获取到订阅类中的所有订阅方法了,并且将它们封装到List< SubscriberMethod>中了,我们先不管注解处理器获取List< SubscriberMethod>的逻辑,继续看register方法,获取到List< SubscriberMethod>后开始遍历List< SubscriberMethod>,对每个SubscriberMethod调用subscribe
subscribe主要干了两件事
1、将订阅方法和订阅者封装到Subscription中,然后为每个事件类型创建一个CopyOnWriteArrayList列表subscriptions,并将Subscription插入到subscriptions中。事件类型就是我们post的事件的class对象
2、对粘性事件进行处理
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取事件类型
Class eventType = subscriberMethod.eventType;
//将订阅者和订阅方法封装到Subscription中
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//通过事件类型找到Subscription集合
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//如果还没有订阅类 订阅了该事件的方法,就创建一个list,把当前的Subscription放进去
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中
subscribedEvents.add(eventType);
//下面这个逻辑可以先不看 后面可以结合postSticky流程看
//如果订阅方法支持粘性事件
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
//遍历stickyEvents map 这个map保存了粘性事件
for (Map.Entry, Object> entry : entries) {
Class candidateEventType = entry.getKey();
//如果当前订阅方法订阅的事件列席在stickyEvents中已经有了,就说明之前某个地方发送过该粘性事件
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
//检查发送粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
//检查发送粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
走到这里我们的注册逻辑基本就结束了,注册的结果就是得到subscriptionsByEventType和subscribedEvents,
1、subscriptionsByEventType是一个map,其中 key是事件类型,value是一个集合,保存了该事件的所有订阅(Subscription)。
2、typesBySubscriber也是一个map,其中 key是订阅者,value是一个集合,保存了该订阅者订阅的所有事件。
注册的流程走完了再来看看如何通过注解处理器生成的代码获取SubscriberMethod列表。 下面这个类MyEventBusIndex是注解处理器生成的
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;
static {
key 订阅者类 value 订阅信息
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();
//...
//将订阅信息保存到SUBSCRIBER_INDEX
putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventMainThread", TestFinishedEvent.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onEventBackgroundThread", TestEvent.class, ThreadMode.BACKGROUND),
}));
}
private static void putIndex(SubscriberInfo info) {
//保存所有订阅类的订阅信息
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
SimpleSubscriberInfo定义如下:
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {
private final SubscriberMethodInfo[] methodInfos;
public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {
super(subscriberClass, null, shouldCheckSuperclass);
this.methodInfos = methodInfos;
}
@Override
public synchronized SubscriberMethod[] getSubscriberMethods() {
int length = methodInfos.length;
SubscriberMethod[] methods = new SubscriberMethod[length];
for (int i = 0; i < length; i++) {
SubscriberMethodInfo info = methodInfos[i];
methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
info.priority, info.sticky);
}
return methods;
}
}
MyEventBusIndex类对象会被保存到EventBus的SubscriberMethodFinder中subscriberInfoIndexes成员列表中
private List findUsingInfo(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//找到index类中的订阅信息 ,其实就是调用上面MyEventBusIndex的getSubscriberInfo获取订阅类的SubscriberInfo
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
//获取SimpleSubscriberInfo的getSubscriberMethods方法获取订阅信息
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
//如果没有添加过,就添加订阅方法
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//如果index里没有,就通过反射来查找订阅类中的订阅方法
findUsingReflectionInSingleClass(findState);
}
//继续找父类的
findState.moveToSuperclass();
}
//返回订阅方法列表
return getMethodsAndRelease(findState);
}
ok到现在为止,注册的逻辑终于结束了。
三、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());
}
}
最终是通过事件类型来解除该订阅者的所有注册
private void unsubscribeByEventType(Object subscriber, Class eventType) {
//获取所有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) {
//将active 状态改为 false
subscription.active = false;
//移除订阅关系
subscriptions.remove(i);
i--;
size--;
}
}
}
}
四、EventBus#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;
try {
while (!eventQueue.isEmpty()) {
//遍历所有的事件,进行发送
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
然后调用postSingleEvent进行单个event的发送
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class eventClass = event.getClass();
boolean subscriptionFound = false;
//是否考虑父类的事件类型
if (eventInheritance) {
//遍历获取当前的event类以及其父类的类型
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);
}
}
然后走到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 {
//遍历所有的Subscription 发送事件
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;
}
subscriptionsByEventType这个map 在分析register流程的时候分析过 它的key是事件类型,value是该事件的所有订阅(Subscription)。
接着遍历所有的Subscription调用postToSubscription方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING://发送方在哪里线程,订阅方法就在哪个方法被调用
//直接调用invokeSubscriber,关于invokeSubscriber后面会说
invokeSubscriber(subscription, event);
break;
case MAIN://只在主线程调用订阅方法
if (isMainThread) {
//如果当前是在主线程直接调用invokeSubscriber
invokeSubscriber(subscription, event);
} else {
//如果不在主线程,利用handler机制切到主线程再调用invokeSubscriber
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) {
//如果在主线程就切到子线程后调用invokeSubscriber
backgroundPoster.enqueue(subscription, event);
} else {
//如果在子线程直接调用invokeSubscriber
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
//利用线程池执行invokeSubscriber方法
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
不管是在子线程还是在主线程最终都是通过invokeSubscriber方法,利用反射调用订阅对象的订阅方法
void invokeSubscriber(Subscription subscription, Object event) {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
}
五、EventBus#postSticky粘性事件发送流程
public void postSticky(Object event) {
//粘性事件会被保存到stickyEvents中
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
//正常发送事件
post(event);
}
和普通事件不同之处在于粘性事件会被保存到stickyEvents中,这样即使在我们订阅之前已经发送了某个粘性事件,在我们订阅的时候也能收到,还记得订阅流程中有这样几行代码吗?
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//...
//如果订阅方法支持粘性事件
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
//遍历stickyEvents map 这个map保存了粘性事件
for (Map.Entry, Object> entry : entries) {
Class candidateEventType = entry.getKey();
//如果当前订阅方法订阅的事件列席在stickyEvents中已经有了,就说明之前某个地方发送过该粘性事件
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
//检查发送粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
//检查发送粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
到这里EventBus的源码基本就分析完了,是不是很简单?
你可能感兴趣的:(Android源码相关,android,studio)
Android 网络框架之okhttp源码解析
码中之牛
移动开发 Android 开源框架 android kotlin 开发语言 移动开发 网络框架
okhttp使用okhttp则分为Request请求与response响应。request请求体:每一个HTTP请求中都应该包含一个URL,一个GET或POST方法以及Header或其他参数,当然还可以含特定内容类型的数据流。response响应码:响应则包含一个回复代码(200代表成功,404代表未找到),Header和定制可选的body。封装的okhttp库与okhttp使用:blog.csd
[网络安全自学篇] 一.入门笔记之看雪Web安全学习及异或解密示例
鱼馬
网络 网络安全 web安全 笔记 测试工具 职场和发展
最近开始学习网络安全相关知识,接触了好多新术语,感觉自己要学习的东西太多,真是学无止境,也发现了好几个默默无闻写着博客、做着开源的大神。准备好好学习下新知识,并分享些博客与博友们一起进步,加油。非常基础的文章,大神请飘过,谢谢各位看官!文章目录一.工具&术语1.网安术语2.常用工具3.推荐文章二.常见攻击1.SQL注入2.XSS跨站3.越权漏洞4.CSRF跨站请求伪造5.支付漏洞三.音乐异或解密示
JetBrains IDEs和Visual Studio Code的对比
ZhangJiQun&MXP
2021 论文 2021 AI python 教学 vscode ide 编辑器
JetBrainsIDEs和VisualStudioCode的对比JetBrainsIDEs是捷克JetBrains公司开发的一系列集成开发环境(IDE)。以下是具体介绍:IntelliJIDEA是JetBrains公司的一款产品主要产品IntelliJIDEA:一款功能强大且广泛应用的Java集成开发环境,有开源免费的社区版和商业收费的终极版。社区版可开发Java桌面和Android应用,终极版
EOS开发推荐VS Code和CLion做IDE工具
落叶无声9
以太坊 EOS CLion VS code IDE
每一个开发人员都需要一个良好的IDE,EOS开发也是一样,为项目开发过程构建一个良好的IDE环境是第一步。这就是为什么我们要写这个如何使用VSCode或者CLion进行EOS开发的快速教程的原因。我们还为VSCode创建了一些脚本,这些脚本将你在终端中使用的一些命令自动化。设置VisualStudioCode首先,如果你还没有这些VSCode扩展的话,安装一下。对于EOSDapp开发,它们将非常有
第一篇:CTF入门指南:了解CTF的基本概念与比赛形式
菜腿承希
零基础小白入门CTF python java 网络安全 前端
#零基础小白入门CTF解题到成为CTF大佬系列文章##引言CTF(CaptureTheFlag)是一种网络安全竞赛,参赛者需要通过解决各种安全相关的题目来获取“Flag”,从而得分。CTF题目通常涵盖密码学、逆向工程、漏洞利用、Web安全等多个领域。本系列文章将从零基础开始,逐步带你了解CTF的各个知识点,最终帮助你成为一名CTF大佬。##文章目录1.**CTF入门指南:了解CTF的基本概念与比赛
面试必备!HR面常问的20个问题及高分回答秘诀
江-小北
面试 职场和发展
HR面试一般会花大约20分钟,主要会问一些个人情况、处理事情的方法、工作经验、成长经历等相关问题。当你到了HR面,基本上就代表你的面试已经通过了一大半了。不过,还是不要掉以轻心,HR面还是有可能会挂掉。可能是因为没剩什么职位了,或者你在HR面时说了一些不太合适的话。我根据自己的一些面试经历和网上的经验,整理了20个常见的HR面试问题。分享给大家,也欢迎大家在评论区补充自己遇到的HR问题。⭐手里有几
展开说说:Android之View基础知识解析
老梁学Android&HarmonyOS
# View android
View虽不属于Android四代组件,但应用程度却非常非常广泛。在Android客户端,君所见之处皆是View。我们看到的Button、ImageView、TextView等等可视化的控件都是View,ViewGroup是View的子类因此它也是View。但是现在我们把View和ViewGroup当成两个类来看待,ViewGroup可以容纳View和ViewGroup,但View不可以再容纳其他
测试江湖:为什么大多数人宁愿吃生活的苦,也不愿意吃学习的苦
爱吃 香菜
软件测试 自动化测试 职场经验 学习 软件测试 自动化测试 测试工具 职场经验 程序员 功能测试
面试求职:「面试试题小程序」,内容涵盖测试基础、Linux操作系统、MySQL数据库、Web功能测试、接口测试、APPium移动端测试、Python知识、Selenium自动化测试相关、性能测试、性能测试、计算机网络知识、Jmeter、HR面试,命中率杠杠的。(大家刷起来…)职场经验干货:软件测试工程师简历上如何编写个人信息(一周8个面试)软件测试工程师简历上如何编写专业技能(一周8个面试)软件测
Android TCP封装工具类
tangweiguo03051987
android tcp/ip 网络协议
TCP通信的封装,我们可以从以下几个方面进行改进:线程池优化:使用更高效的线程池配置,避免频繁创建和销毁线程。连接重试机制:在网络不稳定时,自动重试连接。心跳机制:保持长连接,避免因超时断开。数据缓冲区优化:动态调整缓冲区大小,适应不同数据量。异常处理增强:区分不同类型的异常,提供更详细的错误信息。代码简洁性:减少冗余代码,提高可读性和可维护性。TCP客户端封装(Java)importandroi
Android打造易用的 WiFi 工具类:WifiUtils 封装实践
tangweiguo03051987
android java wifi
Android在全局范围内使用WifiUtils工具类,我们可以将其设计为一个单例,并通过Application类进行初始化。这样可以确保在整个应用程序中只有一个WifiUtils实例,并且可以在任何地方访问它。以下是实现全局使用的步骤和代码示例:记得在AndroidManifest.xml配置文件中配置权限:1.创建自定义Application类首先,创建一个自定义的Application类,用
美发行业的现状与未来趋势
shboka920702
大数据 生活
美发行业作为美容产业的重要组成部分,近年来发展迅速。随着消费者对个性化服务的需求不断增加,美发行业也在不断变革和创新。然而,行业在快速发展的同时,也面临着诸多挑战和痛点。本文将探讨美发行业的现状、痛点以及未来的发展趋势,并展望如何通过技术手段提升行业效率。根据相关数据显示,全球美发行业市场规模在2022年已达到约1000亿美元,预计到2027年将增长至1500亿美元。中国作为全球第二大美发市场,市
编程语言乱码问题(以Visual Studio Code为例)
博客路人甲
vscode ide visual studio code
在很多时候,我们在写代码或者运行代码时会遇到乱码问题,有时候是因为我们系统本身编码问题,这时候需要我们在“设置”->“时间与日期”中设置语言编码格式;但更多的时候是因为我们脚本文件编码的问题。当我们脚本文件编码出了问题后,我们的编辑窗口以及输出窗口都会遇到乱码问题,这时候就需要我们修改文件编码。1.当编辑窗口出现乱码时,我们需要重新以另一种编码格式打开文件。以vscode为例右下角有一个编码格式,
Visual Studio vs Visual Studio Code
小俊学长
visual studio vscode 产品运营
VisualStudiovsVisualStudioCode:深入探索两款开发工具的异同在软件开发领域,选择合适的开发工具对于提高开发效率、保障项目质量至关重要。微软作为行业巨头,其旗下的VisualStudio(VS)和VisualStudioCode(VSCode)两款开发工具备受开发者青睐。本文将从多个维度深入比较VS与VSCode,帮助开发者根据自身需求做出明智的选择。一、定位与基本特性V
在Fedora上安装MySQL
ZaxfSass
mysql adb 数据库
MySQL是一种流行的开源关系型数据库管理系统,被广泛用于各种应用程序和网站。在Fedora操作系统上安装MySQL非常简单,本文将向您展示安装MySQL的步骤和相关源代码。步骤1:更新系统在安装MySQL之前,首先需要更新您的Fedora系统以确保您拥有最新的软件包和安全补丁。打开终端并执行以下命令:sudodnfupdate步骤2:安装MySQL服务器现在,我们可以使用DNF包管理器在Fedo
Visual Studio Code(VS Code)支持的编程语言
计算机辅助工程
vscode
JavaScript:VSCode原生支持JavaScript,提供语法高亮、代码折叠、自动补全等功能。推荐使用ESLint和Prettier进行代码格式化和错误检查。TypeScript:作为JavaScript的超集,TypeScript在VSCode中也得到原生支持,提供类似的编辑功能。Python:通过安装Python扩展,VSCode支持Python编程,提供调试、Int
Django-ORM-select_related
巴啦啦小魔仙变身
Python Django django 数据库 sqlite python
Django-ORM-select_related作用使用场景示例无select_related的查询有select_related的查询如何理解"只发起一次查询,包含所有相关作者信息"1.select_related的工作原理2.具体示例解析3.为什么只发起一次查询数据库中的books量巨大,使用`select_related`导致服务崩掉,如何解决程序层面优化1.优化select_relate
深度学习复习笔记(6)线性回归——新冠预测项目
Kriol
深度学习初学 深度学习 笔记 线性回归
importmatplotlib.pyplotaspltimporttorch#框架importnumpyasnp#矩阵处理importcsv#读excel文件fromtorch.utils.dataimportDataLoader,Dataset#两个与数据处理相关的包,类Datasetimporttorch.nnasnn#类nn.Module需要用,损失函数需要用fromtorchimport
Flutter 加载网络图片之:FadeInImage + Image.network
zeqiao
Flutter flutter FadeInImage Image.network gif 回调
想实现的效果:1、通过网络url加载图片(支持gif)2、加载之前使用默认占位图3、加载出现问题后要做兜底处理其实可以通过先将图片资源加载到本地文件中,然后读出来设置,虽然要绕一圈,但不失为一个好方案。但是,为了熟悉Flutter的图片相关知识,还是采用官方提供的API来试着实现。下面是最终方案:FadeInImage(image:_addImageLoadListener(imageUrl),/
Spring Cloud相关面试题
努力的搬砖人.
java 经验分享 后端 spring 笔记
以下是150道SpringCloud相关面试题:SpringCloud基础概念1.SpringCloud是什么?SpringCloud是一个基于SpringBoot构建的微服务开发工具集,用于简化分布式系统开发,提供服务注册与发现、配置管理、全链路监控等功能。2.SpringCloud的核心组件有哪些?•Eureka:服务注册与发现。•Ribbon:客户端负载均衡。•Feign:声明式REST客户
Spring中的依赖注入、setter与构造器注入、自动装配与集合注入详细描述及使用(XML版中篇)
KJ.JK
JavaEE进阶教程系列 Spring中的依赖注入 setter与构造器注入 集合注入 Spring的自动装配 Spring的四种依赖注入
文章目录更多相关知识一、setter注入和构造器注入setter注入—引用类型⭐代码演示setter注入—简单类型(八种基本数据类型+String)⭐代码演示构造器注入—引用类型⭐代码演示构造器注入—简单类型⭐代码演示构造器注入—参数适配⭐type属性参数适配代码演示⭐index属性参数适配代码演示⭕setter注入和构造器注入总结二、自动装配自动装配介绍⭐byType装配演示⚡byType装配注
Native 崩溃解析工具
JT-999
Android python
NDKToolsLibrary一个Python工具库,用于简化AndroidNDK崩溃分析。该库通过封装NDK工具来简化操作,支持解析.dmp文件和logcat崩溃日志,并支持灵活配置参数。支持多平台(Linux、Windows、macOS),并提供Shell和Batch脚本便于使用。功能特性解析.dmp文件:使用ndk-stack工具解析崩溃堆栈解析原生崩溃日志:从logcat输出中提取原生崩溃
Flutter——最详细原生交互(MethodChannel、EventChannel、BasicMessageChannel)使用教程
怀君
flutter android Kotlin flutter 交互 Flutter与原生交互
MethodChannel(方法通道)用途:实现双向通信,用于调用原生平台提供的API并获取返回结果。场景:适合一次性操作,如调用相机、获取设备信息等。使用步骤:Flutter端:通过MethodChannel监听事件流。staticconstplatform=MethodChannel('com.example.fltest.plugin.DeviceInfoPlugin');Android端(
为什么说Unity引擎支持跨平台
你一身傲骨怎能输
编程语言 unity 游戏引擎
Unity引擎支持跨平台的主要理由包括以下几点:多平台发布:Unity引擎允许开发人员使用相同的代码和资源来构建应用程序和游戏,并在多个平台上发布,包括Windows、Mac、Linux、iOS、Android、WebGL、PlayStation、Xbox等。这种跨平台发布的能力使开发人员能够将他们的应用程序和游戏带到更广泛的受众中。统一开发环境:Unity提供了一个统一的开发环境,使开发人员可以
Spring Boot整合Redis详解
光阴不负卿
redis redis
Redis的Java客户端有很多,例如Jedis、JRedis、SpringDataRedis等,SpringBoot借助于SpringDataRedis为Redis提供了开箱即用的自动化配置,开发者只需要添加相关依赖并配置Redis连接信息即可,具体步骤如下:创建SpringBoot项目:首先创建SpringBoot项目,添加如下依赖:org.springframework.bootspring
tauri如何实现窗口拖动,自定义标题栏
爱音乐的程序猿
rust语言 前端 tauri rust 前端 桌面软件 exe
文章目录一、tauri是什么?二、封装好的标题栏,引用修改即可使用三相关配置实现细节实现窗口拖动一、tauri是什么?Tauri是一个开源框架,用于创建跨平台的桌面应用程序。它使用Rust编程语言,并结合了现有的Web技术,如HTML、CSS和JavaScript。Tauri旨在提供一个快速、可靠和安全的方式来构建本地应用程序,同时保持Web开发的灵活性和易用性。它支持多个操作系统和架构,包括Wi
数据挖掘|关联分析与Apriori算法详解
皖山文武
数据挖掘 商务智能 数据挖掘 关联分析 Apriori算法 机器学习
数据挖掘|关联分析与Apriori算法1.关联分析2.关联规则相关概念2.1项目2.2事务2.3项目集2.4频繁项目集2.5支持度2.6置信度2.7提升度2.8强关联规则2.9关联规则的分类3.Apriori算法3.1Apriori算法的Python实现3.2基于mlxtend库的Apriori算法的Python实现1.关联分析关联规则分析(Association-rulesAnalysis)是数
OPPO机器学习算法岗(AI智能体)内推
飞300
人工智能 业界资讯
专注于以端设备为中心的AI智能体研究与应用,研究方向包括但不限于智能体与多智能体框架、大模型推理与规划、大模型工具使用等。1、负责大模型驱动的AI智能体框架的实现、评估与优化,并参与构建产品原型;2、设计微调方案、适配算法和调优工程方案,结合智能体应用,实现最佳效果与性能;3、跟踪与研究AI智能体相关前沿技术,并针对大模型推理与规划、工具使用、结构化输出等提出创新性方案。推荐码:X3448036
人工智能混合编程实践:C++调用Python ONNX进行YOLOv8推理
FriendshipT
人工智能混合编程实践 人工智能 c++ python YOLO ONNX 目标检测
人工智能混合编程实践:C++调用PythonONNX进行YOLOv8推理前言相关介绍Python简介C++简介ONNX简介YOLOv8简介前提条件实验环境项目结构C++调用PythonONNX进行YOLOv8推理C++调用Python的相关dll代码framework.hpch.hcxx_pythonModule.hdllmain.cpppch.cppcxx_pythonModule.cppC++
Java小白-Collection集合体系
林深的林
windows python linux
一、Collection集合体系1.核心接口与实现类类型特点实现类底层结构线程安全List有序、可重复、有索引ArrayList动态数组否LinkedList双向链表否Vector动态数组是(同步)Set无序、唯一HashSet哈希表+链表/红黑树否TreeSet红黑树否二、Collection常用API1.添加相关方法方法说明booleanadd(Ee)添加单
Java小白-线程相关
林深的林
java 开发语言
一、线程的创建方式1.继承Thread类通过创建一个类继承Thread类,并重写其run方法,然后在创建该类的实例时启动线程(调用start方法)。示例代码:classMyThreadextendsThread{@Overridepublicvoidrun(){//线程执行的代码}}publicclassMain{publicstaticvoidmain(String[]args){MyThrea
ViewController添加button按钮解析。(翻译)
张亚雄
c
<div class="it610-blog-content-contain" style="font-size: 14px"></div>// ViewController.m
// Reservation software
//
// Created by 张亚雄 on 15/6/2.
mongoDB 简单的增删改查
开窍的石头
mongodb
在上一篇文章中我们已经讲了mongodb怎么安装和数据库/表的创建。在这里我们讲mongoDB的数据库操作
在mongo中对于不存在的表当你用db.表名 他会自动统计
下边用到的user是表明,db代表的是数据库
添加(insert):
log4j配置
0624chenhong
log4j
1) 新建java项目
2) 导入jar包,项目右击,properties—java build path—libraries—Add External jar,加入log4j.jar包。
3) 新建一个类com.hand.Log4jTest
package com.hand;
import org.apache.log4j.Logger;
public class
多点触摸(图片缩放为例)
不懂事的小屁孩
多点触摸
多点触摸的事件跟单点是大同小异的,上个图片缩放的代码,供大家参考一下
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener
有关浏览器窗口宽度高度几个值的解析
换个号韩国红果果
JavaScript html
1 元素的 offsetWidth 包括border padding content 整体的宽度。
clientWidth 只包括内容区 padding 不包括border。
clientLeft = offsetWidth -clientWidth 即这个元素border的值
offsetLeft 若无已定位的包裹元素
数据库产品巡礼:IBM DB2概览
蓝儿唯美
db2
IBM DB2是一个支持了NoSQL功能的关系数据库管理系统,其包含了对XML,图像存储和Java脚本对象表示(JSON)的支持。DB2可被各种类型的企 业使用,它提供了一个数据平台,同时支持事务和分析操作,通过提供持续的数据流来保持事务工作流和分析操作的高效性。 DB2支持的操作系统
DB2可应用于以下三个主要的平台:
工作站,DB2可在Linus、Unix、Windo
java笔记5
a-john
java
控制执行流程:
1,true和false
利用条件表达式的真或假来决定执行路径。例:(a==b)。它利用条件操作符“==”来判断a值是否等于b值,返回true或false。java不允许我们将一个数字作为布尔值使用,虽然这在C和C++里是允许的。如果想在布尔测试中使用一个非布尔值,那么首先必须用一个条件表达式将其转化成布尔值,例如if(a!=0)。
2,if-els
Web开发常用手册汇总
aijuans
PHP
一门技术,如果没有好的参考手册指导,很难普及大众。这其实就是为什么很多技术,非常好,却得不到普遍运用的原因。
正如我们学习一门技术,过程大概是这个样子:
①我们日常工作中,遇到了问题,困难。寻找解决方案,即寻找新的技术;
②为什么要学习这门技术?这门技术是不是很好的解决了我们遇到的难题,困惑。这个问题,非常重要,我们不是为了学习技术而学习技术,而是为了更好的处理我们遇到的问题,才需要学习新的
今天帮助人解决的一个sql问题
asialee
sql
今天有个人问了一个问题,如下:
type AD value
A
意图对象传递数据
百合不是茶
android 意图Intent Bundle对象数据的传递
学习意图将数据传递给目标活动; 初学者需要好好研究的
1,将下面的代码添加到main.xml中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http:/
oracle查询锁表解锁语句
bijian1013
oracle object session kill
一.查询锁定的表
如下语句,都可以查询锁定的表
语句一:
select a.sid,
a.serial#,
p.spid,
c.object_name,
b.session_id,
b.oracle_username,
b.os_user_name
from v$process p, v$s
mac osx 10.10 下安装 mysql 5.6 二进制文件[tar.gz]
征客丶
mysql osx
场景:在 mac osx 10.10 下安装 mysql 5.6 的二进制文件。
环境:mac osx 10.10、mysql 5.6 的二进制文件
步骤:[所有目录请从根“/”目录开始取,以免层级弄错导致找不到目录]
1、下载 mysql 5.6 的二进制文件,下载目录下面称之为 mysql5.6SourceDir;
下载地址:http://dev.mysql.com/downl
分布式系统与框架
bit1129
分布式
RPC框架 Dubbo
什么是Dubbo
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。其核心部分包含: 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。 集群容错: 提供基于接
那些令人蛋痛的专业术语
白糖_
spring Web SSO IOC
spring
【控制反转(IOC)/依赖注入(DI)】:
由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。
简单的说:对象的创建又容器(比如spring容器)来执行,程序里不直接new对象。
Web
【单点登录(SSO)】:SSO的定义是在多个应用系统中,用户
《给大忙人看的java8》摘抄
braveCS
java8
函数式接口:只包含一个抽象方法的接口
lambda表达式:是一段可以传递的代码
你最好将一个lambda表达式想象成一个函数,而不是一个对象,并记住它可以被转换为一个函数式接口。
事实上,函数式接口的转换是你在Java中使用lambda表达式能做的唯一一件事。
方法引用:又是要传递给其他代码的操作已经有实现的方法了,这时可以使
编程之美-计算字符串的相似度
bylijinnan
java 算法 编程之美
public class StringDistance {
/**
* 编程之美 计算字符串的相似度
* 我们定义一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:
* 1.修改一个字符(如把“a”替换为“b”);
* 2.增加一个字符(如把“abdd”变为“aebdd”);
* 3.删除一个字符(如把“travelling”变为“trav
上传、下载压缩图片
chengxuyuancsdn
下载
/**
*
* @param uploadImage --本地路径(tomacat路径)
* @param serverDir --服务器路径
* @param imageType --文件或图片类型
* 此方法可以上传文件或图片.txt,.jpg,.gif等
*/
public void upload(String uploadImage,Str
bellman-ford(贝尔曼-福特)算法
comsci
算法 F#
Bellman-Ford算法(根据发明者 Richard Bellman 和 Lester Ford 命名)是求解单源最短路径问题的一种算法。单源点的最短路径问题是指:给定一个加权有向图G和源点s,对于图G中的任意一点v,求从s到v的最短路径。有时候这种算法也被称为 Moore-Bellman-Ford 算法,因为 Edward F. Moore zu 也为这个算法的发展做出了贡献。
与迪科
oracle ASM中ASM_POWER_LIMIT参数
daizj
ASM oracle ASM_POWER_LIMIT 磁盘平衡
ASM_POWER_LIMIT
该初始化参数用于指定ASM例程平衡磁盘所用的最大权值,其数值范围为0~11,默认值为1。该初始化参数是动态参数,可以使用ALTER SESSION或ALTER SYSTEM命令进行修改。示例如下:
SQL>ALTER SESSION SET Asm_power_limit=2;
高级排序:快速排序
dieslrae
快速排序
public void quickSort(int[] array){
this.quickSort(array, 0, array.length - 1);
}
public void quickSort(int[] array,int left,int right){
if(right - left <= 0
C语言学习六指针_何谓变量的地址 一个指针变量到底占几个字节
dcj3sjt126com
C语言
# include <stdio.h>
int main(void)
{
/*
1、一个变量的地址只用第一个字节表示
2、虽然他只使用了第一个字节表示,但是他本身指针变量类型就可以确定出他指向的指针变量占几个字节了
3、他都只存了第一个字节地址,为什么只需要存一个字节的地址,却占了4个字节,虽然只有一个字节,
但是这些字节比较多,所以编号就比较大,
phpize使用方法
dcj3sjt126com
PHP
phpize是用来扩展php扩展模块的,通过phpize可以建立php的外挂模块,下面介绍一个它的使用方法,需要的朋友可以参考下
安装(fastcgi模式)的时候,常常有这样一句命令:
代码如下:
/usr/local/webserver/php/bin/phpize
一、phpize是干嘛的?
phpize是什么?
phpize是用来扩展php扩展模块的,通过phpi
Java虚拟机学习 - 对象引用强度
shuizhaosi888
JAVA虚拟机
本文原文链接:http://blog.csdn.net/java2000_wl/article/details/8090276 转载请注明出处!
无论是通过计数算法判断对象的引用数量,还是通过根搜索算法判断对象引用链是否可达,判定对象是否存活都与“引用”相关。
引用主要分为 :强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Wea
.NET Framework 3.5 Service Pack 1(完整软件包)下载地址
happyqing
.net 下载 framework
Microsoft .NET Framework 3.5 Service Pack 1(完整软件包)
http://www.microsoft.com/zh-cn/download/details.aspx?id=25150
Microsoft .NET Framework 3.5 Service Pack 1 是一个累积更新,包含很多基于 .NET Framewo
JAVA定时器的使用
jingjing0907
java timer 线程 定时器
1、在应用开发中,经常需要一些周期性的操作,比如每5分钟执行某一操作等。
对于这样的操作最方便、高效的实现方式就是使用java.util.Timer工具类。
privatejava.util.Timer timer;
timer = newTimer(true);
timer.schedule(
newjava.util.TimerTask() { public void run()
Webbench
流浪鱼
webbench
首页下载地址 http://home.tiscali.cz/~cz210552/webbench.html
Webbench是知名的网站压力测试工具,它是由Lionbridge公司(http://www.lionbridge.com)开发。
Webbench能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况。webbench的标准测试可以向我们展示服务器的两项内容:每秒钟相
第11章 动画效果(中)
onestopweb
动画
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
windows下制作bat启动脚本.
sanyecao2314
java cmd 脚本 bat
java -classpath C:\dwjj\commons-dbcp.jar;C:\dwjj\commons-pool.jar;C:\dwjj\log4j-1.2.16.jar;C:\dwjj\poi-3.9-20121203.jar;C:\dwjj\sqljdbc4.jar;C:\dwjj\voucherimp.jar com.citsamex.core.startup.MainStart
Java进行RSA加解密的例子
tomcat_oracle
java
加密是保证数据安全的手段之一。加密是将纯文本数据转换为难以理解的密文;解密是将密文转换回纯文本。 数据的加解密属于密码学的范畴。通常,加密和解密都需要使用一些秘密信息,这些秘密信息叫做密钥,将纯文本转为密文或者转回的时候都要用到这些密钥。 对称加密指的是发送者和接收者共用同一个密钥的加解密方法。 非对称加密(又称公钥加密)指的是需要一个私有密钥一个公开密钥,两个不同的密钥的
Android_ViewStub
阿尔萨斯
ViewStub
public final class ViewStub extends View
java.lang.Object
android.view.View
android.view.ViewStub
类摘要: ViewStub 是一个隐藏的,不占用内存空间的视图对象,它可以在运行时延迟加载布局资源文件。当 ViewSt