React Native特点
跨平台
使用js写出页面组件代码被React框架统一转成Virtual DOM树,Virtual DOM树是UI结构的一层抽象,可以被转换成任何支持端的UI视图。
ReactNative框架将Virtual DOM 转成原APP的UIView树。
热修复
ReactNative的产物bundle包,bundle包中包含的为RN业务所需要的所有资源,包括代码和资源。bundle的加载方式是APP启动时从后台下载,然后通过js虚拟机执行的。
所以可以将每次业务迭代修改后的代码上传到服务,进行用户无感知版本更新。
注意:
bundle中的业务代码不能修改APP现有的原生行为,不能调用私有API,不然禁止上架。
bundle包中的js是经过babel转义后的普通js,而非jsx语法糖。

JS与Native交互的基本原理
JS引擎
iOS侧使用的JavaScriptCore作为bundle产物的js执行引擎。
JS与Native交互的基本原理很简单,就是在JS的全局上下文添加成员变量。原生调用JS是JS在JS上下文中添加方法成员变量,然后原生调用。JS调用原生是原生往JS上下文中添加方法成员变量,然后JS调用。
JS调用原生
通过将block对象赋值给js全局上下文中的全局变量,js内部调用这个全局方法进行执行。
ctx[@"NativeMethod"] = ^(NSString *name) {
// do something
return someResult
}
原生调用JS
先创建一个JS上下文对象,在上下文中添加方法的全局变量。原生获取上下文的全局变量Value, 然后调用,执行这个JS方法。
// 创建一个ctx的JS上下文
JSContent *ctx = [[JSContent alloc] init];
// 创建一个变量name
[ctx evaluateScript:@"var name = 'jack'"];
// 创建一个方法
[ctx evaluateScript:@"var sayHi = function(name) { return 'hello ' + name }"]; // 通过ctx上下文对象,获取到hello方法
JSValue *sayHiUnction = ctx[@"sayHi"];
// 运行js方法
JSValue *greetings = [sayHiUnction callWithArguments:@[@"jack"]; // hello jack
ReactNative框架中原生与JS的调用基本思路也是这种,只不过考虑到大量的Native对象注册会污染js引擎中的上下文,增加了一层Bridge。
原生和JS之间的交互都是通过Bridge这个通道,通过里面的几个基础方法进行交互。原生与JS的交互是异步的。
另外,Facebook为了提升RN框架中JS的执行效率,专门推出了一个JS引擎 Hermes, 在关键指标上,相比于JSCore,V8提升了不少,比较易于RN框架集成。

ReactNative核心知识

RCTBridge: ReactNative中原生与JS交互的通道
RCTBridge用于给js引擎提供原生扩展接口。将原生功能如定位,3D等通过Bridge将其封装成JS接口,然后注入到js引擎的上下文中。
RN框架启动的简单流程为:首先将js代码加载到内存,然后创建RCTBridge实例,然后创建RCTRootContentView内容展示的容器视图,然后调用JS上下文中AppRegistry对象的runApplication方法,并将@[moduleName, appParameters]组件名,参数传递给JS。
// RCTRootView.m
- (void)javaScriptDidLoad:(NSNotification *)notification
{
RCTAssertMainQueue();
RCTBridge *bridge = notification.userInfo[@"bridge"];
if (bridge != _contentView.bridge) {
[self bundleFinishedLoading:bridge];
}
} - (void)bundleFinishedLoading:(RCTBridge *)bridge
{
// 省略创建RCTRootContentView... [self runApplication:bridge]; // 省略添加一个RCTRootContentView...
} - (void)runApplication:(RCTBridge *)bridge
{
NSString *moduleName = _moduleName ?: @""; // 这里是@"NewProject"
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@"initialProps": _appProperties ?: @{},
}; [bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[moduleName, appParameters]
completion:NULL];
}
 
原生调用JS
在JS上下文中,调用JS的方式是通过方法:global.batchedBridge.callFunctionReturnFlushedQueue

所以RN在原生侧的的JS引擎的封装对象中使用成员变量保存了这个JS的函数指针,原生调用JS时,通过传递参数 moduleid 和 methodid 完成方法的调用。
void JSIExecutor::bindBridge() {
std::call_once(bindFlag_, [this] {
SystraceSection s("JSIExecutor::bindBridge (once)");
Value batchedBridgeValue =
runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
if (batchedBridgeValue.isUndefined() || !batchedBridgeValue.isObject()) {
throw JSINativeException(
"Could not get BatchedBridge, make sure your bundle is packaged correctly");
} Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
*runtime_, "callFunctionReturnFlushedQueue");
invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
*runtime_, "invokeCallbackAndReturnFlushedQueue");
flushedQueue_ =
batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
});
}
 
JS调用原生
JS调用原生通常是通过原生主动处理_eventQueue中的事件,特殊情况会直接调用原生注册给JS的nativeFlushQueueImmediate方法, 并传递moduleName 、methodName、callback 参数给这个方法完成调用。

void JSIExecutor::initializeRuntime() {
SystraceSection s("JSIExecutor::initializeRuntime");
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(
*runtime_, std::make_shared<NativeModuleProxy>(nativeModules_))); runtime_->global().setProperty(
*runtime_,
"nativeFlushQueueImmediate",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) {
if (count != 1) {
throw std::invalid_argument(
"nativeFlushQueueImmediate arg count must be 1");
}
callNativeModules(args[0], false);
return Value::undefined();
})); runtime_->global().setProperty(
*runtime_,
"nativeCallSyncHook",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return nativeCallSyncHook(args, count); })); runtime_->global().setProperty(
*runtime_,
"globalEvalWithSourceUrl",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "globalEvalWithSourceUrl"),
1,
[this](
jsi::Runtime &,
const jsi::Value &,
const jsi::Value *args,
size_t count) { return globalEvalWithSourceUrl(args, count); })); if (runtimeInstaller_) {
runtimeInstaller_(*runtime_);
}
bool hasLogger(ReactMarker::logTaggedMarker);
if (hasLogger) {
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
}
}
Virtual DOM 虚拟DOM
 
虚拟DOM的特点
1.用于描述页面的UI结构:在作用上虚拟DOM和普通的DOM是一样的。
2.平台无关性:虚拟DOM表示的UI结构是对UI的一层抽象,它是平台无关的。具体的UI渲染是交个具体的平台渲染引擎进行的,如iOS,安卓自身的渲染引擎。
虚拟DOM对标签的定义
虚拟DOM把标签分为2类:原子型标签,组合型标签。
原子型标签是平台支持型的基础标签,如果RCTView, RCTText。对应浏览器页面中,原子型标签有h1,li,div等。
组合型标签是用户自定义的组件,它在虚拟DOM中对应的是自定义标签构造器函数,页面渲染时调用这个构造函数,创建一个实例,然后调用实例的render方法,组合型标签的render方法内会把组合标签进行拆解,最后拆解成基本的原子型标签。
var ele = {
...
type: type, // 元素的类型
key: key, // 元素key标示
ref: ref, // 元素的引用
props: props, // 元素的参数,包含children
...
} // example 1
<div>hello</div>
// 会被描述为 {type: 'div',
props: {
children: ['hello']
}
} // example 2
<CustomerComponents />
// 会被描述为
{
type: CustomerComponents
}
 
UI渲染
RN框架与浏览器的对比:在浏览器中,JS通过调用DOM API创建UI视图。在RN中,JS通过调用RCTUIManager来创建iOS,Android移动端的UI视图。
RN的UI渲染是基于虚拟DOM的,通过根据不同的平台调用不同平台的Bridge, Brideg再调用不同平台的的RCTUIManager进行UI的创建。
 

其他

三条线程
RN内部有三条线程在同时运行着:Shadow Thread, JS Thread, UI Thread。
JS Thread:JS线程,负责JS与原生的交互,它们的交互是异步的,每次调用都是将block放入队列中,等js代码执行完后,读取事件队列进行处理。
UI Thread:UI主线程,负责页面的交互与渲染, 由RCTUIManager使用。
Shadow Thread: 负责将flex布局转成Native的布局,由yago引擎使用。
三个队列
RN框架内,原生与JS的交换类型分两种:UI和事件,这2这种事件的处理都是异步的,它们都是将事件顺序放置到队列中,在合适的时机被调用。
事件的处理在RCTBridge中处理,UI的处理在RCTUIManager中处理。
JS调用原生异步事件队列:_eventQueue队列
原生调用JS异步事件队列:_pendingCalls队列
UI更新异步事件处理队列:_pendingUIBlocks队列
JSI
javascript interface js虚拟机通用接口层,是针对JS引擎封装的上层API框架,使用JSI做JS引擎调用的优点:
1.底部可以任意替换JS引擎而不影响上层JS引擎的使用。如:可以任意替换JavaScript Core, V8等。
2.通过JSI,JavaScript可以持有C++宿主对象的引用,所以可以直接调用原生方法(UIView, NativeModule),它与现在统一使用Bridge这个通道和消息异步调用比起来,提高了消息发送的及时性,避免了消息队列执行的等待。
React Native核心知识在框架中的使用
 
React Native核心功能在RN项目启动时会进行各自的初始化,生成bundle运行上下文。在类型上可以分为2类:
1.JS与原生的事件处理:创建RCTBridge桥接通道。
2.UI交互与更新的事件处理:创建RCTRootView容器视图。
APP启动,React Native运行环境初始化。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (!self.bridge) {
self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions];
} NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps]; if (@available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
} self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self createRootViewController];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
 
JS与原生的事件处理:创建RCTBridge桥接通道
RCTBridge的主要逻辑是在batchedBridge中,主要初始化流程为:
1.初始化Native Modules
2.创建Native Modules配置表
3.准备JS引擎工厂,创建JS引擎
4.将Modules配置信息注册到JS引擎中
5.加载boundle代码
6.执行boundle代码
- (void)setUp
{
RCT_PROFILE_BEGIN_EVENT(0, @"-[RCTBridge setUp]", nil); //_performanceLogger日志工具初始化 //_bundleURL获取 //batchedBridge创建 self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
[self.batchedBridge start]; RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
} batchedBridge是RCTCXXBridge,它的初始化方法如下:
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
{
RCTAssertParam(bridge); if ((self = [super initWithDelegate:bridge.delegate
bundleURL:bridge.bundleURL
moduleProvider:bridge.moduleProvider
launchOptions:bridge.launchOptions])) {
_parentBridge = bridge;
_performanceLogger = [bridge performanceLogger]; registerPerformanceLoggerHooks(_performanceLogger); /**
* Set Initial State
*/
_valid = YES;
_loading = YES;
_moduleRegistryCreated = NO;
_pendingCalls = [NSMutableArray new];
_displayLink = [RCTDisplayLink new];
_moduleDataByName = [NSMutableDictionary new];
_moduleClassesByID = [NSMutableArray new];
_moduleDataByID = [NSMutableArray new];
_objCModuleRegistry = [RCTModuleRegistry new];
[_objCModuleRegistry setBridge:self];
_bundleManager = [RCTBundleManager new];
[_bundleManager setBridge:self];
_viewRegistry_DEPRECATED = [RCTViewRegistry new];
[_viewRegistry_DEPRECATED setBridge:self];
_callableJSModules = [RCTCallableJSModules new];
[_callableJSModules setBridge:self]; [RCTBridge setCurrentBridge:self]; [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMemoryWarning)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil]; RCTLogSetBridgeModuleRegistry(_objCModuleRegistry);
RCTLogSetBridgeCallableJSModules(_callableJSModules);
}
return self;
} - (void)start
{
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge start]", nil); [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptWillStartLoadingNotification
object:_parentBridge
userInfo:@{@"bridge" : self}]; //启动JS线程
_jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
_jsThread.name = RCTJSThreadName;
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
#if RCT_DEBUG
_jsThread.stackSize *= 2;
#endif
[_jsThread start]; dispatch_group_t prepareBridge = dispatch_group_create(); [_performanceLogger markStartForTag:RCTPLNativeModuleInit]; //1.初始化Native Modules
[self registerExtraModules];
//2.创建Native Modules配置表
// Initialize all native modules that cannot be loaded lazily
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
[self registerExtraLazyModules]; [_performanceLogger markStopForTag:RCTPLNativeModuleInit]; // This doesnt really do anything. The real work happens in initializeBridge.
_reactInstance.reset(new Instance); __weak RCTCxxBridge *weakSelf = self; // 3.准备JS引擎工厂,创建JS引擎
std::shared_ptr<JSExecutorFactory> executorFactory;
if (!self.executorClass) {
if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>)self.delegate;
executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
}
// 4.将Modules配置信息注册到JS引擎中
if (!executorFactory) {
auto installBindings = RCTJSIExecutorRuntimeInstaller(nullptr);
#if RCT_USE_HERMES
executorFactory = std::make_shared<HermesExecutorFactory>(installBindings);
#else
executorFactory = std::make_shared<JSCExecutorFactory>(installBindings);
#endif
}
} else {
id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
}));
} //_turboModuleRegistry是一个TurboModule注册表,TurboModule是JS在RN中的一种优化方式,将常用的JS代码编译成可执行代码,提高执行速度。
/**
* id<RCTCxxBridgeDelegate> jsExecutorFactory may create and assign an id<RCTTurboModuleRegistry> object to
* RCTCxxBridge If id<RCTTurboModuleRegistry> is assigned by this time, eagerly initialize all TurboModules
*/
if (_turboModuleRegistry && RCTTurboModuleEagerInitEnabled()) {
for (NSString *moduleName in [_turboModuleRegistry eagerInitModuleNames]) {
[_turboModuleRegistry moduleForName:[moduleName UTF8String]];
} for (NSString *moduleName in [_turboModuleRegistry eagerInitMainQueueModuleNames]) {
if (RCTIsMainQueue()) {
[_turboModuleRegistry moduleForName:[moduleName UTF8String]];
} else {
id<RCTTurboModuleRegistry> turboModuleRegistry = _turboModuleRegistry;
dispatch_group_async(prepareBridge, dispatch_get_main_queue(), ^{
[turboModuleRegistry moduleForName:[moduleName UTF8String]];
});
}
}
} // Dispatch the instance initialization as soon as the initial module metadata has
// been collected (see initModules)
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}]; // 5.加载boundle代码
// Load the source asynchronously, then store it for later execution.
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self
loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
} sourceCode = source.data;
dispatch_group_leave(prepareBridge);
}
onProgress:^(RCTLoadingProgress *progressData) { }]; // 模块和js代码加载完成后,执行js代码
// Wait for both the modules and source code to have finished loading
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
// 6.执行boundle代码
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
 
初始化Native Modules与创建Native Modules配置表
把本地的RN模块都收集起来,包括RN框架自带的和用户自定义的,将模块信息保存到Bridge的变量中,用于与JS交换。
_moduleDataByName = [NSMutableDictionary new];
_moduleClassesByID = [NSMutableArray new];
_moduleDataByID = [NSMutableArray new];
 
JS发送消息到Native时,通过- (id)moduleForName:(const char *)moduleName;查询到模块详情,进行模块调用。
_objCModuleRegistry = [RCTModuleRegistry new];
[_objCModuleRegistry setBridge:self];
准备JS引擎工厂,创建JS引擎与将Modules配置信息注册到JS引擎中
RN将Native Modules信息收集完成后保存到成员变量中,这个成员变量是一个数组。使用moduleConfig保存模块的模块名,方法名。然后将这些数据注入到JS引擎中。
JS调用原生时,通过模块名,方法名,参数调用原生方法。在原生调用JS时,会将调用放入_pendingCalls队列中,进行异步执行。而JS调原生是将调用放入到_eventQueue队列中,进行异步执行。
JS可以通过方法nativeFlushQueueImmediate直接调用Native,但是一般JS不会这样做,而是等原生自己去_eventQueue队列中自己去取任务做处理。
// js thread only (which surprisingly can be the main thread, depends on used JS executor)
- (void)flushEventsQueue
{
[_eventQueueLock lock];
NSDictionary *events = _events;
_events = [NSMutableDictionary new];
NSMutableArray *eventQueue = _eventQueue;
_eventQueue = [NSMutableArray new];
_eventsDispatchScheduled = NO;
[_eventQueueLock unlock]; for (NSNumber *eventId in eventQueue) {
[self dispatchEvent:events[eventId]];
}
}
 
UI交互与更新的事件处理:创建RCTRootView容器视图
 
RCTRootView为RN页面的入口,在RCTRootView初始化过程中,会创建RCTRootContentView作为内容视图放置在RCTRootView的底部作为根视图。
RCTRootContentView的初始化方法中,在uiManager中将RCTRootContentView注册成根视图。
- (instancetype)initWithFrame:(CGRect)frame
bridge:(RCTBridge *)bridge
reactTag:(NSNumber *)reactTag
sizeFlexiblity:(RCTRootViewSizeFlexibility)sizeFlexibility
{
if ((self = [super initWithFrame:frame])) {
_bridge = bridge;
self.reactTag = reactTag;
_sizeFlexibility = sizeFlexibility;
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[_touchHandler attachToView:self];
[_bridge.uiManager registerRootView:self];
}
return self;
}
RCTUIManager是RN中UI的管理者,它负责处理所有与UI相关的事情,如:注入到JS中的创建View方法。
在createView方法中可以看到,RN对View的操作都是双份的,分别作用在RCTShadowView和UIView上。RCTShadowView和UIView的关系类似于虚拟DOM和DOM的关系。
RCTShadowView是一个虚拟DOM树,是一个结构体,用于描述视图的样式和事件,比较轻量级。
在RN中当调用setState更新组件状态时,就会生成一个新的虚拟DOM,然后RN将新的虚拟DOM与旧的虚拟DOM进行Diff对比,生成差异对象,然后遍历差异对象,将所有的改动更新到UI上。而更新到了Native是先更新到RCTShadowView上,等合适的时候再统一更新到UI上。
UI更新操作也是异步的,更新任务被放置在_pendingUIBlocks队列上,在UI变化时或Bridge批出来结束时刷新这个队列。
RCT_EXPORT_METHOD(createView
: (nonnull NSNumber *)reactTag viewName
: (NSString *)viewName rootTag
: (nonnull NSNumber *)rootTag props
: (NSDictionary *)props)
{
RCTComponentData *componentData = _componentDataByName[viewName];
if (componentData == nil) {
RCTLogError(@"No component found for view with name \"%@\"", viewName);
} // Register shadow view
RCTShadowView *shadowView = [componentData createShadowViewWithTag:reactTag];
if (shadowView) {
[componentData setProps:props forShadowView:shadowView];
_shadowViewRegistry[reactTag] = shadowView;
RCTShadowView *rootView = _shadowViewRegistry[rootTag];
RCTAssert(
[rootView isKindOfClass:[RCTRootShadowView class]] || [rootView isKindOfClass:[RCTSurfaceRootShadowView class]],
@"Given `rootTag` (%@) does not correspond to a valid root shadow view instance.",
rootTag);
shadowView.rootView = (RCTRootShadowView *)rootView;
} // Dispatch view creation directly to the main thread instead of adding to
// UIBlocks array. This way, it doesnt get deferred until after layout.
__block UIView *preliminaryCreatedView = nil; void (^createViewBlock)(void) = ^{
// Do nothing on the second run.
if (preliminaryCreatedView) {
return;
} preliminaryCreatedView = [componentData createViewWithTag:reactTag rootTag:rootTag]; if (preliminaryCreatedView) {
self->_viewRegistry[reactTag] = preliminaryCreatedView;
}
}; // We cannot guarantee that asynchronously scheduled block will be executed
// *before* a block is added to the regular mounting process (simply because
// mounting process can be managed externally while the main queue is
// locked).
// So, we positively dispatch it asynchronously and double check inside
// the regular mounting block. RCTExecuteOnMainQueue(createViewBlock); [self addUIBlock:^(__unused RCTUIManager *uiManager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
createViewBlock(); if (preliminaryCreatedView) {
[componentData setProps:props forView:preliminaryCreatedView];
}
}]; [self _shadowView:shadowView didReceiveUpdatedProps:[props allKeys]];
}
参考文章
https://juejin.cn/post/6916452544956858382#heading-11
https://juejin.cn/post/6844904184542822408
https://juejin.cn/post/6844904184500715527

ReactNative原理与核心知识点的更多相关文章

  1. Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  2. Java核心知识点 --- 线程中如何创建锁和使用锁 Lock , 设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  3. jquery-2 jQuery原理和核心方法(多看学习视频)

    jquery-2  jQuery原理和核心方法(多看学习视频) 一.总结 一句话总结:jQuery就是普通的js对象,只不过方法比较多而已,属性就length一个. 1.jquery的链式操作的底层原 ...

  4. Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件

    本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...

  5. Canal:同步mysql增量数据工具,一篇详解核心知识点

    老刘是一名即将找工作的研二学生,写博客一方面是总结大数据开发的知识点,一方面是希望能够帮助伙伴让自学从此不求人.由于老刘是自学大数据开发,博客中肯定会存在一些不足,还希望大家能够批评指正,让我们一起进 ...

  6. Redis 面霸篇:高频问题横扫核心知识点

    「码哥字节」从高频面试问题跟大家一起横扫 Redis 核心知识点,从根本上理解 Redis ,不做八股文的工具人,做扭转乾坤的大神. 码哥到如今已经写了 9 篇 Redis 连载,后台有小伙伴也让我写 ...

  7. javascript中的一些核心知识点以及需要注意的地方

    前言 近期杂事甚多,这些事情的积累对知识体系的提升有好处,但是却不能整理出来,也整理不出来 比如说我最近研究的Hybrid在线联调方案便过于依赖于业务,就算分享也不会有人读懂,若是抽一点来分享又意义不 ...

  8. Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍

    1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...

  9. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

  10. Hbase的架构原理、核心概念

    Hbase的架构原理.核心概念 1.Hbase的表.行.列.列族 2.核心组件: Table和region Table在行的方向上分割为多个HRegion, 一个region由[startkey,en ...

随机推荐

  1. 一个.Net简单、易用的配置文件操作库

    在我们日常项目开发中,操作INI/CFG配置文件,往往会通过调用WinAPI来实现,WinAPI接口参数只支持字符串,而我们项目中,往往数据类型是多种多样的,在保存和获取配置值,我们就要进行类型的转换 ...

  2. pysimplegui之popup弹出框

    弹出框其实跟信息框差不多,在写界面的时候经常用,具体如下 "高级呼叫"是以"弹出"开头的呼叫.它们是与用户沟通的最基本形式.它们以它们创建的窗口类型命名,即弹出 ...

  3. [软件过程/软件生命周期模型]软件过程的工具链&技术链【待续】

    0 宣言:DevOps & RUP统一过程建模 1 项目管理 (需求管理 / 缺陷管理 / ...) 禅道(前身:bugfree) [在线协作] JIRA(项目与事务跟踪工具) 与禅道类同,但 ...

  4. [Linux]scp/sshpass:物理主机与虚拟机的文件传输

    最初写这篇文章的时候,对 openssh大家族的工具套件(例如: ssh.sshd.ssl.scp等)不太熟悉,现在看来这个文章的标题(虚拟机与物理机)是存在问题的. 本质上,本文关心的并不是[虚拟机 ...

  5. tkinter的标签和按钮以及输入和文本

    一.标签和文本 import tkinter as tk #1.定义tk的实例对象,也就是窗口对象 window = tk.TK() #2.设置窗口大小无法缩小和放大 window.resiable( ...

  6. day103:MoFang:用户登录部分:客户端提交登录信息&APICloud集成防水墙验证码&保存用户登录状态

    目录 bug:修复jsonrpc修改源码以后celery无法运行的问题 1.客户端提交登录信息 2.在APICloud中集成防水墙验证码 3.保存用户登录状态 bug:修复jsonrpc修改源码以后c ...

  7. day49:django:wsgrief&模板渲染Jinjia2&django的MTV/MVC框架&创建/启动一个django项目

    目录 1.自定义web框架wsgiref版 2.自定义web框架wsgiref版-优化版 3.模板渲染JinJa2 4.MTV和MVC框架 5.django:下载安装&创建启动 自定义web框 ...

  8. ai问答:使用vite如何配置多入口页面

    Vite 是一个 web 开发构建工具,它可以用于开发单页应用和多页应用.要在 Vite 中配置多入口,可以: 在 vite.config.js 中定义多个 entry 入口: export defa ...

  9. 6R机械臂运动规划及仿真

    博客地址:https://www.cnblogs.com/zylyehuo/ 参考链接 Moveit!机械臂控制 文件下载-古月ROS教程视频配套资料 解决Could not find a packa ...

  10. 合合信息亮相CCIG2023:多位大咖共话智能文档未来,文档图像内容安全还面临哪些技术难题?

    ​ 近日,中国图象图形大会(CCIG 2023)(简称"大会")在苏州圆满落幕.本届大会以"图象图形·向未来"为主题,由中国科学技术协会指导,中国图象图形学学会 ...