框架解读 | Retrofit设计剖析

这篇具有很好参考价值的文章主要介绍了框架解读 | Retrofit设计剖析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作者:Calculus_小王

Retrofit是一个类型安全的HTTP客户端,可以通过注解将HTTP API转换为Java接口,并使用动态代理,CallAdapter和Converter来发起请求和解析响应。

本文 着重于 Retrofit的架构设计,对于其注解解析能力不作详细阐述

本文基于retrofit:2.6.2

示例

本示例仅以最基础的retrofit发起请求为例,关于kotlin suspend或rxjava等封装,会在源码阶段进行一同剖析

// 定义一个接口,用注解描述HTTP请求
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}

// 创建一个Retrofit实例,并指定基础URL和转换器
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

// 同步或异步执行Call对象,获取响应数据
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
    // 处理响应数据
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
    // 处理失败情况
    }
});

源码剖析

通常在retrofit的使用中,会结合rxjava或者kotlin suspend关键字一同使用,但与示例代码无异的是这两步:

  1. 得到Service对象
  2. 调用Service方法
// 通过Retrofit创建接口的实现类
GitHubService service = retrofit.create(GitHubService.class);

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

而对于retrofit,我们最好奇的无非是其如何联动的OkHttp,借助其发出请求。OkHttp中发起请求是通过Call.enqueue(),那本文的目标方向就是这个方法的调用

得到Service对象

retrofit.create得到的是由动态代理newProxyInstance生成的Service对象,重点在于其中的invoke方法,对于通常情况,最终调用的都是loadServiceMethod(method)执行的 invoke(),那接着看看它返回了什么对象,并且其 invoke() 究竟做了些什么

// Retrofit.java
public <T> T create(final Class<T> service) {
  Utils.validateServiceInterface(service);
  if (validateEagerly) {
    eagerlyValidateMethods(service);
  }
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
        private final Platform platform = Platform.get();
        private final Object[] emptyArgs = new Object[0];

        @Override public @Nullable Object invoke(Object proxy, Method method,
            @Nullable Object[] args) throws Throwable {
          // If the method is a method from Object then defer to normal invocation.
          if (method.getDeclaringClass() == Object.class) {
            return method.invoke(this, args);
          }
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
        }
      });
}

Service的每个方法最终都对应着一个HttpServiceMethod,而其也有几个实现类CallAdapted(通常情况)、SuspendForResponse(suspend使用)、SuspendForBody

// Retrofit.java
ServiceMethod<?> loadServiceMethod(Method method) {
  // 这里还做了缓存,这里注意喔,缓存的是method,而不是service对应的class
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      // 最终return的是这个result,跟进去看看
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

// ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 这里进行了Request注解方面的解析,感兴趣的可以跟进去看,重点关注其中的`parseMethodAnnotation()`和`parseParameter()`
    // 内部区分了`methodAnnotations`和`parameterAnnotations`两种类型注解
    // 最终封装得到RequestFactory,也就转化为了okhttp请求时需要的一些内容
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    // ……
    // 继续往下看,其实返的就是HttpServiceMethod对象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

// HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    // ……
    // 虽然suspend会有些区别,但你完全可以理解为 方法 return的对象类型
    // 比如联动rxjava,就会返回一个Observable<T>
    adapterType = method.getGenericReturnType();
    
    // ……
    // Call适配器,Call其实就是OkHttp的Call,适配器的意义就是将其转化为对应的类型,比如rxjava的Observable
    // 但其内部仍会调用Call的方法
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    
    // 这个是结果解析器,gson就是添加在这里
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>(requestFactory,
          callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
          continuationBodyNullable);
    }
}

前面讲到了invoke(),我们继续。对于HttpServiceMethod的几个子类,均没有实现该方法,发现在其父类中,转而调用了adapt,留给其子类去实现

@Override final @Nullable ReturnT invoke(Object[] args) {
    // 这个Call不是OkHttp的Call
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
}

protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

// 其实在call的设计中,就完全能看到okhttp的影子了
public interface Call<T> extends Cloneable {
  /**
   * Synchronously send the request and return its response.
   *
   * @throws IOException if a problem occurred talking to the server.
   * @throws RuntimeException (and subclasses) if an unexpected error occurs creating the request
   * or decoding the response.
   */
  Response<T> execute() throws IOException;

  /**
   * Asynchronously send the request and notify {@code callback} of its response or if an error
   * occurred talking to the server, creating the request, or processing the response.
   */
  void enqueue(Callback<T> callback);

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute or enqueue a call more than once.
   */
  boolean isExecuted();

  /**
   * Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not
   * yet been executed it never will be.
   */
  void cancel();

  /** True if {@link #cancel()} was called. */
  boolean isCanceled();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call<T> clone();

  /** The original HTTP request. */
  Request request();
}

调用Service方法

create的动态代理细节中我们得知,retrofit service每个方法的调用,都将映射到对应类型的HttpServiceMethod.adapt()

普通调用

// 调用接口方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");

// 同步或异步执行Call对象,获取响应数据
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
    // 处理响应数据
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
    // 处理失败情况
    }
});

最普通的情况,返回值为原始Call,那就对应的是CallAdapted,然而这里令人头疼的是,它又套了一层。这里设计的原因就是为了兼容如Rxjava等场景

// CallAdapted.java
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  return callAdapter.adapt(call);
}

那这些callAdapter来自于哪里呢?回到前面传入的地方

callAdapterFactories()
    >>> retrofit.callAdapter(returnType, annotations)
        >>> nextCallAdapter(null, returnType, annotations)

// nextCallAdapter()
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    // 可以想象的是,callAdapterFactories会根据returnType去匹配给到对象的adpter
    // 至于这个,对比下rxjava联动时需要添加的代码.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    // 也就是说Retrofit在建造过程中,会逐步的传进去
    CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
        return adapter;
    }
}

// Retrofit.Builder.build()
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

那对于最普通的这种调用,那必然就是DefaultCallAdapterFactory了,而得到adapter最终是ExecutorCallbackCall

// DefaultCallAdapterFactory.java
public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  if (getRawType(returnType) != Call.class) {
    return null;
  }
  if (!(returnType instanceof ParameterizedType)) {
    throw new IllegalArgumentException(
        "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
  }
  final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

  final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
      ? null
      : callbackExecutor;

  return new CallAdapter<Object, Call<?>>() {
    @Override public Type responseType() {
      return responseType;
    }

    @Override public Call<Object> adapt(Call<Object> call) {
      return executor == null
          ? call
          : new ExecutorCallbackCall<>(executor, call);
    }
  };
}

回顾下示例代码,调用service method得到Call后,调用其enqueue,那也就是说最终走入了ExecutorCallbackCall.enqueue

// ExecutorCallbackCall.java
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");
  // 这里的delegate,其实就是构造时传入的`OkHttpCall`,当然这还不是实际OkHttp的Call
  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      // 当结果回来时,需要进行线程切换
      // 至于这个callbackExecutor感兴趣的可以自行追踪下,其实就是retrofit在build时生成的一个MainThreadExecutor
      // 由platform.defaultCallbackExecutor()得到,实际内部就是个handler……
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          if (delegate.isCanceled()) {
            // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
          } else {
            callback.onResponse(ExecutorCallbackCall.this, response);
          }
        }
      });
    }

    @Override public void onFailure(Call<T> call, final Throwable t) {
      callbackExecutor.execute(new Runnable() {
        @Override public void run() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}

那请求其实还是由OkHttpCall发起,中间的无论HttpServiceMethod还是CallAdapter,无非就是为了扩展性和兼容性进行的设计罢了

// OkHttpCall.java
@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    call = rawCall;
    failure = creationFailure;
    if (call == null && failure == null) {
      try {
        // 这里由callFactory.newCall生成
        // 而这个callFactory就是retrofit.client(okHttpClient)
        // 到这明白了吧,这个OkHttpCall终究还是OkHttpClient.RealCall套了一层壳
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

  if (failure != null) {
    callback.onFailure(this, failure);
    return;
  }

  if (canceled) {
    call.cancel();
  }
  // 下面的内容,对于熟悉OkHttp的小伙伴来说已经毫无悬念了
  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      Response<T> response;
      try {
        // 这里进行了结果的解析,其中用到了`responseConverter`,也就是addConverterFactory
        // 同样的,也是类似于CallAdapter的设计
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        throwIfFatal(e);
        callFailure(e);
        return;
      }

      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
        throwIfFatal(t);
        t.printStackTrace(); // TODO this is not great
      }
    }

    @Override public void onFailure(okhttp3.Call call, IOException e) {
      callFailure(e);
    }

    private void callFailure(Throwable e) {
      try {
        callback.onFailure(OkHttpCall.this, e);
      } catch (Throwable t) {
        throwIfFatal(t);
        t.printStackTrace(); // TODO this is not great
      }
    }
  });
}

其实到这,Retrofit的大体设计已经结束了。其中HttpServiceMethodCallAdapter是其中的关键

Rxjava调用

书接上文普通调用时,对于Rxjava的联动时,区分点在于CallAdapter

addCallAdapterFactory(RxJava2CallAdapterFactory.create());

同样去看factory.get会得到RxJavaCallAdapter

// RxJavaCallAdapter.java
@Override public Object adapt(Call<R> call) {
  // 这里根据同步\异步 将OkHttpCall传了进去,结合rxjava操作符,发起okhttp请求,并把结果发射出去
  // 可以看对应类中的call(),这里就不展开了蛤,相信你已经可以独当一面了
  // 当然,主要是我对rxjava操作符不太熟……
  OnSubscribe<Response<R>> callFunc = isAsync
      ? new CallEnqueueOnSubscribe<>(call)
      : new CallExecuteOnSubscribe<>(call);

  OnSubscribe<?> func;
  if (isResult) {
    func = new ResultOnSubscribe<>(callFunc);
  } else if (isBody) {
    func = new BodyOnSubscribe<>(callFunc);
  } else {
    func = callFunc;
  }
  // 这里包装之后,最终返回了出去,得到了通常使用的Observable
  Observable<?> observable = Observable.create(func);

  if (scheduler != null) {
    observable = observable.subscribeOn(scheduler);
  }

  if (isSingle) {
    return observable.toSingle();
  }
  if (isCompletable) {
    return observable.toCompletable();
  }
  return observable;
}

Suspend调用

对于协程结合retrofit,通常就是scope.launch{ service.method() }

CoroutineScope(Dispatchers.IO).launch {
    try {
        val repos = service.listRepos("octocat")
        // 处理响应数据,通常会给一个callback,外面再通过livedata去发射处理好的数据
    } catch (e: Exception) {
        // 处理异常情况
    }
}

对于suspend,必然结合协程,也就取代了rxjava的线程切换能力,那HttpServiceMethod对应SuspendForResponse(主体差不多,那就挑这个将)\SuspendForBody,而CallAdapter没啥花里胡哨的,得到的Call就是最普通的ExecutorCallbackCall

// SuspendForResponse.java
@Override protected Object adapt(Call<ResponseT> call, Object[] args) {
  call = callAdapter.adapt(call);

  //noinspection unchecked Checked by reflection inside RequestFactory.
  Continuation<Response<ResponseT>> continuation =
      (Continuation<Response<ResponseT>>) args[args.length - 1];

  // See SuspendForBody for explanation about this try/catch.
  try {
    // 对于suspend的阻塞,await是再熟悉不过了的
    return KotlinExtensions.awaitResponse(call, continuation);
  } catch (Exception e) {
    return KotlinExtensions.yieldAndThrow(e, continuation);
  }
}

寻寻觅觅,蓦然回首那人却在灯火阑珊处,最终ExecutorCallbackCall得到了调用,这里是个suspendCancellableCoroutine的挂起使用,结合resume进行唤起

suspend fun <T : Any> Call<T>.awaitResponse(): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

总结

其实对于Retrofit的设计最耐人寻味的莫非是通过动态代理机制配合HttpServiceMethod,实现了在method的颗粒度上完成了扩展设计,而CallAdapterConverter无异于在整体的兼容性和解耦上更加锦上添花。

这其实也给注解的适用场景提供了非常好的借鉴意义,通常可能会局限于APT去进行插桩或代码生成

• Retrofit使用动态代理机制,通过Proxy.newProxyInstance方法创建接口的代理类
并将所有方法调用委托给内部的InvocationHandler处理
• InvocationHandler负责根据方法的注解创建ServiceMethod对象
ServiceMethod是一个抽象类,它封装了请求的相关信息,如URL,HTTP方法,参数,请求体等
ServiceMethod的具体实现类是HttpServiceMethod
它根据Retrofit配置的CallAdapter和Converter来创建Call对象和解析响应数据
• CallAdapter是一个接口,它定义了如何将Call对象转换为其他类型,如Observable,Deferred等
Retrofit提供了默认的CallAdapter,也可以通过addCallAdapterFactory方法添加自定义的CallAdapter
• Converter是一个接口,它定义了如何将请求体和响应体转换为Java对象
• Retrofit提供了一些内置的Converter,如GsonConverter,也可以通过addConverterFactory方法添加自定义的Converter

Android 学习笔录

Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap文章来源地址https://www.toymoban.com/news/detail-639995.html

到了这里,关于框架解读 | Retrofit设计剖析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • android 五大应用开发框架(1),腾讯竟然又偷偷开源了一套Android原生UI框架

    2、Android Runtime Android包含一个核心库的集合,提供大部分在Java编程语言核心类库中可用的功能。每一个Android应用程序是Dalvik虚拟机中的实例,运行在他们自己的进程中。Dalvik虚拟机设计成,在一个设备可以高效地运行多个虚拟机。Dalvik虚拟机可执行文件格式是.dex,dex格式是

    2024年04月09日
    浏览(72)
  • Android开发 基于ARouter开源的路由框架的YmRouter

    目录 一、ARouter的简介 二,集成  1、添加jitpack 仓库 2、依赖YmRouter库 单model项目 多model项目 三、使用和ARouter的小区别 3.1、和ARouter的区别 3.2、简单的初始化  3.3、简单使用 3.4 携带值跳转Activity ARouter是一个用于Android的路由框架,它能够帮助开发者实现组件之间的通信和页面

    2024年02月02日
    浏览(42)
  • 基于android studio开发的火车票购票系统app,android移动开发课设,毕业设计

    基于android studio开发实现火车票购票系统app 适用于android移动开发学习项目,课程设计,毕业设计等 开发工具:android studio 或者intellij idea专业版 操作系统:windows10 java: JDK11 构建工具Gradle : gradle-7.0.0 模拟器AVD:pixel 3XL API 30 具体AVD配置详情如下 APP功能 该APP包含17个Activity,每

    2024年02月09日
    浏览(55)
  • 【.NET源码解读】深入剖析中间件的设计与实现

    .NET本身就是一个基于中间件(middleware)的框架,它通过一系列的中间件组件来处理HTTP请求和响应。在之前的文章《.NET源码解读kestrel服务器及创建HttpContext对象流程》中,已经通过源码介绍了如何将HTTP数据包转换为.NET的HttpContext对象。接下来,让我们深入了解一下.NET是如何

    2024年02月11日
    浏览(43)
  • 安卓开发面试问题回答技巧,腾讯竟然又偷偷开源了一套Android原生UI框架

    偶然看到知乎的内推帖,投了个简历,下午hr姐姐call我,安排面试选在3天后,然而又要笔试阿里,所以没怎么复习。 8点起床,9点过比较紧张的去了创业园,感觉知乎氛围很好,在那等了一小会,有前台大叔给你倒水。 应该是个参加工作不久的研究僧师兄,出了一道算法题

    2024年03月12日
    浏览(72)
  • 移动应用开发实验一Android studio设计三种计算器的UI

    使用必要的布局方式,设计下面三种计算器的界面: 简单的计算器 科学计算器 程序计算器 边框的设置是建立一个drawable的xml文件,然后写了边框宽度、颜色、圆角和内边距。调用的时候用到了background属性 。

    2024年02月11日
    浏览(59)
  • 深入剖析gRPC:Google开源的高性能RPC框架

    在本篇文章中,我们将深入剖析gRPC,Google开源的高性能RPC框架。gRPC是一种基于HTTP/2的高性能、可扩展的RPC框架,它使用Protocol Buffers作为接口定义语言,可以在多种编程语言之间实现无缝通信。 gRPC的核心设计理念是:通过使用HTTP/2作为传输协议,实现高效、可扩展的RPC通信

    2024年02月19日
    浏览(55)
  • Spring Boot源码解读与原理剖析:深入探索Java开发的奥秘!

    关注+点赞+评论,评论区回复“Spring Boot源码解读与原理剖析:深入探索Java开发的奥秘!” 每篇最多 评论3条 !!采用抽奖助手自动拉取评论区有效评论送书两本, 开奖时间:9月11号 承载着作者的厚望,掘金爆火小册同名读物 《Spring Boot源码解读与原理剖析》 正式出书!

    2024年02月10日
    浏览(52)
  • 50.现有移动端开源框架及其特点—FeatherCNN与TensorFlow Lite

    50.1 FeatherCNN FeatherCNN 是由腾讯 AI 平台部研发的基于 ARM 架构的高效 CNN 推理库,该项目支持 Caffe 模型,且具有高性能、易部署、轻量级三大特性。 该项目具体特性如下: 高性能:无论是在移动设备(iOS / Android),嵌入式设备(Linux)还是基于 ARM 的服务器(Linux)上,Feathe

    2024年02月02日
    浏览(39)
  • 【开源与项目实战:开源实战】77 | 开源实战一(下):通过剖析Java JDK源码学习灵活应用设计模式

    上一节课,我们讲解了工厂模式、建造者模式、装饰器模式、适配器模式在 Java JDK 中的应用,其中,Calendar 类用到了工厂模式和建造者模式,Collections 类用到了装饰器模式、适配器模式。学习的重点是让你了解,在真实的项目中模式的实现和应用更加灵活、多变,会根据具体

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包