首页 > 其他分享 >安卓架构组件-依赖注入

安卓架构组件-依赖注入

时间:2024-06-06 17:11:13浏览次数:28  
标签:架构 val Created 安卓 private fun 组件 login class

安卓依赖注入

什么是依赖注入

依赖注入(DI,Dependency Injection)是一种广泛的编程技术。把依赖(所需对象)传递给其它对象创建,好处是类的耦合更加松散,遵循依赖倒置的原则。

类获取所需对象

class Engine {  
    fun start() {  
        println("engine start")  
    }  
}
class Car {  
    private val engine: Engine = Engine()  
  
    fun start(){  
        engine.start()  
    }  
}

Car对Engine强依赖,如果需要其它类型的Engine,需要增加一个新的Engine,必须对Car进行改动。

依赖注入获取所需对象

interface Engine {  
    fun start()  
}  
  
class VEngine : Engine{  
    override fun start() {  
        println("VEngine start")  
    }  
}  
  
class WEngine : Engine{  
    override fun start() {  
        println("WEngine start")  
    }  
}

class Car(private val engine: Engine) {  
    fun start(){  
        engine.start()  
    }  
}

在构造函数中接收Engine对象作为参数,而不是初始化时构造自己的Engine对象,这就叫做依赖注入。
依赖注入还有很多其它的方式,如变量的get/set,就一一不介绍了。

安卓中手动实现依赖注入

手动实现依赖注入,就是依赖注入的原理。依赖注入框架会生成同样功能的样板代码。
假设有个登录场景,流程大概是这样:
LoginActivity->LoginViewModel->UserRepository

class UserRepository(
	private val localDataSource: UserLocalDataSource,
    private val remoteDataSource: UserRemoteDataSource  
)
class UserLocalDataSource()
class UserRemoteDataSource(
	private val loginService: LoginRetrofitService  
)

在需要的位置手动注入

在需要的地方创建依赖,缺点比较明显:

  1. 大量的样板代码
  2. 必须需要按照顺序声明依赖
  3. 复用困难。
/**
* 在LoginActivity的onCreate函数里:
*/
//创建UserRemoteDataSource需要的依赖
val retrofit = Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(LoginService::class.java)
//创建Repository需要的依赖
val remoteDataSource = UserRemoteDataSource(retrofit)
val localDataSource = UserLocalDataSource()
//创建ViewModel需要的依赖
val userRepository = UserRepository(localDataSource, remoteDataSource)
//创建ViewModel
loginViewModel = LoginViewModel(userRepository)

使用容器管理依赖

如果想要复用对象,可以创建一个类来初始化所需的依赖。

class AppContainer {
	private val retrofit = Retrofit.Builder()
	.baseUrl("https://example.com").build()
	.create(LoginService::class.java)
	private val remoteDataSource = UserRemoteDataSource(retrofit)
	private val localDataSource = UserLocalDataSource()
	val userRepository = UserRepository(localDataSource, remoteDataSource)  
}

如果需要在整个app中使用,可以放到application中:

class MyApplication : Application(){
	val appContainer = AppContainer()
}
/**
* 在LoginActivity的onCreate函数里:
*/
val appContainer = (application as MyApplication).appContainer  
loginViewModel = LoginViewModel(appContainer.userRepository)

使用容器来管理依赖还是有样板代码,且需要手动为依赖项创建实例对象。

管理依赖项的声明周期

之前把UserRepository的生命周期放在了application,在app被关闭前永远不会被释放。如果数据非常大,会导致内存占用过高。
假如,只有在LoginActivity需要依赖,其它位置不需要依赖:

  1. AppContainer 内部需要新增一个LoginContainer,用来存放UserRepository。
  2. 在LoginActivity:onCreate时手动创建LoginContainer并放到AppContainer,onDestory时把AppContainer设置为null,主动释放引用。
    根据生命周期来管理依赖项,这样时比较合理的。

Dagger实现依赖注入

什么是Dagger

Dagger是一个依赖注入的库,通过自动生成代码的方式,实现依赖注入。由于是在编译时生成的代码,性能会高于基于反射的方案。Dagger生成的代码和手动实现依赖注入生成的代码相似。

  • 每次调用函数都重新创建对象
//@Inject 注解会告诉Dagger,需要注入.
class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class UserRemoteDataSource @Inject constructor(){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
}
//DaggerApplicationGraph.create() 会创建新对象  
private val applicationGraph:ApplicationGraph = DaggerApplicationGraph.create()  
//applicationGraph.repository() 会创建新对象  
private val repository1 = applicationGraph.repository()  
private val repository2 = applicationGraph.repository()
/**
输出如下:
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
*/
  • 首次创建对象,之后全局复用这个单例对象.
@Singleton  
class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class UserRemoteDataSource @Inject constructor(){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
}
/**  
 * @Component 注解,用于 interface
 * Dagger会生成一个对应的类,以Dagger开头,ApplicationGraph就是DaggerApplicationGraph
 * 调用函数会返回对应的对象, Dagger会自动添加依赖
 * @Singleton 注解,用于标识为全局单例
 */
@Singleton  
@Component  
interface ApplicationGraph {  
    fun repository(): UserRepository  
}
@Singleton  
@Component  
interface LoginGraph {  
    fun repository(): UserRepository  
}
class One{  
    //DaggerApplicationGraph.create() 会创建新对象  
    private val applicationGraph:ApplicationGraph = DaggerApplicationGraph.create()  
    //applicationGraph.repository() 会创建新对象  
    private val repository1 = applicationGraph.repository()  
    private val repository2 = applicationGraph.repository()  
    init {  
        println("One Created")  
    }  
}
class Two{  
    private val loginGraph:LoginGraph = DaggerLoginGraph.create()  
    private val repository1 = loginGraph.repository()  
    private val repository2 = loginGraph.repository()  
    init {  
        println("Two Created")  
    }  
}
/**
One 和 Two 使用同一个对象,因为@Singleton注解是全局单例
输出如下:
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
One Created
UserLocalDataSource Created
UserRemoteDataSource Created
UserRepository Created
Two Created
*/

在安卓中使用Dagger

  • 对Activity中的字段进行注入
/**  
 * 为了演示方便,这里没有继承ViewModel  
 */
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
}
/**  
 * Activity 是用安卓系统实例化的,所以无法被Dagger创建  
 * 初始化的代码需要放在onCreate方法中  
 * 使用手动调用inject的方式,对字段进行注入,需要被注入的字段必须有@Inject注解  
 */  
class LoginActivity : AppCompatActivity() {  
    @Inject lateinit var loginViewModel: LoginViewModel  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        //调用inject,告诉Dagger,可以对当前对象的@Inject字段进行注入了  
        (applicationContext as MyApplication).applicationGraph.inject(this)  
        //调用完成,loginViewModel可以使用了  
  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_login)  
    }  
}
/**  
 * @Component 注解,用于 interface * Dagger会生成一个对应的类  
 * 调用函数会返回对应的对象, Dagger会自动添加依赖  
 * @Singleton 注解,用于标识为全局单例  
 * inject 调用函数手动注入带有@Inject注解的字段,函数名称是任意的,参数是需要注入的对象.
 */
@Singleton  
@Component  
interface ApplicationGraph {  
    fun repository(): UserRepository  
    fun inject(activity: LoginActivity)  
}
class MyApplication : Application() {  
    val applicationGraph: ApplicationGraph = DaggerApplicationGraph.create()  
}
  • 主动告知如何提供实例
//增加一个参数
class UserRemoteDataSource @Inject constructor(private val loginService: LoginService){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        loginService.login()  
    }  
}
//LoginService 只能通过Builder.create()创建
interface LoginService {  
    private class LoginServiceImpl : LoginService{  
        init {  
            println("LoginServiceImpl Created")  
        }  
    }  
    fun login() = println("login")  
    class Builder{  
        fun create(): LoginService {  
            return LoginServiceImpl()  
        }  
    }  
}
/**  
 * @DisableInstallInCheck 用于屏蔽绑定生命周期,这个是hilt的内容.  
 * @Module 是dagger的注解,用来告诉dagger可以提供实例对象.  
 * @Provides 用于@Module注解内,提供对应类型的实例对象,函数名任意.也可以添加@Singleton注解创建单例.
 */
@DisableInstallInCheck  
@Module  
class LoginModule {  
    @Provides  
    fun providerLoginService(): LoginService{  
        return LoginService.Builder().create()  
    }  
}
/**  
 * @Component 注解,用于 interface * Dagger会生成一个对应的类,以Dagger开头,ApplicationGraph就是DaggerApplicationGraph  
 * modules 参数用来指定对象该如何提供,必须带有@Module注解  
 * 调用函数会返回对应的对象, Dagger会自动添加依赖  
 * @Singleton 注解,用于标识为全局单例  
 * inject 调用函数手动注入带有@Inject注解的字段,函数名称是任意的,参数是需要注入的对象.  
 */
@Singleton  
@Component(modules = [LoginModule::class])  
interface ApplicationGraph {  
    fun repository(): UserRepository  
  
    fun inject(activity: LoginActivity)  
}
  • 子组件和作用域,限定作用域为生命周期
class MyApplication : Application() {  
    val applicationGraph: ApplicationComponent = DaggerApplicationComponent.create()  
}
@Singleton  
@Component(modules = [LoginModule::class, Subcomponent::class])  
interface ApplicationComponent {  
    fun loginComponent(): LoginComponent.Factory  
}
@Scope  
@Retention(value = AnnotationRetention.RUNTIME)  
annotation class ActivityScope
@ActivityScope  
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository,  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
    fun login(){  
        userRepository.login()  
    }  
}
@ActivityScope  
@Subcomponent  
interface LoginComponent {  
  
    @Subcomponent.Factory  
    interface Factory{  
        fun create(): LoginComponent  
    }  
  
    fun inject(activity: LoginActivity)  
    fun inject(fragment: LoginFragment)  
  
    fun repository(): UserRepository  
}
@DisableInstallInCheck  
@Module  
class LoginModule {  
    @Singleton  
    @Provides    fun providerLoginService(): LoginService{  
        return LoginService.Builder().create()  
    }  
}
@ActivityScope  
class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
  
    fun login(){  
        remoteDataSource.login()  
    }  
}
class UserRemoteDataSource @Inject constructor(  
    private val loginService: LoginService,  
    private val loginService2: LoginService,  
){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        loginService.login()  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class LoginActivity : AppCompatActivity() {  
    lateinit var loginComponent: LoginComponent  
  
    @Inject lateinit var loginViewModel: LoginViewModel  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        loginComponent = (application as MyApplication).applicationGraph.loginComponent().create()  
        loginComponent.inject(this)  
  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_login)  
  
        findViewById<Button>(R.id.activity_login_bt_open).setOnClickListener {  
            startActivity(Intent(this, LoginActivity::class.java))  
        }  
    }  
}
class LoginFragment : Fragment() {  
    @Inject  
    lateinit var loginViewModel: LoginViewModel  
    override fun onCreateView(  
        inflater: LayoutInflater, container: ViewGroup?,  
        savedInstanceState: Bundle?  
    ): View? {  
        (activity as LoginActivity).loginComponent.inject(this)  
        val view = inflater.inflate(R.layout.fragment_login, container, false)  
        view.findViewById<Button>(R.id.fragment_login_bt_login).setOnClickListener {  
            loginViewModel.login()  
        }  
        return view  
    }  
}
@ActivityScope  
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository,  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
    fun login(){  
        userRepository.login()  
    }  
}
interface LoginService {  
    private class LoginServiceImpl : LoginService{  
        init {  
            println("LoginServiceImpl Created")  
        }  
    }  
    fun login() = println("login")  
    class Builder{  
        fun create(): LoginService {  
            return LoginServiceImpl()  
        }  
    }  
}
@ActivityScope  
@Subcomponent  
interface LoginComponent {  
  
    @Subcomponent.Factory  
    interface Factory{  
        fun create(): LoginComponent  
    }  
  
    fun inject(activity: LoginActivity)  
    fun inject(fragment: LoginFragment)  
  
    fun repository(): UserRepository  
}

通过限定作用域的方式,把依赖注入和生命周期绑定,Activity和Activity中的Fragment使用同一个ViewModel。
Dagger虽然可以实现精细的依赖注入,但是使用起来非常繁琐。

Hilt实现依赖注入

什么是Hilt

Hilt是基于Dagger构建的用于安卓的依赖注入库,简化在安卓上实现依赖注入。

把依赖项注入安卓类

Hilt可以很方便的注入到安卓类中
比如把ViewModel

class UserRepository @Inject constructor(  
    private val localDataSource: UserLocalDataSource,  
    private val remoteDataSource: UserRemoteDataSource  
){  
    init {  
        println("UserRepository Created")  
    }  
    fun login(){  
        remoteDataSource.login()  
    }  
}
class UserLocalDataSource @Inject constructor() {  
    init {  
        println("UserLocalDataSource Created")  
    }  
}
class UserRemoteDataSource @Inject constructor(){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        println("login")  
    }  
}
class LoginViewModel @Inject constructor(  
    private val userRepository: UserRepository,  
) {  
    init {  
        println("LoginViewModel Created")  
    }  
    fun login(){  
        userRepository.login()  
    }  
}
/**  
 * @AndroidEntryPoint 注解,可以被Hilt注入  
 */  
@AndroidEntryPoint  
class LoginActivity : AppCompatActivity() {  
    @Inject lateinit var viewModel: LoginViewModel  
  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_login)  
  
        findViewById<Button>(R.id.activity_login_bt_open).setOnClickListener {  
            startActivity(Intent(this, LoginActivity::class.java))  
        }  
    }  
}
class LoginFragment : Fragment() {  
    lateinit var viewModel: LoginViewModel  
  
    override fun onCreateView(  
        inflater: LayoutInflater, container: ViewGroup?,  
        savedInstanceState: Bundle?  
    ): View? {  
        val view = inflater.inflate(R.layout.fragment_login, container, false)  
        viewModel = (activity as LoginActivity).viewModel  
        view.findViewById<Button>(R.id.fragment_login_bt_login).setOnClickListener {  
            viewModel.login()  
        }  
        return view  
    }  
}
/**  
 * @HiltAndroidApp 注解:  
 * 必须在Application 中添加, Hilt会生成一个类作为依赖的容器, 作为依赖注入的入口.  
 */@HiltAndroidApp  
class MyApplication : Application()

Hilt模块

模块的Bind

可以被直接构造的接口实现可以通过Bind注入

class UserRemoteDataSource @Inject constructor(  
    private val analyticsService: AnalyticsService  
){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        println("login")  
        analyticsService.logEvent("login")  
    }  
}
interface AnalyticsService {  
    fun logEvent(eventName: String)  
}
class AnalyticsServiceImpl @Inject constructor(  
    @ApplicationContext val context: Context  
) : AnalyticsService {  
    override fun logEvent(eventName: String) {  
        println("Context: $context LogEvent: $eventName")  
    }  
}
@Module  
@InstallIn(ActivityComponent::class)  
abstract class AnalyticsServiceModule {  
    @Binds  
    abstract fun bindAnalyticsService(analyticsService: AnalyticsServiceImpl): AnalyticsService  
}

模块的Provider

无法被直接构造的接口实现可以通过Provider注入

@Module  
@InstallIn(ActivityComponent::class)  
class LoginServiceModule {  
    @Provides  
    fun provideLoginService(): LoginService {  
        return LoginServiceImpl.Builder().build()  
    }  
}
interface LoginService {  
    fun login()  
}
class LoginServiceImpl private constructor(): LoginService {  
    class Builder {  
        fun build(): LoginService {  
            return LoginServiceImpl()  
        }  
    }  
    init {  
        println("LoginServiceImpl Created")  
    }  
    override fun login() {  
        println("login")  
    }  
}
class UserRemoteDataSource @Inject constructor(  
    private val analyticsService: AnalyticsService,  
    private val loginService: LoginService  
){  
    init {  
        println("UserRemoteDataSource Created")  
    }  
    fun login(){  
        loginService.login()  
        analyticsService.logEvent("login")  
    }  
}

同一类型提供多个绑定

Provides如果不带限定标签,只会返回一种类型的实现。
可以通过限定标签来区分,返回对应的实现。
Hilt限定符默认提供了 @ApplicationContex 和 @ActivityContext,用来提供两种不同类型的Context

@Qualifier  
@Retention(AnnotationRetention.BINARY)  
annotation class DebugLog  
  
@Qualifier  
@Retention(AnnotationRetention.BINARY)  
annotation class ErrorLog  
  
@Module  
@InstallIn(SingletonComponent::class)  
object LogServiceModule {  
    @DebugLog  
    @Provides    
    fun provideDebugLogger(): LogService {  
        return LogServiceDebugImpl()  
    }  
  
    @ErrorLog  
    @Provides    
    fun provideErrorLogger(): LogService {  
        return LogServiceErrorImpl()  
    }  
}
@Module  
@InstallIn(ActivityComponent::class)  
class LoginServiceModule {  
    @Provides  
    fun provideLoginService(@DebugLog logService: LogService): LoginService {  
        return LoginServiceImpl.Builder().build(logService)  
    }  
}
class LoginServiceImpl private constructor(val logService:LogService): LoginService {  
    class Builder {  
        fun build(logService:LogService): LoginService {  
            return LoginServiceImpl(logService)  
        }  
    }  
    init {  
        logService.log("LoginServiceImpl Created")  
    }  
    override fun login() {  
        logService.log("login")  
    }  
}

class AnalyticsServiceImpl @Inject constructor(  
    @ApplicationContext val context: Context,  
    @ErrorLog val logService: LogService  
) : AnalyticsService {  
    override fun logEvent(eventName: String) {  
        logService.log("Context: $context LogEvent: $eventName")  
    }  
}

Hilt为安卓类生成的组件

组件生命周期和作用域

Hilt组件 创建时机 销毁时机 作用域 备注
SingletonComponent Application#onCreate() Application被销毁 @Singleton 相当于是单例的
ActivityComponent Activity#onCreate() Activity#onDestroy() @ActivityScoped 会随着生命周期注入
ActivityRetainedComponent 首次Activity#onCreate() 最后一次Activity#onDestroy() @ActivityRetainedScoped Fragment的ViewModel会随着Fragment回收,但ActivityRetainedComponent只会随着Activity回收,比ViewModel生命周期更长。
ViewModelComponent ViewModel 已创建 ViewModel 已销毁 @ViewModelScoped 和ViewModel的生命周期相同
ViewComponent View#super() View 已销毁 @ViewScoped 和View的生命周期相同
ViewWithFragmentComponent View#super() View的拥有者被销毁 @ViewScoped带有 @WithFragmentBindings注解的View 比如Fragment导航离开屏幕,Fragment还在,但View被销毁时仍然保留。
FragmentComponent Fragment#onAttach() Fragment#onDestroy() @FragmentScoped 和Fragment的生命周期相同
ServiceComponent Service#onCreate() Service#onDestroy() @ServiceScoped 和Service的生命周期相同

组件默认绑定

可以使用Model安装到默认绑定,实现注入。比如上面提到的"同一类型提供多个绑定"

安卓组件 默认绑定
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application、Activity
FragmentComponent Application、Activity、Fragment
ViewComponent Application、Activity、View
ViewWithFragmentComponent Application、Activity、Fragment、View
ServiceComponent Application、Service

在Hilt不支持的类中注入依赖项

使用@EntryPoint让任意接口可以被注入.
使用@EntryPointAccessors获取被注入的对象.
因为是SingletonComponent,所以要使用Application的Context 。如果是ActivityComponent就需要使用Activity的Context。

class ExampleContentProvider : ContentProvider() { 
	@EntryPoint
	@InstallIn(SingletonComponent::class)
	interface ExampleContentProviderEntryPoint { 
		fun analyticsService(): AnalyticsService 
	}
	fun doSomeThing(){
		val appContext = context?.applicationContext ?: throw IllegalStateException()
		val hiltEntryPoint = EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java)
		val analyticsService = hiltEntryPoint.analyticsService()
	}
}

标签:架构,val,Created,安卓,private,fun,组件,login,class
From: https://www.cnblogs.com/laomuji666/p/18235652

相关文章

  • AI算力暴增至120TOPS 英特尔Lunar Lake架构解析
    随着下一代AIPC硬件核心LunarLake的发布,英特尔4年5个制程节点演进也逐步迎来富有革命性的时刻。面对AI时代指数级的算力需求增长,英特尔LunarLake,也就是第二代酷睿Ultra平台的CPU+GPU+NPU算力突破到了120TOPS,这将为基于其打造的AIPC赋予更加强劲、高效的AI性能体验。在台北电......
  • 在Linux中,有哪些基本组件?
    Linux系统由几个核心组件构成,这些组件协同工作,为用户提供了一个功能强大且灵活的操作环境。Linux的基本组件主要包括:内核(Kernel):Linux内核是操作系统的核心部分,它是系统软件和硬件之间的桥梁。内核负责管理硬件资源(如CPU、内存、磁盘驱动器和网络接口),提供基本服务,如进程管理......
  • 安卓中的一些问题
    https://www.jianshu.com/p/878c80f66dc3安卓的三种架构安卓中的最开始基本架构和java是一致的,都是MVC架构。Modelviewcontroller层模型层视图层控制层在安卓中呢,其Activity就是控制层(controller),其res资源目录下的layout下的xml文件就是视图层(view),但是layout定义的是......
  • 若依框架构造树形结构数据
    若依框架构造树形结构数据一、数据库表结构以分组为例,仅创建分组所需字段,其余业务需要字段后续再添加即可SETNAMESutf8mb4;SETFOREIGN_KEY_CHECKS=0;--------------------------------Tablestructureforggroup_data------------------------------DROPTABLE......
  • Android课程设计课题题目推荐(安卓期末大作业,毕业设计,Androidstudio)
    博主介绍:本人专注于Android/java/数据库/微信小程序技术领域的开发,以及有好几年的计算机毕业设计方面的实战开发经验和技术积累;尤其是在安卓(Android)的app的开发和微信小程序的开发,很是熟悉和了解;本人也是多年的Android开发人员;希望我发布的此篇文件可以帮助到您;......
  • react中推荐使用发布订阅模式,进行跨多层级的组件间通信和事件传递吗?
    在React中,虽然发布订阅模式(Pub/Sub)可以作为一种实现跨多层级组件间通信的方法,但它并不是React官方推荐的主要手段,尤其是在ReactHooks和ContextAPI普及之后。React推荐的跨组件通信方法主要包括:Propsdrilling:最直接的方式,通过props从父组件向子组件传递数据,适合简单的数据流......
  • Web系统打包成安卓app
    将Web端打包成安卓App可以使用一些跨平台开发工具,如ReactNative、Flutter等。这些工具可以将Web端的代码转换为原生的安卓应用程序,从而实现Web端应用的移动化。目录 一、ReactNative操作流程 二、Flutter操作流程一、ReactNative操作流程安装ReactNative......
  • 界面组件DevExpress Reports v23.2增强用户体验 - 轻松导航Web设计器
    DevExpressReporting是.NETFramework下功能完善的报表平台,它附带了易于使用的VisualStudio报表设计器和丰富的报表控件集,包括数据透视表、图表,因此您可以构建无与伦比、信息清晰的报表。DevExpressReportsv23.2(我们最近的主要更新)包含了对DevExpressWeb报表设计器的智能......
  • provide inject vue3 父子组件 传参方式
    provideinjectvue3父子组件传参方式当子组件有30个的时候,这个就有优势了,在父组件provide一次,在子组件里面inject这个变量(实际上是通过hooks提供,也可以是个函数)。下面看下截图父组件:子组件:父组件provide子组件在父组件,就不用一堆props这里有一个特别的好处就是结构......
  • 微软官方开源免费的Blazor UI组件库 - Fluent UI Blazor
    前言今天大姚给大家分享一个由微软官方开源(MITLicense)、免费的BlazorUI组件库:FluentUIBlazor。全面的ASP.NETCoreBlazor简介和快速入门FluentUIBlazor介绍FluentUIBlazor是一个基于Blazor的组件库,提供了一系列的UI组件以及FluentUI的设计系统。该库可以帮助开......