当前位置: 首页 > news >正文

Android RxJava框架源码解析(四)

目录

  • 一、观察者Observer创建过程
  • 二、被观察者Observable创建过程
  • 三、subscribe订阅过程
  • 四、map操作符
  • 五、线程切换原理

简单示例1:

private Disposable mDisposable;
Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {e.onNext("test");}}).subscribe(new Observer<String>() { @Overridepublic void onSubscribe(Disposable d) {mDisposable = d;}@Overridepublic void onNext(String s) {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}});@Overrideprotected void onDestroy() {super.onDestroy();if (mDisposable != null) {if (!mDisposable.isDisposed()) {mDisposable.dispose();}}}

特别注意:上面示例代码中的mDisposable最后必须要释放掉,不然会出现内存泄漏

一、观察者Observer创建过程

  首先对观察者Observer源码开始进行简单分析下:
Observer.java

public interface Observer<T> {//表示一执行subscribe订阅就会执行该函数,这个函数一定执行在主线程中void onSubscribe(@NonNull Disposable d);// 表示拿到上一个流程的数据void onNext(@NonNull T t);// 表示拿到上一个流程的错误数据void onError(@NonNull Throwable e);// 表示事件流程结束void onComplete();
}

  具体的对象创建是在上面示例代码1中的new Observer<String>()操作,这个称这个为自定义观察者

二、被观察者Observable创建过程

  分析完观察者Observer的创建,现在来分析下被观察者Observable的创建流程,

Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {e.onNext("test");}})

  将new ObservableOnSubscribe()过程可以理解为是自定义source的过程。

new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {e.onNext("test");}}

执行Observable.create()代码流程
Observable.java

public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {ObjectHelper.requireNonNull(source, "source is null"); //校验是否为nullreturn RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));}

其中,RxJavaPlugins.onAssembly()采用了hook技术,如果没有重写RxJavaPlugins.setOnObservableAssembly()方法,这个可以不要考虑。
ObservableCreate.java

public final class ObservableCreate<T> extends Observable<T> {final ObservableOnSubscribe<T> source; // 自定义sourcepublic ObservableCreate(ObservableOnSubscribe<T> source) {this.source = source;}@Overrideprotected void subscribeActual(Observer<? super T> observer) {CreateEmitter<T> parent = new CreateEmitter<T>(observer);observer.onSubscribe(parent);try {source.subscribe(parent);} catch (Throwable ex) {Exceptions.throwIfFatal(ex);parent.onError(ex);}}static final class CreateEmitter<T>extends AtomicReference<Disposable>implements ObservableEmitter<T>, Disposable {private static final long serialVersionUID = -3434801548987643227L;final Observer<? super T> observer;CreateEmitter(Observer<? super T> observer) {this.observer = observer;}@Overridepublic void onNext(T t) {if (t == null) {onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));return;}if (!isDisposed()) {observer.onNext(t);}}@Overridepublic void onError(Throwable t) {if (!tryOnError(t)) {RxJavaPlugins.onError(t);}}@Overridepublic boolean tryOnError(Throwable t) {if (t == null) {t = new NullPointerException("onError called with null. Null values are generally not allowed in 2.x operators and sources.");}if (!isDisposed()) {try {observer.onError(t);} finally {dispose();}return true;}return false;}@Overridepublic void onComplete() {if (!isDisposed()) {try {observer.onComplete();} finally {dispose();}}}@Overridepublic void setDisposable(Disposable d) {DisposableHelper.set(this, d);}@Overridepublic void setCancellable(Cancellable c) {setDisposable(new CancellableDisposable(c));}@Overridepublic ObservableEmitter<T> serialize() {return new SerializedEmitter<T>(this);}@Overridepublic void dispose() {DisposableHelper.dispose(this);}@Overridepublic boolean isDisposed() {return DisposableHelper.isDisposed(get());}}/*** Serializes calls to onNext, onError and onComplete.** @param <T> the value type*/static final class SerializedEmitter<T>extends AtomicIntegerimplements ObservableEmitter<T> {private static final long serialVersionUID = 4883307006032401862L;final ObservableEmitter<T> emitter;final AtomicThrowable error;final SpscLinkedArrayQueue<T> queue;volatile boolean done;SerializedEmitter(ObservableEmitter<T> emitter) {this.emitter = emitter;this.error = new AtomicThrowable();this.queue = new SpscLinkedArrayQueue<T>(16);}@Overridepublic void onNext(T t) {if (emitter.isDisposed() || done) {return;}if (t == null) {onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));return;}if (get() == 0 && compareAndSet(0, 1)) {emitter.onNext(t);if (decrementAndGet() == 0) {return;}} else {SimpleQueue<T> q = queue;synchronized (q) {q.offer(t);}if (getAndIncrement() != 0) {return;}}drainLoop();}@Overridepublic void onError(Throwable t) {if (!tryOnError(t)) {RxJavaPlugins.onError(t);}}@Overridepublic boolean tryOnError(Throwable t) {if (emitter.isDisposed() || done) {return false;}if (t == null) {t = new NullPointerException("onError called with null. Null values are generally not allowed in 2.x operators and sources.");}if (error.addThrowable(t)) {done = true;drain();return true;}return false;}@Overridepublic void onComplete() {if (emitter.isDisposed() || done) {return;}done = true;drain();}void drain() {if (getAndIncrement() == 0) {drainLoop();}}void drainLoop() {ObservableEmitter<T> e = emitter;SpscLinkedArrayQueue<T> q = queue;AtomicThrowable error = this.error;int missed = 1;for (;;) {for (;;) {if (e.isDisposed()) {q.clear();return;}if (error.get() != null) {q.clear();e.onError(error.terminate());return;}boolean d = done;T v = q.poll();boolean empty = v == null;if (d && empty) {e.onComplete();return;}if (empty) {break;}e.onNext(v);}missed = addAndGet(-missed);if (missed == 0) {break;}}}@Overridepublic void setDisposable(Disposable s) {emitter.setDisposable(s);}@Overridepublic void setCancellable(Cancellable c) {emitter.setCancellable(c);}@Overridepublic boolean isDisposed() {return emitter.isDisposed();}@Overridepublic ObservableEmitter<T> serialize() {return this;}}}

这里将ObservableCreate的源码全部放在这,作为一个埋点

  其实,Observable.create()方法主要功能就是创建了一个ObservableCreate对象,并将自定义的source传给ObservableCreate。该方法最终返回的是ObserverableCreate对象。

三、subscribe订阅过程

  分析执行subscribe()订阅流程,并将自定义观察者作为参数传入。
Observable.java

@Overridepublic final void subscribe(Observer<? super T> observer) {ObjectHelper.requireNonNull(observer, "observer is null"); // 功能校验,判定observer是否为nulltry {observer = RxJavaPlugins.onSubscribe(this, observer);ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");subscribeActual(observer); } catch (NullPointerException e) { // NOPMDthrow e;} catch (Throwable e) {Exceptions.throwIfFatal(e);// can't call onError because no way to know if a Disposable has been set or not// can't call onSubscribe because the call might have set a Subscription alreadyRxJavaPlugins.onError(e);NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");npe.initCause(e);throw npe;}}

  首先会执行一些功能校验,最后执行到subscribeActual()方法中。
Observable.java

 protected abstract void subscribeActual(Observer<? super T> observer);

  subscribeActual()是一个抽象类,从而最终调用的是ObservableCreate的subscribeActual()方法中。

ObservableCreate.java

@Overrideprotected void subscribeActual(Observer<? super T> observer) {  // observer为自定义观察者// 自定义一个CreateEmitter发射器CreateEmitter<T> parent = new CreateEmitter<T>(observer); // 执行该方法就会执行自定义观察者的onSubscribe()方法中observer.onSubscribe(parent);try {source.subscribe(parent);} catch (Throwable ex) {Exceptions.throwIfFatal(ex);parent.onError(ex);}}

subscribeActual()方法里面会执行如下三个操作:
1)CreateEmitter<T> parent = new CreateEmitter<T>(observer); --> 首先会创建一个CreateEmitter发射器,并将自定义观察者传入该发射器中
2)observer.onSubscribe(parent);–> 执行自定义观察者的onSubscribe()方法,所以该方法也是最先执行调用,并且一定在主线程中
3)source.subscribe(parent); -->执行自定义source的subscribe()订阅操作,从而跳转到示例代码1中ObservableOnSubscribe的subscribe()方法,并将CreateEmitter发射器作为参数传入进去

new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {e.onNext("test");}}

  执行e.onNext("test")就会跳转到CreateEmitter发射器中的onNext()方法
ObservableCreate.java

static final class CreateEmitter<T>extends AtomicReference<Disposable>implements ObservableEmitter<T>, Disposable {final Observer<? super T> observer;CreateEmitter(Observer<? super T> observer) {this.observer = observer;}@Overridepublic void onNext(T t) {if (t == null) {onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));return;}if (!isDisposed()) {observer.onNext(t);  //执行该流程,observer为自定义观察者}}...}

  该observer为上面流程中自定义的CreateEmitter发射器CreateEmitter<T> parent = new CreateEmitter<T>(observer);传入进来的自定义观察者对象,执行observer.onNext(t)该语句就调到示例代码1中的

@Override
public void onNext(String s) {}

Observable与Observer订阅的过程时序图如下:
在这里插入图片描述

  在标准的观察者设计模式中,是一个“被观察者”,多个“观察者”,并且需要“被观察者”发出改变通知后,所以的“观察者”才能观察到
  在RxJava观察者设计模式中,是多个“被观察者”,一个“观察者”,并且需要 起点(被观察者) 和 终点(观察者) 在“订阅”一次后,才发出改变通知,终点(观察者)才能观察到

图1:RxJava简单订阅过程:
在这里插入图片描述

四、map操作符

加入map操作符之后的简单示例代码2:

private Disposable mDisposable;// 创建ObserverCreate
Observable.create(new ObservableOnSubscribe<String>() { //自定义source@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {e.onNext("test");}})// ObservableCreate.map.map(new Function<String, String>() {@Overridepublic String apply(String s) throws Exception {return s;}})// ObservableMap.subscribe.subscribe(new Observer<String>() { //自定义观察者@Overridepublic void onSubscribe(Disposable d) {mDisposable = d;}@Overridepublic void onNext(String s) {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}});@Overrideprotected void onDestroy() {super.onDestroy();if (mDisposable != null) {if (!mDisposable.isDisposed()) {mDisposable.dispose();}}}

这个示例代码2写法采用装饰模型

图2加入map操作符之后的流程:
在这里插入图片描述

从①~⑥流程简称为封包裹,⑦ ~⑨流程简称为拆包裹

  其实图1与图2的区别不大,主要就是多了一个ObservableMap封包裹的流程,其他流程都类似。针对这个区别进行代码流程阐述下:
  从示例代码2中执行map()操作进行分析:
Observable.java

public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {ObjectHelper.requireNonNull(mapper, "mapper is null");return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));}

进行创建ObservableMap对象

ObservableMap.java

public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> {final Function<? super T, ? extends U> function;public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) {super(source); //source指ObservableCreatethis.function = function; // 自定义的Function方法}@Overridepublic void subscribeActual(Observer<? super U> t) {source.subscribe(new MapObserver<T, U>(t, function)); //这里面的t为下一层包裹即图2中的自定义观察者,source指上一层ObservableCreate}
...
}

  这里需要注意,在ObservableMap()构造函数中,参数source指从上一层传过来的ObservableCreate对象,参数function指示例代码2中的new Function()方法。

 .map(new Function<String, String>() 

  执行示例代码2中的.subscribe()其实就是执行到了ObservableMap类的subscribeActual()方法,在这个方法中会对MapObserver进行封装一层包裹,并将下一层的包裹即自定义观察者也就是参数t传入。

MapObserver为ObservableMap的内部类。

ObservableMap.java

static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> {final Function<? super T, ? extends U> mapper;MapObserver(Observer<? super U> actual, Function<? super T, ? extends U> mapper) {super(actual); // actual为自定义观察者this.mapper = mapper;}...
}

  在执行图2的第⑧步流程时,就会调用执行包裹1的onNext()方法,即MapObserver类的onNext();
ObservableMap.java

@Override
public void onNext(T t) {if (done) {return;}if (sourceMode != NONE) {actual.onNext(null);return;}U v;try {// 代码1v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");} catch (Throwable ex) {fail(ex);return;}// 代码2actual.onNext(v);
}

1:代码1
  执行mapper.apply(t)流程的时候,其实就是调用了示例代码2中的apply()方法。
Function.java

public interface Function<T, R> {R apply(@NonNull T t) throws Exception;
}
@Override
public String apply(String s) throws Exception {return s;
}

2:代码2
   actual.onNext(v);中的actual是在ObservableMap构造函数传过来的,actual对应图2中的自定义观察者对象,也就是对应图2中的第9步流程。

五、线程切换原理

subscribeOn:给上面代码分配线程
observeOn:给下面代码分配线程

Scheduler分类:

调度器类型效果
Schedulers.computation()用于计算任务,如事件循环或回调处理,不要用于IO操作(IO操作使用Schedulers.io());默认线程数等于处理器的数量
Schedulers.from(executor)使用指定的Executor作为调度器
Schedulers.immediate()在当前线程立即开始执行任务
Schedulers.io()用于IO密集型任务
Schedulers.newThread()为每个任务创建一个新任务
Schedulers.trampoline()当其他排队的任务完成后,在当前线程排队开始执行
AndroidSchedulers.mainThread()用于Android的UI更新操作

相关文章:

Android RxJava框架源码解析(四)

目录一、观察者Observer创建过程二、被观察者Observable创建过程三、subscribe订阅过程四、map操作符五、线程切换原理简单示例1&#xff1a; private Disposable mDisposable; Observable.create(new ObservableOnSubscribe<String>() {Overridepublic void subscribe(…...

Linux信号-进程退出状态码

当进程因收到信号被终止执行退出后&#xff0c;父进程可以通过wait或waitpid得到它的exit code。进程被各信号终止的退出状态码总结如下&#xff1a;信号编号信号名称信号描述默认处理方式Exit code1SIGHUP挂起终止12SIGINT终端中断终止23SIGQUIT终端退出终止、coredump1314SIG…...

springcloud+vue实现图书管理系统

一、前言&#xff1a; 今天我们来分享一下一个简单的图书管理系统 我们知道图书馆系统可以有两个系统&#xff0c;一个是管理员管理图书的系统&#xff0c;管理员可以&#xff08;1&#xff09;查找某一本图书情况、&#xff08;2&#xff09;增加新的图书、&#xff08;3&…...

GEE学习笔记 六十:GEE中生成GIF动画

生成GIF动画这个是GEE新增加的功能之一&#xff0c;这一篇文章我会简单介绍一下如何使用GEE来制作GIF动画。 相关API如下&#xff1a; 参数含义&#xff1a; params&#xff1a;设置GIF动画显示参数&#xff0c;详细的参数可以参考ee.data.getMapId() callback&#xff1a;回调…...

react中的useEffect

是函数组件中执行的副作用&#xff0c;副作用就是指每次组件更新都会执行的函数&#xff0c;可以用来取代生命周期。 1. 基本用法 import { useEffect } from "react"; useEffect(()>{console.log(副作用); });2. 副作用分为需要清除的和不需要清除 假如设置…...

故障安全(Crash-Safe) 复制

二进制日志记录是故障安全的:MySQL 仅记录完成的事件或事务使用 sync-binlog 提高安全性默认值是1&#xff0c;最安全的&#xff0c;操作系统在每次事务后写入文件将svnc-binloq 设置为0&#xff0c;当操作系统根据其内部规则写入文件的同时服务器崩溃时性能最好但事务丢失的可…...

Spring aop之针对注解

前言 接触过Spring的都知道&#xff0c;aop是其中重要的特性之一。笔者在开发做项目中&#xff0c;aop更多地是要和注解搭配&#xff1a;在某些方法上加上自定义注解&#xff0c;然后要对这些方法进行增强(很少用execution指定&#xff0c;哪些包下的哪些方法要增强)。那这时就…...

【JavaScript速成之路】JavaScript数据类型转换

&#x1f4c3;个人主页&#xff1a;「小杨」的csdn博客 &#x1f525;系列专栏&#xff1a;【JavaScript速成之路】 &#x1f433;希望大家多多支持&#x1f970;一起进步呀&#xff01; 文章目录前言数据类型转换1&#xff0c;转换为字符串型1.1&#xff0c;利用“”拼接转换成…...

21-绑定自定义事件

绑定自定义事件 利用自定义事件获取子组件的值 父组件给子组件绑定一个自定义事件&#xff0c;实际上是绑定到了子组件的实例对象vc上&#xff1a; <!-- 自定义myEvent事件 --> <Student v-on:myEventgetStudentName/>在父组件中编写getStudentName的实现&#…...

【Mysql】触发器

【Mysql】触发器 文章目录【Mysql】触发器1. 触发器1.1 介绍1.2 语法1.2.1 创建触发器1.2.2 查看触发器1.2.3 删除触发器1.2.4 案例1. 触发器 1.1 介绍 触发器是与表有关的数据库对象&#xff0c;指在insert、update、delete之前(BEFORE)或之后(AFTER)&#xff0c;触发并执行…...

CODESYS开发教程11-库管理器

今天继续我们的小白教程&#xff0c;老鸟就不要在这浪费时间了&#x1f60a;。 前面一期我们介绍了CODESYS的文件读写函数库SysFile。大家可能发现了&#xff0c;在CODESYS的开发中实际上是离不开各种库的使用&#xff0c;其中包括系统库、第三方库以及用户自己开发的库。实际…...

【UnityAR相关】Unity Vuforia扫图片成模型具体步骤

1 资产准备 导入要生成的fbx模型&#xff08;带有材质&#xff09;&#xff0c; 你会发现导入fbx的材质丢失了&#xff1a; 选择Standard再Extract Materials导出材质到指定文件夹下&#xff08;我放在Assets->Materials了 ok啦&#xff01; 材质出现了&#xff0c; 模型…...

2023年全国最新保安员精选真题及答案2

百分百题库提供保安员考试试题、保安职业资格考试预测题、保安员考试真题、保安职业资格证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 21.一般来说&#xff0c;最经济的巡逻方式是&#xff08;&#xff09;。 A:步巡 B:…...

keil5安装了pack包但是还是不能选择device

一开始&#xff0c;我以为是keil5无法安装 STM32 芯片包&#xff0c;打开device倒是可以看到stm公司的芯片包&#xff0c;但是没有我想要的stm32f1。 我按照网上的一些说法&#xff0c;找到了这个STM32F1 的pack芯片包&#xff0c;但是我双击安装的时候&#xff0c;它的安装位…...

秒杀系统设计

1.秒杀系统的特点 瞬时高并发 2.预防措施 2.1.流量限制 对于一个相同的用户&#xff0c;限制请求的频次对于一个相同的IP&#xff0c;限制请求的频次验证码&#xff0c;减缓用户请求的次数活动开启之前&#xff0c;按钮先置灰&#xff0c;防止无效的请求流入系统&#xff0…...

全面认识数据指标体系

什么是数据指标体系&#xff1f; 看了下百度百科&#xff0c;竟然没有数据指标这个词条&#xff0c;看来这个词大家平时还用的不多啊。那只有间接偷懒一下&#xff0c;分别查下指标和数据这两个词条的含义&#xff0c;在组合起来看看。 数据&#xff1a;数据是指对客观事件进…...

热榜首推!阿里内部都在用的Java后端面试笔记,主流技术全在里面了!备战2023Java面试,拿理想offer

纵观今年的技术招聘市场&#xff0c; Java依旧是当仁不让的霸主 &#xff01;即便遭受 Go等新兴语言不断冲击&#xff0c;依旧岿然不动。究其原因&#xff1a;Java有着极其成熟的生态&#xff0c;这个不用我多说&#xff1b;Java在 运维、可观测性、可监 控性方面都有着非常优秀…...

Android架构设计——【 APT技术实现butterknife框架 】

APT简介 APT英文全称&#xff1a;Android annotation process tool是一种处理注释的工具&#xff0c;它对源代码文件进行检测找出其中的Annotation&#xff0c;使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文…...

线程的基本概念

文章目录基础概念线程与进程什么是进程&#xff1f;什么是线程&#xff1f;进程和线程的区别&#xff1a;多线程什么是多线程&#xff1f;多线程的局限性串行、并行、并发同步异步、阻塞非阻塞线程的创建1、继承Thread类&#xff0c;重写run方法2、实现Runnable接口&#xff0c…...

java面试题中常见名词注解

一.常见名词注解 1.mysql索引&#xff0c;索引数据结构&#xff0c;hash&#xff0c;二叉树&#xff0c;B树&#xff0c;B树&#xff0c;红黑树&#xff0c; mysql索引&#xff1a;帮助mysql高效获取数据的数据结构&#xff0c;通俗来说&#xff0c;数据库索引就好比一本书的…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统

Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...