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

Android 12 源码分析 —— 应用层 三(SystemUIFactory及其Dependency解析)

Android 12 源码分析 —— 应用层 三(SystemUIFactory及其Dependency解析)

在上一篇文章中,介绍了SystemUI的启动流程,并且简单提及了Dagger2用来管理各个SystemUI中要用的依赖。而这部分代码就在:mContextAvailableCallback.onContextAvailable(this);流程中。而这部分流程我们只是一笔带过,并没有仔细分析。

接下来将会从这个调用开始,介绍SystemUI中Component,subcomponent的初始化,并理解应该怎么使用这些Component。

本文会先以一个例子,简单引入Dagger2中对各个注解的生成模板,然后在介绍SystemUI中重要component和subcomponent的创建过程,最后过渡到一个关键的类Dependency

注意:此处之所以另起一个例子来说明Dagger2的生成模板,完全是因为Dagger2对SystemUI的生成代码太多,导致不太好列成文档

理解Dagger2的模板

为了能够更好理解Dagger2的生成代码的模板,我们按照如下的例子进行编写,然后查看生成的类.

将下面的代码放入一个DemoComponent.java的文件中

//最顶层的Component,其有两个模块,分别为:
//      DemoSubComponent1.DemoModule1
//      DemoSubComponent2.DemoModule2
//最顶层Component,提供两个接口,用于得到SubComponent的Factory和Builder类
@Component(modules = {DemoSubComponent1.DemoModule1.class,DemoSubComponent2.DemoModule2.class})
public interface DemoComponent {DemoSubComponent1.Factory getSubComponent1Factory();DemoSubComponent2.Builder getSubComponent2Builder();
}//SubComponent1,提供如下两个功能
//      1. 获得DemoAClass对象
//      2. 向DemoInject对象中,注入需要的对象
@SysUISingleton
@Subcomponent
interface DemoSubComponent1{@Subcomponent.Factoryinterface Factory {DemoSubComponent1 create();}DemoAClass getAClass();void inject(DemoInject inject);//SubComponent1隶属于的模块//指定了DemoInterface 和DemoBinds之间的关系,当需要DemoInterface的时候,//  Dagger2会创建对应的DemoBinds对象@Module(subcomponents = DemoSubComponent1.class)abstract class DemoModule1{@Bindsabstract DemoInterface getInterface(DemoBinds bindings);}
}//SubComponent2,提供一个功能
//    1. 获取DemoBClass对象
@Subcomponent
interface DemoSubComponent2{//为了和SubComponent1作区分,这里使用了Builder类来创建SubComponent2@Subcomponent.Builderinterface Builder {DemoSubComponent2 build();}DemoBClass getBClass();//SubComponent2隶属的模块,该模块还提供了一个@Provides@Module(subcomponents = DemoSubComponent2.class)class DemoModule2{//当需要DemoProvider对象的时候,就会调用此方法@ProvidesDemoProvider getProvider(){return new DemoProvider();}}
}//用于演示@Binds
interface DemoInterface{void test();
}//用于演示@Binds
class DemoBinds implements DemoInterface{@InjectDemoBinds(){}@Overridepublic void test() {}
}//用于演示@Provides
class DemoProvider{}//用于演示,inject函数
class DemoInject{@InjectDemoAClass a;
}//用于演示,生命周期
@SysUISingleton
class DemoAClass{@Injectpublic DemoAClass(){}
}//用于演示返回的对象
class DemoBClass{@Injectpublic DemoBClass(){}
}

有了上面所画的图,接下来查看,Dagger2根据图生成的文件,生成的文件有:

  1. DaggerDemoComponent.java
  2. DemoAClass_Factory.java
  3. DemoBClass_Factory.java
  4. DemoBinds_Factory.java
  5. DemoInject_MembersInjector.java
  6. DemoSubComponent2_DemoModule2_GetProviderFactory.java
  7. DemoSubComponent2_DemoModule2_Proxy.java

接下来我们挨个查看生成的7个文件,分别是什么内容,DaggerDemoComponent.java中会使用剩下的6个类。因此,我们先看剩下的6个类,最后再查看DaggerDemoFactory.java

DemoAClass_Factory,DemoBClass_Factory,DemoBinds_Factory

DemoAClass_Factory的源码如下:

//对于任何类来讲,只要其构造函数带有@Inject,则Dagger2都会创建一个对应的工厂类,
//叫做:XXX_Factory,它实现了Factory<T>接口
//欲使用这个工厂类,则调用create()方法,得到这个工厂类的实例,工厂类的实例永远只有一个
//欲使用这个工厂类产生的对象,则调用newInstance()方法,得到工厂类的产生对象,
//工厂类产生的对象可以有多个
public final class DemoAClass_Factory implements Factory<DemoAClass> {@Overridepublic DemoAClass get() {return newInstance();}public static DemoAClass_Factory create() {return InstanceHolder.INSTANCE;}public static DemoAClass newInstance() {return new DemoAClass();}//用于缓存工厂类实例private static final class InstanceHolder {private static final DemoAClass_Factory INSTANCE = new DemoAClass_Factory();}
}

DemoBClass_Factory,DemoBinds_Factory同DemoAClass_Factory一模一样,不再啰嗦。

DemoInject_MembersInjector

DemoInject_MembersInjector用于辅助成员的注入,因为在DemoInject类中如下的代码

class DemoInject{@InjectDemoAClass a;
}

整个辅助类源码如下:


//每一个辅助注入的类,都是MembersInjector<T>的具体实现
//在实现中,对要注入的成员,使用Provider<T>表示,抽象为:T的提供者。它会在辅助类创建的时候被
//初始化好
//        对被注入的类,使用instance表示
//同工厂类一样,辅助注入类的实例化,也是通过create()方法进行
//同工厂类不一样的是,每次create,都会创建不同的辅助注入对象
//注入方式,则直接调用injectMembers()函数进行
public final class DemoInject_MembersInjector implements MembersInjector<DemoInject> {private final Provider<DemoAClass> aProvider;public DemoInject_MembersInjector(Provider<DemoAClass> aProvider) {this.aProvider = aProvider;}public static MembersInjector<DemoInject> create(Provider<DemoAClass> aProvider) {return new DemoInject_MembersInjector(aProvider);}@Overridepublic void injectMembers(DemoInject instance) {injectA(instance, aProvider.get());}@InjectedFieldSignature("com.android.systemui.dagger.DemoInject.a")public static void injectA(Object instance, Object a) {((DemoInject) instance).a = (DemoAClass) a;}
}

上面的辅助注入类也很简单,接着往下看

DemoSubComponent2_DemoModule2_GetProviderFactory

DemoSubComponent2_DemoModule2_GetProviderFactory类表示的是对@Provides的实现工厂类,源码如下:


//对于任何一个被@Provides标记的函数,Dagger2都会创建一个提供者工厂类,它实现了Factory<T>
//同DemoAClass工厂类一样,它也会有一个create()方法,用于实例化提供者工厂类
//不同之处在于,每次调用都会创建一个新的提供者工厂类//只有需要提供者工厂类,生产提供者时,才会调用get()方法,用于返回生产出来的对象。public final class DemoSubComponent2_DemoModule2_GetProviderFactory implements Factory<DemoProvider> {private final DemoSubComponent2.DemoModule2 module;public DemoSubComponent2_DemoModule2_GetProviderFactory(DemoSubComponent2.DemoModule2 module) {this.module = module;}@Overridepublic DemoProvider get() {return getProvider(module);}public static DemoSubComponent2_DemoModule2_GetProviderFactory create(DemoSubComponent2.DemoModule2 module) {return new DemoSubComponent2_DemoModule2_GetProviderFactory(module);}public static DemoProvider getProvider(DemoSubComponent2.DemoModule2 instance) {return Preconditions.checkNotNullFromProvides(instance.getProvider());}
}

上面的代码,依然很简单,不再啰嗦

DemoSubComponent2_DemoModule2_Proxy

DemoSubComponent2_DemoModule2_Proxy是对抽象模块的表示,源码如下:

public final class DemoSubComponent2_DemoModule2_Proxy {private DemoSubComponent2_DemoModule2_Proxy() {}public static DemoSubComponent2.DemoModule2 newInstance() {return new DemoSubComponent2.DemoModule2();}
}

在DemoModule2中,没有任何需要实现的地方,且@Binds已经语义足够明显,所以上面生成的类,没有任何过多的地方。较简单

接下来,就是看看DaggerDemoComponent如何使用上面创建的类来处理他们之间的依赖关系

DaggerDemoComponent

DaggerDemoComponent是整个依赖注入的入口,其源码如下:

//实现我们的DemoComponent接口,其命名为DaggerAAA_BBB。其中下划线后面的名字,为内部类的名字
public final class DaggerDemoComponent implements DemoComponent {private DaggerDemoComponent() {}//builder()方法和create()方法,都是为了创建DaggerDemoComponent的实例public static Builder builder() {return new Builder();}public static DemoComponent create() {//委托给Builder类创建return new Builder().build();}//返回subcomponent1的Factory类,用于创建subcomponent1@Overridepublic DemoSubComponent1.Factory getSubComponent1Factory() {//见后文return new DemoSubComponent1Factory();}//返回subcomponent2的Bulder类,用于创建subcomponent2@Overridepublic DemoSubComponent2.Builder getSubComponent2Builder() {return new DemoSubComponent2Builder();}//用于创建DaggerDemoComponent,在这个类中,只是简单的new了一个DaggerDemoComponent即可public static final class Builder {private Builder() {}@Deprecatedpublic Builder demoModule2(DemoSubComponent2.DemoModule2 demoModule2) {Preconditions.checkNotNull(demoModule2);return this;}public DemoComponent build() {return new DaggerDemoComponent();}}//实现DemoSubComponent1的Factory接口,该接口提供create()方法来创建对应的实例private final class DemoSubComponent1Factory implements DemoSubComponent1.Factory {@Overridepublic DemoSubComponent1 create() {//创建DemoSubComponent1的实例对象,并返回return new DemoSubComponent1Impl();}}//subcomponet1的具体实现private final class DemoSubComponent1Impl implements DemoSubComponent1 {private Provider<DemoAClass> demoAClassProvider;private DemoSubComponent1Impl() {initialize();}//辅助构造函数,对DemoAClass的提供者,进行初始化//因为我们将DemoAClass的生命周期和DemoSubComponent1的生命周期,都标记为//@SysUISingleton//所以他们同生同死,为了达到这个效果,需要在subcomponent1中持有对DemoAClass的提供者,//而不是每次重新创建(对比下面的getBClass()函数)//这样,在需要DemoAClass的地方(如getAClass()函数),就直接让提供者来提供,而提供者保证了//每次都提供同一个对象//DemoAClass的提供者,我们首先想到的就是,前面介绍过的DemoAClass_Factory工厂类,//调用其newInstance()//就会创建一个DemoAClass对象。但是它并不能保证每次提供同一个对象,因此再次对//DemoAClass_Factory进行封装//这个封装就是DoubleCheck对象,它保证了每次提供同一个对象,同时也保证了线程安全//DoubleCheck最终也会使用DemoAClass_Factory类来实例化DemoAClass对象@SuppressWarnings("unchecked")private void initialize() {this.demoAClassProvider = DoubleCheck.provider(DemoAClass_Factory.create());}//调用DemoAClass的提供者,让其提供同一个对象@Overridepublic DemoAClass getAClass() {return demoAClassProvider.get();}//向DemoInject中注入需要的成员@Overridepublic void inject(DemoInject inject) {injectDemoInject(inject);}//调用对应的辅助注入类,注入需要的成员private DemoInject injectDemoInject(DemoInject instance) {DemoInject_MembersInjector.injectA(instance, demoAClassProvider.get());return instance;}}//subcomponnet2的Builder接口实现private final class DemoSubComponent2Builder implements DemoSubComponent2.Builder {@Overridepublic DemoSubComponent2 build() {//创建subcomponent2对象,并直接返回return new DemoSubComponent2Impl();}}//subcomponent2的具体实现类private final class DemoSubComponent2Impl implements DemoSubComponent2 {//因为DemoSubComponent2和DemoBClass并不是同一生命周期,所以,每次都创建一个新的//DemoBClass对象返回//因此,也就不需要相应的提供者类,也就不需要相应的对提供者类进行初始化    private DemoSubComponent2Impl() {}@Overridepublic DemoBClass getBClass() {return new DemoBClass();}}
}

在Component中完成了如下的工作:

  1. 实现了component,subcomponent,以及他们对应的Builder, Factory类,subcomponent是component的内部类
  2. 实现对应的接口方法,这些方法,会委托给其他的xxx_Factory,xxxx_MembersInjector辅助类来完成
  3. 被委托的xxx_Factory工厂类,xxx_MembersInjector辅助类,都会被正确的初始化

至此,我们对Dagger2生成的模板有了一个整体把握,并对其中的实现细节,做了一些注释说明。

接下来我们继续看看SystemUI中的重要组件的创建过程

SystemUI 创建Dagger2依赖的起点

第一篇文章,我们一笔带过了SystemUIApplication.onCreate()中的mContextAvailableCallback.onContextAvailable(this).

这就是Dagger2创建依赖的起点,mContextAvailableCallback的赋值,在SystemUIAppComponentFactory.java中。

如下:

@NonNull
@Override
//此函数在创建SystemUIApplication之前调用
public Application instantiateApplicationCompat(@NonNull ClassLoader cl, @NonNull String className)throws InstantiationException, IllegalAccessException, ClassNotFoundException {//调用父类方法,创建Application,此处的app实例为SystemUIApplicationApplication app = super.instantiateApplicationCompat(cl, className);if (app instanceof ContextInitializer) {//给SystemUIApplication的mContextAvailableCallback赋值((ContextInitializer) app).setContextAvailableCallback(context -> {//SystemUIApplication的onCreate处开始调用//1. 首先创建SystemUIFactory(SystemUIFactory会负责创建各种依赖)//2. 然后马上注入SystemUIAppComponentFactory所需的成员(即//	ContextComponentHelper)SystemUIFactory.createFromConfig(context);SystemUIFactory.getInstance().getSysUIComponent().inject(SystemUIAppComponentFactory.this);});}return app;
}

从上面我们看见了SystemUIFactory调用静态方法createFromConfig进行创建。从名字也能猜测一二:从配置中创建SystemUIFactory

唉,这个就值得思考了,为何要从配置中创建?难道可以配置成不同的SytemUIFactory吗?每个SystemUIFactory有什么区别吗?

我们带着问题往下看

SystemUIFactory

SystemUIFactory从名字上,似乎是SystemUI的工厂类。在上一篇文章Android 12 源码分析 —— 应用层 二(SystemUI大体组织和启动过程):http://t.csdn.cn/AuzsL的自定义组件小节中,我们自定以的组件继承了SystemUI

难道SystemUIFactory是它的工厂类?

我们来看看源码,为了减少干扰,我们从上一小节中的createFromConfig方法开始查看,如下

public static void createFromConfig(Context context) {createFromConfig(context, false);
}@VisibleForTesting
//最终调用的地方
public static void createFromConfig(Context context, boolean fromTest) {//1. 如果SystemUIFactory,即mFactory,已经存在,则什么也不做if (mFactory != null) {return;}//2. 倘若1不满足,则读取配置文件中的类名,然后使用反射创建这个对象//config_systemUIFactoryComponent的值即为com.android.systemui.SystemUIFactory//那么请思考,是不是还有另外的类名,事实上是有的,还可以为://com.android.systemui.tv.TvSystemUIFactory.我们后面来比较他们之间的区别final String clsName = context.getString(R.string.config_systemUIFactoryComponent);if (clsName == null || clsName.length() == 0) {throw new RuntimeException("No SystemUIFactory component configured");}//3. 创建完成对象之后,则调用init方法try {Class<?> cls = null;cls = context.getClassLoader().loadClass(clsName);mFactory = (SystemUIFactory) cls.newInstance();mFactory.init(context, fromTest);} catch (Throwable t) {Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);throw new RuntimeException(t);}
}

接下来,继续进入其初始化函数中,如下:

@VisibleForTestingpublic void init(Context context, boolean fromTest)throws ExecutionException, InterruptedException {//是否要运行初始化的一个判断,全部满足下面的条件则初始化//1. 非测试模块运行//2. 是主用户//3. 是主进程mInitializeComponents = !fromTest&& android.os.Process.myUserHandle().isSystem()&& ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());//又见GlobalRootComponent。在上一篇文章中已经提及过//这里就是去构建整个应用最最顶层的那个ComponentmRootComponent = buildGlobalRootComponent(context);//从GlobalRootComponent中,获取需要创建依赖的Builder。然后创建依赖//1. 先创建WmComponent (这是一个与SysUIComponent站在同一维度的Component,它负责提供WindowManager相关的东西,此处可不用太过在意,后面会详解)mWMComponent = mRootComponent.getWMComponentBuilder().build();if (mInitializeComponents) {//然后初始化mWMComponent.init();}//2. 再创建SysUIComponentSysUIComponent.Builder builder = mRootComponent.getSysUIComponent();if (mInitializeComponents) {//将需要的各种食材,放入Builder,最后build()一下就是最终对象//使用Builder,可以不用考虑这些函数的调用顺序,他们会在Builder类中被处理好builder = prepareSysUIComponentBuilder(builder, mWMComponent).setPip(mWMComponent.getPip()).setLegacySplitScreen(mWMComponent.getLegacySplitScreen()).setSplitScreen(mWMComponent.getSplitScreen()).setOneHanded(mWMComponent.getOneHanded()).setBubbles(mWMComponent.getBubbles()).setHideDisplayCutout(mWMComponent.getHideDisplayCutout()).setShellCommandHandler(mWMComponent.getShellCommandHandler()).setAppPairs(mWMComponent.getAppPairs()).setTaskViewFactory(mWMComponent.getTaskViewFactory()).setTransitions(mWMComponent.getTransitions()).setStartingSurface(mWMComponent.getStartingSurface()).setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper());} else {builder = prepareSysUIComponentBuilder(builder, mWMComponent).setPip(Optional.ofNullable(null)).setLegacySplitScreen(Optional.ofNullable(null)).setSplitScreen(Optional.ofNullable(null)).setOneHanded(Optional.ofNullable(null)).setBubbles(Optional.ofNullable(null)).setHideDisplayCutout(Optional.ofNullable(null)).setShellCommandHandler(Optional.ofNullable(null)).setAppPairs(Optional.ofNullable(null)).setTaskViewFactory(Optional.ofNullable(null)).setTransitions(Transitions.createEmptyForTesting()).setStartingSurface(Optional.ofNullable(null)).setTaskSurfaceHelper(Optional.ofNullable(null));}//创建,然后初始化mSysUIComponent = builder.build();if (mInitializeComponents) {mSysUIComponent.init();}//3. 通过SysUIComponent获得Dependency对象,然后调用start()函数进行初始化,//	(似乎叫做init()函数更加合理一点)Dependency dependency = mSysUIComponent.createDependency();dependency.start();}

在SystemUIFactory.init()函数中,主要做了四件事:

  1. 创建并初始化GlobalRootComponent
  2. 创建并初始化WMComponent
  3. 创建并初始化SysUIComponent
  4. 创建并初始化Dependency

这4个对象,即为SystemUI整个应用需要用到的组件,通过这4个组件,就可以得到Dagger2中的各种各样的依赖。

在挨个介绍这4个对象之前。我们先处理一下前面的疑问,分别有:

  1. SystemUIFactory为何要从配置中创建?
  2. SystemUIFactory的命名,是不是感觉有些奇怪了?

回答问题1:因为SystemUIFactory不仅仅可以直接实例化SysetmUIFactory,有时还需要实例化SystemUIFactory的子类(比如TvSystemUIFactory),之所以需要有一个TvSystemUIFactory,是因为它需要一个不同版本的GlobalRootComponent叫做TvGloabRootComponent.而这个TvGlobalRootComponent继承于GlobalRootComponent

回答问题2:显然SystemUIFactory提供的实例创建更应该叫GloablRootComponentFactory。而不应该叫SystemUIFactory.我想这也是开发人员,不够严谨的地方。
当然,也可能还有我未理解到的意思

此时,SystemUIFactory其实已经没有什么内容可以介绍了,剩下的内容无外乎getter,内容简单,此处略过

接下来就是几个Component的创建过程了

GlobalRootComponent

GlobalRootComponent的创建,需要从它的图说起。在Android 12 源码分析 —— 应用层 二(SystemUI大体组织和启动过程):http://t.csdn.cn/AuzsL一文中,我们给GlobalRootComponent定义了一个Builder接口。

如下:

@Singleton
@Component(modules = {GlobalModule.class,SysUISubcomponentModule.class,WMModule.class})
public interface GlobalRootComponent {@Component.Builderinterface Builder {@BindsInstanceBuilder context(Context context);GlobalRootComponent build();}//省略无关紧要部分
}

那么Dagger2对这个Builder是怎么生成的呢?

Dagger2生成的GlobalRootComponent.Builder

在编译的过程中,上面的Builder接口,将会被Dagger2实现,并生成一个名叫Builder的类。如下:
该类位于:

out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/kapt/gen/sources/com/android/systemui/dagger/DaggerGlobalRootComponent.java
private static final class Builder implements GlobalRootComponent.Builder {private Context context;@Overridepublic Builder context(Context context) {//1. 先判断传入的参数是否为空//2. 将传入的参数,赋值给内成员(这也是在上一篇文章中提及的BindsInstance的语义)this.context = Preconditions.checkNotNull(context);return this;}@Overridepublic GlobalRootComponent build() {//1. 先判断context是否为空//2. new一个DaggerGlobalRootComponennt对象,该对象就是Dagger2中实现//	GlobalRootComponent接口的对象Preconditions.checkBuilderRequirement(context, Context.class);return new DaggerGlobalRootComponent(new GlobalModule(), context);}}

接下来我们看看Dagger2对GlobalRootComponent的实现细节。

Dagger2生成的GlobalRootComponent

生成的文件位于:

out/soong/.intermediates/frameworks/base/packages/SystemUI/SystemUI-core/android_common/kapt/gen/sources/com/android/systemui/dagger/DaggerGlobalRootComponent.java

内容特别多,为了简化分析,我会缩进部分代码,并对其中的实现细节进行简化,因为他们的实现和前面介绍的模板几乎没有差别,现在只需对其结构进行了解。

public final class DaggerGlobalRootComponent implements GlobalRootComponent {//定义一个成员变量,类型为Provider<T> 它可以通过//Provider<T>.get() 得到对应的具体的对象//正是这个get()函数,可以让成员真正被需要的时候才初始化//这样可以减少内存的使用//可将其理解为:Context的提供者(同前面的模板一起理解,几乎可以肯定,它的实现为//							DoubleCheck类)private Provider<Context> contextProvider;//省略余下的类似的成员定义//构造器函数,Dagger2根据我们画的图,会正确的处理构造函数所需的依赖private DaggerGlobalRootComponent(GlobalModule globalModuleParam, Context contextParam) {this.context = contextParam;initialize(globalModuleParam, contextParam);}//辅助构造函数的方法,用于初始化成员变量@SuppressWarnings("unchecked")private void initialize(final GlobalModule globalModuleParam, final Context contextParam) {//在方法体中,Dagger2根据我们画的图,自动去找寻需要的对象,并初始化本对象成员//这也正是Dagger2的最大作用,它可以简化我们的代码编写。自动帮我们完成各种各样的依赖//方法体中的赋值相对简单。不做过多介绍//这里面其实就是:使用DoubleCheck对成员进行初始化(结合前面的模板进行思考)} //同构造函数一样,mainResources你也不需要知道为什么会出现在这个地方(这属于Dagger2的内部//				细节)。Dagger2会自动处理好对于这个函数的调用private Resources mainResources() {//AAA_BBBFactory通常表示,AAA类的BBB方法的工厂类,它实现了Factory<T>接口return FrameworkServicesModule_ProvideResourcesFactory.provideResources(context);}//同MainResources()函数一样,Dagger2自动处理好其调用private Executor mainExecutor() {return GlobalConcurrencyModule_ProvideMainExecutorFactory.provideMainExecutor(context);}//这个是实现的GlobalRootComponent的接口.在这个接口中,直接new了一个对象并返回//倘若每次调用这个方法,则会创建不同的对象。这是因为他们的生命周期不一样//GlobalRootComponent的生命周期为@SingleTton//而WMComponent.Builder的生命周期为@WMSingleton@Overridepublic WMComponent.Builder getWMComponentBuilder() {return new WMComponentBuilder();}//同WMComponent.Builder一样,直接new了一个对象返回,因为其生命周期为//@SysUISingleton@Overridepublic SysUIComponent.Builder getSysUIComponent() {return new SysUIComponentBuilder();}//对GlobalRootComponent的接口方法的实现。在实现中,使用了ThreadFactoryImpl_Factory类来//实例化对象//这里可能就会有以问题了,为何不是直接new一个对象,反而用了ThreadFactoryImpl_Factory来实例化//因为在GlobalConcurrencyModule里面有如下的绑定关系//@Binds//public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl)//在需要ThreadFactory的时候,就会创建ThreadFactoryImpl实例。而ThreadFactoryImpl实例,//则由//其对应的工厂类(ThreadFactoryImpl_Factory)来创建@Overridepublic ThreadFactory createThreadFactory() {return ThreadFactoryImpl_Factory.newInstance();}//返回一个为空的Optionalprivate static <T> Provider<Optional<T>> absentJdkOptionalProvider() {@SuppressWarnings("unchecked") // safe covariant castProvider<Optional<T>> provider = (Provider<Optional<T>>) ABSENT_JDK_OPTIONAL_PROVIDER;return provider;}//使用Provider包装一个非空的Optional,所有操作最后都会调用到其内部的delegateprivate static final class PresentJdkOptionalInstanceProvider<T> implements Provider<Optional<T>> {}//暂时未知private static final class PresentJdkOptionalLazyProvider<T> implements Provider<Optional<Lazy<T>>> {}//实现Builder接口//各个接口,只是简单的setterprivate static final class Builder implements GlobalRootComponent.Builder {}//实现接口private final class WMComponentBuilder implements WMComponent.Builder {}//实现子组件,只要是其子组件,就在内部实现一个非静态的类,因为subcomponent含有//component的所有依赖关系,因此需要实现为一个内部类,这样它就会持有一个对外部类的引用//同理,WMComponentImpl的subcomponent也会在其内部出现private final class WMComponentImpl implements WMComponent {}//实现Builderprivate final class SysUIComponentBuilder implements SysUIComponent.Builder {}//实现子组件private final class SysUIComponentImpl implements SysUIComponent {}
}

在对GlobalRootImpl接口的实现中,分别完成了如下的工作:

  1. 根据Dagger2中的图实现对应的构造函数
  2. 根据Dagger2中的图实现对应的接口方法
  3. 对subcomponent,将其实现为内部类
  4. 对各个Builder接口,进行实现

从上我们也可以看到,其和我们的Demo模板的生产,差异不大,只是体量变大了很多

接下来看看WMComponent的实现

WMComponent

WMComponent的定义如下

@WMSingleton
@Subcomponent(modules = {WMShellModule.class})
public interface WMComponent {//@Subcomponent.Builder定义了Builder接口//在上面,已经看到了WMComponent.Builder的接口实现了@Subcomponent.Builderinterface Builder {WMComponent build();}//默认方法,用来初始化整个Shell.此处可以暂时不用管Shell对应的逻辑语义//实际上,你可以将其理解为对WindowManager的部分功能的封装并提供接口让用户与WM可以交互//犹如Bash shell(Bourne-Again Shell)在OS中的作用default void init() {getShellInit().init();}//用来获取ShellInit的实现,它只会在上面的init函数中被调用//ShellInit的抽象:表示对Shell进行初始化的一个入口点。@WMSingletonShellInit getShellInit();//返回一个Optional<ShellCommandHandler>对象@WMSingletonOptional<ShellCommandHandler> getShellCommandHandler();//省略类似部分
}

在上面的图中,我们“绘制”了WMComponent的Builder接口,同时还绘制了可以获取ShellInit的接口。

在查看Dagger2生成的代码之前,我们看看,我们是怎么绘制图,让Dagger2来生成ShellInit的。

再次注意:ShellInit的抽象表示的是WMShell的一个入口对象。

从上面的代码可以知道,WMComponent的模块有WMShellModule。直接搜寻其源码,查看是否有ShellInit的返回,发现并没有。但是WMShellModule的还包含一个WMShellBaseModule在其中可以搜寻到ShellInit的返回。如下

WMShellBaseModule

WMShellBaseModuel,告诉了Dagger2如何创建ShellInit对象,如下

@Module
public abstract class WMShellBaseModule{//省略部分代码//通过@Provides告诉Dagger2,当需要ShellInit的时候,将会调用本函数//即返回ShellInitImpl的asShellInit()方法//而ShellInitImpl的对象,则通过下一个方法进行提供@WMSingleton@Providesstatic ShellInit provideShellInit(ShellInitImpl impl) {return impl.asShellInit();}//告诉Dagger2如果需要ShellInitImpl对象的时候,则由本方法提供//在本方法中,通过new一个ShellInitImpl对象来创建对应的ShellInitImpl@WMSingleton@Providesstatic ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController,DragAndDropController dragAndDropController,ShellTaskOrganizer shellTaskOrganizer,Optional<BubbleController> bubblesOptional,Optional<LegacySplitScreenController> legacySplitScreenOptional,Optional<SplitScreenController> splitScreenOptional,Optional<AppPairsController> appPairsOptional,Optional<PipTouchHandler> pipTouchHandlerOptional,FullscreenTaskListener fullscreenTaskListener,Transitions transitions,StartingWindowController startingWindow,@ShellMainThread ShellExecutor mainExecutor) {return new ShellInitImpl(displayImeController,dragAndDropController,shellTaskOrganizer,bubblesOptional,legacySplitScreenOptional,splitScreenOptional,appPairsOptional,pipTouchHandlerOptional,fullscreenTaskListener,transitions,startingWindow,mainExecutor);}
}

从上面的代码可以知道,WMShell的初始化入口点,是ShellInitImpl的asShellInit()方法返回的对象.至于该对象内部是如何进行初始化的,会在合适的时候介绍到,现在我们专注于WMComponent的各种初始化上

同理对WMComponent中的其他接口方法,实现细节类似。

接下来看看Dagger2对WMComponenet相关代码的实现。首先是其Builder的实现

Dagger2生成的WMComponent.Builder

在前文中的GlobalRootComponent的实现中,我们看到了WMComponent.Builder的实现,现将其内容展现如下:

public final class DaggerGlobalRootComponent implements GlobalRootComponent{//省略部分代码private final class WMComponentBuilder implements WMComponent.Builder{@Overridepublic WMComponent build(){//直接new一个WMComponentImpl,这个对象是对WMComponent接口的实现return new WMComponentImpl();}}//省略部分代码
}

接下来看看,Dagger2对WMComponent接口的实现。

Dagger2生成的WMComponent

WMComponent作为GlobalRootComponent的subcomponent,则WMComponent被放置在了DaggerGlobalRootComponent的内部,如下:

public final class DaggerGlobalRootComponent implements GlobalRootComponent{//省略部分代码private final class WMComponentImpl implements WMComponent {//同component一样,将成员定义成Provider的封装//只要在需要的时候才通过其Provider<T>.get()方法获取其内部的实现private Provider<Handler> provideShellMainHandlerProvider;//省略相似代码//生成的构造函数,通过调用initialize()方法进行初始化private WMComponentImpl() {initialize();}//对内部的成员进行初始化@SuppressWarnings("unchecked")private void initialize() {//Dagger2自动找到需要的依赖,然后对本对象的成员,进行赋值初始化操作}//实现其对应的接口@Overridepublic ShellInit getShellInit() {return provideShellInitProvider.get();}//省略其他相似代码}//省略部分代码
}

从上面生成的代码中,我们看一看到WMComponent这个subcomponent 几乎和GlobalRootComponent的结构一致。

而这些生成内的东西,现在不需要关系,我们只需要知道其结构和大体抽象代码,在真正的业务实现细节中,会在有需要的地方进行提及。

再次提醒:WMComponent为Window Manager提供给SystemUI的交互接口。现在只需知道这一层抽象意义即可

接下来继续看看SysUIComponent.

SysUIComponent

先看看源码中,是如何定义SysUIComponent的,即我们如何绘制SysUIComponent的,如下:


@SysUISingleton
@Subcomponent(modules = {DefaultComponentBinder.class,DependencyProvider.class,SystemUIBinder.class,SystemUIModule.class,SystemUIDefaultModule.class})
public interface SysUIComponent {//又来Builder模式//并传递WMComponent中相应的组件给SysUIComponent//正是基于此,WMComponent组件的初始化必须在SysUIComponent组件之前完成//且可以通过提供Optional<?>.empty()来替换其中的组件,这也是SystemUIFactory中的init()//函数中的部分代码@SysUISingleton@Subcomponent.Builderinterface Builder {//绑定实例,内容如前文,对应的语义见上一篇博文<http://t.csdn.cn/4LyLq>@BindsInstanceBuilder setPip(Optional<Pip> p);//省略一些相似代码//build函数,返回SysUIComponent接口的实现//其实,仔细看过前面的WMComponent就会知道,返回的实例多半叫做SysUIComponentImplSysUIComponent build();}//默认的初始化操作,为了保持跟WMComponent一致的操作步调,这里依然有一个init()接口//但是它的函数体为空default void init() {// Do nothing}//返回一个BootCompleteCacheImpl对象//这个对象,我们在前一篇博文<http://t.csdn.cn/4LyLq>中介绍过,用于缓存是否所有的//SystemUI服务都已经启动完成//其实,这里读者可以思考一下,是否BootCompleteCacheImpl对象还会再需要?如果不需要了,//它能被GC回收吗?//如果不能回收,是不是代表BootCompleteCacheImpl的生命周期不应该这样设置?@SysUISingletonBootCompleteCacheImpl provideBootCacheImpl();//返回一个Dependency对象//Dependency对象,提供了SystemUI各个组件的快捷访问,后文我们将会其进行详细介绍@SysUISingletonDependency createDependency();//省略相同的代码//inject接口,表示对PeopleProvider对象中,注入被@Inject标记的成员void inject(PeopleProvider peopleProvider);
}

其实有了上面的注释,以及前面的模板,读者应该已经知道了Dagger2对上面的一些生成代码了。

接下来我们会先看对应的Builder接口的生成,然后再看SysUIComponent接口的生成

Dagger2生成的SysUIComponent.Builder

SysUIComponent.Builder接口依然在DaggerGlobalRootComponent类中,如下:

private final class SysUIComponentBuilder implements SysUIComponent.Builder {//成员的定义,省略//定义必要的成员,用于在build阶段生成最终的SysUIComponentImpl//各种setter接口函数的实现,用于保存传入的参数,很简单,省略//接口的build函数实现,先检查,然后new一个SysUIComponentImpl对象@Overridepublic SysUIComponent build() {Preconditions.checkBuilderRequirement(setPip, Optional.class);Preconditions.checkBuilderRequirement(setLegacySplitScreen, Optional.class);Preconditions.checkBuilderRequirement(setSplitScreen, Optional.class);Preconditions.checkBuilderRequirement(setAppPairs, Optional.class);Preconditions.checkBuilderRequirement(setOneHanded, Optional.class);Preconditions.checkBuilderRequirement(setBubbles, Optional.class);Preconditions.checkBuilderRequirement(setTaskViewFactory, Optional.class);Preconditions.checkBuilderRequirement(setHideDisplayCutout, Optional.class);Preconditions.checkBuilderRequirement(setShellCommandHandler, Optional.class);Preconditions.checkBuilderRequirement(setTransitions, ShellTransitions.class);Preconditions.checkBuilderRequirement(setStartingSurface, Optional.class);Preconditions.checkBuilderRequirement(setTaskSurfaceHelper, Optional.class);return new SysUIComponentImpl(new DependencyProvider(), new NightDisplayListenerModule(), new UserModule(), setPip, setLegacySplitScreen, setSplitScreen, setAppPairs, setOneHanded, setBubbles, setTaskViewFactory, setHideDisplayCutout, setShellCommandHandler, setTransitions, setStartingSurface, setTaskSurfaceHelper);}}

Builder函数的实现很是简单

  1. 保存必要的中间对象,如各种Optional<T>
  2. new一个SysUIComponent接口的实现

接下来看看SysUIComponent的实现,其实到这里,大家已经非常熟悉了,它的结构等同于GlbalRootComponent,也等同于WMComponent

Dagger2生成的SysUIComponent

作为GlobalRootComponent的subcomponent,所以SysUIComponent的生成依然在DaggerGlobalRootComponent中,如下

public final class DaggerGlobalRootComponent implements GlobalRootComponent {//省略不相干的部分private final class SysUIComponentImpl implements SysUIComponent {//省略成员定义,其结构和内容,相似于GlobalRootComponent和WMComponent,故不再啰嗦//对应的构造函数private SysUIComponentImpl(DependencyProvider dependencyProviderParam,NightDisplayListenerModule nightDisplayListenerModuleParam, UserModule userModuleParam,Optional<Pip> setPipParam, Optional<LegacySplitScreen> setLegacySplitScreenParam,Optional<SplitScreen> setSplitScreenParam, Optional<AppPairs> setAppPairs,Optional<OneHanded> setOneHandedParam, Optional<Bubbles> setBubblesParam,Optional<TaskViewFactory> setTaskViewFactoryParam,Optional<HideDisplayCutout> setHideDisplayCutoutParam,Optional<ShellCommandHandler> setShellCommandHandlerParam,ShellTransitions setTransitionsParam, Optional<StartingSurface> setStartingSurfaceParam,Optional<TaskSurfaceHelper> setTaskSurfaceHelper) {//因为成员函数过多,因此分成了7个初始化辅助函数进行初始化}//initialize,initialize1,initialize2,initialize3,initialize4,initialize5,//initialize6//这几个函数都是用于初始化成员的,其中初始化成员的方法和过程,跟GlobalRootComponent//和WMComponent类似@SuppressWarnings("unchecked")private void initialize()//实现的接口,省略,较简单//对PeopleProvider中被@Inject标注的成员,进行赋值@Overridepublic void inject(PeopleProvider peopleProvider) {injectPeopleProvider(peopleProvider);}//使用辅助类 Type_MembersInjector。进行成员的赋值//PeopleProvider_MembersInjector.injectMPeopleSpaceWidgetManager()的实现就为://instance.mPeopleSpaceWidgetManager = mPeopleSpaceWidget;private PeopleProvider injectPeopleProvider(PeopleProvider instance) {PeopleProvider_MembersInjector.injectMPeopleSpaceWidgetManager(instance, peopleSpaceWidgetManagerProvider.get());return instance;}//SysUIComponent的subcomponent的builder的实现private final class ExpandableNotificationRowComponentBuilder implements ExpandableNotificationRowComponent.Builder {}//SysUIComponent的subcomponent的实现private final class ExpandableNotificationRowComponentImpl implements ExpandableNotificationRowComponent {}//SysUIComponent的subcomponent的Factory的实现private final class KeyguardBouncerComponentFactory implements KeyguardBouncerComponent.Factory {}//SysUIComponent的subcomponent的实现private final class KeyguardBouncerComponentImpl implements KeyguardBouncerComponent {}//省略相似部分}
}

从上面的生成结果中,我们可以看到,SysUIComponent的生成,跟GlobalRootComponent很是类似。主要完成:

  1. 其内部subcomponent的Bulider的实现
  2. 其内部subcomponent的Factory的实现
  3. 其内部subcomponent的实现
  4. 其内部接口方法的实现

有了上面的介绍,现在基本上已经非常明确,各个组件的初始化过程和内部实现细节了。

接下来还剩下最后一个Dependency的实现细节

Dependency

Dependency对应的抽象是什么?这里直接给出一个结论:为了方便在各个模块中能够直接引用Dagger2中的依赖。举个例子,如果我要通过SysUIComponent访问某个依赖,则可能出现如下的调用情况:

d=sysuicomponent.getxxx().getxxx().getxxx().getxxx();
d.xxx();

上面的代码中,出现了多层的getxxx()函数,因此,为了方便,将一些常见的依赖,通过Dependency来调用即可。现在提供的方法为:Dependency.get(依赖.class)从而获得对应的依赖

这也是这个类的名字的由来。

有了上面的观点就非常容易看懂Dependency.java文件了

如下:

@SysUISingleton
public class Dependency {//一些简单的成员定义,省略//由Dagger2进行创建@Injectpublic Dependency() {}//初始化各个依赖protected void start() {//mProviders是一个以Object为对象,以LazyDependencyCreator为值的Map//LazyDependencyCreator封装创建依赖的方法,用于在需要的时候创建mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);//省略相似部分//将Dependency对象保存在Static字段中,便于访问Dependency.setInstance(this);}//省略一些简单的函数//当访问某个具体的依赖时,最终的访问点private synchronized <T> T getDependencyInner(Object key) {@SuppressWarnings("unchecked")//1. 首先检查依赖的缓存中,是否有对应的依赖,其中缓存使用了ArrayMap进行存储(注意其和//	HashMap之间的差别)//2. 如果没有,调用createDependency创建T obj = (T) mDependencies.get(key);if (obj == null) {obj = createDependency(key);//3. 如果创建完成,就将其加入缓存中,方便未来调用。注意此处有同步控制,防止并发//	带来的问题mDependencies.put(key, obj);//4. 如果某些依赖,需要使用dump功能,则将其注册到DumpManager。如同//	SystemUIService中的初始化最后部分一样if (autoRegisterModulesForDump() && obj instanceof Dumpable) {mDumpManager.registerDumpable(obj.getClass().getName(), (Dumpable) obj);}}return obj;}//创建依赖的具体实现@VisibleForTestingpublic <T> T createDependency(Object cls) {//1. 检查参数,必须是DependencyKey类型或者Class类型Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);//2. 通过key,得到LazyDependencyCreator对象。这个对象通常封装着一个用于创建具//	体对象的方法。如Lazy<T>的get()方法@SuppressWarnings("unchecked")LazyDependencyCreator<T> provider = mProviders.get(cls);if (provider == null) {throw new IllegalArgumentException("Unsupported dependency " + cls+ ". " + mProviders.size() + " providers known.");}//3. 调用封装的方法(如Lazy<T>.get()),创建具体的对象return provider.createDependency();}//定义延迟创建的一种封装形式private interface LazyDependencyCreator<T> {T createDependency();}//对依赖的key的封装public static final class DependencyKey<V> {private final String mDisplayName;public DependencyKey(String displayName) {mDisplayName = displayName;}@Overridepublic String toString() {return mDisplayName;}}//省略掉一些简单的代码
}

从上面可以知道,Dependency就做了两件事情:

  1. 缓存需要用的依赖的Lazy<T>封装
  2. 缓存需要用的依赖的具体实例。
  3. 然后在需要的时候通过get()函数,即可得到相应的依赖

至此,我们介绍了SystemUIFactory的init()方法中,重要组件的创建和初始化细节。也完成了整个SystemUI的启动

现在将整个启动过程,总结如下:

  1. system_server启动之后,开始启动各种服务
  2. 在启动其他服务的时候,会先通过PackageManager,获得要启动systemui的组件名字,然后根据名字启动systemui组件
  3. 在上面一步获得的名字就是SystemUIService。
  4. 启动SystemUIService,则会先创建SystemUIApplication,在创建之前会先调用SystemUIAppComponentFactory添加相应的依赖注入
  5. SystemUIApplication创建之后,通过调用SystemUIFactory.init() 方法初始化Dagger2中的依赖。(本文的内容)
  6. 同时在SystemUIApplication中会监听系统的启动广播。
  7. 接着创建SystemUIService,再创建之前,还会先调用SystemUIAppComponentFactory相应的方法,添加依赖注入
  8. 创建SystemUIService之后,通过SystemUIApplication启动各种服务

接下来,我们将进入SystemUI的UI部分。看看状态栏是如何被加入整个屏幕的。然后一步步拆分,查看下拉状态栏中的各个图标的实现

因为最近要上课,尽量保持一周一更

相关文章:

Android 12 源码分析 —— 应用层 三(SystemUIFactory及其Dependency解析)

Android 12 源码分析 —— 应用层 三&#xff08;SystemUIFactory及其Dependency解析&#xff09; 在上一篇文章中&#xff0c;介绍了SystemUI的启动流程&#xff0c;并且简单提及了Dagger2用来管理各个SystemUI中要用的依赖。而这部分代码就在&#xff1a;mContextAvailableC…...

考前冲刺上岸浙工商MBA的备考经验分享

2023年对于许多人来说都是不平凡的一年&#xff0c;历经三年的抗争&#xff0c;我们终于成功结束了疫情。而我也很幸运的被浙工商MBA项目录取&#xff0c;即将开始全新的学习生活。身为一名已在职工作6年的人&#xff0c;能够重回校园真是一种特别令人激动的体验。今天&#xf…...

XmlDocument.SelectNodes 不起作用

今天采用Xpath读取Xml节点&#xff0c;怎么都读不出。 问题分析&#xff1a; 错误代码如下&#xff1a; XmlDocument xmlD new XmlDocument();xmlD.PreserveWhitespace true;xmlD.LoadXml(xStr);xmlD.SelectNodes("job-scheduling-data/schedule/job");经排查 do…...

部署单点elasticsearch

部署elasticsearch 创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络 docker network create es-net 拉取镜像 我们采用elasticsearch的7.12.1版本的镜像 docker pull elasticsearch:7.12.1 运行 运行docker命令&a…...

ElementUI浅尝辄止16:Tag 标签

用于标记和选择。 1.如何使用&#xff1f; 由type属性来选择tag的类型&#xff0c;也可以通过color属性来自定义背景色。<el-tag>标签一</el-tag> <el-tag type"success">标签二</el-tag> <el-tag type"info">标签三</e…...

Java虚拟机(JVM)框架

见&#xff1a;GitHub - eHackyd/Java_JVM: Java虚拟机(JVM)框架的学习笔记...

配置Publisher 的编译规则

步骤 1&#xff1a;创建ROS Package 使用以下命令创建一个新的ROS软件包&#xff1a; catkin_create_pkg my_publisher_package roscpp std_msgs步骤 2&#xff1a;编辑 CMakeLists.txt 文件 打开您的ROS软件包的 CMakeLists.txt 文件&#xff0c;通常位于软件包的根目录。您…...

【SpringBoot】接口实现:SpringBoot实现博客系统的文章列表页接口代码

以下是一个简单的Spring Boot博客系统的文章列表页接口代码示例&#xff1a; java RestController RequestMapping("/articles") public class ArticleController {Autowiredprivate ArticleService articleService;GetMapping("/")public List<Artic…...

如何使用SQL系列 之 如何在SQL中插入数据

简介 结构化查询语言&#xff0c;通常被称为SQL&#xff0c;在允许您将数据插入表中方面提供了极大的灵活性。例如&#xff0c;你可以使用VALUES关键字指定单独的行数据&#xff0c;使用SELECT查询从现有表中复制整组数据&#xff0c;以及以使SQL自动插入数据的方式定义列。 …...

【LeetCode题目详解】1281题 整数的各位积和之差 面试题 01.01. 判定字符是否唯一 python题解(作业一二)

本文章以python为例! 一、力扣第1281题&#xff1a;整数的各位积和之差 问题描述&#xff1a; 1281. 整数的各位积和之差 给你一个整数 n&#xff0c;请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。 示例 1&#xff1a; 输入&#xff1a;n 234 输出…...

1.12 进程注入ShellCode套接字

在笔者前几篇文章中我们一直在探讨如何利用Metasploit这个渗透工具生成ShellCode以及如何将ShellCode注入到特定进程内&#xff0c;本章我们将自己实现一个正向ShellCodeShell&#xff0c;当进程被注入后&#xff0c;则我们可以通过利用NC等工具连接到被注入进程内&#xff0c;…...

MySQL 日志系统

重要日志模块 日志文件bin logredo log**关于循环写入和擦除的checkpoint 规则**redo log 怎么刷入磁盘的 binlog 和 redo log 有什么区别&#xff1f;undo log 日志文件 错误日志&#xff08;error log&#xff09;&#xff1a; 错误日志文件对 MySQL 的启动、运行、关闭过程进…...

LeetCode刷题---Two Sum(一)

文章目录 &#x1f340;题目&#x1f340;解法一&#x1f340;解法二&#x1f340;哈希表 &#x1f340;题目 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每…...

算法通关村第十七关——插入区间

LeetCode435,给定一个区间的集合&#xff0c;找到需要移除区间的最小数量&#xff0c;使剩余区间互不重叠。 示例1&#xff1a; 输入&#xff1a;interva1s[[1,3],[6,9]],newInterva1[2,5] 输出&#xff1a;[[1,5]&#xff0c;[6,9]] 解释&#xff1a;新区间[2,5]与[1,3]重…...

Jenkins java8安装版本安装

一、首先准备Jenkins、Jdk8、Tomcat9安装包 根据Jenkins官网介绍&#xff0c;Jenkins支持Java8的版本如下&#xff1a; 我们选择2.164版本进行安装&#xff0c;根据版本号支持输入下载地址&#xff1a;https://archives.jenkins.io/war/2.164/jenkins.war&#xff0c;进行下载…...

线上问诊:数仓开发(二)

系列文章目录 线上问诊&#xff1a;业务数据采集 线上问诊&#xff1a;数仓数据同步 线上问诊&#xff1a;数仓开发(一) 线上问诊&#xff1a;数仓开发(二) 文章目录 系列文章目录前言一、DWS1.最近1日汇总表1.交易域医院患者性别年龄段粒度问诊最近1日汇总表2.交易域医院患者…...

Ansible自动化运维工具(三)

目录 Ansible 的脚本 --- playbook 剧本 ​编辑2.vars模块实战实例 3.指定远程主机sudo切换用户 4.when模块实战实例 5.with_items迭代模块实战实例 6.Templates 模块实战实例 &#xff08;1&#xff09;先准备一个以 .j2 为后缀的 template 模板文件&#xff0c;设置引用…...

ChatGPT在创新和创业中的应用如何?

ChatGPT是一种基于大规模预训练的语言模型&#xff0c;它在创新和创业中有着广泛的应用。作为一种具备自然语言处理能力的模型&#xff0c;ChatGPT可以与用户进行对话&#xff0c;并提供相关的信息、建议和创意。以下是ChatGPT在创新和创业中的一些应用&#xff1a; 创意生成和…...

Log4j2 配置日志记录发送到 kafka 中

前言 log4j2 在 2.11.0 之后的版本&#xff0c;已经内置了 KafkaAppender 支持可以将打印的日志直接发送到 kafka 中&#xff0c;在这之前如果想要集中收集应用的日志&#xff0c;就需要自定义一个 Layout 来实现&#xff0c;相对来说还是比较麻烦的。 官网文档&#xff1a;L…...

Linux用户与组管理(03)(八)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、组管理 1、概述 2、用户信息查看 总结 前言 今天是学习用户与组管理的最后一节课&#xff0c;这节课主要是组管理的内容&#xff0c;希望能一起学习&#xff…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

高考志愿填报管理系统---开发介绍

高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发&#xff0c;采用现代化的Web技术&#xff0c;为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## &#x1f4cb; 系统概述 ### &#x1f3af; 系统定…...