Android MVI框架的使用

news2024/9/22 11:23:50

AndroidMviFrame

AndroidMviFrame 是一个Android简单易用的项目框架

文档下面会对框架中所使用的一些核心技术进行阐述。该框架作为技术积累的产物,会一直更新维护,如果有技术方面的谈论或者框架中的错误点,可以在 GitHub 上提 Issues,我会及时进行回应并进行修复。

希望这个框架项目能给大家带来帮助,喜欢可以Start🌟。

AndroidMviFrame项目地址

模块

app:壳工程

是依赖所有组件的壳,该工程中只要MainActivity相关信息。

libs/lib_base:

项目的基础公共模块,存放着各种基类封装、对远程库的依赖、以及工具类、三方库封装,该组件是和项目业务无关的,和项目业务相关的公共部分需要放在 lib_common 中。

libs/lib_common:

项目的业务公共模块,这里面存放着项目里各个业务组件的公共部分。

项目使用技术栈为:

组件化

组件化是指解耦复杂系统时,将多个功能模板拆分、重组的过程。在Android工程表现上就是把app按照其业务的不同,划分为不同的Module。

组件化架构的目的就是让每个业务模块变得相对独立,各个组件在组件模式下可以独立开发调试,集成模式下又可以集成到“app壳工程”中,从而得到一个具有完整功能的APP。

配置如下 gradle.properties

当设置成true时,单个module可以运行
singleModule=false

当你生成一个新的Module的时候,需要做以下配置,这样才可以保证你的Module当组件运行的时候,万无一失。

第一步:

`

if (!singleModule.toBoolean()) {
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}
apply from: '../dependencies.gradle'

android {
    sourceSets {
        main {
            if (!singleModule.toBoolean()) {
                //如果是library,则编译manifest下AndroidManifest.xml
                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
            } else {
                //如果是application,则编译主目录下AndroidManifest.xml
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }
    defaultConfig {
        if (singleModule.toBoolean()) {
            applicationId rootProject.applicationId
        }
        kapt {
            arguments {
                arg("AROUTER_MODULE_NAME", project.getName())
            }
        }

        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }

        ndk {
            abiFilters rootProject.ext.abiFilters
        }
    }


    buildTypes {
        debug {}
        stagdebug {
            consumerProguardFiles 'proguard-rules.pro'
        }
        stagrelease {
            consumerProguardFiles 'proguard-rules.pro'
        }
        release {
            consumerProguardFiles 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar", '*.aar'])
    implementation project(':libs:lib_common')

    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
}

`

复制以上内容,覆盖module下的build.gradle文件

第二步:

在module的main文件夹下边创建manifest文件夹,在此文件夹下创建AndroidManifest.xml

至此组件化就配置完成了。

MVI

在这里插入图片描述

  • Model层: 它是一个 ViewModel,其中执行不同的同步或异步任务。它接受 UserIntents 作为输入并产生一个或多个连续状态作为输出。

  • View层: 视图只是处理它从 ViewModel 接收到的不可变状态以更新 UI。它还允许将用户操作传输到 ViewModel 以完成定义的任务。

  • Intent层: 表示用户与 UI 交互时的意图。例如,单击按钮刷新数据列表将被建模为 Intent。为了避免与 Android 框架 Intent 混淆,我们将在本文的其余部分将其称为 UserIntent。。

关于MVI base层的封装这里不在过多叙述,感兴趣的可以直接clone项目研究,这里我们就是说如何使用。

这里我们以module_home为例:

第一步:在项目里添加service文件夹,创建HomeService接口类

``

interface HomeService {

    @POST(ComServerApi.API_BANNER)
    suspend fun getBanner(@Body requestBody: RequestBody): BaseData<List<WanBean>>

    @GET(ComServerApi.API_COIN_RANK)
    suspend fun getRankList(): BaseData<RankBean>
}

第二步:封装Home接口网络层

``

class HomeRepo : BaseRepository() {
    private val service = RetrofitUtil.getService(HomeService::class.java)

    suspend fun requestWanData(drinkId: String): BaseData<List<WanBean>> {
        val requestBody = jsonRequest()
            .p("drinkId", drinkId)
            .body()
        return executeRequest { service.getBanner(requestBody) }
    }

    suspend fun requestRankData(): BaseData<RankBean> {
        return executeRequest { service.getRankList() }
    }
}

第三步:添加HomeMviState状态

``

data class MviState(val bannerUiState: BannerUiState, val detailUiState: DetailUiState?) : IUiState

sealed class BannerUiState {
    object INIT : BannerUiState()
    data class SUCCESS(val models: List<WanBean>) : BannerUiState()
}

data class MviSingleUiState(val message: String) : ISingleUiState
sealed class DetailUiState {
    object INIT : DetailUiState()
    data class SUCCESS(val detail: RankBean) : DetailUiState()
}

第四步:封装Model调用网络接口

``

class HomeMviModel : BaseMviModel<MviState, MviSingleUiState>() {
    private val mLoginRepo = HomeRepo()

    fun initData(bundle: Bundle?) {

    }

    override fun initUiState(): MviState {
        return MviState(BannerUiState.INIT, DetailUiState.INIT)
    }

    /**
     * 获取banner信息
     */
    fun loadBannerData() {
        requestDataWithFlow(
            showLoading = true,
            request = { mLoginRepo.requestWanData("12345") },
            successCallback = { data ->
                sendUiState {
                    copy(bannerUiState = BannerUiState.SUCCESS(data))
                }
            },
            failCallback = {}
        )
    }

    //请求List数据
    fun loadDetailData() {
        requestDataWithFlow(
            showLoading = false,
            request = { mLoginRepo.requestRankData() },
            successCallback = { data ->
                sendUiState {
                    copy(detailUiState = DetailUiState.SUCCESS(data))
                }
            },
            failCallback = {}
        )
    }

}

第五步:Activity/Fragment初始化mViewModel

`

private val mViewModel: HomeMviModel by viewModels()

调用接口
mViewModel.loadDetailData()

获取后台数据
mViewModel.uiStateFlow.flowWithLifecycle2(
            this, Lifecycle.State.STARTED,
            prop1 = MviState::detailUiState
        ) { state ->
            when (state) {
                is DetailUiState.INIT -> {}
                is DetailUiState.SUCCESS -> {
                    val list = state.detail.datas
                    mBinding.tvShowUser1.text = "显示Banner数据2:${Gson().toJson(list)}"
                }
            }
        }

`

项目使用的三方库

  • Kotlin
  • Kotlin-Coroutines-Flow
  • Lifecycle
  • ViewBinding
  • OkHttp:okhttp网络请求
  • Retrofit:封装okhttp网络请求
  • MMKV:腾讯基于 mmap 内存映射的 key-value 本地存储组件
  • Coil:一个 Android 图片加载库,通过 Kotlin 协程的方式加载图片
  • ARoute:阿里用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
  • BaseRecyclerViewAdapterHelper:一个强大并且灵活的 RecyclerViewAdapter
  • EventBus:适用于 AndroidJava 的发布/订阅事件总线
  • AndroidAutoSize:今日头条屏幕适配方案终极版
  • smart刷新框架:Android智能下拉刷新框架-SmartRefreshLayout
  • 规避 64K 限制:规避 64K 限制
  • immersionbar:沉浸式实现
  • AgentWeb:AgentWeb 是一个基于的 Android WebView
Kotlin 协程

关于kotlin协程,具体可以看一下几片文章

  • kotlin语法进阶 - 协程(一)协程基础
  • 万字长文 - Kotlin 协程进阶

关于Flow,具体可以看一下几片文章

  • Flow官网
  • Flow原理解析
  • Flow 与Live Data对比
  • Kotlin Coroutines Flow 系列(1-5):
Lifecycle

``

class ActivityLifecycleCallbacksImpl : Application.ActivityLifecycleCallbacks {

    private val TAG = "ActivityLifecycle"
    private var mCount: Int = 0

    override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
        ActivityStackManager.addActivityToStack(activity)
        Log.d(TAG, "${activity.javaClass.simpleName} --> onActivityCreated")
    }

    override fun onActivityStarted(activity: Activity) {
        Log.d(TAG, "${activity.javaClass.simpleName} --> onActivityStarted")

        mCount++
        if (mCount == 1 && BaseConstants.isBackground) {
            //说明应用重新进入了前台
            BaseConstants.isBackground = false
        }
    }

    override fun onActivityResumed(activity: Activity) {
        Log.d(TAG, "${activity.javaClass.simpleName} --> onActivityResumed")
    }

    override fun onActivityPaused(activity: Activity) {
        Log.d(TAG, "${activity.javaClass.simpleName} --> onActivityPaused")
    }

    override fun onActivityStopped(activity: Activity) {
        Log.d(TAG, "${activity.javaClass.simpleName} --> onActivityStopped")

        mCount--
        if (mCount <= 0 && !BaseConstants.isBackground ) {
            //说明应用进入了后台
            BaseConstants.isBackground = true
            Toast.makeText(activity, "奇宝进入后台", Toast.LENGTH_LONG).show()
        }
    }

    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
        Log.d(TAG, "${activity.javaClass.simpleName} --> onActivitySaveInstanceState")
    }

    override fun onActivityDestroyed(activity: Activity) {
        ActivityStackManager.popActivityToStack(activity)
        Log.d(TAG, "${activity.javaClass.simpleName} --> onActivityDestroyed")
    }

在Appliction的onCreate()初始化监听

``

// 全局监听 Activity 生命周期
registerActivityLifecycleCallbacks(ActivityLifecycleCallbacksImpl())

官方文档:https://developer.android.com/jetpack/androidx/releases/lifecycle

ViewBinding

通过视图绑定功能,可以更轻松地编写可与视图交互的代码。用于代替findViewById

在build.gradle里边的 android {}

``

buildFeatures {
    viewBinding true
}

ViewBinding 在项目中的使用

HomeActivity : BaseMviActivity<ActivityHomeBinding>

override fun getViewBing(layoutInflater: LayoutInflater): ActivityHomeBinding {
    return ActivityHomeBinding.inflate(layoutInflater)
}

官方文档: https://developer.android.com/topic/libraries/view-binding

MMKV

MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。

项目中已封装

``

object MMKVSpUtils {

    /**
     * 初始化
     */
    fun initMMKV(context: Context): String? = MMKV.initialize(context)

    /**
     * 保存数据(简化)
     * 根据value类型自动匹配需要执行的方法
     */
    fun put(key: String, value: Any) =
        when (value) {
            is Int -> putInt(key, value)
            is Long -> putLong(key, value)
            is Float -> putFloat(key, value)
            is Double -> putDouble(key, value)
            is String -> putString(key, value)
            is Boolean -> putBoolean(key, value)
            else -> false
        }

    fun putString(key: String, value: String): Boolean? = MMKV.defaultMMKV()?.encode(key, value)

    fun getString(key: String, defValue: String): String? =
        MMKV.defaultMMKV()?.decodeString(key, defValue)

    fun putInt(key: String, value: Int): Boolean? = MMKV.defaultMMKV()?.encode(key, value)

    fun getInt(key: String, defValue: Int): Int? = MMKV.defaultMMKV()?.decodeInt(key, defValue)

    fun putLong(key: String, value: Long): Boolean? = MMKV.defaultMMKV()?.encode(key, value)

    fun getLong(key: String, defValue: Long): Long? = MMKV.defaultMMKV()?.decodeLong(key, defValue)

    fun putDouble(key: String, value: Double): Boolean? = MMKV.defaultMMKV()?.encode(key, value)

    fun getDouble(key: String, defValue: Double): Double? =
        MMKV.defaultMMKV()?.decodeDouble(key, defValue)

    fun putFloat(key: String, value: Float): Boolean? = MMKV.defaultMMKV()?.encode(key, value)

    fun getFloat(key: String, defValue: Float): Float? =
        MMKV.defaultMMKV()?.decodeFloat(key, defValue)

    fun putBoolean(key: String, value: Boolean): Boolean? = MMKV.defaultMMKV()?.encode(key, value)

    fun getBoolean(key: String, defValue: Boolean): Boolean? =
        MMKV.defaultMMKV()?.decodeBool(key, defValue)

    fun putStringSet(key: String, value: Set<String>): Boolean? = MMKV.defaultMMKV()?.encode(key, value)

    fun getStringSet(key: String): Set<String>? =
        MMKV.defaultMMKV()?.decodeStringSet(key)

    fun contains(key: String): Boolean? = MMKV.defaultMMKV()?.contains(key)
}

官网地址:https://github.com/Tencent/MMKV

Coil

一个 Android 图片加载库,通过 Kotlin 协程的方式加载图片。

项目中已封装

``

/**加载远端图片*/
fun ImageView.loadImage(url: String) {
    CoilUtils.loadImage(this, url)
}

/**加载远端图片*/
fun ImageView.loadImageDefault(url: String, resoure: Int) {
    CoilUtils.loadImageDefault(this, url, resoure)
}

/**加载远端图片*/
fun ImageView.loadImageCenterCrop(url: String) {
    CoilUtils.loadImageCenterCrop(this, url)
}

/**加载远端圆形图片*/
fun ImageView.loadCircleImage(url: String) {
    CoilUtils.loadCircleImage(this, url)
}

/**加载远端视频 帧*/
fun ImageView.loadImageVideo(url: String) {
    CoilUtils.loadImageVideo(this, url)
}

/**加载远端圆角图片*/
fun ImageView.loadRoundImage(url: String, angle: Float) {
    CoilUtils.loadRoundImage(this, url, angle)
}

/**加载本地圆角图片*/
fun ImageView.loadRoundImage(resoure: Int, angle: Float) {
    CoilUtils.loadRoundImage(this, resoure, angle)
}


/**加载本地图片资源*/
fun ImageView.loadImage(resoure: Int) {
    CoilUtils.loadImage(this, resoure)
}

/**加载本地圆角图片资源*/
fun ImageView.loadCircleImage(resoure: Int) {
    CoilUtils.loadCircleImage(this, resoure)
}

/**加载本地视频资源  文件*/
fun ImageView.loadImage(file: File) {
    CoilUtils.loadImage(this, file)
}

/**加载本地视频资源  文件*/
fun ImageView.loadLocalImage(file: File) {
    CoilUtils.loadCircleImage(this, file)
}

/**加载本地圆角图片*/
fun ImageView.loadRoundImage(file: File, angle: Float) {
    CoilUtils.loadRoundImage(this, file, angle)
}

/**加载远端gif*/
fun ImageView.loadImageGif(url: String) {
    CoilUtils.loadImageGif(this, url)
}

/**加载本地gif*/
fun ImageView.loadImageGif(resoure: Int) {
    CoilUtils.loadImageGif(this, resoure)
}

/**加载远端svg*/
fun ImageView.loadImageSvg(url: String) {
    CoilUtils.loadImageSvg(this, url)
}

/**加载远端svg*/
fun ImageView.loadImageSvg(url: String, listener: EventListener) {
    CoilUtils.loadImageSvg(this, url, listener)
}

/**加载本地svg*/
fun ImageView.loadLocalSvg(file: File, listener: EventListener) {
    CoilUtils.loadLocalSvg(this, file, listener)
}

/**加载本地svg*/
fun ImageView.clear() {
    this.dispose()
}

object CoilUtils {

    val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
        .crossfade(true)
        .placeholder(R.mipmap.ic_launcher)
        .error(R.mipmap.ic_launcher)


    /**加载视频首帧*/
    fun loadImageDefault(imageView: ImageView, url: String, resoure: Int) {
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
            .placeholder(resoure)
            .build()
        imageView.load(url, imageLoader)
    }

    /**加载远端图片*/
    fun loadImageVideo(imageView: ImageView, url: String) {
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
            .components {
//                add(VideoFrameFileFetcher(BaseAppliction.mContext))
//                add(VideoFrameUriFetcher(BaseAppliction.mContext))
                add(VideoFrameDecoder.Factory())
            }
            .build()
        imageView.load(url, imageLoader)
    }


    /**加载远端图片*/
    fun loadImageRoundDefault(imageView: ImageView, url: String, resoure: Int, angle: Float) {
        val round = ScreenUtils.dip2px(BaseAppliction.mContext, angle).toFloat()
        imageView.load(url) {
            transformations(
                RoundedCornersTransformation(
                    topLeft = round,
                    topRight = round,
                    bottomLeft = round,
                    bottomRight = round
                )
            )
                .placeholder(resoure)
                .build()
        }

    }

    /**加载远端图片*/
    fun loadImage(imageView: ImageView, url: String) {
        imageView.load(url)
    }

    /**加载远端图片*/
    fun loadImageCenterCrop(imageView: ImageView, url: String) {
        imageView.scaleType = ImageView.ScaleType.CENTER_CROP
        imageView.load(url)
    }

    /**加载远端圆形图片*/
    fun loadCircleImage(imageView: ImageView, url: String) {
        imageView.load(url) {
            transformations(RoundedCornersTransformation(360f))
        }
    }

    /**加载远端圆角图片*/
    fun loadRoundImage(imageView: ImageView, url: String, angle: Float) {
        imageView.scaleType = ImageView.ScaleType.CENTER_CROP
        val round = ScreenUtils.dip2px(BaseAppliction.mContext, angle).toFloat()
        imageView.load(url) {
            transformations(
                RoundedCornersTransformation(
                    topLeft = round,
                    topRight = round,
                    bottomLeft = round,
                    bottomRight = round
                )
            )
        }
    }

    /**加载远端圆角图片*/
    fun loadRoundImage(imageView: ImageView, resoure: Int, angle: Float) {
        val round = ScreenUtils.dip2px(BaseAppliction.mContext, angle).toFloat()
        imageView.load(resoure) {
            transformations(
                RoundedCornersTransformation(
                    topLeft = round,
                    topRight = round,
                    bottomLeft = round,
                    bottomRight = round
                )
            )
        }
    }

    /**加载本地圆角图片*/
    fun loadRoundImage(imageView: ImageView, file: File, angle: Float) {
        val round = ScreenUtils.dip2px(BaseAppliction.mContext, angle).toFloat()
        imageView.load(file) {
            transformations(
                RoundedCornersTransformation(
                    topLeft = round,
                    topRight = round,
                    bottomLeft = round,
                    bottomRight = round
                )
            )
        }
    }

    /**加载本地图片资源*/
    fun loadImage(imageView: ImageView, resoure: Int) {
        imageView.load(resoure)
    }

    /**加载远端圆形图片*/
    fun loadCircleImage(imageView: ImageView, resoure: Int) {
        imageView.load(resoure) {
            transformations(RoundedCornersTransformation(360f))
        }
    }

    /**加载本地视频资源  文件*/
    fun loadImage(imageView: ImageView, file: File) {
        imageView.load(file)
    }


    /**加载本地视频资源  文件*/
    fun loadCircleImage(imageView: ImageView, file: File) {
        imageView.load(file) {
            transformations(RoundedCornersTransformation(360f))
        }
    }


    /**加载远端gif*/
    fun loadImageGif(imageView: ImageView, url: String) {
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
            .components {
                if (SDK_INT >= 28) {
                    add(ImageDecoderDecoder.Factory())
                } else {
                    add(GifDecoder.Factory())
                }
            }
            .build()
        imageView.load(url, imageLoader)
    }

    /**加载本地gif*/
    fun loadImageGif(imageView: ImageView, resoure: Int) {
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
            .components {
                if (SDK_INT >= 28) {
                    add(ImageDecoderDecoder.Factory())
                } else {
                    add(GifDecoder.Factory())
                }
            }
            .build()

        imageView.load(resoure, imageLoader)
    }

    /**加载远端svg*/
    fun loadImageSvg(imageView: ImageView, url: String) {
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
            .components {
                add(SvgDecoder.Factory())
            }
            .build()

        imageView.load(url, imageLoader)
    }


    /**加载远端svg*/
    fun loadImageSvg(imageView: ImageView, url: String, listener: EventListener) {
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
            .components {
                add(SvgDecoder.Factory())
            }
            .eventListener(listener)
            .build()

        imageView.load(url, imageLoader)
    }

    /**加载本地svg*/
    fun loadLocalSvg(imageView: ImageView, file: File, listener: EventListener) {
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext)
            .components {
                add(SvgDecoder.Factory())
            }
            .eventListener(listener)
            .build()

        imageView.load(file, imageLoader)
    }


    /**加载远端url 转换成Drawable*/
    fun loadDrawable(url: String): Drawable? {
        var drawable: Drawable? = null
        val imageLoader = ImageLoader.Builder(BaseAppliction.mContext).build()
        val imageRequest = ImageRequest.Builder(BaseAppliction.mContext).data(url).target {
            drawable = it
        }.build()
        imageLoader.enqueue(imageRequest)
        return drawable

    }

}

官网地址:https://github.com/coil-kt/coil

ARoute

ARoute 是阿里巴巴的一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦

``

@Route(path = RouteHomeUtils.Home_HomeActivity)
class HomeActivity : BaseMviActivity<ActivityHomeBinding>() {}

//跳转
fun goHomeActivity(url: String) {
        ARouter.getInstance()
            .build(RouteHomeUtils.Home_HomeActivity)
            .withString(CommonConstant.KEY_URL,url)
            .navigation()
    }

官网地址:https://github.com/alibaba/ARouter

EventBus

``

override fun onCreate() {
    EventBusUtils.register(this)
    super.onDestroy()
}

override fun onDestroy() {
    EventBusUtils.unRegister(this)
    super.onDestroy()
}

//发送事件
EventBusUtils.postEvent(ShowHomeActivePop())

//接受事件
@Subscribe
fun showHomeActiveEvent(event: ShowHomeActivePop) {
      
}

官方文档:https://github.com/greenrobot/EventBus

AndroidAutoSize

``

<manifest>
    <application>            
        <meta-data
            android:name="design_width_in_dp"
            android:value="360"/>
        <meta-data
            android:name="design_height_in_dp"
            android:value="640"/>           
     </application>           
</manifest>
<meta-data
    android:name="android.max_aspect"
    android:value="2.4" />
<!--适配华为(huawei)刘海屏-->
<meta-data
    android:name="android.notch_support"
    android:value="true" />
<!--适配小米(xiaomi)刘海屏-->
<meta-data
    android:name="notch.config"
    android:value="portrait|landscape" />

官网地址:[https://github.com/JessYanCoding/AndroidAutoSize](

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/196361.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DAMA认证(CDGA/CDGP)证书好考吗

随着数字化经济的不断发展&#xff0c;企业对数据重视程度越来越高&#xff0c;致使越来越多得数字人关注到DAMA认证。很多小伙伴都会有这样的疑问&#xff0c;DAMA认证&#xff08;CDGA/CDGP认证&#xff09;好考吗&#xff1f;通过率怎么样?今天小编就在这里做一下简单的说明…

UniRx之操作符详解-Linq语法

前言 UniRx中由很多操作符&#xff0c;注意要分为三类 Linq操作符&#xff0c;和Linq语法风格一致Rx操作符&#xff0c;从Rx.Net库继承下来的操作符。UniRx操作符&#xff0c;UniRx针对Unity的独有操作符。 Rx和Linq Linq是微软的一项技术&#xff0c;新增一种自然查询的SQ…

时间序列预测

问题简介 简单来说&#xff0c;时间序列是按照时间顺序&#xff0c;按照一定的时间间隔取得的一系列观测值&#xff0c;比如我们上边提到的国内生产总值&#xff0c;消费者物价指数&#xff0c;利率&#xff0c;汇率&#xff0c;股票价格等等。时间间隔可以是日&#xff0c;周…

数字IC设计 Synopsys EDA Tools的安装补充

数字IC Synopsys 七件套的Ubuntu安装步骤 推荐大佬的安装教程&#xff0c;本人亲测可用&#xff0c;在这里表示十分感谢&#xff01; 数字IC设计的第一步——Synopsys EDA Tools的安装 跟着大佬的教程仔细点可以一步到位的&#xff01; 在这里备忘本人遇到的几个粗心导致的问…

浅谈Spring IoC容器

目录 1.IoC容器 2.依赖注入 1.IoC容器 IOC: Inversion of Control&#xff0c;是一种设计思想。 在spring框架中&#xff0c;Spring 通过IoC容器进行管理所有Java对象的实例化和初始化&#xff0c;控制对象与对象之间的依赖关系。 IoC管理的对象称为Bean&#xff0c;它与使…

“华为杯”研究生数学建模竞赛2005年-【华为杯】A题:行车时间估计和最优路线选择(附获奖论文)

赛题描述 A: Highway Traveling time Estimate and Optimal Routing Ⅰ Highway traveling time estimate is crucial to travelers. Hence, detectors are mounted on some of the US highways. For instance, detectors are mounted on every two-way six-lane highways o…

MySQL 百万级数据,如何做分页查询?

随着业务的增长&#xff0c;数据库的数据也呈指数级增长&#xff0c;拿订单表为例&#xff0c;之前的订单表每天只有几千个&#xff0c;一个月下来不超过十万。而现在每天的订单大概就是2w&#xff0c;目前订单表的数据已经达到了700w。这带来了各种各样的问题&#xff0c;今天…

国产ETL工具/ETL 产品 (BeeDI ) 集团财务 双向同步 审核平台

项目需求核心 实时同步、双向同步、部分同步、日志解析同步、断点续传 项目需求概要 35分公司财务数据实时同步汇总中心平台 &#x1f51b; 中心平台财务数据实时同步分发35分公司 项目需求内容 35分公司数据中部分表数据同步到中心库对应表&#xff0c;10张表分公司表年数…

【MyBatis】mybatis缓存机制

1. 缓存基础知识:缓存: cache缓存的作用: 通过减少IO的方式, 来提高程序的执行效率mybatis缓存包括:一级缓存: 讲话查询的数据存储到SqlSession中二级缓存: 将查询的数据存储到SqlSessionFactory中或者集成第三方的缓存: 比如EhCache...mybatis缓存只针对DQL语句, 也就是说缓存…

【1145. 二叉树着色游戏】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中&#xff0c;给出二叉树的根节点 root&#xff0c;树上总共有 n 个节点&#xff0c;且 n 为奇数&#xff0c;其中每个节点上的值从 1 到 n 各不…

WPF使用AvalonEdit实现代码高亮显示、搜索、替换功能

很多工程软件拥有自己定义的脚本语言&#xff0c;作为程序员用惯了具有高亮显示和智能提示功能的编辑器&#xff0c;所以针对特定的脚本自己开发一个编辑器。主要采用WPF、C#语言以及AvalonEdit控件。 文章目录AvlonEdit控件实现自定义高亮显示实现文本搜索实现文本替换自定义搜…

磁疗效果因“病”而异,坚持=胜利!

磁疗在我国具有悠久的历史早在春秋战国时期&#xff0c;就有神医扁鹊运用磁疗治病的记载。 北京军区总医院理疗科主任&#xff0c;中华生物磁学研究会秘书长周万松认为&#xff0c;关于磁场改善血液循环这一点是肯定的&#xff0c;磁场可以扩张血管、加速血流、改善血液循环。但…

【H5游戏】-整一个简单的解压小游戏【抽纸巾】

专栏简介 &#x1f492;个人主页 &#x1f4f0;专栏目录 点击上方查看更多内容 &#x1f4d6;心灵鸡汤&#x1f4d6;社会不相信庸俗&#xff0c;成功需要汗水和寂寞铸就。无数次的质疑或是嘲笑&#xff0c;这些都无所谓&#xff0c;记住自己是谁&#xff0c;自己想要什么就…

Django的信号机制解读

Django的信号 Django的信号机制不同于Linux的信号机制&#xff0c;Django 中的信号用于在框架执行操作时解耦。当某些动作发生的时候&#xff0c;系统会根据信号定义的函数执行相应的操作 Django的信号主要包含以下三个要素&#xff1a; 发送者&#xff08;sender&#xff0…

网络隔离后的文件传输怎么解决?深度解析4种主流方案

网络隔离对于很多企业来说并不陌生&#xff0c;出于数据安全的考虑&#xff0c;为了隔离有害的网络和可能的网络攻击&#xff0c;越来越多的企业在内部进行了网络隔离。隔离的形态和方式有多种&#xff0c;总体上主要以物理隔离和逻辑隔离为主。网络隔离可以一定程度上甚至彻底…

功能测试环境搭建

前言新年好呀大家&#xff0c;大家都复工了吗~小编已经在搬砖中了&#x1f636;在假期中小编学习了一点功能测试方面知识&#xff0c;打算写篇博客记录下来&#xff0c;希望这篇博客可以帮到需要的朋友。流程图一、测试计划测试计划需要在所有的测试工作之前进行&#xff0c;一…

HTTP协议(2)

1)当我们在浏览器上面输入一个搜狗搜索的网址之后&#xff0c;浏览器就会给搜狗的服务器发送了一个HTTP请求&#xff0c;这样咱们的搜狗的服务器就会返回一个HTTP响应&#xff1b; 2)当这个响应结果被浏览器解析之后&#xff0c;就展示成了我们目前所看到的页面内容&#xff0c…

20230203英语学习

What Is a Dog Anyway? 狗的争议性起源&#xff1a;到底怎么定义“狗” The geographer Jared Diamond has called domestication the worst mistake humans ever made. And yet, the first domestication — the turning of wolves into dogs — was an impressive feat.Hu…

java对象的比较

上一章中关于PriorityQueue的使用要注意&#xff1a; 1. PriorityQueue中放置的元素必须要能够比较大小&#xff0c;不能插入无法比较大小的对象&#xff0c;否则会抛出 ClassCastException异常 2. 不能插入null对象&#xff0c;否则会抛出NullPointerException 3. 没有容量限制…

C/C++ 内存泄漏检测

C/C 内存泄漏检测内存泄漏的两个问题使用宏定义覆盖 malloc 和 free 函数使用 hook 钩子最近学习了 C/C 内存泄漏检测的相关知识&#xff0c;写博客记录一下。 内存泄漏的两个问题 是否有内存泄漏&#xff1f;内存泄漏是在代码的哪一行&#xff1f; 检测内存泄漏主要从上面两…