JUC下的CompletableFuture详解

详细介绍

CompletableFuture是Java 8引入的一个实现Future接口的类,它代表一个异步计算的结果。与传统的Future相比,CompletableFuture提供了更丰富的功能,比如链式调用、组合异步操作、转换结果、异常处理等,极大地增强了Java在处理异步编程的能力。

核心API与方法
  1. 创建实例

    • CompletableFuture<Void/Type> supplyAsync(Supplier<U> supplier):在ForkJoinPool.commonPool()或自定义Executor中异步执行supplier,并返回一个新的CompletableFuture,其结果类型由Supplier决定。
    • CompletableFuture<Void> runAsync(Runnable runnable):同上,但Runnable没有返回值,因此CompletableFuture的类型为Void。
    • 还有非Async版本,如supplyAsync(Supplier<U> supplier, Executor executor),允许指定执行器。
  2. 链式调用与转换结果

    • .thenApply(Function<? super T,? extends U> fn):当前阶段正常完成时,使用给定的函数fn转换结果,并返回一个新的CompletableFuture。
    • .thenAccept(Consumer<? super T> action):当前阶段正常完成时,执行给定的action消费结果,无返回值。
    • .thenCompose(Function<? super T,? extends CompletionStage<U>> fn):当前阶段完成时,使用给定的函数fn将结果转换为另一个CompletionStage,并将其结果作为新的CompletableFuture。
  3. 组合异步操作

    • .thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn):当两个CompletableFuture都完成时,使用BiFunction组合它们的结果。
    • .thenComposeBoth(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends CompletionStage<V>> fn):类似于.thenCombine(),但fn返回的是另一个CompletionStage,而非直接结果。
  4. 异常处理

    • .exceptionally(Function<Throwable,? extends T> fn):如果出现异常,使用提供的函数fn处理异常,并返回一个包含处理结果的新CompletableFuture。
    • .handle(BiFunction<? super T, Throwable, ? extends U> fn):无论正常完成还是异常结束,都会调用fn处理结果或异常,返回一个新的CompletableFuture。
  5. 完成与等待

    • .join():等待计算完成,如有异常直接抛出,适用于不需要区分成功失败的场景。
    • .get():同join,但此方法会抛出具体的ExecutionException或InterruptedException,便于区分错误类型。
  6. 触发动作

    • .whenComplete(BiConsumer<? super T,? super Throwable> action):无论CompletableFuture成功完成还是异常结束,都会执行action,但不改变结果或处理异常。
    • .whenCompleteAsync(BiConsumer<? super T,? super Throwable> action):异步版本的whenComplete。
实现原理浅析

   CompletableFuture基于Java的Fork/Join框架实现,利用工作窃取算法提高线程利用率。每个CompletableFuture对象内部维护了一个状态机,用于跟踪任务的完成状态(初始化、完成、取消、异常)。当一个阶段的任务完成时,它会触发后续阶段的任务开始执行,这一过程通过内部的等待队列和信号机制高效完成,实现了非阻塞的链式调用。

性能与最佳实践
  • 线程池选择:合理选择线程池,避免过度使用ForkJoinPool.commonPool(),尤其是在IO密集型任务中,应考虑使用自定义的固定大小线程池。
  • 避免过度链式:虽然链式调用使代码简洁,但过度使用可能导致逻辑难以理解和维护,适当使用中间变量命名可以帮助理解。
  • 异常传播与日志:确保异常处理逻辑完整,利用日志记录异常信息,便于调试。
  • 资源释放:在涉及资源操作的异步任务中,确保资源能够正确关闭或释放,避免资源泄露。

核心特性

1. 非阻塞式执行

CompletableFuture 允许任务异步执行,而不需要调用线程等待其完成。这意味着主线程可以继续执行其他操作,而不是被阻塞等待结果,提高了程序的响应性和并发性能。

2. 异步回调

它支持链式调用回调函数(如 thenApply, thenAccept, thenCompose 等),允许在异步操作完成时自动执行后续操作,无论是成功还是失败。这种机制使得复杂的异步流程控制变得简单直观。

3. 组合异步任务

CompletableFuture 提供了多种方法来组合多个异步操作,比如 thenCombine, thenCompose,可以将多个独立的异步任务按照依赖关系组织起来,实现任务间的流水线处理,提高程序的并发执行效率。

4. 异步转换结果

通过 thenApply 方法,可以在异步操作完成后,对结果进行转换或进一步处理,而无需等待整个过程完成,这对于构建复杂的异步工作流特别有用。

5. 错误处理

提供了错误处理机制,如 exceptionallyhandle 方法,可以优雅地捕获并处理异步执行过程中发生的异常,增强了程序的健壮性。

6. 自动线程管理

默认情况下,CompletableFuture 会使用 ForkJoinPool.commonPool() 来执行异步任务,但也可以通过 supplyAsync(Supplier<U> supplier, Executor executor) 方法指定自定义的 Executor,从而实现对线程资源的精细控制。

7. 可取消性

如同 FutureCompletableFuture 也支持任务的取消操作,通过 cancel(boolean mayInterruptIfRunning) 方法可以尝试取消一个正在执行或尚未开始的任务。

8. 延迟计算与懒初始化

通过 CompletableFuture.supplyAsync(Supplier<U> supplier) 方法,可以在需要结果时才真正触发计算,这对于资源敏感或计算密集型操作特别有用。

9. 高度可组合性

由于其丰富的API,CompletableFuture 可以轻松地与其他 CompletableFuture 实例组合,形成复杂的异步逻辑,而代码依然保持清晰和可维护。

10. 函数式编程风格

它鼓励使用函数式编程的思维,通过传递函数(Lambda表达式)来定义任务的执行逻辑和结果处理逻辑,使得代码更加简洁、易读且易于推理。

这些特性使 CompletableFuture 成为了Java中处理异步编程的强大工具,尤其是在构建高性能、响应式系统时,能够有效地提升代码的效率和可维护性。

使用场景

微服务架构中的异步通信

在微服务架构中,服务间通常通过HTTP、gRPC或其他协议进行通信。当一个服务需要调用多个下游服务获取数据时,如果采用同步方式,每个调用都会阻塞当前线程直到响应,这会大大增加请求的总耗时。使用CompletableFuture,可以并行发起这些调用,然后通过组合操作处理所有响应,显著减少整体响应时间,提升用户体验。

示例场景:电商平台的订单服务需要在创建订单时,异步调用库存服务检查商品库存、用户服务验证用户信息、支付服务预扣款等多个下游服务,使用CompletableFuture可以高效地并行处理这些任务。

数据库批量操作

对于大量数据的读写操作,如批量插入、更新或查询,直接在主线程中执行可能会导致长时间阻塞。通过CompletableFuture,可以将这些操作分批异步执行,每批操作完成后合并结果,既充分利用了系统资源,又保证了操作的高效性。

示例场景:系统日志收集模块,需要定时从多个来源拉取日志并存储到数据库中。利用CompletableFuture异步处理每个数据源的日志收集和存储任务,最后合并所有操作结果。

文件处理与上传下载

处理大文件的上传、下载或转换操作时,这些操作往往比较耗时,直接在主线程中执行会影响用户体验。使用CompletableFuture可以将这些操作放在后台执行,同时可以提供进度监控、结果通知等功能。

示例场景:云存储服务中,用户上传大文件后,服务端需要异步进行病毒扫描、格式转换等操作,完成后通知用户或进行下一步处理。

定时任务与周期性任务

在实现定时任务或周期性任务时,如定时数据分析、报表生成等,可以利用CompletableFuture配合ScheduledExecutorService进行异步调度,避免阻塞主线程,并且可以方便地控制任务的执行策略(如首次执行延迟、执行间隔等)。

示例场景:每天凌晨自动统计前一天的销售数据并生成报表,通过CompletableFuture安排在低峰时段异步执行,不影响白天的业务操作。

并发限制与资源池管理

在处理大量并发请求时,直接为每个请求分配线程可能会迅速耗尽资源。通过自定义线程池与CompletableFuture结合,可以有效控制并发数量,例如使用Semaphore限制并发数,确保系统稳定运行。

示例场景:在爬虫系统中,需要并发抓取大量网页,但不能无限制地创建线程。通过CompletableFuture结合Semaphore限制并发抓取的数量,既保证了抓取效率,又防止了资源耗尽。

使用示例:

通过一个具体的场景来展示其核心特性的应用:模拟在线购物平台中,用户下单后,系统需要异步执行三个任务——检查库存、验证用户支付能力和发送订单确认邮件,并在所有任务完成后,更新订单状态。

示例场景
  1. 检查库存:确认所购商品是否有足够的库存。
  2. 验证用户支付能力:检查用户的账户余额是否足够支付订单金额。
  3. 发送订单确认邮件:向用户邮箱发送订单确认信息。
代码示例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class CompletableFutureDemo {public static void main(String[] args) throws Exception {// 创建一个线程池,用于执行异步任务ExecutorService executor = Executors.newFixedThreadPool(3);// 模拟用户下单Order order = new Order(1, "Product A", 100);// 异步检查库存CompletableFuture<Boolean> stockCheckFuture = CompletableFuture.supplyAsync(() -> checkStock(order), executor);// 异步验证用户支付能力CompletableFuture<Boolean> paymentValidationFuture = CompletableFuture.supplyAsync(() -> validatePayment(order), executor);// 异步发送订单确认邮件CompletableFuture<Void> emailSendingFuture = CompletableFuture.runAsync(() -> sendConfirmationEmail(order), executor);// 当所有任务完成时,更新订单状态CompletableFuture<Void> allTasks = CompletableFuture.allOf(stockCheckFuture, paymentValidationFuture, emailSendingFuture);// 等待所有任务完成allTasks.get(); // 注意:在实际生产中应避免使用get()阻塞主线程,这里仅作示例// 假设所有任务成功完成,更新订单状态updateOrderStatus(order, true);executor.shutdown();}// 检查库存的模拟方法private static boolean checkStock(Order order) {// ... 实际库存检查逻辑System.out.println("Checking stock for order: " + order);return true; // 假设库存充足}// 验证用户支付能力的模拟方法private static boolean validatePayment(Order order) {// ... 实际支付能力验证逻辑System.out.println("Validating payment for order: " + order);return true; // 假设支付能力充足}// 发送订单确认邮件的模拟方法private static void sendConfirmationEmail(Order order) {// ... 实际邮件发送逻辑System.out.println("Sending confirmation email for order: " + order);}// 更新订单状态的模拟方法private static void updateOrderStatus(Order order, boolean isSuccess) {if (isSuccess) {System.out.println("Order " + order + " processed successfully. Status updated.");} else {System.out.println("Order processing failed for " + order);}}// 订单类static class Order {int orderId;String productName;int amount;public Order(int orderId, String productName, int amount) {this.orderId = orderId;this.productName = productName;this.amount = amount;}@Overridepublic String toString() {return "Order{" +"orderId=" + orderId +", productName='" + productName + '\'' +", amount=" + amount +'}';}}
}
解释说明
  • 使用CompletableFuture.supplyAsync来启动异步任务,对于有返回值的任务(如库存检查和支付能力验证)。
  • 使用CompletableFuture.runAsync来启动无返回值的任务(如发送邮件)。
  • 利用CompletableFuture.allOf来组合多个CompletableFuture实例,等待所有任务完成。
  • 通过get()方法阻塞等待所有任务完成(实际应用中,应避免在主线程中阻塞等待,可以考虑使用.thenAccept().thenRun()等方法继续异步处理)。
  • 在所有异步任务完成后,调用updateOrderStatus方法更新订单状态。

注意事项

1. 线程池的选择与管理
  • 选择合适的线程池:默认情况下,如果没有显式提供ExecutorCompletableFuture会使用ForkJoinPool.commonPool(),这适合CPU密集型任务。对于IO密集型任务或大量短期任务,考虑自定义单线程或固定大小的线程池。
  • 避免资源耗尽:合理设置线程池大小,避免因创建过多线程而导致资源耗尽或系统崩溃。
  • 及时关闭线程池:使用完毕后,通过shutdownshutdownNow方法关闭线程池,尤其是对于短生命周期的线程池。
2. 异常处理
  • 全面的异常捕捉:确保每个可能抛出异常的异步操作都有相应的异常处理逻辑,如使用exceptionallyhandle方法。
  • 避免异常丢失:如果不恰当处理,异常可能在异步链中被忽略,导致问题难以追踪。
3. 避免过度链式调用
  • 代码可读性:过度的链式调用会使代码变得难以阅读和维护。适时使用局部变量存储中间结果,或拆分为多个方法。
  • 性能考量:链式调用过长可能导致不必要的复杂性,影响性能,尤其是在异常处理和资源管理方面。
4. 合理使用组合方法
  • 并行与串行:理解.thenCompose.thenCombine.thenApply等方法的区别,合理安排任务的执行顺序和依赖关系。
  • allOf与anyOf:使用.allOf等待所有任务完成,或.anyOf等待任一任务完成,根据需求选择合适的方法。
5. 避免阻塞操作
  • 非阻塞性质:尽量避免在使用CompletableFuture时调用阻塞方法,如.get(),除非确实需要阻塞等待结果。考虑使用.join()或异步处理结果。
  • 超时处理:如果必须使用.get(),考虑设置超时参数,防止无限等待。
6. 资源泄漏预防
  • 外部资源管理:在异步任务中使用外部资源(如数据库连接、文件句柄)时,确保资源被正确关闭,即使任务异常终止也要清理资源。
7. 测试与调试
  • 测试难度:异步代码的测试通常比同步代码更复杂。确保为异步流程编写充分的单元测试和集成测试。
  • 日志记录:适当添加日志记录,特别是关键路径和异常处理部分,有助于调试和问题定位。
8. 并发控制与限流
  • 并发控制:对于需要限制并发度的操作,如数据库写入,可以使用Semaphore等工具控制并发访问。
  • 限流保护:对于高负载场景,考虑在入口处实施限流策略,避免因并发度过高导致的服务不稳定。

优缺点

优点
  1. 提高并发性与响应性:通过非阻塞的异步执行,CompletableFuture允许程序在等待某个操作完成的同时执行其他任务,显著提高了系统的并发处理能力和响应速度。

  2. 链式编程与易于组合:丰富的API支持链式调用,使得异步任务的组织和逻辑流转清晰明了,易于构建复杂的异步流程。同时,提供了多种组合操作,如.thenCompose().thenCombine(),使得任务间的协作和依赖管理变得简单。

  3. 强大的异常处理机制:通过.exceptionally().handle()等方法,CompletableFuture允许开发者优雅地处理异步执行过程中的异常,确保程序的健壮性。

  4. 灵活性与可定制性:开发者可以根据需要选择不同的线程池执行异步任务,或者利用.supplyAsync().runAsync()的重载方法指定特定的Executor,提供了高度的灵活性和对资源的控制能力。

  5. 易于测试与调试:虽然异步编程通常比同步编程更难测试,但通过明确的链式调用和异常处理逻辑,CompletableFuture的代码结构相对清晰,有助于单元测试和调试。

缺点
  1. 代码可读性与维护性:虽然链式调用提高了编码效率,但在处理复杂逻辑时,过度的链式可能会导致代码变得冗长且难以阅读,特别是当涉及到多个条件分支和异常处理时。

  2. 潜在的资源管理问题:如果不小心管理,尤其是在大量使用CompletableFuture时,可能会导致线程池资源耗尽,或因忘记关闭资源(如数据库连接)而引发资源泄漏。

  3. 调试难度:异步编程的调试相较于同步编程更为复杂。异常的传播和处理路径可能跨越多个异步阶段,使得定位问题变得更加困难。

  4. 潜在的性能开销:虽然异步执行可以提升整体性能,但如果任务本身非常轻量级,创建和管理CompletableFuture实例以及线程上下文切换的开销可能会抵消异步带来的好处。

  5. 阻塞风险:尽管鼓励非阻塞使用,但在某些场景下,如直接使用.get()方法等待结果,可能会导致线程阻塞,影响程序的响应性。

可能遇到的问题及解决方案

1. 死锁问题

问题描述:在使用CompletableFuture时,如果存在相互等待的情况,可能导致死锁。例如,一个任务的完成依赖于另一个任务的结果,而后者又反过来等待前者的完成。

解决方案:仔细设计任务之间的依赖关系,避免循环等待。使用正确的组合方法,如.thenCompose()而不是.thenApply()来避免不必要的阻塞等待。

2. 资源泄漏

问题描述:未正确管理外部资源(如数据库连接、文件句柄)可能导致资源泄漏,尤其是在异步任务异常终止时。

解决方案:确保使用try-with-resources语句或finally块来确保资源被正确关闭。对于异步操作中的资源,考虑使用自定义的CompletableFuture,以便在任务完成或异常时清理资源。

3. 异常处理不当

问题描述:异常信息可能在异步链中丢失,尤其是当使用.thenApply().thenCompose()等方法时,异常不会自动传递到链的下一个阶段。

解决方案:积极使用.exceptionally().handle()方法来捕获和处理异常,确保异常信息被妥善处理并记录。

4. 性能瓶颈

问题描述:不合理的线程池配置或过度使用CompletableFuture可能导致线程创建过多,消耗过多系统资源,甚至引起OutOfMemoryError。

解决方案:根据任务特性合理配置线程池,例如,对于IO密集型任务,使用较大的线程池;对于CPU密集型任务,线程池大小应接近可用处理器的数量。避免过度创建CompletableFuture实例,必要时复用已完成的实例。

5. 代码可读性差

问题描述:过度的链式调用和复杂的异步逻辑可能会降低代码的可读性和可维护性。

解决方案:适度分解复杂逻辑,将长链式调用分解为多个小方法或使用中间变量存储结果。对于复杂的流程控制,考虑使用设计模式(如状态模式、责任链模式)来简化逻辑。

6. 调试困难

问题描述:异步执行的非线性特性使得通过日志或调试器跟踪程序流程变得困难。

解决方案:利用日志记录关键步骤和异常信息,使用条件断点和线程Dump分析工具来辅助调试。在设计阶段,尽量使异步逻辑清晰有序,便于追踪问题。

7. 过度阻塞

问题描述:虽然CompletableFuture鼓励非阻塞使用,但不当使用.get().join()方法可能导致主线程或工作线程阻塞。

解决方案:尽量使用.thenApply().thenAccept()等非阻塞方法处理结果。若需等待多个任务完成,优先使用.allOf().anyOf()结合.thenRun(),而非直接阻塞等待。

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

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

相关文章

TikTok shop多账户需要防关联吗?

TikTok是一个非常垂直的平台&#xff0c;每个账号的内容都应该尽可能的垂直&#xff0c;这样平台才能引流更多的流量。但是&#xff0c;TikTokShop只有一两个账号&#xff0c;流量往往难以保证&#xff0c;所以很多商家选择了TikTok的多账号运营模式。 众所周知&#xff0c;多店…

什么是SOL链跟单机器人与阻击机器人?

SOL链作为一个快速增长的区块链生态系统&#xff0c;为各种应用程序提供了丰富的发展机会。在SOL链上&#xff0c;智能合约的应用已经开始蓬勃发展&#xff0c;其中包括了许多与加密货币交易相关的应用。在本文中&#xff0c;我们将介绍在SOL链上开发的阻击机器人&#xff08;S…

【静态分析】软件分析课程实验A3-死代码检测

官网&#xff1a; 作业 3&#xff1a;死代码检测 | Tai-e 参考&#xff1a; https://www.cnblogs.com/gonghr/p/17981720 --------------------------------------------------------------------- 1 作业导览 为 Java 实现一个死代码&#xff08;dead code&#xff09;检…

【计算机毕业设计】springboot果蔬种植销售一体化服务平台

伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对果蔬种植销售一体化服务管理进行规范而严格是十分有必要的&#xff0c;所以许许多多的 信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套果蔬种植销售一体化服务平台&…

RS2255XN功能和参数介绍及PDF资料

RS2255XN是一款由Runic&#xff08;润石&#xff09;公司生产的模拟开关。以下是关于RS2255XN的一些技术参数和特点&#xff1a; 封装&#xff1a;MSOP-10 电源电压范围&#xff1a;2.5V至5.5V 工作温度范围&#xff1a;-40C至125C 类型&#xff1a;模拟开关 品牌&#xff1a;R…

如何使用Whisper音频合成模型

Whisper 是一个通用语音识别模型&#xff0c;由 OpenAI 开发。它可以识别多种语言的语音&#xff0c;并将其转换为文本。Whisper 模型采用了深度学习技术&#xff0c;具有高准确性和鲁棒性。 1、技术原理及架构 Whisper 的工作原理&#xff1a;音频被分割成 30 秒的片段&#…

云计算导论(2)---云计算基础

文章目录 1. 分布式计算2. 分布式计算系统架构3. 分布式计算关键技术4. 分布式计算性能优化方法5. 云计算的基本概念6. 云计算的关键技术 1. 分布式计算 1. 定义&#xff1a;分布式计算是一种计算方法&#xff0c;将一个大型任务拆分成多个小任务&#xff0c;并分配给多台计算机…

c#绘制渐变色的Led

项目场景&#xff1a; c#绘制渐变色的button using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using static System.Windows.Forms.AxHost;namespace WindowsFormsApp2 {public class Gradie…

【C++】-类模板-002

1创建类模板 &#xff08;1&#xff09;新建工程 &#xff08;2&#xff09; &#xff08;3&#xff09; &#xff08;4&#xff09; &#xff08;5&#xff09;模板运行结果 2【UI】设计器 &#xff08;1&#xff09;跳转到【UI】设计器 &#xff08;2&#xff09;添加…

纯血鸿蒙APP实战开发——一镜到底“页面转场”动画

介绍 本方案做的是页面点击卡片跳转到详情预览的转场动画效果 效果图预览 使用说明 点击首页卡片跳转到详情页&#xff0c;再点击进入路由页面按钮&#xff0c;进入新的路由页面 实现思路 首页使用了一种视觉上看起来像是组件的转场动画&#xff0c;这种转场动画通常是通过…

教你解决PUBG绝地求生打完一把游戏无法返回大厅的问题

《绝地求生》&#xff08;PUBG&#xff09;作为风靡全球的战术竞技大作&#xff0c;凭借其高度还原的战场氛围和扣人心弦的生存挑战吸引了大量游戏玩家。不过&#xff0c;部分玩家在经历了一场紧张激烈的比赛后&#xff0c;遭遇了一个小困扰&#xff1a;游戏未能顺畅过渡到结算…

C++基础中的存储类别

存储的类别是变量的属性之一&#xff0c;C语言定义了4种变量的存储类别&#xff0c;分别是auto变量、static变量、register变量和extern变量。以下重点介绍这几种类型。 一、auto变量 auto变量是C默认的存储类型。函数内未加存储类型说明的变量均被称为自动变量&#xff0c;即…

docker-compose完成mysql8.0+环境搭建

1、准备my.cnf文件到指定目录&#xff08;和基础的增加了一个default_authentication_pluginmysql_native_password 的身份验证插件配置信息&#xff09; 原因&#xff1a;官方提到&#xff1a; 该方式可以解决&#xff1a;Authentication plugin ‘caching_ sha2_password‘ c…

FebHost:什么是乌兹别克斯坦.UZ域名?

.uz域名是专门分配给乌兹别克斯坦的国家代码顶级域&#xff08;ccTLD&#xff09;。与代表英国的 “.uk” 或代表法国的 “.fr” 等其他国家代码顶级域类似&#xff0c;”.uz” 是一个代表特定国家的双字母代码。在这种情况下&#xff0c;它代表乌兹别克斯坦。 .uz 域名在建立…

可微分矢量图形光栅化用于编辑和学习

图1. 我们引入了一种通过反向传播将光栅和矢量域联系起来的矢量图形可微分光栅化器。可微分光栅化实现了许多新颖的矢量图形应用。&#xff08;a&#xff09;在几何约束下&#xff0c;通过局部优化图像空间度量&#xff08;如不透明度&#xff09;来实现交互式编辑。&#xff0…

《第一行代码》第二版学习笔记(10)——基于位置的服务

文章目录 一、使用百度定位二、获取经纬度使用百度地图移动到我的位置并让“我”显示在地图上 Android Studio中没有signingReport文件&#xff0c;解决参考文档 一、使用百度定位 下载百度LBS开放平台的SDK 在项目的app.gradle文件下添加依赖&#xff1a;implementation fil…

fb设备驱动框架分析

一、字符设备注册过程&#xff1a; 归根到底&#xff0c;fb设备也是一个字符设备&#xff0c;所以逃不开常规的字符设备驱动框架&#xff1a; Linux内核中编写字符设备驱动通常遵循以下步骤&#xff1a; ①、定义主设备号&#xff1a; 在Linux中&#xff0c;每个字符设备都…

2024洗地机选购指南 | 怎么选洗地机不会被坑?

家里的地板总是需要打扫&#xff0c;但工作忙碌的我们往往没有足够的时间来打理。洗地机不仅能够帮助我们节省宝贵的时间&#xff0c;还能让我们的家变得一尘不染。今天&#xff0c;笔者将为大家讲讲挑选洗地机的技巧&#xff0c;告诉大家怎么挑选洗地机不会被坑&#xff0c;顺…

ECO 视频分类模型

ECO分类模型 ECO 分类模型&#xff0c;可以对视频进行分类&#xff0c;视频是静止画面的集合&#xff0c;并短时间内进行播放&#xff0c;在人眼中形成了视频&#xff0c;通过 FPS 单位进行计算&#xff0c;指的是每秒显示多少张图片。如果直接把图片组合一张大图&#xff0c;…

开源直播电商系统(仿抖音电商模式)

当下&#xff0c;传统的图文电商模式正在走向没落&#xff0c;以“抖音”为首的直播电商模式备受用户追捧&#xff0c;它具有直观与互动的特点&#xff0c;拥有传统电商所不具备的优势。而且&#xff0c;当前正是直播电商的红利期&#xff0c;很多主播和品牌商都通过直播电商业…