Moya 网络框架
Moya 网络框架
通过 Moya 进行网络请求的一般步骤如下:
1. 定义 TargetType:为每个 API 请求创建一个枚举,遵循 TargetType 协议,指定基础 URL、请求路径、方法、参数等。
2. 创建 MoyaProvider:实例化 MoyaProvider,并传入对应的 TargetType 枚举。
3. 发送请求:使用 MoyaProvider 发起请求并处理响应。
4. 解析数据:对响应数据进行解析(通常是 JSON 格式)。
5. 使用 RxSwift(可选):如果你需要响应式编程,可以将请求转换为 Observable,并使用 RxSwift 提供的操作符来处理异步数据流。
6. 处理错误:通过检查 MoyaError 来处理网络请求中的各种错误。
定义enum类型,有多种接口就定义多少种,然后实现TargetType协议
import Foundation
//导入网络框架
import Moyaenum DefaultService {//广告列表case ads(position : Int)case sheets(size:Int)case sheetDetail(data: String)case register(data : User)
}// MARK: - 实现TargetType协议
extension DefaultService:TargetType {//返回API地址var baseURL: URL {return URL(string: Config.ENDPOINT)!}//返回每个请求的路径var path: String {switch self {case.ads(_):return "v1/ads"case .sheets :return "v1/sheets"case .sheetDetail(let data):return "v1/sheets/\(data)"case .register(_) :return "v1/users"default:fatalError("error")}}//请求方式var method: Moya.Method {switch self {case .register :return .postdefault:return .get}}var task: Moya.Task {switch self {case .ads(let position):return ParamUtil.urlRequestParamters(["position":position])case .sheets(let size):return ParamUtil.urlRequestParamters(["size":size])default ://不传递任何参数return .requestPlain}}var headers: [String : String]? {var headers: Dictionary<String,String> = [:]return headers}}
使用方法
let provider = MoyaProvider<DefaultService>()provider.request(.sheets(size: 10)) { result inprint(result)switch result {case let .success(response) :let data = response.datalet statusCode = response.statusCodelet datastring = String(data: data,encoding: .utf8)!print(statusCode)print(datastring)case let .failure(error) :print(error)}}
可以看到,最后返回的形式为字符串,不方便使用,所以需要进一步进行封装,将其转换为对象
使用RxSwift框架请求网络
//rxSwift 方式
provider.rx.request(.sheets(size: 10)).debug("Request").subscribe { event inswitch event {case let .success(response) :print("hello ")let data = response.datalet statusCode = response.statusCodelet datastring = String(data: data,encoding: .utf8)!print(statusCode)print(datastring)if let r = SheetListResponse.deserialize(from: datastring){print(r.status)print(r.data.data[0].title!)}case let .failure(error) :print(error)}}.disposed(by: rx.disposeBag) //释放相关资源,防止内存泄露
使用框架解析JSON数据
使用第三方库HandyJSON
根据给出的JSON格式,建立相关的类即可
然后把字符串转换成相对应的格式即可
if let r = SheetListResponse.deserialize(from: datastring)
封装自动解析JSON功能
对Observable进行拓展
// MARK: - 扩展Observable
extension Observable{/// 将字符串解析为对象////// - Parameter type: 要转为的类/// - Returns: 转换后的观察者对象public func mapObject<T:HandyJSON>(_ type:T.Type) -> Observable<T> {map { data in//将参数尝试转为字符串guard let dataString = data as? String else {//data不能转为字符串throw IxueaError.objectMapping}guard let result = type.deserialize(from: dataString) else{throw IxueaError.objectMapping}//解析成功//返回解析后的对象return result}}
}
在 Swift 中,map 是一种高阶函数,通常用于对集合(如数组、字典、可选值等)中的元素进行转换或映射。在你的代码中,map 用于将某个数据转换为期望的类型。
map 的基本作用:
map 会对序列中的每个元素执行一个闭包,然后返回一个新序列,其中每个元素都经过闭包的处理。
let numbers = [1, 2, 3]
let doubledNumbers = numbers.map { $0 * 2 }
// doubledNumbers == [2, 4, 6]
在这个例子中,map 会遍历 numbers 数组中的每个元素,并将每个元素乘以 2,然后返回一个新的数组。
代码中 map 的使用:
代码中使用 map 似乎是在处理一些数据转换,具体是将某种类型的数据(data)尝试转为字符串类型,并解析成目标类型对象。详细讲解如下:
map { data in// 将参数尝试转为字符串guard let dataString = data as? String else {// data 不能转为字符串throw IxueaError.objectMapping}guard let result = type.deserialize(from: dataString) else {throw IxueaError.objectMapping}// 解析成功// 返回解析后的对象return result
}
分解讲解:
1. map { data in … }
• map 会对集合中的每个元素执行闭包中的代码,并返回一个新集合。这里的闭包是 data in { … },它接受集合中的每个元素作为输入(在这里是 data)。
• data 可能是一个原始数据,可以是任何类型,但在此代码中,它最终被尝试转换为 String 类型。
2. guard let dataString = data as? String else { throw IxueaError.objectMapping }
• 使用 guard let 尝试将 data 转换为 String 类型。如果转换失败(即 data 不是一个字符串),则会跳转到 else 语句,抛出错误(IxueaError.objectMapping),中断当前的操作并退出。
• guard let 语句是一种早期退出的方式,用来简化条件判断并在条件不满足时处理错误。
3. guard let result = type.deserialize(from: dataString) else { throw IxueaError.objectMapping }
• 假设 type 是一个类型,它提供了 deserialize(from:) 方法,用来将 dataString 字符串解析成目标类型的对象。
• 如果解析失败(即 deserialize 返回 nil),同样会抛出一个错误。deserialize 可能返回一个 nil,表示无法从字符串中成功解析出对象。
4. return result
• 如果 data 成功转换为字符串,并且字符串成功被解析为目标类型的对象,最终的 result 会被返回。这个 result 就是经过 map 处理后得到的新元素。
总结:
• map:遍历集合中的每个元素,并应用闭包中的代码,返回转换后的新集合。
• guard:用于提前退出函数,如果条件不成立则抛出错误(throw IxueaError.objectMapping)。
• deserialize:将字符串解析成目标类型,如果失败则抛出错误。
代码的核心目的是将原始数据转换为字符串,然后再将字符串解析为目标类型对象。如果过程中有任何失败的地方(如类型转换失败或解析失败),都会抛出错误并停止处理。
对网络部分进行封装
为了解决JSON中相同类型对复用,所以将一些公共部分对类型写成一个类去继承,这样就避免了对于每一个JSON格式都需要再进行定义该类型。
同理,我们不可能对于每一个JSON去创建一个对应的类,所以使用范型,定义一些公共部分,每次传入该JSON应该属于什么类型即可。即定义了详情网络解析类,解析列表网络请求类
对Observable拓展moya网络相关功能
//
// ObservableMoyaExtension.swift
// 对Observable拓展moya网络相关功能
//
// Created by Unlimited_z on 2025/2/16.
//import Foundation
import HandyJSON
import Moya
import RxSwiftpublic enum IxueaError : Swift.Error {case objectMapping
}// MARK: - 扩展Observable
extension Observable{/// 将字符串解析为对象////// - Parameter type: 要转为的类/// - Returns: 转换后的观察者对象public func mapObject<T:HandyJSON>(_ type:T.Type) -> Observable<T> {map { data in//将参数尝试转为字符串guard let dataString = data as? String else {//data不能转为字符串throw IxueaError.objectMapping}guard let result = type.deserialize(from: dataString) else{throw IxueaError.objectMapping}//解析成功//返回解析后的对象return result}}
}//http网络请求观察者
public class HttpObserver<Element>:ObserverType{/// ObserverType协议中用到了泛型E/// 所以说子类中就要指定E这个泛型/// 不然就会报错public typealias E = Elementpublic typealias successCallback = ((E)-> Void)/// 请求成功回调var success:successCallback/// 请求失败回调var error:((BaseResponse?,Error?)-> Bool)?var controller:BaseLogicController?/// 构造方法////// - Parameters:/// - onSuccess: 请求成功的回调/// - onError: 请求失败的回调init(_ success:@escaping successCallback,_ error: ((BaseResponse?,Error?)-> Bool)?) {self.success = successself.error = error}/// 当RxSwift框架里面发送了事件回调////// - Parameter event: <#event description#>public func on(_ event: Event<Element>) {switch event {case .next(let data):
// print("HttpObserver next \(data)")//将值尝试转为BaseResponselet baseResponse = data as? BaseResponseif baseResponse?.status != 0 {//状态码不等于0//表示请求出错了handlerResponse(baseResponse:baseResponse)} else {//请求正常success(data)}case .error(let error)://请求失败
// print("HttpObserver error:\(error)")handlerResponse(error:error)case .completed://请求完成
// print("HttpObserver completed")break}}/// 尝试处理错误网络请求////// - Parameters:/// - baseResponse: 请求返回的对象/// - error: 错误信息func handlerResponse(baseResponse:BaseResponse?=nil,error:Error?=nil) {if self.error != nil && self.error!(baseResponse,error) {//回调失败block//返回true,父类不自动处理错误//子类需要关闭loading,当前也可以父类关闭//暴露给子类的原因是,有些场景会用到//例如:请求失败后,在调用一个接口,如果中途关闭了//用户能看到多次显示loading,体验没那么好} else {//自动处理错误ExceptionHandleUtil.handlerResponse(baseResponse,error)}}}// MARK: - 扩展ObservableType
// 目的是添加两个自定义监听方法
// 一个是只观察请求成功的方法
// 一个既可以观察请求成功也可以观察请求失败
extension ObservableType{/// 观察成功和失败事件////// - Parameter onSuccess: <#onSuccess description#>/// - Returns: <#return value description#>func subscribe(_ success:@escaping ((Element)-> Void),_ error: @escaping ((BaseResponse?,Error?)-> Bool)) -> Disposable {//创建一个Disposablelet disposable = Disposables.create()//创建一个HttpObserverlet observer = HttpObserver<Element>(success,error)//创建并返回一个Disposablesreturn Disposables.create(self.asObservable().subscribe(observer),disposable)}/// 观察成功的事件////// - Parameter onSuccess: <#onSuccess description#>/// - Returns: <#return value description#>func subscribeSuccess(_ controller:BaseLogicController?=nil, _ success:@escaping ((Element)-> Void) ) -> Disposable {let disposable = Disposables.create()let observer = HttpObserver<Element>(success,nil)return Disposables.create(self.asObservable().subscribe(observer),disposable)}
}
可以理解为,对第三方库的重写或是封装
这段代码的核心目的是将 Moya 网络请求的响应封装在 RxSwift 的 Observable 中,并提供更灵活的网络请求错误处理和数据绑定机制。
1. mapObject 扩展
public func mapObject<T:HandyJSON>(_ type:T.Type) -> Observable<T> {map { data inguard let dataString = data as? String else {throw IxueaError.objectMapping}guard let result = type.deserialize(from: dataString) else{throw IxueaError.objectMapping}return result}
}
作用:
• mapObject 是对 Observable 的扩展,用于将网络请求的结果(假设是 JSON 格式的字符串)转换为一个指定类型(如 User 或其他符合 HandyJSON 的对象)。
• 它会将原始的 data 转换为字符串,并通过 HandyJSON 库来解析为对象。
• 如果转换失败,会抛出 IxueaError.objectMapping 错误。
调用顺序:
1. 你在 Observable 中调用 mapObject,传入目标类型 T。
2. 通过 map 操作符对响应数据进行处理,把响应的 JSON 字符串解析为指定类型的对象。
2. HttpObserver 类
public class HttpObserver<Element>: ObserverType {public typealias E = Elementvar success: (E) -> Voidvar error: ((BaseResponse?, Error?) -> Bool)?public init(_ success: @escaping (E) -> Void, _ error: ((BaseResponse?, Error?) -> Bool)?) {self.success = successself.error = error}public func on(_ event: Event<Element>) {switch event {case .next(let data):let baseResponse = data as? BaseResponseif baseResponse?.status != 0 {handlerResponse(baseResponse: baseResponse)} else {success(data)}case .error(let error):handlerResponse(error: error)case .completed:break}}func handlerResponse(baseResponse: BaseResponse? = nil, error: Error? = nil) {if self.error != nil && self.error!(baseResponse, error) {// custom error handling} else {ExceptionHandleUtil.handlerResponse(baseResponse, error)}}
}
作用:
• HttpObserver 是 ObserverType 的实现,它处理 Observable 发出的事件。
• on 方法根据收到的事件类型(next、error、completed)来执行相应的处理:
• next:如果请求成功(状态码 status 为 0),则调用 success 回调;如果请求失败(状态码非 0),则调用 handlerResponse 处理错误。
• error:请求失败,调用 handlerResponse。
• completed:请求完成,什么都不做。
• handlerResponse 根据具体情况调用 ExceptionHandleUtil.handlerResponse 来处理错误。
调用顺序:
1. HttpObserver 被用来观察 Observable 发送的事件(例如网络请求的结果)。
2. 当 Observable 发出 next 事件时,HttpObserver 会检查响应的状态码并决定是否调用 success 回调或处理错误。
3. 如果有错误,handlerResponse 会被调用,根据情况执行自定义的错误处理。
3. ObservableType 扩展
extension ObservableType {func subscribe(_ success: @escaping ((Element) -> Void), _ error: @escaping ((BaseResponse?, Error?) -> Bool)) -> Disposable {let disposable = Disposables.create()let observer = HttpObserver<Element>(success, error)return Disposables.create(self.asObservable().subscribe(observer), disposable)}func subscribeSuccess(_ success: @escaping ((Element) -> Void)) -> Disposable {let disposable = Disposables.create()let observer = HttpObserver<Element>(success, nil)return Disposables.create(self.asObservable().subscribe(observer), disposable)}
}
作用:
• 这里对 ObservableType 进行了扩展,添加了自定义的 subscribe 和 subscribeSuccess 方法:
• subscribe:这个方法接收成功和失败的回调,并将它们传给 HttpObserver 处理。
• subscribeSuccess:只关心请求成功的回调,不处理失败情况。这个方法的 error 参数为 nil。
调用顺序:
1. 外部通过 subscribe 或 subscribeSuccess 方法订阅 Observable。
2. 这些方法内部会创建一个 HttpObserver 并将它绑定到 Observable 上。
3. 当 Observable 发出事件时(例如 next、error、completed),HttpObserver 会接收到这些事件并执行相应的回调。
4. 外部调用顺序
假设你在外部调用了以下代码:
provider.rx.request(.getUserInfo(userId: "123")).asObservable().mapObject(User.self).subscribe { event inswitch event {case .next(let data):print("User Info: \(data)")case .error(let error):print("Request failed: \(error)")case .completed:print("Completed")}}.disposed(by: disposeBag)
调用流程:
1. provider.rx.request(.getUserInfo(userId: “123”)) 发起网络请求,返回一个 Observable。
2. asObservable() 将 Single 转换为 Observable。
3. mapObject(User.self) 将响应的 JSON 字符串转换为 User 对象。
4. subscribe 方法订阅 Observable,并传入一个闭包来处理 next、error 和 completed 事件。
• 如果请求成功,next 事件会被触发,data 会是一个 User 对象。
• 如果请求失败,error 事件会被触发,error 参数为错误信息。
• completed 事件会在请求完成时触发。
5. 内存管理
.disposed(by: disposeBag)
• disposeBag 用于管理订阅的生命周期,确保在视图控制器销毁时取消订阅,防止内存泄漏。
总结
• 外部代码通过 provider.rx.request() 发起请求,接着通过 mapObject 转换响应数据。
• subscribe 方法将请求的成功和失败回调传递给 HttpObserver,并处理事件。
• HttpObserver 根据响应的 status 来决定调用 success 或处理错误。
• 内部错误处理通过 handlerResponse 实现,允许开发者自定义错误处理逻辑。
相关文章:
Moya 网络框架
Moya 网络框架 通过 Moya 进行网络请求的一般步骤如下: 1. 定义 TargetType:为每个 API 请求创建一个枚举,遵循 TargetType 协议,指定基础 URL、请求路径、方法、参数等。 2. 创建 MoyaProvider:实例化 MoyaProvider&…...
FreeRTOS第3篇:链表的“精密齿轮”——列表与列表项
文章目录 1 列表与列表项:FreeRTOS的“排队系统”2 列表操作:FreeRTOS的“排队算法”3 列表的应用场景:FreeRTOS的“任务调度枢纽”4 源码级洞察:列表的“灵魂代码”5 实战:列表操作实验6 总结与思考引言:嵌入式系统的“任务候车厅” 想象你正在管理一座繁忙的火车站:乘…...
React.memo 使用详解与最佳实践
React.memo 使用详解与最佳实践 引言React.memo 是什么?使用场景实战示例示例解析自定义比较函数使用注意事项总结 引言 在 React 应用程序中,性能优化是一个永恒的话题。当父组件状态发生变化时,即使子组件的 props 没有改变,子…...
SpringBoot中集成SaToken
SpringBoot中集成SaToken 1. 写一个拦截器2. 对拦截器的说明&解释2. 拦截器 1. 写一个拦截器 import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.stp.StpUtil; import org.springframework.beans.factory.annotation.Value; import org.spri…...
网络安全-攻击流程-应用层
应用层攻击针对OSI模型的第七层(应用层),主要利用协议漏洞、业务逻辑缺陷或用户交互弱点,直接威胁Web应用、API、数据库等服务。以下是常见应用层攻击类型及其流程,以及防御措施: 1. SQL注入(SQ…...
Ubuntu 24.04.1 LTS 本地部署 DeepSeek 私有化知识库
文章目录 前言工具介绍与作用工具的关联与协同工作必要性分析 1、DeepSeek 简介1.1、DeepSeek-R1 硬件要求 2、Linux 环境说明2.1、最小部署(Ollama DeepSeek)2.1.1、扩展(非必须) - Ollama 后台运行、开机自启: 2.2、…...
微信小程序中缓存数据全方位解惑
微信小程序中缓存数据全方位解惑 微信小程序中的数据缓存是提升用户体验和优化性能的重要手段,跟电脑浏览器中的Local Storage的性质一样。以下是关于微信小程序数据缓存的相关知识点和示例的详细介绍: 1. 数据缓存的类型 微信小程序提供了两种数据缓…...
python语言进阶之函数
目录 前言 函数的创建和调用 函数创建 调用函数 参数传递 形式参数和实际参数 位置参数 数量必须与定义时一致 位置必须与定义时一致 关键字参数 为参数设置默认值 可变参数 **parameter 返回值 变量的作用域 局部变量 全局变量 匿名函数 前言 提到函数&…...
Mybatis-扩展功能
逻辑删除乐观锁 MyBatisPlus从入门到精通-3(含mp代码生成器) Db静态工具类 Spring依赖循环问题 代码生成器 MybatisPlus代码生成器 枚举处理器 我们这里用int来存储状态 需要注解,很不灵活 希望用枚举类来代替这个Integer 这样的话我…...
青少年编程与数学 02-009 Django 5 Web 编程 16课题、权限管理
青少年编程与数学 02-009 Django 5 Web 编程 16课题、权限管理 一、授权授权的主要特点和作用授权的类型应用场景 二、权限系统使用Django内置的权限系统使用组管理权限使用第三方库在视图中应用权限 三、权限管理示例步骤 1: 创建Django项目和应用步骤 2: 定义模型和权限步骤 …...
Baklib知识中台构建企业智能运营核心架构
内容概要 在数字化转型的浪潮中,企业对于知识的系统化管理需求日益迫切。Baklib作为新一代的知识中台,通过构建智能运营核心架构,为企业提供了一套从知识汇聚到场景化落地的完整解决方案。其核心价值在于将分散的知识资源整合为统一的资产池…...
Java爬虫获取1688商品搜索API接口的实现指南
在电商数据分析、市场调研以及商品选品等领域,按关键字搜索1688商品并获取相关数据是一项重要的任务。本文将详细介绍如何使用Java爬虫技术,通过1688的API接口按关键字搜索商品,并解析返回的数据。以下是实现的完整步骤和代码示例。 一、前期…...
Ubuntu启动geteck/jetlinks实战:Docker启动
参考: JetLinks 物联网基础平台 安装Docker Ubuntu下载安装Docker-Desktop-CSDN博客 sudo apt install -y docker-compose 下载源码 # github亦可 git clone https://gitee.com/jetlinks/jetlinks-community.git cd jetlinks-community 启动 cd docker/run-a…...
保姆级GitHub大文件(100mb-2gb)上传教程
GLF(Git Large File Storage)安装使用 使用GitHub desktop上传大于100mb的文件时报错 The following files are over 100MB. lf you commit these files, you will no longer beable to push this repository to GitHub.com.term.rarWe recommend you a…...
【16届蓝桥杯寒假刷题营】第2期DAY1I
4.有向无环的路径数 - 蓝桥云课 问题描述 给定 N 个节点 M 条边的有向无环图,请你求解有多少条 1 到 N 的路径。 由于答案可能很大,你只需要输出答案对 998244353 取模后的结果。 输入格式 第一行包含 2 个正整数 N,M,表示有向无环图的节…...
WEB安全--SQL注入--PDO与绕过
一、PDO介绍: 1.1、原理: PDO支持使用预处理语句(Prepared Statements),这可以有效防止SQL注入攻击。预处理语句将SQL语句与数据分开处理,使得用户输入的数据始终作为参数传递给数据库,而不会直…...
SQL与数据库程序设计
1.1986年,10月美国国家标准局颁布了SQL语言的美国标准,称为SQL86 2.SQL(Structured Query Language)又称为结构化查询语言 3.建立索引的主要目的是加快查找的速度 4.在基本表上建立一个或者多个索引 5. 一个基本表是最多只能建立一个聚簇索引 6.CAL…...
软考高级《系统架构设计师》知识点(五)
计算机网络 网络概述和模型 计算机网络是计算机技术与通信技术相结合的产物,它实现了远程通信、远程信息处理和资源共享。 计算机网络的功能:数据通信、资源共享、管理集中化、实现分布式处理、负载均衡。 网络性能指标:速率、带宽(频带宽度或…...
DeepSeek 助力 Vue 开发:打造丝滑的面包屑导航(Breadcrumbs)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…...
Ubuntu 系统 LVM 逻辑卷扩容教程
Ubuntu 系统 LVM 逻辑卷扩容教程 前言 在 Linux 系统中,LVM(Logical Volume Manager)是一种逻辑卷管理工具,允许管理员动态调整磁盘空间,而无需重启系统。 本文将详细介绍如何使用 LVM 扩容逻辑卷,以实现…...
美团一面,有点难度。
一位粉丝朋友分享了最近参与美团民宿旅游业务线的一面的经历,全程约1小时,面试官围绕高并发、分布式事务、性能优化等高频考点展开追问,问题密集且注重落地细节。以下是完整问题整理回答思路扩展解析,助你避坑! 一、项…...
7-Zip Final绿色版:高效压缩解压缩工具
在工作与学习旅程中,我们时常需要与各式各样的文件和文件夹打交道。为了更有效地利用存储空间或促进文件的便捷传输,压缩与解压工具自然而然地成为了我们不可或缺的助手。在众多同类工具中,7-Zip凭借其高效能、免费及开源的特性,深…...
详解如何使用Pytest内置Fixture tmp_path 管理临时文件
关注开源优测不迷路 大数据测试过程、策略及挑战 测试框架原理,构建成功的基石 在自动化测试工作之前,你应该知道的10条建议 在自动化测试中,重要的不是工具 临时目录在测试中起着至关重要的作用,它为执行和验证代码提供了一个可控…...
QML使用ChartView绘制饼状图
一、工程配置 首先修改CMakeLists.txt,按下图修改: find_package(Qt6 6.4 REQUIRED COMPONENTS Quick Widgets) PRIVATEtarget_link_libraries(appuntitledPRIVATE Qt6::QuickPRIVATE Qt6::Widgets )其次修改main.cpp,按下图修改ÿ…...
用大模型学大模型03-数学基础 概率论 最大似然估计(MLE)最大后验估计(MAP)
https://metaso.cn/s/r4kq4Ni 什么是最大似然估计(MLE)最大后验估计(MAP)?深度学习中如何应用,举例说明。 好的,我现在需要回答关于最大似然估计(MLE)和最大后验估计&…...
Rust学习总结之结构体(一)
一:结构体定义 定义结构体,需要使用 struct 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 字段(field…...
【Android开发】华为手机安装包安装失败“应用是非正式版发布版本,当前设备不支持安装”问题解决
问题描述 我们将Debug版本的安装包发送到手机上安装,会发现华为手机有如下情况 解决办法 在文件gradle.properties中粘贴代码: android.injected.testOnlyfalse 最后点击“Sync now”,等待重新加载gradle资源即可 后面我们重新编译Debug安装…...
Ubuntu添加桌面快捷方式
以idea为例 一. 背景 在ubuntu中,很多时候是自己解压的文件并没有桌面快捷方式,需要自己找到对应的目录的执行文件手动打开,很麻烦 而只需要在 /usr/share/applications 中创建自定义的desktop文件就能自动复制到桌面 二. 添加方法 创建desk…...
day09_实时类标签/指标
文章目录 day09_实时类标签/指标一、日志数据实时采集2、Flume简介2.3 项目日志数据采集Flume配置2.3.1 涉及的Flume组件和参数2.3.2 Nginx日志采集2.3.3 用户行为日志采集 二、Nginx日志数据统计1、日志格式说明2、数据ETL2.1 日志抽取2.1.1 正则表达式2.1.2 基于Spark实现Ngi…...
排序算法的魔法世界:用C语言揭开数据排列的奥秘
当数据开始跳集体舞:排序的意义 想象你面前有一群调皮的数字精灵在开派对,7和3在跳探戈,9和1在玩捉迷藏,5和2在抢蛋糕。这时候就需要排序算法这位神奇的派对管家出场了!它像音乐指挥家一样挥动魔棒,让所有数字精灵乖乖排成整齐的队伍。在计算机的世界里,排序算法就是处…...
