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

在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.mfbz.cn/a/127300.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为认证 | 11月底这门HCIP认证即将发布!

非常荣幸地通知您&#xff0c;华为认证HCIP-Storage V5.5&#xff08;中文版&#xff09;预计将于2023年11月30日正式对外发布。为了帮助您做好学习、培训和考试计划&#xff0c;现进行预发布通知&#xff0c;请您关注。 01 发布概述 基于“平台生态”战略&#xff0c;围绕“云…

理解RNN以及模型搭建代码

RNN结构 这是一张不直观易懂的RNN结构示意图。但也是大家见得最多结构示意图。 RNN模型解释 RNN一文就讲解清楚的博客&#xff0c;看这里&#xff1a;https://zhuanlan.zhihu.com/p/408998328 RNN为什么梯度消失和梯度爆炸&#xff0c;看这里&#xff1a;https://zhuanlan.z…

Windows搭建Web站点:免费内网穿透发布至公网

目录 什么是cpolar&#xff1f; 概述 1. 注册并安装cpolar内网穿透 2. 搭建一个静态Web站点 2.1 下载演示站点 2.2 本地运行演示站点 2.3 本地浏览测试站点是否正常 3. 本地站点发布公网可访问 3.1 登录cpolar web ui管理界面 3.2 启动website隧道 3.3 获取公网URL地…

AI:76-基于机器学习的智能城市交通管理

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

Power Automate-创建和运行

网站&#xff1a;Microsoft Power Automate 根据自己需求选择创建 选择需要的触发方式 点击添加新步骤 可以选择多种微软应用或者自定义应用连接 此处以向SharePoint列表追加项为例&#xff0c;要提前创建好SharePoint列表&#xff0c;并写好表结构 搜索SharePoint&#xff0…

【机器学习】给大家推荐几个资源

我写博客的目的就是让大家了解人工智能背后的数学原理&#xff0c;但人工智能这个话题太大了&#xff0c;背后涉及到的知识非常庞大&#xff0c;仅靠写几篇文章传播力度有限&#xff0c;况且知识传播过程中也容易引入误解&#xff0c;所以授之以鱼不如授之以渔&#xff0c;这里…

NIO 笔记(二)Netty框架专题

【笔记来自&#xff1a;it白马】 Netty框架 前面我们学习了Java为我们提供的NIO框架&#xff0c;提供使用NIO提供的三大组件&#xff0c;我们就可以编写更加高性能的客户端/服务端网络程序了&#xff0c;甚至还可以自行规定一种通信协议进行通信。 NIO框架存在的问题 但是之…

openfeign客户端A调用服务B,服务B抛出异常时,客户端A接收的几种情况

openfeign客户端A调用服务B&#xff0c;服务B抛出异常时&#xff0c;客户端A接收的几种情况 一、openfeign客户端调用远程服务时&#xff0c;远程服务抛出异常后&#xff0c;根据调用方法的返回类型的不同&#xff0c;会有不同的返回结果0 先看非openfeign调用的情况&#xff1…

在vue中如果头像为空时用姓名第一个字当头像

业务场景:当个人资料或者用户头像没有图片时&#xff0c;默认使用户名字中第一个汉字做头像。 效果图&#xff1a; 完整代码&#xff1a; <el-avatarsize"large" style"width: 45px; height: 45px; line-height: 45px; font-size: 24px"v-if"…

海淀区委网信办领导到中睿天下调研

近日&#xff0c;北京市海淀区委宣传部副部长&#xff0c;海淀区委网信办主任黄英、海淀区委网信办副主任陈魁一行到中睿天下北京总部调研&#xff0c;中睿天下副总经理李小琼、联合创始人魏海宇、国防营销中心总经理吕宗辉参加。 海淀区委网信办一行参观了中睿天下展厅&#x…

元核云亮相金博会,智能质检助力金融合规

11月初&#xff0c;第五届中新&#xff08;苏州&#xff09;数字金融应用博览会&#xff5c;2023金融科技大会在苏州国际博览中心举办&#xff0c;围绕金融科技发展热点领域及金融行业信息科技领域重点工作&#xff0c;分享优秀实践经验&#xff0c;探讨数字化转型路径与未来发…

基于IGT-DSER智能网关实现GE的PAC/PLC与罗克韦尔(AB)的PLC之间通讯

工业自动化领域的IGT-DSER智能网关模块支持GE、西门子、三菱、欧姆龙、AB等各种品牌的PLC之间通讯(相关资料下载)&#xff0c;同时也支持PLC与Modbus协议的工业机器人、智能仪表等设备通讯。网关有多个网口、串口&#xff0c;也可选择WIFI无线通讯。无需编程开发&#xff0c;只…

3线硬件SPI+DMA驱动 HX8347 TFT屏

3线硬件SPIDMA驱动 HX8347 TFT屏&#xff0c;实现用DMA清屏。 参考&#xff1a;基于stm32 标准库spi驱动st7789彩屏TFT(使用DMA)-技术天地-深圳市修德电子有限公司 一、源码 HX8347.h #ifndef USER_HX8347_H_ #define USER_HX8347_H_#define SPI_hardware #define SPI_hardw…

基于表面电势的AlGaN/GaN MODFET紧凑模型

标题&#xff1a;A Surface-Potential-Based Compact Model for AlGaN/GaN MODFETs 来源&#xff1a;IEEE TRANSACTIONS ON ELECTRON DEVICES&#xff08;11年&#xff09; 摘要 - 本文首次构建了基于表面势&#xff08;SP&#xff09;的AlGaN/GaN调制掺杂场效应晶体管&#…

说说地址栏输入 URL 敲下回车后发生了什么?

面试官&#xff1a;说说地址栏输入 URL 敲下回车后发生了什么? 一、简单分析 简单的分析&#xff0c;从输入 URL到回车后发生的行为如下&#xff1a; URL解析DNS 查询TCP 连接HTTP 请求响应请求页面渲染 二、详细分析 URL解析 首先判断你输入的是一个合法的 URL 还是一个…

C++初阶(九)内存管理

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、C/C内存分布1、选择题2、填空题3、sizeof 和 strlen 区别&#xff1f;4、总结 二、 C语言…

19.8 Boost Asio 异或加密传输

异或加密是一种对称加密算法&#xff0c;通常用于加密二进制数据。异或操作的本质是对两个二进制数字进行比较&#xff0c;如果它们相同则返回0&#xff0c;如果不同则返回1。异或加密使用一把密钥将明文与密文进行异或运算&#xff0c;从而产生密文。同时&#xff0c;使用相同…

数字马力笔试面试复盘

笔试——10月9日19&#xff1a;00 单选&#xff1a;30题 16.如何获取AJAX 请求的响应状态码? A通过AJAX对象的 statusCode 属性获取 B通过AJAX对象的responseText 属性获取C通过AJAX对象的status 属性获取 D通过AJAX对象的responseCode属性获取 答案&#xff1a;可以通过AJAX…

Python学习笔记--枚举类的使用

一、枚举类的使用 实际开发中&#xff0c;我们离不开定义常量&#xff0c;当我们需要定义常量时&#xff0c;其中一个办法是用大写变量通过整数来定义&#xff0c;例如月份&#xff1a; JAN 1 FEB 2 MAR 3 ... NOV 11 DEC 12当然这样做简单快捷&#xff0c;缺点是类型是…

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

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