Android笔记(十二):结合Compose实现Handler机制处理多线程的通信

news2024/11/29 4:27:38

在Android应用中常常结合多线程处理多个任务。不可避免,多个线程之间需要数据通信。Hanlder消息处理机制是异步处理的方式之一。通过Handler机制可以实现在不同的线程之间的通信。

一、主线程和工作线程

1.主线程

一个Android的移动应用启动时会单独启动一个进程。这个进程中可以存在多个线程。但是这么多线程中有且仅有一个主线程,即UI线程。

Android应用程序运行时创建UI主线程,它主要是负责控制UI界面的显示、更新和控件交互。

2.工作线程

在一个应用中会定义多个线程来完成不同的任务,分担主线程的责任,避免主线程阻塞。这些自定义的线程为工作线程。值得注意地是,一旦主线程阻塞超过规定的时间,会出现ANR问题,道中程序中断运行。

3.线程的定义

Kotlin采用下列方式定义并启动线程

thread{
//线程体
}

下列定义了一个简单的计时器,通过定义工作线程,修改显示时间的状态,达到更新界面,动态显示计时的目的。

@Composable
fun MainScreen(){
    var timer by remember{mutableStateOf(0)}
    var running by remember{mutableStateOf(true)}
    var display by remember{mutableStateOf("计时器")}

    Column(modifier = Modifier.fillMaxSize(),
        horizontalAlignment =  Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center){
        Text(text = display,fontSize = 30.sp)
        Row(horizontalArrangement =  Arrangement.Center){
            TextButton(onClick = {
                running = true
                thread{
                    while(running){
                        Thread.sleep(1000)
                        timer += 1
                        display = "${timer} 秒"
                    }
                }
            }){
               Icon(imageVector = Icons.Filled.PlayArrow,contentDescription = "play")
               Text("开始")
            }
            TextButton(onClick = {
                running = false
            }){
                Icon(imageVector = Icons.Default.Close,contentDescription = "pause")
                Text("暂停")
            }
            TextButton(onClick = {
                running = false
                timer = 0
            }){
                Icon(imageVector = Icons.Filled.Refresh,contentDescription = "refresh")
                Text("停止")
            }
        }
    }
}

运行结果如下所示:
在这里插入图片描述

二、Handler处理机制概述

在这里插入图片描述
Handler机制中有如下:

Looper是一个循环者。在线程内部定义,每个线程只能定义一个Looper。Looper内部封装了消息队列MessageQueue。
Message表示的是消息也称为任务。有时将Runnable线程体对象封装成Message对象来处理其中的任务。Handler对象发送、接受并进行处理消息Message。
MessageQueue是消息队列。当产生一个消息时,关联Looper的Handler对象会将消息Message对象发送给MessageQueue消息队列。Looper对消息队列进行管理和控制,控制消息Message进出消息队列。

在《Android移动应用开发(微课版)》P170的图5-3很好地对Handler机制实现主线程和工作线程的通信。

三、Compose组件中使用Handler机制

将通过动态显示图片来观察Handler机制对不同线程之间的通信。

1.定义数据层

class ImageRepository(private val handler: Handler){
    val imageList = listOf(
        R.mipmap.scene1,
        R.mipmap.scene2,
        R.mipmap.scene3,
        R.mipmap.scene4,
        R.mipmap.scene5)
    fun requestImage(imageId: MutableState<Int>){
           thread{
               //定义工作线程
              while(imageId.value in 0 until imageList.size){
                  Thread.sleep(1000)
                  var message = Message.obtain()
                  message.what= 0x123
                  message.arg1 = imageList[imageId.value]
                  imageId.value = imageId.value+1
                  //发送数据
                  handler.sendMessage(message)
              }
           }
    }
}

2.定义界面

@Composable
fun MainScreen(imageState: MutableState<Int>, imageRepository: ImageRepository){
    var currentSelected = remember{mutableStateOf(0)}

    Column(modifier = Modifier.fillMaxSize(),
           horizontalAlignment = Alignment.CenterHorizontally,
           verticalArrangement = Arrangement.Center){
        if(imageState.value!=0)
            Image(painter=painterResource(imageState.value),contentDescription="image")
        else{
            Text("等待加载图片")
        }

        Row{
            for(i in 0 until imageRepository.imageList.size){
                RadioButton(selected = currentSelected.value-1==i,
                    onClick = {
                        currentSelected.value = i
                    })
            }
        }

        Row{
            Button(onClick = {
                imageRepository.requestImage(currentSelected)
            }){
                Text("动态显示")
            }
            Button(onClick = {
                imageState.value = 0
                currentSelected.value = 0
            }){
                Text("重置")
            }
        }
    }
}

3.定义主活动

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            //图片列表的序号状态
            val imageState = remember{mutableStateOf(0)}
            //定义Handler对象
            val handler=object: Handler(Looper.getMainLooper()) {
                override fun handleMessage(msg: Message) {
                    super.handleMessage(msg)
                    if(msg.what == 0x123){
                        //接受数据,并修改状态值
                        imageState.value = msg.arg1
                    }
                }
            }
            //定义图片仓库
            val imageRepository = ImageRepository(handler)

            ForCourseTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    MainScreen(imageState=imageState,imageRepository = imageRepository)
                }
            }
        }
    }
}

运行结果如下图所示:

在这里插入图片描述

四、显示在线图片的应用实例

本例结合Compose+ViewModel+Handler实现在线图片的显示应用,运行效果如下显示:
在这里插入图片描述
上述运行效果的采用的风景图片来源于“https://so1.360tres.com/t01166f28ff8d9dc33e.jpg”,特此说明。如有侵权,可以删除。

1.项目模块配置

在模块对应的build.gradle.kt中设置:

  //viewModel
  implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2"
  //使用coil显示在线的图片
  implementation("io.coil-kt:coil-compose:2.4.0")

在项目的AndroidManifest.xml增加使用Internet的许可

<uses-permission android:name="android.permission.INTERNET" />

2.项目架构

在这里插入图片描述
在这里:
数据层:定义数据存储和要访问处理的数据;
ViewModel层:是视图模型层,承担业务逻辑的定义和更新界面的任务。ViewModel调用数据层的数据,经过业务处理得到新的数据,用这些数据去修改UI层的界面;
UI层:界面层,可以观察VIewModel内存储状态数据的变化,根据ViewModel提供的数据,更新界面。

3.数据层

class ImageRepository (val handler: Handler){
    //如果需要测试,请自行在网络中查找图片资源,下列的网址均为不存在,只为示例而已。
    val imageLst:List<String> = listOf(
        "https://somesite.com/1.jpg",
        "https://somesite.com/2.jpg",
        "https://somesite.com/3.jpg",
        "https://somesite.com/4.jpg",
        "https://somesite.com/5.jpg",
        "https://somesite.com/6.jpg"
    )

    /**
     * 根据列表的索引号获取图像的URL
     * @param imageId Int
     */
    fun requestImage(){
        //自定义工作线程
        thread {
            for(imageId in 0 until imageLst.size){
                Thread.sleep(1000)
                var message = Message.obtain()
                message.what = 0x123
                message.arg1 = imageId+1
                message.obj = imageLst[imageId]
                handler.sendMessage(message)
            }
        }
    }
}

4.视图模型层

class LoadImageViewModel(val imageRepository: ImageRepository): ViewModel() {
    val _currentImageId = MutableStateFlow<Int>(0)
    val currentImageId = _currentImageId.asStateFlow()

    val _currentImage= MutableStateFlow<String>("")
    val currentImage = _currentImage.asStateFlow()

    /**
     * 修改图片索引
     */
    fun changeImageIndex(){
        _currentImageId.value =(_currentImageId.value+1)%imageRepository.imageLst.size
    }
    
    /**
     * Change image index
     * 修改图片索引
     */
    fun changeImageIndex(index:Int){
        _currentImageId.value = index%imageRepository.imageLst.size
    }
    
    /**
     * 修改当前的图片链接
     */
    fun changeImage(url:String){
        _currentImage.value = url
    }
    
    /**
     * Request image
     * 请求图片
     */
    fun requestImage(){
        imageRepository.requestImage()
    }
}

5.定义界面

@Composable
fun ImageScreen(viewModel:LoadImageViewModel,repository:ImageRepository){
    //数据层中保存的在线图片列表
    val images = repository.imageLst
    //获取当前图片状态
    val imageURLState = viewModel.currentImage.collectAsState()
    //获取当前图片索引
    val imageIdState = viewModel.currentImageId.collectAsState()

    Box(modifier = Modifier
        .fillMaxSize()
        .padding(20.dp)
        .background(Color.Black),
        contentAlignment = Alignment.Center){
        Column(horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center){
            if(!imageURLState.value.isBlank()) {
                AsyncImage(modifier= Modifier
                    .width(400.dp)
                    .height(400.dp)
                    .border(BorderStroke(1.dp, Color.Blue)),
                    model = imageURLState.value,
                    contentDescription = null)
            }
            else
                Text(modifier = Modifier
                    .width(300.dp)
                    .height(500.dp),
                    text = "等待加载图片",fontSize = 20.sp,textAlign = TextAlign.Center)
            if(imageURLState.value.isNotBlank())
                Row(horizontalArrangement = Arrangement.Center) {
                    for (i in 0 until images.size) {
                        RadioButton(
                            colors = RadioButtonDefaults.colors(selectedColor = Color.Green),
                            selected = i==imageIdState.value,
                            onClick =null)
                    }
                }

            Row(modifier = Modifier
                .fillMaxWidth()
                .padding(end = 10.dp), horizontalArrangement = Arrangement.Center){
                
                TextButton(colors = ButtonDefaults.buttonColors(contentColor = Color.Blue, containerColor = Color.LightGray),
                    onClick={
                    	//请求图片	
                        viewModel.requestImage()
                    }){
                    Text("动态显示图片",fontSize=16.sp)
                }

                TextButton(colors = ButtonDefaults.buttonColors(contentColor = Color.Blue,
                    containerColor = Color.LightGray),onClick={
                    viewModel.changeImageIndex(0)
                }){
                    Text("重置",fontSize = 16.sp)
                }
            }
        }
    }
}

6.主活动

class MainActivity : ComponentActivity() {
    lateinit var viewModel:LoadImageViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            //定义Handler对象
            val handler = object: Handler(Looper.getMainLooper()){
                override fun handleMessage(msg: Message) {
                    super.handleMessage(msg)
                    if(msg.what == 0x123){
                        //修改图片
                        viewModel.changeImage(msg.obj as String)
                        //修改图片索引
                        viewModel.changeImageIndex(msg.arg1)
                    }
                }
            }

            val imageRepository = ImageRepository(handler)
            viewModel = LoadImageViewModel(imageRepository)

            Ch06_DemoTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    //加载界面
                    ImageScreen(viewModel = viewModel, repository = imageRepository )
                }
            }
        }
    }
}

五、结合线程池和HandlerCompact实现Handler机制的多线程通信

在上述的显示在线图片的应用中,应用还可以优化。通过线程池和HandlerCompact获得的Handler,可以让多线程的通信的代码会更加简单。

1.在应用中定义线程池

在上述的代码中每次都是创建一个新的线程,这样会造成较多的问题:
(1)浪费资源,因为有些线程的资源没有及时回收,导致占用资源
(2)并发线程的管理困难
其实可以结合线程池来实现多线程的实现。线程池可以很好管控多个线程,可以提高响应速度,降低系统资源的消耗。而且线程池使用方便。
因为线程池往往在多处使用,因此,将线程池的处理放置在Application中,代码如下:

class MyApplication : Application() {
    //获取可用核心的数量,可供 Java 虚拟机使用的处理器数
    private val NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors()
    //创建线程体队列
    private val workQueue: BlockingQueue<Runnable> = LinkedBlockingQueue<Runnable>()
    //设置闲时线程的在终端线程前的等待时间
    private val KEEP_ALIVE_TIME = 1L
    //设置时间单位为秒
    private val KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS
    //创建一个线程池管理器
    val threadPoolExecutor:ThreadPoolExecutor = ThreadPoolExecutor(
        NUMBER_OF_CORES,   //初始化线程池的大小
        NUMBER_OF_CORES,   //最大线程池的个数
        KEEP_ALIVE_TIME,
        KEEP_ALIVE_TIME_UNIT,
        workQueue
    )
}

为了实现利用Application完成初始化的功能,需要在AndroidManifest.xml中增加android:name的配置,配置的内容如下:

<application
android:name=“.MyApplication”
…>

2.定义数据层

(1)定义结果类
Result类限制了Sucess和Error两种情况,Result.Success可以返回对象,Result.Error返回要抛出的异常。

sealed class Result<out R>{
    data class Success<out T>(val data:T):Result<T>()
    data class Error(val exception:Exception):Result<Nothing>()
}

(2)定义图像仓库

/**
 * 定义图形仓库
 * @property executor Executor
 * @property resultHandler Handler
 * @property imageLst List<String>
 * @constructor
 */
class ImageRepository(private val executor: Executor,
                      private val resultHandler: Handler) {
    val imageLst:List<String> = listOf(
        "https://somesite.com/1.jpg",
        "https://somesite.com/2.jpg",
        "https://somesite.com/3.jpg",
        "https://somesite.com/4.jpg",
        "https://somesite.com/5.jpg",
        "https://somesite.com/6.jpg"
    )


    /**
     * 请求加载数据
     * @param imageId Int 在图片列表中的序号
     * @param callBack Function1<Result<String>, Unit> 回调
     */
    fun loadImageById(imageId:Int,
                      callBack:(Result<String>)->Unit){
        //线程池中创建一个新线程并执行
        executor.execute{
            try{
                //按照列表索引请求图片资源
                val successResult = loadSynImageById(imageId)
                //与主线程通信
                resultHandler.post{
                    callBack(successResult)
                }
            }catch(e:Exception){
                val errorResult = Result.Error(e)
                resultHandler.post{
                	callBack(errorResult)
                }
            }
        }
    }

    private fun loadSynImageById(imageId:Int):Result<String>{
        if (imageId >= imageLst.size && imageId < 0)
            return Result.Error(Exception("图片索引存在问题"))
        return Result.Success(imageLst[imageId])
    }
}

3.定义ViewModel

class LoadImageViewModel(val imageRepository: ImageRepository): ViewModel() {
    val _currentImageId = MutableStateFlow<Int>(0)

    val _currentImage= MutableStateFlow<String>("")
    val currentImage = _currentImage.asStateFlow()

    /**
     * Change image indxe
     * 修改图片索引
     */
    fun changeImageIndex(){
        _currentImageId.value =(_currentImageId.value+1)%imageRepository.imageLst.size
    }

    /**
     * Request image
     * 请求图片
     */
    fun requestImage(){
        imageRepository.loadImageById(_currentImageId.value){it:Result<String>->
            when(it){
                //获取成功,修改在线图片的url
                is Result.Success<String>-> _currentImage.value = it.data
                //获取失败,提供失败的描述信息
                else->_currentImage.value = "加载在线图片资源失败"
            }
        }
    }
}

4.定义界面

@Composable
fun ImageScreen(viewModel:LoadImageViewModel){
    //获取当前图片状态
    val imageURLState = viewModel.currentImage.collectAsState()
    //获取当前图片索引
    val imageIdState = viewModel.currentImageId.collectAsState()

    Box(modifier = Modifier
        .fillMaxSize()
        .padding(20.dp)
        .background(Color.Black),
        contentAlignment = Alignment.Center){
        Column(horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center){
            if(!imageURLState.value.isBlank()) {
                AsyncImage(modifier= Modifier
                    .width(400.dp)
                    .height(400.dp)
                    .border(BorderStroke(1.dp, Color.Blue)),
                    model = imageURLState.value,
                    contentDescription = null)
            }
            else
                Text(modifier = Modifier
                    .width(300.dp)
                    .height(500.dp),
                    text = "等待加载图片",fontSize = 20.sp,textAlign = TextAlign.Center)
            
            Row(modifier = Modifier
                .fillMaxWidth()
                .padding(end = 10.dp), horizontalArrangement = Arrangement.Center){
                
                TextButton(colors = ButtonDefaults.buttonColors(contentColor = Color.Blue, containerColor = Color.LightGray),
                    onClick={
                    	//请求图片	
                        imageViewModel.requestImage()
                        //修改索引
            		    imageViewModel.changeImageIndex()
                    }){
                    Text("动态显示图片",fontSize=16.sp)
                }
            }
        }
    }
}

5.定义主活动

在主活动中,既也在主线程中,通过HandlerCompat这个访问Handler特性的帮助类,调用createAsync方法不但获取一个Handler对象,而且不必服从于同步的障碍。通过获得的Handler对象调用post方法来发布消息Message或将Runnable(线程体)对象。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            //创建应用对象
            val application = application as MyApplication
            //创建处理器
            val handler = HandlerCompat.createAsync(Looper.getMainLooper())
            //创建图形仓库
            val respository = ImageRepository(executor = application.threadPoolExecutor,
                resultHandler = handler)

            val imageViewModel = LoadImageViewModel(respository)

            Ch06_DemoTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ImageScreen(imageViewModel = imageViewModel)
                }
            }
        }
    }
}

参考文献

陈轶 《Android移动应用开发(微课版)》 P166-P170 清华大学出版社

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

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

相关文章

虚幻C++基础 day4

虚幻中的UI 虚幻中的比较常用的UI&#xff1a;Widget Blueprint又称UMG虚幻中的两种布局&#xff1a; 网格布局锚布局 创建Widget Blueprint 网格布局 有点类似Qt中的网格布局&#xff0c;将UI面板进行行列切分Horizontal Box&#xff1a;水平分布Vertical Box&#xff1a;…

【C++】——类与对象(二)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

微信号绑定50个开发者小程序以后超额如何删除不用的

我们在开发微信小程序的时候&#xff0c;当前开发者工具登录的必须是该小程序的开发者才能进行小程序的开发&#xff0c;添加开发者的步骤是&#xff1a; 添加开发者 1、进入微信开放平台&#xff0c;然后扫码进入管理平台 2、找到下图所示位置 3:、输入要添加的微信账号&am…

python调用chrome实现网页自动操作

一. 内容简介 python调用chrome实现网页自动操作。 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 链接&#xff1a; 三.主要流程 3.1 下载驱动和插件 调用谷歌浏览器&#xff0c;需要下载浏览器驱动&#xff08;https://registry.npmmirror.co…

fastadmin 表单页面,根据一个字段的值显示不同字段

表单中有计费方式&#xff0c;选中不同的计费方式显示不同的字段如下图 根据选择不同的计费方式&#xff1a;重量或夹板。展示不同相关字段&#xff1a;每件重量/每夹板件数量 add.html <div class"form-group"><label class"control-label col-xs-12…

redisson中的分布式锁二

公平锁&#xff08;Fair Lock&#xff09; 基于Redis的Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。同时还提供了异步&#xff08;Async&#xff09;、反射式&#xff08;Reactive&#xff09;和RxJava2标准的接口。它保证了当…

C#开发的OpenRA游戏之世界存在的属性CombatDebugOverlay(3)

C#开发的OpenRA游戏之世界存在的属性CombatDebugOverlay(3) 这次来分析CombatDebugOverlay属性,这个属性只有在调试游戏的时候才会使用。当你设置这个属性的时候,就可以看到如下图的结果: 可以看到物品的周边都有一个圆圈,以及有一些十字的点位标志。 那些十字表示的点…

LCD英文字模库(16x8)模拟测试程序

字模 字模&#xff0c;就是把文字符号转换为LCD能识别的像素点阵信息。 电子发烧友可能都熟悉字模的用途。就是调用者通过向LCD模块发送字模数据&#xff0c;LCD根据字模数据在LCD面板上相应的像素描绘出图形或文字。 现在&#xff0c;大部分的LCD都内置了字模库&#xff0c…

【脑源成像】术前癫痫的电源成像 评价:现状与未来展望

Electric source imaging for presurgical epilepsy evaluation: current status and future prospects 术前癫痫的电源成像 评价:现状与未来展望论文下载&#xff1a;算法程序下载&#xff1a;摘要1 项目介绍1.1. 选择要综述的文章2. 电源成像的基本原理4. ESI的实践方面4.1脑…

EPLAN中的电位,编号和报表

一、电位-eplan路由的理论基础 电位&#xff0c;信号和网络是eplan中的隐藏三君子。官网帮助中对电位和信号的解释如下&#xff1a; 在 EPLAN 中区分电位和信号。通过电位使连接属性的默认值和电位信息进入到项目中。 通过电位定义点或电位连接点定义一个电位或信号。此处录入…

Autosar模块介绍:Memory_2(NVM)

上一篇 | 返回主目录 | [下一篇] Autosar模块介绍&#xff1a;Memory_2(NVM 1 基本术语解释2 NVRAM Block组成关系2.1 基础Block类型2.1.1 NV Block2.1.2 RAM Block2.1.3 ROM Block2.2.4 Administrative Block 2.2 NVRAM Block类型 1 基本术语解释 编号缩写原文解释1NVNon Vol…

万字详解Java的三大特性:封装 | 继承 | 多态

前言&#xff1a;面向对象程序设计的三大特征就是&#xff1a;封装&#xff0c;继承&#xff0c;多态。在前文介绍了类和对象后&#xff0c;我们就可以继而学习什么是封装&#xff0c;怎么用类的子类来实现继承和多态 目录 一.面向对象的特性 1.封装性 2.继承性 3.多态性…

一种优雅的调用第三方接口的思路及实现

之前的项目调用第三方接口时&#xff0c;往往用HttpUtils类似的静态方法调用。比较丑&#xff0c;不通用。如下&#xff0c;这是截取项目中某人调用的一段代码&#xff0c;非常不雅&#xff1a; 经改进后&#xff0c;采用了动态代理技术来实现&#xff0c;效果如下&#xff1a…

麒麟KYLINOS中制作Ghost镜像文件

原文链接&#xff1a;麒麟KYLINOS中制作Ghost镜像文件 hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇在麒麟KYLINOS桌面操作系统2203中制作Ghost镜像文件的文章&#xff0c;首先需要将系统进行备份&#xff0c;然后在系统自带的备份还原工具中点击一键Ghost制作镜像…

【LeetCode】每日一题 2023_11_10 咒语和药水的成功对数(练习二分)

文章目录 刷题前唠嗑题目&#xff1a;咒语和药水的成功对数题目描述代码与解题思路偷看大佬题解 结语 刷题前唠嗑 LeetCode? 启动&#xff01;&#xff01;&#xff01; 可恶&#xff0c;今天的题目怎么也这么长 题目&#xff1a;咒语和药水的成功对数 题目链接&#xff1a…

微服务-我对Spring Clound的理解

官网&#xff1a;https://spring.io/projects/spring-cloud 官方说法&#xff1a;Spring Cloud 为开发人员提供了快速构建分布式系统中一些常见模式的工具&#xff08;例如配置管理、服务发现、熔断器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话…

paddleOcr训练一个属于自己的模型

自己配置开发环境的时候踩了不少坑&#xff0c;现在记录下来&#xff0c;以后需要了可以直接找到现成的代码&#xff0c;也希望能够帮到有需要的小伙伴 目录 &#xff08;一&#xff09;、运行环境搭建1、Anaconda部分Anaconda安装Anaconda创建虚拟环境指令Anaconda Prompt工作…

k8s二进制(ETCD的部署安装)

角色ip组件k8s-master192.168.11.169kube-apiserver,kube-controller-manager,kube-scheduler,etcdk8s-node1192.168.11.164kubelet,kube-proxy,docker,etcdk8s-node2192.168.11.166kubelet,kube-proxy,docker,etcd 1、为etcd签发证书 1、证书的下载(任意机器上执行都可以) …

双十一剁手节,MobPush助力各大电商平台提前锁定潜力用户

一年一度的购物狂欢节双十一还未到来&#xff0c;各大电商平台却早已摩拳擦掌跃跃欲试。据统计&#xff0c;10月31日晚8点&#xff0c;京东百亿补贴日开场不到4分钟&#xff0c;京东百亿补贴频道成交额破亿&#xff0c;抖音商城整体GMV较去年双11同期提升200%&#xff0c;支付用…

SPRINGBOOT整合CXF发布WEB SERVICE和客户端调用(用户和密码验证)

主要分为客户端和服务端 服务端 pom配置 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.3</version><relativePath/> <!-- lookup parent fro…