技术标签: 面试 java android 移动开发 Android
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
retrofit实例的创建,使用了builder模式,从下面的源码中可以看出
public static final class Builder {
Builder(Platform platform) {
this.platform = platform;
converterFactories.add(new BuiltInConverters());
}
public Builder() {
// Platform.get()方法可以用于判断当前的环境
this(Platform.get());
}
public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();// 新建Client,留到之后newCall什么的
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
}
好玩的地方开始了,我们先来看看这个方法
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() {
// platform 可以分辨出你是在android,还是java8,又或者别的
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
// 这里的invoke,Object方法都走这里,比如equals、toString、hashCode什么的
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
// java8默认方法,1.8的新特性
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 这里是核心代码了
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
可以看出创建API使用了动态代理,根据接口动态生成的代理类,将接口的都转发给了负责连接代理类和委托类的InvocationHandler实例,接口方法也都通过其invoke方法来处理。
在invoke方法中,首先会通过Platform.get()方法判断出当前代码的执行环境,之后会先把Object和Java8的默认方法进行一个处理,也是在进行后续处理之前进行去噪。其中的关键代码其实就是最后三句,这也是这篇文章将要分析的
erviceMethod<?, ?> loadServiceMethod(Method method) {
// 从缓存里面取出,如果有的话,直接返回好了
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 为null的话,解析方法的注解和返回类型、参数的注解he参数类型,新建一个ServiceMethod
result = new ServiceMethod.Builder<>(this, method).build();// ->
// 新建的ServiceMethod加到缓存列表里面
serviceMethodCache.put(method, result);
}
}
return result;
}
CallAdapter和Converter等到后面再分析,这里先看看parseMethodAnnotation(annotation),功能和其名字一样,其对方法注解进行了解析
/**
* 解析方法注解,呜啦啦
* 通过判断注解类型来解析
* @param annotation
*/
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}
// 其他的一些方法注解的解析
...
}
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {// 已经赋值过了
throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod, httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
// value为设置注解方法时候,设置的值,官方例子中的users/{user}/repos or user
if (value.isEmpty()) {
return;
}
// 查询条件的一些判断
...
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
`
在解析注解时,先通过instanceof判断出注解的类型,之后调用parseHttpMethodAndPath方法解析注解参数值,并设置httpMethod、relativeUrl、relativeUrlParamNames等属性。
上面说了API中方法注解的解析,现在来看看方法参数注解的解析,这是通过调用parseParameterAnnotation方法生成ParameterHandler实例来实现的,代码比较多,这里挑选@Query来看看。
else if (annotation instanceof Query) {
Query query = (Query) annotation;
String name = query.value();
boolean encoded = query.encoded();
Class<?> rawParameterType = Utils.getRawType(type);// 返回基础的类
gotQuery = true;
// 可以迭代,Collection
if (Iterable.class.isAssignableFrom(rawParameterType)) {
if (!(type instanceof ParameterizedType)) {
throw parameterError(p, rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ "<String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);// 返回基本类型
Converter<?, String> converter =
retrofit.stringConverter(iterableType, annotations);
return new ParameterHandler.Query<>(name, converter, encoded).iterable();
} else if (rawParameterType.isArray()) {// Array
Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());// 如果是基本类型,自动装箱
Converter<?, String> converter =
retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.Query<>(name, converter, encoded).array();
} else {// Other
Converter<?, String> converter =
retrofit.stringConverter(type, annotations);
return new ParameterHandler.Query<>(name, converter, encoded);
}
在@Query中,将分成Collection、array、other三种情况处理参数,之后根据这些参数,调用ParameterHandler中的Query静态类,创建出一个ParameterHandler实例。这样循环直到解析了所有的参数注解,组合成为全局变量parameterHandlers,之后构建请求时会用到
ServiceMethod创建完成之后,我们来看看下一行代码中的OkHttpCall类,里面的包含了请求的执行和响应处理,我们来看看异步请求的做法
OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
@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 {
call = rawCall = createRawCall();// 创建OkHttp3.Call
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
# 结语
* 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
* 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
* 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
* OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。
**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。
**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**
[外链图片转存中...(img-Rt2Il25S-1630837571864)]

这里有个讲解非常明白,虽然没证明。点击打开链接 还有个数据结构的学习 点击打开链接 伸展树/Treap/划分树/ 归并树步骤:tarjan算法的步骤是(当dfs到节点u时):1 在并查集中建立仅有u的集合,设置该集合的祖先为u1 对u的每个孩子v: 1.1 tarjan之 1.2 合并v到父节点u的集合,确保集合的祖先是u2 设置u为已遍历3
Linux下提供top、ps命令查看当前cpu、mem使用情况,简要介绍如下:一、使用ps查看进程的资源占用ps -aux查看进程信息时,第三列就是CPU占用。[[email protected] utx86]# ps -aux | grep my_processWarning: bad syntax, perhaps a bogus '-'? See/usr/share/doc/procps-3.2....
为什么使用方法一般来说,我们使用方法是为了解决代码过于臃肿,阅读性差,维护性差的问题,把代码封装成一个工具类,是为了我们后期开发的便利。那么我们最常见的方法,也是第一个看到的方法就是main方法,那么我们就以main方法为例,讲一下方法结构的组成。main方法public static void main(String[] args) {// 方法体}/*public stati...
桔妹导读:随着云规模不断扩大以及业务层面对延迟、带宽的要求越来越高,采用DPDK 加速网络报文处理的方式在横向纵向扩展都出现了局限性。可编程芯片成为业界热点。本文主要讲述了可编程网卡芯片...
Description在n个数中,找出出现次数最多那个数字,并且输出出现的次数。如果有多个结果,输出数字最小的那一个。Input单组数据,第一行数字n(1<=n<=100000)。接下来有n个数字,每个数字不超过100000000Output出现次数最多的数字和次数。SampleInput31 1 2Output1 2答案:#include <iostream>#include<bits/stdc++.h>#define ll long
文章目录chapter14 高级I/O非阻塞I/O记录锁I/O多路转接POSIX异步I/O函数readv和writev存储映射I/Ochapter14 高级I/O非阻塞I/O非阻塞I/O使我们可以发出open、read和write这样的I/O操作,并使这些操作不会永远阻塞。如果这种操作不能完成,则调用立刻出错返回,表示该操作如继续执行将阻塞。对于一个给定的描述符,有两种为其指定非阻塞I/O的方法:如果调用open获得描述符,则可指定O_NONBLOCK标志对于已经打开的一个描述符,则可调用fc
01.保持强烈的忧患意识(1).危机激发斗志;(2).对成功视而不见;02.人人都要成为奋斗者(1).迎难而上;(2).先苦后甜;(3).以勤补拙;(4).继续艰苦奋斗;03.用“三高政策”催生行动力(1).高工资催生高效率;(2).高工资带来高压力;(3).高压力激发高效率;04.务实务虚并存,发挥机制优势(1).务实出成效;(2).专注于本职工作;(3).小改进,大奖励;(4).虚实结合[基层...
查看主机的IP网段(我在房东的网络下接了路由器),192.168.28.117。修改/Library/Preferences/VMware Fusion/vmnet8目录下的nat.conf文件 网关的设置要两个配置文件相同,一般为28.2的ip,其它的配置的IP需要更改。 一般换了网络环境以后,VMware内的虚拟机设置为固定IP的,以NAT方式与主机共享的网络需要重新进行设置。DNS设置
为什么80%的码农都做不了架构师?>>> ...
一、需求分析1、现网描述a)如上图所示,本网络为xxx银行支行网络架构,此网络中有两套运行的网络,生产网与管理网,默认情况下,生产网通过联通200M访问总行网络,当联通200down掉以后,通过电信100M访问总行。管理网通过电信100M访问总行网络,当电信100Mdown掉以后,通过联通200M访问总行,确保网络的搞可靠性。SW1、SW2为接入交换机,接入业务P管理PC与服务器。SW3、SW4为汇聚交换机。由于流量过大,同时又为了冗余性,需要在SW3、SW4之间配置链路聚合,AR1、AR2为核心路由.
第一章 信息收集1.1 收集域名信息1.1.1 Whois查询1.1.2 备案信息查询1.2 收集敏感信息1.3 收集子域名信息1、子域名检测工具2、搜索引擎枚举3、第三方聚合应用枚举4、证书透明度公开日志枚举1.4 收集常用端口信息1.5 指纹识别1.6 查找真实IP1、目标服务器存在CDN2、判断目标是否使用了CDN3、绕过CDN寻找真实IP4、验证获取的IP1.7 收集敏感文件目录1.8 社会工程学1.1 收集域名信息1.1.1 Whois查询https://whois.aizhan.com
这篇文章接着前面的Android编译系统分析(一)继续, 主要分析Android OS以及module的编译行为.