RxFace - 用 RxJava, Retrofit, Okhttp 处理人脸识别的简单用例
RxFace
用 RxJava, Retrofit, Okhttp 处理人脸识别的简单用例
Overview
这是一个人脸识别的简单 Demo, 使用了 FacePlusPlus 的接口。他们的/detection/detect
人脸识别接口可以使用普通的 get
也可以用 post
传递图片二进制流的形式。其中 post
的时候遇到了相当多的坑,下面会提。
该 demo 的网络请求库使用了 Retrofit 并集成了 OkHttp,使用 RxJava 进行封装,方便以流的形式处理网络回调以及图片处理,View 的注入框架用了 ButterKnife,图片加载使用 Glide。
Difficult point
当直接使用 get
通过传图片 Url 拿到人脸识别数据的话是相当简单的,如下请求链接只要使用 Retrofit
的 get
请求的 @QueryMap
传递参数即可: Get数据Demo。
主要存在的困难点是,当获取本地图片,再使用 post
传二进制图片数据时,post
要使用 MultipartTypedOutput
,可参考 stackoverflow 的回答。然而,这样并没有结束,根据 FacePlusPlus 提供的 SDK Sample 里的 Httpurlconnection 的得到的 post
请求头是这样的:
[Content-Disposition: form-data; name="api_key", Content-Type: text/plain; charset=US-ASCII, Content-Transfer-Encoding: 8bit]
[Content-Disposition: form-data; name="img"; filename="NoName", Content-Type: application/octet-stream, Content-Transfer-Encoding: binary]
而使用 Retrofit
默认实现的话,我们这样来实现:
public static MultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){ byte[] data = getBitmapByte(bitmap); MultipartTypedOutput multipartTypedOutput = new MultipartTypedOutput(); multipartTypedOutput.addPart("api_key", new TypedString(Constants.API_KEY)); multipartTypedOutput.addPart("api_secret", new TypedString(Constants.API_SECRET)); multipartTypedOutput.addPart("img", new TypedByteArray("application/octet-stream", data)); return multipartTypedOutput; }
根据 Sample 的请求头,RestAdapter
的请求头参数我们这样设置来:
private RequestInterceptor mRequestInterceptor = new RequestInterceptor() { @Override public void intercept(RequestFacade request) { request.addHeader("connection", "keep-alive"); request.addHeader("Content-Type", "multipart/form-data; boundary="+ getBoundary() + "; charset=UTF-8"); } };
但是!!!它得到的String参数的头是这样的,这里没有贴出其他的差异,
Content-Disposition: form-data; name="api_key" Content-Type: text/plain; charset=UTF-8 Content-Length: 32 Content-Transfer-Encoding: 8bit
所以需要重写三个类:
MultipartTypedOutput
为 final 类,所以重写为 CustomMultipartTypedOutput
,并使其构造函数,增加 boundary 的设置;
TypedString
默认的编码格式是UTF-8
,所以重写为 AsciiTypeString
类,使其编码格式改为 US-ASCII
;
TypedByteArray
默认的的 fileName()
方法返回的是 null,而当传图片数据时需要 fileName 为 "NoName",所以重写为 CustomTypedByteArray
类,设置其 fileName 为 "NoName"。
同时需要注意的是在设置 RestAdapter
的 header 时,其 boundary 一定要和 CustomMultipartTypedOutput
的 boundary 相同,否则服务端无法匹配的!(这个地方,一时没注意,被整了一个多小时才发现!!)
最后 body 的传参,这样来得到:
public static CustomMultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){ byte[] data = getBitmapByte(bitmap); CustomMultipartTypedOutput multipartTypedOutput = new CustomMultipartTypedOutput(boundary); multipartTypedOutput.addPart("api_key", "8bit", new AsciiTypeString(Constants.API_KEY)); multipartTypedOutput.addPart("api_secret", "8bit", new AsciiTypeString(Constants.API_SECRET)); multipartTypedOutput.addPart("img", new CustomTypedByteArray("application/octet-stream", data)); return multipartTypedOutput; }
Preview
More about me
Acknowledgments
- Glide -Glide
- Retrofit - Retrofit
- OkHttp - OkHttp
- RxJava - RxJava
- ButterKnife - ButterKnife