Android app开发中Retrofit框架的初步上手使用

Retrofit 2.0
先来说一下Retrofit 2.0版本中一些引人注意的地方。

在Retrofit 2.0中,最大的改动莫过于减小库的体积,首先,Retrofit 2.0去掉了对所有的HTTP客户端的兼容,而钟情于OkHttpClient一个,极大地减少了各种适配代码,原因一会儿说;其次,拆库,比如将对RxJava的支持设置为可选(需要额外引入库);再比如将各个序列化反序列化转换器支持设置为可选(需要额外引入库)。于2.0抛弃HttpClient和HttpURLConnection,为了减小库体积是一方面,另外一个重要的原因作为一个专门为Android&Java 应用量身打造的Http栈,OkHttpClient越来越受到各个大的开源项目的青睐,毕竟它本身就是开源的。

此外,OkHttpClient与HttpClient相比,它最大的改进是自带工作线程池,所以上层应用无需自己去维护复杂的并发模型,而HttpClient仅仅提供了一个线程安全的类,所以还需要上层应用去处理并发的逻辑(实际上Volley一部分工作就是干这个)。从这个角度来说,Retrofit得益于OkHttpClient的优势,较之于Volley是一种更加先进的网络框架。

由于Retrofit不需要去关心并发工作线程的维护,所以它可以全力关注于如何精简发送一个请求的代价,实际上使用Retrofit发送一个请求实在是太easy了!

简单示例

首先定义请求接口,即程序中都需要什么请求操作

public interface GitHubService {
 @GET("/users/{user}/repos")
 List<Repo> listRepos(@Path("user") String user);
}

然后通过RestAdapter生成一个刚才定义的接口的实现类,使用的是动态代理。

RestAdapter restAdapter = new RestAdapter.Builder()
  .setEndpoint("https://api.github.com")
  .build();

GitHubService service = restAdapter.create(GitHubService.class);

现在就可以调用接口进行请求了

List<Repo> repos = service.listRepos("octocat");

使用就是这么简单,请求时直接调用接口就行了,甚至不用封装参数,因为参数的信息已经在定义接口时通过Annotation定义好了。

从上面的例子可以看到接口直接返回了需要的Java类型,而不是byte[]或String,解析数据的地方就是Converter,这个是可以自定义的,默认是用Gson解析,也就是说默认认为服务器返回的是Json数据,可以通过指定不同的Convert使用不同的解析方法,如用Jackson解析Json,或自定义XmlConvert解析xml数据。

Retrofit的使用就是以下几步:

  • 定义接口,参数声明,Url都通过Annotation指定
  • 通过RestAdapter生成一个接口的实现类(动态代理)
  • 调用接口请求数据
  • 接口的定义要用用Rtrofit定义的一些Annotation,所以先看一下Annotation的。

Annotation

以上面的示例中的接口来看

@GET("/group/{id}/users")
List<User> groupList(@Path("id") int groupId);

先看@GET

/** Make a GET request to a REST path relative to base URL. */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
@RestMethod("GET")
public @interface GET {
 String value();
}

@GET本身也被几个Anotation注解,@Target表示@GET注解是用于方法的,value方法就返回这个注解的value值,在上例中就是/group/{id}/users,然后就是@RestMethod

@Documented
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
public @interface RestMethod {
 String value();
 boolean hasBody() default false;
}

RestMethod是一个用于Annotation的Annotation,比如上面的例子中用来注解的@GET,value方法就返回GET,hasBody表示是否有Body,对于POST这个方法就返回true

@Documented
@Target(METHOD)
@Retention(RUNTIME)
@RestMethod(value = "POST", hasBody = true)
public @interface POST {
 String value();
}

Retrofit的Annotation包含请求方法相关的@GET、@POST、@HEAD、@PUT、@DELETA、@PATCH,和参数相关的@Path、@Field、@Multipart等。

定义了Annotation要就有解析它的方法,在Retrofit中解析的位置就是RestMethodInfo,但在这之前需要先看哪里使用了RestMethodInfo,前面说了Retrofit使用了动态代理生成了我们定义的接口的实现类,而这个实现类是通过RestAdapter.create返回的,所以使用动态代理的位置就是RestAdapter,接下来就看一下RestAdapter。

RestAdapter

RestAdapter restAdapter = new RestAdapter.Builder()
  .setEndpoint("https://api.github.com")
  .build();

GitHubService service = restAdapter.create(GitHubService.class);

public RestAdapter build() {
 if (endpoint == null) {
  throw new IllegalArgumentException("Endpoint may not be null.");
 }

 ensureSaneDefaults();

 return new RestAdapter(endpoint, clientProvider, httpExecutor, callbackExecutor,
   requestInterceptor, converter, profiler, errorHandler, log, logLevel);
}

setEndPoint就不说了,接口中定义的都是相对Url,EndPoint就是域名,build方法调用ensureSaneDefaults()方法,然后就构造了一个RestAdapter对象,构造函数的参数中传入了EndPoint外的几个对象,这几个对象就是在ensureSaneDefaults()中初始化的。

private void ensureSaneDefaults() {
 if (converter == null) { converter = Platform.get().defaultConverter(); }
 if (clientProvider == null) { clientProvider = Platform.get().defaultClient(); }
 if (httpExecutor == null) { httpExecutor = Platform.get().defaultHttpExecutor(); }
 if (callbackExecutor == null) { callbackExecutor = Platform.get().defaultCallbackExecutor(); }
 if (errorHandler == null) { errorHandler = ErrorHandler.DEFAULT; }
 if (log == null) { log = Platform.get().defaultLog(); }
 if (requestInterceptor == null) { requestInterceptor = RequestInterceptor.NONE; }
}

ensureSaneDefaults()中初始化了很多成员,errorHandler、log就不看了,其他的除了requestInterceptor都是通过Platform对象获得的,所以要先看下Platform

Platform

private static final Platform PLATFORM = findPlatform();
 static final boolean HAS_RX_JAVA = hasRxJavaOnClasspath();

 static Platform get() {
  return PLATFORM;
 }

 private static Platform findPlatform() {
  try {
   Class.forName("android.os.Build");
   if (Build.VERSION.SDK_INT != 0) {
    return new Android();
   }
  } catch (ClassNotFoundException ignored) {
  }

  if (System.getProperty("com.google.appengine.runtime.version") != null) {
   return new AppEngine();
  }

  return new Base();
 }

使用了单例的PLATFORM,通过findPlatform()初始化实例,如果是Android平台就使用Platform.Android,如果是Google AppEngine就使用Platform.AppEngine,否则使用Platform.Base,这些都是Platform的子类,其中AppEngine又是Base的子类。

Platform是一个抽象类,定义了以下几个抽象方法,这几个方法的作用就是返回一些RestAdapter中需要要用到成员的默认实现

abstract Converter defaultConverter(); // 默认的Converter,用于将请求结果转化成需要的数据,如GsonConverter将JSON请求结果用Gson解析成Java对象
 abstract Client.Provider defaultClient(); // Http请求类,如果是AppEngine就使用`UrlFetchClient`,否则如果有OKHttp就使用OKHttp,如果是Android,2.3以后使用HttpURLConnection,2.3以前使用HttpClient
 abstract Executor defaultHttpExecutor(); // 用于执行Http请求的Executor
 abstract Executor defaultCallbackExecutor(); // Callback调用中用于执行Callback的Executor(可能是同步的)
 abstract RestAdapter.Log defaultLog(); // Log接口,用于输出Log

看完Platform的接口再看ensureSaneDefaults就清楚了,初始化转化数据的Converter、执行请求的Client、执行请求的Executor、执行Callback的Executor、Log输出类、错误处理类和用于在请求前添加额外处理的拦截请求的Interceptor。
Converter默认都是用的GsonConverter,就不看了,defaultClient返回执行网络请求的Client

Platform.Android

@Override Client.Provider defaultClient() {
 final Client client;
 if (hasOkHttpOnClasspath()) {
  client = OkClientInstantiator.instantiate();
 } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
  client = new AndroidApacheClient();
 } else {
  client = new UrlConnectionClient();
 }
 return new Client.Provider() {
  @Override public Client get() {
   return client;
  }
 };
}

Platform.Base

@Override Client.Provider defaultClient() {
 final Client client;
 if (hasOkHttpOnClasspath()) {
  client = OkClientInstantiator.instantiate();
 } else {
  client = new UrlConnectionClient();
 }
 return new Client.Provider() {
  @Override public Client get() {
   return client;
  }
 };
}

Platform.AppEngine

@Override Client.Provider defaultClient() {
 final UrlFetchClient client = new UrlFetchClient();
 return new Client.Provider() {
  @Override public Client get() {
   return client;
  }
 };
}

对于Android,优先使用OKHttp,否则2.3以后使用HttpUrlConnection,2.3以前使用HttpClient

defaultHttpExecutor就是返回一个Executor,执行请求的线程在这个Executor中执行,就做了一件事,把线程设置为后台线程

defaultCallbackExecutor用于执行Callback类型的请求时,提供一个Executor执行Callback的Runnable

Platform.Base

@Override Executor defaultCallbackExecutor() {
  return new Utils.SynchronousExecutor();
}
Platform.Android

@Override Executor defaultCallbackExecutor() {
  return new MainThreadExecutor();
}

SynchronousExecutor

static class SynchronousExecutor implements Executor {
  @Override public void execute(Runnable runnable) {
   runnable.run();
  }
}

MainThreadExecutor

public final class MainThreadExecutor implements Executor {
 private final Handler handler = new Handler(Looper.getMainLooper());

 @Override public void execute(Runnable r) {
  handler.post(r);
 }
}

如果是Android,通过Handler将回调发送到主线程执行,如果非Android,直接同步执行。
Platform看完了,RestAdapter的成员初始化完成,就要看怎么通过RestAdapter.create生成我们定义的接口的实现类了

RestAdapter.create

 public <T> T create(Class<T> service) {
  Utils.validateServiceClass(service);
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
    new RestHandler(getMethodInfoCache(service)));
 }

 Map<Method, RestMethodInfo> getMethodInfoCache(Class<?> service) {
  synchronized (serviceMethodInfoCache) {
   Map<Method, RestMethodInfo> methodInfoCache = serviceMethodInfoCache.get(service);
   if (methodInfoCache == null) {
    methodInfoCache = new LinkedHashMap<Method, RestMethodInfo>();
    serviceMethodInfoCache.put(service, methodInfoCache);
   }
   return methodInfoCache;
  }
 }

使用了动态代理,InvocationHandler是RestHandler,RestHandler有一个参数,是Method->RestMethodInfo的映射,初始化时这个映射是空的。重点就是这两个了:RestHandler,RestMethodInfo,

@Override public Object invoke(Object proxy, Method method, final Object[] args)
  throws Throwable {
 // If the method is a method from Object then defer to normal invocation.
 if (method.getDeclaringClass() == Object.class) { // 1
  return method.invoke(this, args);
 }

 // Load or create the details cache for the current method.
 final RestMethodInfo methodInfo = getMethodInfo(methodDetailsCache, method); // 2

 if (methodInfo.isSynchronous) { // 3
  try {
   return invokeRequest(requestInterceptor, methodInfo, args);
  } catch (RetrofitError error) {
   Throwable newError = errorHandler.handleError(error);
   if (newError == null) {
    throw new IllegalStateException("Error handler returned null for wrapped exception.",
      error);
   }
   throw newError;
  }
 }

 if (httpExecutor == null || callbackExecutor == null) {
  throw new IllegalStateException("Asynchronous invocation requires calling setExecutors.");
 }

 // Apply the interceptor synchronously, recording the interception so we can replay it later.
 // This way we still defer argument serialization to the background thread.
 final RequestInterceptorTape interceptorTape = new RequestInterceptorTape();
 requestInterceptor.intercept(interceptorTape); // 4

 if (methodInfo.isObservable) { // 5
  if (rxSupport == null) {
   if (Platform.HAS_RX_JAVA) {
    rxSupport = new RxSupport(httpExecutor, errorHandler);
   } else {
    throw new IllegalStateException("Observable method found but no RxJava on classpath");
   }
  }

  return rxSupport.createRequestObservable(new Callable<ResponseWrapper>() {
   @Override public ResponseWrapper call() throws Exception {
    return (ResponseWrapper) invokeRequest(interceptorTape, methodInfo, args);
   }
  });
 }

 Callback<?> callback = (Callback<?>) args[args.length - 1]; // 6
 httpExecutor.execute(new CallbackRunnable(callback, callbackExecutor, errorHandler) {
  @Override public ResponseWrapper obtainResponse() {
   return (ResponseWrapper) invokeRequest(interceptorTape, methodInfo, args);
  }
 });

 return null; // Asynchronous methods should have return type of void.
}

执行请求时会调用RestHandler的invoke方法,如上所示,主要是上面代码中标注有6点

如果调用的是Object的方法,不做处理直接调用。
通过getMethodInfo获取调用的Method对应的RestMethodInfo,前面说了,构造RestHandler对象时传进来了一个Method->RestMethodInfo的映射,初始时是空的。

 static RestMethodInfo getMethodInfo(Map<Method, RestMethodInfo> cache, Method method) {
  synchronized (cache) {
   RestMethodInfo methodInfo = cache.get(method);
   if (methodInfo == null) {
    methodInfo = new RestMethodInfo(method);
    cache.put(method, methodInfo);
   }
   return methodInfo;
  }

在getMethodInfo中判断如果相应的映射不存在,就建立这个映射,并如名字所示缓存起来

  • 如果是同步调用(接口中直接返回数据,不通过Callback或Observe),直接调用invokeRequest
  • 如果是非同步调用,先通过RequestInterceptorTape记录拦截请求,记录后在后台线程做实际拦截,后面会提到。
  • 如果是Observe请求(RxJava),执行第5步,对RxJava不了解,略过
  • 如果是Callback形式,交由线程池执行

接口中的每一个Method有一个对应的RestMethodInfo,关于接口中Annotation信息的处理就都在这里了

RestMethodInfo

private enum ResponseType {
  VOID,
  OBSERVABLE,
  OBJECT
}
RestMethodInfo(Method method) {
  this.method = method;
  responseType = parseResponseType();
  isSynchronous = (responseType == ResponseType.OBJECT);
  isObservable = (responseType == ResponseType.OBSERVABLE);
}

在构造函数中调用了parseResponseType,parseResponseType解析了方法签名,根据方法的返回值类型及最后一个参数的类型判断方法的类型是哪种ResponseType

无论是哪种ResponseType,最终都是调用invokeRequest执行实际的请求,接下来依次看下invokeRequest的执行步骤

RestAdapter.invokeRequest

第一步是调用methodInfo.init()解析调用的方法,方法里有做判断,只在第一次调用时解析,因为处一次解析后这个对象就被缓存起来了,下次调同一个方法时可以直接使用

 synchronized void init() {
  if (loaded) return;

  parseMethodAnnotations();
  parseParameters();

  loaded = true;
 }

在RestMethodInfo.init中分别调用

  • parseMethodAnnotations():解析所有方法的Annotation
  • parseParameters():解析所有参数的Annotation
for (Annotation methodAnnotation : method.getAnnotations()) {
 Class<? extends Annotation> annotationType = methodAnnotation.annotationType();
 RestMethod methodInfo = null;
 // Look for a @RestMethod annotation on the parameter annotation indicating request method.
 for (Annotation innerAnnotation : annotationType.getAnnotations()) {
  if (RestMethod.class == innerAnnotation.annotationType()) {
   methodInfo = (RestMethod) innerAnnotation;
   break;
  }
 }
 ...
}

在parseMethodAnnotations中,会获取方法所有的Annotation并遍历:
对于每一个Annotation,也会获取它的Annotation,看它是否是被RestMethod注解的Annotation,如果是,说明是@GET,@POST类型的注解,就调用parsePath解析请求的Url,requestParam(URL中问号后的内容)及Url中需要替换的参数名(Url中大括号括起来的部分)
寻找Headers Annotation解析Header参数
解析RequestType:SIMPLE,MULTIPART,FORM_URL_ENCODED
parseParameters解析请求参数,即参数的Annotation,@PATH、@HEADER、@FIELD等

第二步是RequestBuilder和Interceptor,这两个是有关联的,所以一起看。

RequestBuilder requestBuilder = new RequestBuilder(serverUrl, methodInfo, converter);
requestBuilder.setArguments(args);
requestInterceptor.intercept(requestBuilder);
Request request = requestBuilder.build();

先说RequestInterceptor,作用很明显,当执行请求时拦截请求以做一些特殊处理,比如添加一些额外的请求参数。

/** Intercept every request before it is executed in order to add additional data. */
public interface RequestInterceptor {
 /** Called for every request. Add data using methods on the supplied {@link RequestFacade}. */
 void intercept(RequestFacade request);

 interface RequestFacade {
  void addHeader(String name, String value);
  void addPathParam(String name, String value);
  void addEncodedPathParam(String name, String value);
  void addQueryParam(String name, String value);
  void addEncodedQueryParam(String name, String value);
 }

 /** A {@link RequestInterceptor} which does no modification of requests. */
 RequestInterceptor NONE = new RequestInterceptor() {
  @Override public void intercept(RequestFacade request) {
   // Do nothing.
  }
 };
}

RequestInterceptor只有一个方法intercept,接收一个RequestFacade参数,RequestFacade是RequestInterceptor内部的一个接口,这个接口的方法就是添加请求参数,Query、Header什么的。大概可以看出RequestInterceptor的作用了,如果RequestFacade表示一个请求相关的数据,RequestInteceptor.intercept的作用就是向这个RequestFacade中添加额外Header,Param等参数。

RequestFacade的一个子类叫RequestBuilder,用来处理Request请求参数,在invokeRequest中会对RequestBuilder调用intercept方法向RequestBuilder添加额外的参数。

有一个叫RequestInterceptorTape的类,同时实现了RequestFacade与RequestInterceptor,它的作用是:

当作为RequestFacade使用时作为参数传给一个RequestInteceptor,这个RequestInterceptor调用它的addHeader等方法时,它把这些调用及参数记录下来
然后作为RequestInterceptor使用时,将之前记录的方法调用及参数重新应用到它的intercept参数RequestFacade中
在RestHandler.invoke中,如果判断方法的调用不是同步调用,就通过下面的两行代码将用户设置的interceptor需要添加的参数记录到RequestInterceptorTape,然后在invokeRequest中再实际执行参数的添加。

// Apply the interceptor synchronously, recording the interception so we can replay it later.
// This way we still defer argument serialization to the background thread.
final RequestInterceptorTape interceptorTape = new RequestInterceptorTape();
requestInterceptor.intercept(interceptorTape);

RequestBuilder.setArguments()解析调用接口时的实际参数。然后通过build()方法生成一个Request对象

第三步执行请求,Response response = clientProvider.get().execute(request);

第四步就是解析并分发请求结果了,成功请求时返回结果,解析失败调用ErrorHandler给用户一个自定义异常的机会,但最终都是通过异常抛出到invoke()中的,如果是同步调用,直接抛异常,如果是Callback调用,会回调Callback.failure

CallbackRunnable

请求类型有同步请求,Callback请求,Observable请求,来看下Callback请求:

Callback<?> callback = (Callback<?>) args[args.length - 1];
httpExecutor.execute(new CallbackRunnable(callback, callbackExecutor, errorHandler) {
  @Override public ResponseWrapper obtainResponse() {
   return (ResponseWrapper) invokeRequest(interceptorTape, methodInfo, args);
  }
});

Callback请求中函数最后一个参数是一个Callback的实例,httpExecutor是一个Executor,用于执行Runnable请求,我们看到,这里new了一个CallbackRunnable执行,并实现了它的obtainResponse方法,看实现:

abstract class CallbackRunnable<T> implements Runnable {
 private final Callback<T> callback;
 private final Executor callbackExecutor;
 private final ErrorHandler errorHandler;

 CallbackRunnable(Callback<T> callback, Executor callbackExecutor, ErrorHandler errorHandler) {
  this.callback = callback;
  this.callbackExecutor = callbackExecutor;
  this.errorHandler = errorHandler;
 }

 @SuppressWarnings("unchecked")
 @Override public final void run() {
  try {
   final ResponseWrapper wrapper = obtainResponse();
   callbackExecutor.execute(new Runnable() {
    @Override public void run() {
     callback.success((T) wrapper.responseBody, wrapper.response);
    }
   });
  } catch (RetrofitError e) {
   Throwable cause = errorHandler.handleError(e);
   final RetrofitError handled = cause == e ? e : unexpectedError(e.getUrl(), cause);
   callbackExecutor.execute(new Runnable() {
    @Override public void run() {
     callback.failure(handled);
    }
   });
  }
 }

 public abstract ResponseWrapper obtainResponse();
}

就是一个普通的Runnable,在run方法中首先执行obtailResponse,从名字可以看到是执行请求返回Response,这个从前面可以看到执行了invokeRequest,和同步调用中一样执行请求。
紧接着就提交了一个Runnable至callbackExecutor,在看Platform时看到了callbackExecotor是通过Platform.get().defaultCallbackExecutor()返回的,Android中是向主线程的一个Handler发消息

值得注意的事,对于同步调用,如果遇到错误是直接抛异常,而对于异步调用,是调用Callback.failure()

Mime

执行网络请求,需要向服务端发送请求参数,如表单数据,上传的文件等,同样需要解析服务端返回的数据,在Retrofit中对这些做了封装,位于Mime包中,也只有封装了,才好统一由指定的Converter执行数据的转换

TypedInput和TypedOutput表示输入输出的数据,都包含mimeType,并分别支持读入一个InputStream或写到一个OutputStrem

/**
 * Binary data with an associated mime type.
 *
 * @author Jake Wharton (jw@squareup.com)
 */
public interface TypedInput {

 /** Returns the mime type. */
 String mimeType();

 /** Length in bytes. Returns {@code -1} if length is unknown. */
 long length();

 /**
  * Read bytes as stream. Unless otherwise specified, this method may only be called once. It is
  * the responsibility of the caller to close the stream.
  */
 InputStream in() throws IOException;
}

/**
 * Binary data with an associated mime type.
 *
 * @author Bob Lee (bob@squareup.com)
 */
public interface TypedOutput {
 /** Original filename.
  *
  * Used only for multipart requests, may be null. */
 String fileName();

 /** Returns the mime type. */
 String mimeType();

 /** Length in bytes or -1 if unknown. */
 long length();

 /** Writes these bytes to the given output stream. */
 void writeTo(OutputStream out) throws IOException;
}

TypedByteArray,内部数据是一个Byte数组

 private final byte[] bytes;

 @Override public long length() {
  return bytes.length;
 }

 @Override public void writeTo(OutputStream out) throws IOException {
  out.write(bytes);
 }

 @Override public InputStream in() throws IOException {
  return new ByteArrayInputStream(bytes);
 }

TypedString,继承自TypedByteArray,内部表示是一样的

public TypedString(String string) {
  super("text/plain; charset=UTF-8", convertToBytes(string));
 }

 private static byte[] convertToBytes(String string) {
  try {
   return string.getBytes("UTF-8");
  } catch (UnsupportedEncodingException e) {
   throw new RuntimeException(e);
  }
 }

其他的也一样,从名字很好理解:TypedFile,MultipartTypedOutput,FormEncodedTypedOutput。

其他

Retrofit对输入和输出做了封装,通过TypedOutput向服务器发送数据,通过TypedInput读取服务器返回的数据。

通过MultipartTypedOutput支持文件上传,读取服务器数据时,如果要求直接返回未解析的Response,Restonse会被转换为TypedByteArray,所以不能是大文件类的

Retrofit支持不同的Log等级,当为LogLevel.Full时会把Request及Response的Body打印出来,所以如果包含文件就不行了。

Retrofit默认使用GsonConverter,所以要想获取原始数据不要Retrofit解析,要么自定义Conveter,要么直接返回Response了,返回Response也比较麻烦

总体来说Retrofit看起来很好用,不过要求服务端返回数据最好要规范,不然如果请求成功返回一种数据结构,请求失败返回另一种数据结构,不好用Converter解析,接口的定义也不好定义,除非都返回Response,或自定义Converter所有接口都返回String

(0)

相关推荐

  • Android Retrofit实现多图片/文件、图文上传功能

    什么是 Retrofit ? Retrofit是Square开发的一个Android和Java的REST客户端库.这个库非常简单并且具有很多特性,相比其他的网络库,更容易让初学者快速掌握.它可以处理GET.POST.PUT.DELETE-等请求,还可以使用picasso加载图片. 一.再次膜拜下Retrofit Retrofit无论从性能还是使用方便性上都很屌!!!,本文不去介绍其运作原理(虽然很想搞明白),后面会出专题文章解析Retrofit的内部原理:本文只是从使用上解析Retrofit实现

  • android Retrofit2+okHttp3使用总结

    使用前准备 Build.gradle文件配置 dependencies配置 compile 'com.squareup.retrofit2:retrofit:2.0.0' compile 'com.squareup.retrofit2:converter-gson:2.0.0' compile 'com.squareup.okhttp3:logging-interceptor:3.2.0' 网络框架搭建 服务创建类封装(HTTP): public class ServiceGenerator {

  • Android Retrofit 2.0框架上传图片解决方案

    本文为大家分享了 Android Retrofit 2.0框架上传图片解决方案,具体内容如下 1.单张图片的上传 /** * 上传一张图片 * @param description * @param imgs * @return */ @Multipart @POST("/upload") Call<String> uploadImage(@Part("fileName") String description, @Part("file\&qu

  • Android使用Retrofit仿微信多张图片拍照上传

    Android 仿照微信发说说,既能实现拍照,选图库,多图案上传,使用Retrofit技术. 使用方法:详见http://www.jb51.net/article/103009.htm 项目的运行效果: 服务器端接收文件的action UploadFile.java @Controller public class UploadFile extends ActionSupport { /** * */ private static final long serialVersionUID = 1L

  • Android中Retrofit 2.0直接使用JSON进行数据交互

    之前使用Retrofit都是将JSON串转化为POJO对象,针对不同的业务协议,定义相应的接口和参数列表.但是此种方式一般用在自己内部协议基础上,具体大的项目中,有些第三方的集成功能,一般都采用统一的方式即请求JSON和回应JSON进行数据交互,不可能每个第三方协议都会去定义与协议相应的POJO对象. HTTP肯定有GET和POST方法,先定义Retrofit Api的interface: package com.hdnetworklib.network.http; import java.ut

  • Android Retrofit和Rxjava的网络请求

    Android  Retrofit和Rxjava的网络请求 去年的时候好多公司就已经使用Rxjava和Retrofit了,最近自自己学习了一下,感觉真的是很好用,让自己的网络请求变得更简单了,而且封装性极强. 首先做一下准备工作,导入需要引用的文件 compile 'com.android.support:appcompat-v7:25.1.0' testCompile 'junit:junit:4.12' compile 'io.reactivex:rxjava:1.1.0' compile

  • Android中Retrofit+OkHttp进行HTTP网络编程的使用指南

    Retrofit介绍: Retrofit(GitHub主页https://github.com/square/okhttp)和OkHttp师出同门,也是Square的开源库,它是一个类型安全的网络请求库,Retrofit简化了网络请求流程,基于OkHtttp做了封装,解耦的更彻底:比方说通过注解来配置请求参数,通过工厂来生成CallAdapter,Converter,你可以使用不同的请求适配器(CallAdapter), 比方说RxJava,Java8, Guava.你可以使用不同的反序列化工具

  • 简略分析Android的Retrofit应用开发框架源码

    面对一个项目,对于Android应用开发框架的选择,我想过三种方案: 1.使用Loader + HttpClient + GreenDao + Gson + Fragment,优点是可定制性强,由于使用Google家自己的Loader和LoaderManager,代码健壮性强. 缺点是整套代码学习成本较高,使用过程中样板代码较多,(比如每一个Request都需要产生一个新类) 2.Volley,作为Google在IO大会上得瑟过的一个网络库,其实不算什么新东西(2013 IO发布),使用较为简单

  • Android Retrofit 中文乱码问题的解决办法

    Android Retrofit 中文乱码问题的解决办法 使用retrofit和rxjava,提交数据时需注意,当数据中有中文时,传到后台,可能会是乱码,需处理: 解决: 1.GET请求改成POST; 2.参数Field改成Query 3.加上@FormUrlEncoded 如下: @FormUrlEncoded @POST("/test/test") Call<Response> register(@Field("name") String name)

  • Android app开发中Retrofit框架的初步上手使用

    Retrofit 2.0 先来说一下Retrofit 2.0版本中一些引人注意的地方. 在Retrofit 2.0中,最大的改动莫过于减小库的体积,首先,Retrofit 2.0去掉了对所有的HTTP客户端的兼容,而钟情于OkHttpClient一个,极大地减少了各种适配代码,原因一会儿说;其次,拆库,比如将对RxJava的支持设置为可选(需要额外引入库):再比如将各个序列化反序列化转换器支持设置为可选(需要额外引入库).于2.0抛弃HttpClient和HttpURLConnection,为了

  • Android App开发的自动化测试框架UI Automator使用教程

    Android的自动化测试有很多框架,其中ui automator是google官方提供的黑盒UI相关的自动化测试工具,(GitHub主页:case使用java写,今天实践了一下官方文档中样例程序,其中还是有一些小问题需要总结一下的. 环境准备: 1.JDK(是的,你没看错,基础的android开发环境必备),以及对应的环境变量配置,不会的可以自己百度下下 2.Android Studio(IDE尊崇个人意愿) 3.android SDK以及配置 4.ANT(主要用于build我们的脚本,生成j

  • Android app开发中的Fragment入门学习教程

    在Android3.0上开始引入了一个新概念叫Fragment.它有自己的布局文件,可以作为组件排布,也可以相互组合去实现不同的布局显示.使用Fragment可以重复利用代码,并且可以满足不同设备尺寸的需求.Fragment不能单独存在,只能存在于Activity中,而一个Activity可以拥有多个Fragment.很重要的一点是,Fragment可以和Activity中的其它组件一起使用,无需重写所有Activity的接口.所以使用Fragment就可以这样来完成上例中"主界面-详细界面&q

  • Android App开发中ViewPager组件的入门使用教程

    首先让大家有个全局的认识,直接上个项目,看看仅仅通过这几行代码,竟然就能完成如此强悍的功能.下篇再仔细讲讲为什么要这么写. 效果图: 实现了三个view间的相互滑动 第一个VIEW向第二个VIEW滑动: 第二个VIEW向第三个VIEW滑动: 一.新建项目,引入ViewPager控件 ViewPager.它是google SDk中自带的一个附加包的一个类,可以用来实现屏幕间的切换. 1.在主布局文件里加入 <RelativeLayout xmlns:android="http://schem

  • Android App开发中使用RecyclerView实现Gallery画廊的实例

    什么是RecyclerView         RecyclerView是Android 5.0 materials design中的组件之一,相应的还有CardView.Palette等.看名字我们就能看出一点端倪,没错,它主要的特点就是复用.我们知道,Listview中的Adapter中可以实现ViewHolder的复用.RecyclerView提供了一个耦合度更低的方式来复用ViewHolder,并且可以轻松的实现ListView.GridView以及瀑布流的效果. RecyclerVie

  • 浅谈Android app开发中Fragment的Transaction操作

    在Android中,对Fragment的操作都是通过FragmentTransaction来执行.而从Fragment的结果来看,FragmentTransaction中对Fragment的操作大致可以分为两类: 显示:add() replace() show() attach() 隐藏:remove() hide() detach() 对于每一组方法,虽然最后产生的效果类似,但方法背后带来的副作用以及对Fragment的生命周期的影响都不尽相同. add() vs. replace() 只有在

  • 举例讲解Android应用开发中OTTO框架的基本使用

    OTTO是一个EventBus类型的事件传输总线,它可以提供"存储转发"的功能,让你APP中各个组件的交流更加便利,让你的程序分层更加清晰. 使用场景 OTTO基于Observer设计模式.它有发布者,订阅者这两个主要对象.OTTO的最佳实践就是通过反射牺牲了微小的性能,同时极大的提高了程序的耦合度,更加利于MVP分工开发与维护.业务层开发者在处理资源(比如Db, REST等)后并发布消息,展示层开发者(比如Activity/Fragment)就可以处理消息,而不用关心数据是怎么来的(

  • Android App开发中创建Fragment组件的教程

    你可以认为Fragment作为Activity的一个模块部分,有它自己的生命周期,获取它自己的事件,并且你可以在Activity运行的时候添加或者移除它(有点像你可以在不同的Activity中重用的一个"子Activity").这节课程讲述如何使用Support Library继承Fragment类,所以你的应用程序仍然是兼容运行的系统版本低于Android1.6的设备. 注意:如果你决定你的应用要求的最低的API级别是11或者更高,你不需要使用Support Library,反而能使

  • 深入解析Android App开发中Context的用法

    Context在开发Android应用的过程中扮演着非常重要的角色,比如启动一个Activity需要使用context.startActivity方法,将一个xml文件转换为一个View对象也需要使用Context对象,可以这么说,离开了这个类,Android开发寸步难行,对于这样一个类,我们又对他了解多少呢.我就说说我的感受吧,在刚开始学习Android开发时,感觉使用Context的地方一直就是传入一个Activity对象,久而久之感觉只要是Context的地方就传入一个Activity就行

  • Android App开发中Gradle构建过程的配置方法

    在build文件中使用了Android或者Java插件之后就会自动创建一系列可以运行的任务. Gradle中有如下一下默认约定的任务: 1. assemble 该任务包含了项目中的所有打包相关的任务,比如java项目中打的jar包,Android项目中打的apk 2. check 该任务包含了项目中所有验证相关的任务,比如运行测试的任务 3. build 该任务包含了assemble和check 4. clean 该任务会清空项目的所有的输出,删除所有在assemble任务中打的包 assemb

随机推荐