当前位置: 首页 > news >正文

谈谈我对Retrofit源码的理解

文章目录

  • 一、Retrofit简介
  • 二、使用介绍
    • 2.1 app / build.gradle添加依赖
    • 2.2 创建 Retrofit 实例
    • 2.3 创建 API 接口定义文件
    • 2.4 使用 Retrofit 进行网络请求
  • 三、源码分析
    • 3.1 创建 Retrofit 实例: 建造者模式创建Retrofit
    • 3.2 实例化API接口: 动态代理模式
    • 3.3 获取Observable返回值
      • 3.3.1 ServiceMethod.java
      • 3.3.2 HttpServiceMethod.java
      • 3.3.3 HttpServiceMethod.java
      • 3.3.4 CallAdapted.java
      • 3.3.5 RxJava3CallAdapter.java
    • 3.4 发起网络请求
      • 3.4.1 CallEnqueueObservable.java
      • 3.4.2 OkHttpCall.java
      • 3.4.3 GsonResponseBodyConverter.java
  • 四、参考文档

一、Retrofit简介

square/retrofit Github地址 最新版本从这里获取!

Retrofit 是一款由 Square 公司开发的用于 Android 和 Java 应用程序的网络请求库,旨在简化 HTTP 网络请求的过程,Retrofit 最初是在 2013 年开源发布的,已经风靡了10年。其优点如下

  • 简化网络请求的过程:Retrofit 可以根据 API 接口定义文件自动生成网络请求代码,从而避免了手动创建网络请求代码的繁琐工作。
  • 方便处理网络请求:Retrofit 支持使用注解(如 @GET、@POST 等)来指定网络请求的方法和参数,同时支持异步网络请求和回调处理,方便处理网络请求和响应。
  • 易于定制和扩展:Retrofit 提供了一些定制和扩展机制,如自定义网络请求和响应处理器、支持多种转换器(如 JSON 转换器、XML 转换器等)等,可以满足不同的网络请求需求
  • 支持缓存:Retrofit 支持网络请求结果的缓存,可以在没有网络连接时提供离线数据访问的功能
  • 基于 OkHttp:Retrofit 是基于 OkHttp 实现的,OkHttp 是一个高效、灵活和易于使用的网络请求库,因此 Retrofit 也具有 OkHttp 的优点,如连接池、请求队列等

二、使用介绍

2.1 app / build.gradle添加依赖

dependencies {implementation 'com.squareup.retrofit2:retrofit:2.9.0'implementation 'com.squareup.retrofit2:converter-gson:2.9.0'// 下面三个是rxjava相关的依赖,如果你的项目不使用rxjava可以不用依赖//  注意adapter-rxjava3,如果你的rxjava版本是2,那这里是adapter-rxjava2,这个自己网上找吧implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
}

2.2 创建 Retrofit 实例

Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.example.com/").addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // rxjava call.addConverterFactory(GsonConverterFactory.create()) // json解析.build()             

2.3 创建 API 接口定义文件

具体如何定义可以参考Retrofit 官方文档介绍学习相关注解的使用方法

public interface IService {@GET("app/update")Observable<Integer> getUpdateInfo();@GET("app/test")Call<Integer> getTestInfo();
}

2.4 使用 Retrofit 进行网络请求

IService apiService = retrofit.create(IService.class);apiService.getUpdateInfo().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Integer>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {}@Overridepublic void onNext(@NonNull Integer integer) {}@Overridepublic void onError(@NonNull Throwable e) {}@Overridepublic void onComplete() {}});}

三、源码分析

通过上面四步我们可以使用Retrofit发起一个完整的网络请求, 接下来让我们学习一下源码

3.1 创建 Retrofit 实例: 建造者模式创建Retrofit

Retrofit.java
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addCallAdapterFactory(RxJava3CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build();
Retrofit.java 的 Retrofit.Builderpublic Retrofit build() {if (baseUrl == null) {throw new IllegalStateException("Base URL required.");}// 对应 Builder.client() 如果你赋值的话,这里就不会为nullokhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {// 没有定义的话默认使用OkHttpClientcallFactory = new OkHttpClient();}// 对应 Builder.callbackExecutor(), 如果你赋值的话,这里就不会为nullExecutor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {// platform 是Android, defaultCallbackExecutor()  --> new MainThreadExecutor()// MainThreadExecutor 创建一个主线程的Looper Handler,当异步任务处理完毕会把结果通过post的方式发到主线程callbackExecutor = platform.defaultCallbackExecutor();}// 对应 Builder.addCallAdapterFactory(), 我们使用了RxJava3CallAdapterFactory.create() -->//  RxJava3CallAdapterFactoryList<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);// java8 开始新增 CompletableFutureCallAdapterFactory + DefaultCallAdapterFactory// java8 以下只有 DefaultCallAdapterFactorycallAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));List<Converter.Factory> converterFactories =new ArrayList<>(1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());converterFactories.add(new BuiltInConverters());// 对应 Builder.addConverterFactory(), 我们使用了GsonConverterFactory.create() --> GsonConverterFactoryconverterFactories.addAll(this.converterFactories);converterFactories.addAll(platform.defaultConverterFactories());return new Retrofit(callFactory,baseUrl,unmodifiableList(converterFactories),unmodifiableList(callAdapterFactories),callbackExecutor,validateEagerly);}}

目前Retrofit的相关信息已配置完毕

Retrofit(okhttp3.Call.Factory callFactory,HttpUrl baseUrl,List<Converter.Factory> converterFactories,List<CallAdapter.Factory> callAdapterFactories,@Nullable Executor callbackExecutor,boolean validateEagerly) {this.callFactory = callFactory;this.baseUrl = baseUrl;this.converterFactories = converterFactories; // Copy+unmodifiable at call site.this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.this.callbackExecutor = callbackExecutor;this.validateEagerly = validateEagerly;}

3.2 实例化API接口: 动态代理模式

IService apiService = retrofit.create(IService.class);

多年前分析Retrofit源码的时候我也很懵逼, 啥是动态代理? 为啥要用动态代理? 从官方的介绍或者其他博主人云亦云的观点把我搞得一头雾水,其实这个问题很简单!不是因为动态代理才有需求,而是因为需求才有动态代理

假设你是Retrofit的开发人员, 现在使用Retrofit的人都会给你传参数(url,requestMethod,header,params), 你该怎么设计框架才可以拿到用户的参数?

大部分人的第一反应就是: 暴露个方法呗! 那要是参数可变呢?重载! 那url散落代码各处梳理不便呢?em…

聪明的你开始想到了注解!创建一个网络请求接口A,里面每个方法对应每个网络请求,方法的注解对应着需要的参数,再创建一个实现类Aimpl,在Aimpl的每个方法中去反射获取A对应方法的注解然后去发起请求。

我们继续思考一下,Aimpl除了解析不同的方法外,基本上内容完全相同?如果存在接口ABCD,是不是还得对应生成Aimpl Bimpl Cimpl Dimpl这样的实现类?有没有什么办法可以再优化一下? 有! 这就是动态代理

篇幅有限这里就不展开动态代理的官方介绍了,有兴趣的同学可以自行查询下。

 public <T> T create(final Class<T> service) {// 这里会校验这个class是不是接口,不是接口就会直接抛异常validateServiceInterface(service);// Proxy.newProxyInstance 动态代理典型用法, 内部通过newInstance反射创建一个实例return (T)Proxy.newProxyInstance(service.getClassLoader(),new Class<?>[] {service},// 这个InvocationHandler相当于一个Hook,只要你调用了这个实例的方法都会回调invokenew InvocationHandler() {private final Platform platform = Platform.get();private final Object[] emptyArgs = new Object[0];@Overridepublic @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {// 如果是Object类的话就不hook了,执行Object的方法吧if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}args = args != null ? args : emptyArgs;// platform 是Android, isDefaultMethod 判断这个接口的方法是不是default方法, Java8开始支持接口内增加有实现体的 default方法,这里的意思是: 我只拦截没有实现的方法,有实现体的方法不会拦截return platform.isDefaultMethod(method)? platform.invokeDefaultMethod(method, service, proxy, args)// loadServiceMethod 是重头戏的开始,等会继续分析: loadServiceMethod(method).invoke(args);}});}

3.3 获取Observable返回值

apiService.getUpdateInfo()  --> 触发上文提到的 loadServiceMethod
ServiceMethod<?> loadServiceMethod(Method method) {// retrofit不会无脑create一次,反射一次,也会有缓存机制的ServiceMethod<?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = ServiceMethod.parseAnnotations(this, method);serviceMethodCache.put(method, result);}}return result;}

3.3.1 ServiceMethod.java

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {// 对注解的校验&&解析headers,method,baseUrl,contentType 等信息RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);// ....return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);}

3.3.2 HttpServiceMethod.java

这里有对Kotlin suspend函数的处理,以后再讲,我们还是分析Java的流程

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {// ....// 结合3.1 创建Retrofit实例的代码  // Retrofit.callAdapter -->  Retrofit.nextCallAdapter --> callAdapterFactories.get().get()// --> RxJava3CallAdapterCallAdapter<ResponseT, ReturnT> callAdapter =createCallAdapter(retrofit, method, adapterType, annotations);// Retrofit.responseBodyConverter --> Retrofit.nextResponseBodyConverter --> converterFactories.responseBodyConverter --> GsonResponseBodyConverterConverter<ResponseBody, ResponseT> responseConverter =createResponseConverter(retrofit, method, responseType);// callFactory 默认OkHttpClientokhttp3.Call.Factory callFactory = retrofit.callFactory;if (!isKotlinSuspendFunction) {// 回到3.2 loadServiceMethod(method)最终返回的是 CallAdapted, 而CallAdapted没有实现invoke方法而是其父类HttpServiceMethod实现return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);} else{//.....}}

3.3.3 HttpServiceMethod.java

@Overridefinal @Nullable ReturnT invoke(Object[] args) {// OkHttpCall是Retrofit对OkHttp的包装Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);return adapt(call, args);}// adapt是抽象方法,具体实现还是交给了CallAdapted
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

3.3.4 CallAdapted.java

@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {// 3.3.2 提到 callAdapter 就是 RxJava3CallAdapterreturn callAdapter.adapt(call);}

3.3.5 RxJava3CallAdapter.java

 @Overridepublic Object adapt(Call<R> call) {// 由于我们使用的 RxJava3CallAdapterFactory.create() 默认 isAsync 传了true,所以这里最终返回的就是 CallEnqueueObservableObservable<Response<R>> responseObservable =isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);// ... } else {observable = responseObservable;}// ...return RxJavaPlugins.onAssembly(observable);}
}

所以 apiService.getUpdateInfo() 最终返回的是 CallEnqueueObservable

3.4 发起网络请求

我在Rxjava线程切换原理终于在2023年有了答案这篇博客中提到:Rxjava最重要的一个环节就是subscribe, 正是有了这个环节才会产生 订阅流和回调流

// 直接去看 CallEnqueueObservable 的 subscribeActual , 这里如果跟不上的话可以去看一下我的Rxjava的博客
apiService.getUpdateInfo().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Integer>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {}@Overridepublic void onNext(@NonNull Integer integer) {}@Overridepublic void onError(@NonNull Throwable e) {}@Overridepublic void onComplete() {}});

3.4.1 CallEnqueueObservable.java

@Overrideprotected void subscribeActual(Observer<? super Response<T>> observer) {// Since Call is a one-shot type, clone it for each new observer.Call<T> call = originalCall.clone();// 根据 3.3.3 call就是OkHttpCallCallCallback<T> callback = new CallCallback<>(call, observer);observer.onSubscribe(callback);if (!callback.isDisposed()) {// 真正发起网络请求的地方开始了call.enqueue(callback);}}

3.4.2 OkHttpCall.java

 @Overridepublic void enqueue(final Callback<T> callback) {// ...call = rawCall = createRawCall();// ....call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {//...response = parseResponse(rawResponse);callback.onResponse(OkHttpCall.this, response);//...}@Overridepublic void onFailure(okhttp3.Call call, IOException e) {callFailure(e);}});}
private okhttp3.Call createRawCall() throws IOException {// 结合代码 3.3.2 callFactory 其实就是 OkHttpClient --> RealCall.newRealCall 这里就进入了OkHttp的代码,以后还会分析OKHttp,这里就到此为止okhttp3.Call call = callFactory.newCall(requestFactory.create(args));if (call == null) {throw new NullPointerException("Call.Factory returned null.");}return call;}
 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {// ... 这会处理code码,比如 code < 200 || code >= 300 code == 204 || code == 205等// 根据3.3.2 responseBodyConverter --> GsonResponseBodyConverter 就是这里的 responseConverterT body = responseConverter.convert(catchingBody);// ...}

3.4.3 GsonResponseBodyConverter.java

@Overridepublic T convert(ResponseBody value) throws IOException {// Json解析JsonReader jsonReader = gson.newJsonReader(value.charStream());try {T result = adapter.read(jsonReader);if (jsonReader.peek() != JsonToken.END_DOCUMENT) {throw new JsonIOException("JSON document was not fully consumed.");}return result;} finally {value.close();}}

拿到请求结果后再根据RxJava的回调流回调给订阅者完成整个请求的全部流程

四、参考文档

Retrofit 源码分析
一定能看懂的 Retrofit 最详细的源码解析!
Android 网络框架之Retrofit源码解析
【面试 反思】Retrofit源码与设计 7 连问

相关文章:

谈谈我对Retrofit源码的理解

文章目录一、Retrofit简介二、使用介绍2.1 app / build.gradle添加依赖2.2 创建 Retrofit 实例2.3 创建 API 接口定义文件2.4 使用 Retrofit 进行网络请求三、源码分析3.1 创建 Retrofit 实例: 建造者模式创建Retrofit3.2 实例化API接口: 动态代理模式3.3 获取Observable返回值…...

八股文(三)

目录 一、 如何理解原型与原型链 二、 js继承 三、 vuex的使用 1.mutation和action的区别 mutation action 2.Vuex都有哪些API 四、 前端性能优化方法 五、 类型判断 题目 &#xff08;1&#xff09;typeof判断哪个类型会出错&#xff08;即结果不准确&#xff09;&…...

2023最新实施工程师面试题

1、两电脑都在同一个网络环境中,A 电脑访问不到 B 电脑的共享文件。此现象可能是哪些 方面所导致?怎样处理? 答:首先你要确定是不是在一个工作组内,只有在一个工作组内才可以共享文件,然后看一个看一看有没有防火墙之类的,然后确定文件是不是已经共享 2、 电脑开机时风扇…...

安卓逆向_6 --- JNI 和 NDK

Java 本机接口规范内容&#xff1a;https://docs.oracle.com/en/java/javase/19/docs/specs/jni/index.html JNI官方中文资料&#xff1a;https://blog.csdn.net/yishifu/article/details/52180448 NDK 官方文档&#xff1a;https://developer.android.google.cn/training/ar…...

Pod控制器

K8S之控制器详解#简介#在kubernetes中&#xff0c;按照Pod的创建方式可以将其分为两类&#xff1a;自主式:kubernetes直接创建出来的Pod&#xff0c;这种Pod删除后就没有了&#xff0c;也不会重建。控制器创建pod:通过Pod控制器创建的Pod&#xff0c;这种Pod删除之后还会自动重…...

微服务到云原生

微服务到云原生 微服务 微服务架构&#xff08;Microservice Architecture&#xff09;是一种架构概念&#xff0c;旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦。 微服务是一种架构风格&#xff0c;一个大型复杂软件应用由一个或多个微服务组成。系统中的各…...

Spring Security 实现自定义登录和认证(1):使用自定义的用户进行认证

1 SpringSecurity 1.1 导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>1.2 编写配置类 在spring最新版中禁用了WebSecurityConfigurerAdapter…...

Spring Cloud(微服务)学习篇(七)

Spring Cloud(微服务)学习篇(七) 1.使用代码的方式实现流量限制规则 1.1 变更SentinelController类 1.1.1 加入的代码 //流控限制 (一个或多个资源限流), postConstruct注解的作用是保证项目一启动就会加载,// 一个rule就是一个规则PostConstructpublic void FlowRule(){Li…...

嵌入式安防监控项目——前期知识复习

目录 一、概述 二、C语言 三、数据结构 四、IO进程 五、网络 六、ARM体系结构和接口技术 七、系统移植 八、内核驱动 一、概述 我再报班之前学过51和32&#xff0c;不过都是自学的。报班开始先从应用层入手的&#xff0c;C语言和数据结构。只要是个IT专业的大学这都是必…...

SpringAOP——基础知识

AOP AOP全称是Aspect Oriented Programming 即面向切面编程&#xff0c;是对一类统一事务的集中处理 例如&#xff0c;我们的网页许多地方都需要进行登陆验证&#xff0c;这时就需要在很多地方添加重复的验证代码&#xff0c;而AOP可以集中配置需要登陆验证的地方&#xff0c;…...

kafka3.0安装使用

一&#xff1a;定义 Kafka传 统定义&#xff1a;Kafka是一个分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域。 Kafka最 新定义 &#xff1a; Kafka是 一个开源的 分 布式事件流平台 &#xff08;Event St…...

Centos7(阿里云)_安装Mysql8.0

1.安装MySQL 新人可以试用一个月的阿里云&#xff0c;centos7的 一开始可能确实会自带mariadb&#xff0c;所以可以在网上随便找个教程开始尝试安装MySQL&#xff0c;当然大概率出错&#xff0c;然后此时你的rpm下面已经有了一个版本的mysql安装包。 以我为例&#xff0c;随便…...

【Java】JVM

一、介绍 1.什么是JVM? JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的机器&#xff0c;是通过在实际的计算机上仿真模拟各种功能实现的。JVM包含一套字节码指令集&#xff0c;一组寄存器&#xff0c;一个栈&#xff0c;一个垃圾回收堆和一个存储方法域。JVM屏…...

Linux 和数据库笔记-06

今日内容介绍全天内容无需立马掌握MySQL 的高级功能应用数据库设计ER模型定义: E 代表实体(数据表), R 代表联系(数据表之间对应的字段)![image-20200530092701017](Linux 和数据库 day06 随堂笔记.assets/image-20200530092701017.png)关系常见分类一对一一对多多对多外键如果…...

MySQL面试题-事务篇

1.事务的特性&#xff08;ACID&#xff09; 事务&#xff08;Transaction&#xff09;是指一组操作被看作是一个不可分割的工作单元&#xff0c;这组操作要么全部执行成功&#xff0c;要么全部执行失败。事务的特性通常用 ACID 四个单词来描述&#xff0c;它们分别代表原子性&…...

Linux嵌入式开发 | 汇编驱动LED(1)

文章目录&#x1f697; &#x1f697;Linux嵌入式开发 | 汇编驱动LED&#xff08;1&#xff09;&#x1f697; &#x1f697;初始化IO&#x1f697; &#x1f697;STM32&#x1f697; &#x1f697;使能GPIO时钟&#x1f697; &#x1f697;设置IO复用&#x1f697; &#x1f6…...

什么是EventLoop?怎么测试Node或页面的性能

Event Loop 机制大家应该都有了解。本文利用 EventLoop 去做一个有趣的检测node或页面性能的代码&#xff0c;顺便介绍了一下EventLoop&#xff0c;希望对大家有所帮助&#xff01; Event Loop Event Loop 机制大家应该都有了解。我先重复总结一下。 Node.js 和 Javascript 的…...

1018 锤子剪刀布 1025 反转链表

现给出两人的交锋记录&#xff0c;请统计双方的胜、平、负次数&#xff0c;并且给出双方分别出什么手势的胜算最大。 输入格式&#xff1a; 输入第 1 行给出正整数 N&#xff08;≤10 5 &#xff09;&#xff0c;即双方交锋的次数。随后 N 行&#xff0c;每行给出一次交锋的信…...

卷积神经网络的原理及实现

专栏&#xff1a;神经网络复现目录 卷积神经网络 本章介绍的卷积神经网络&#xff08;convolutional neural network&#xff0c;CNN&#xff09;是一类强大的、为处理图像数据而设计的神经网络。 基于卷积神经网络架构的模型在计算机视觉领域中已经占主导地位&#xff0c;当今…...

【C++知识点】重载

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;C/C知识点 &#x1f4e3;专栏定位&#xff1a;整理一下 C 相关的知识点&#xff0c;供大家学习参考~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...