A type-safe HTTP client for Android and the JVM

Related tags

android java
Overview

Retrofit

A type-safe HTTP client for Android and Java.

For more information please see the website.

Download

Download the latest JAR or grab from Maven central at the coordinates com.squareup.retrofit2:retrofit:2.9.0.

Snapshots of the development version are available in Sonatype's snapshots repository.

Retrofit requires at minimum Java 8+ or Android API 21+.

R8 / ProGuard

If you are using R8 the shrinking and obfuscation rules are included automatically.

ProGuard users must manually add the options from retrofit2.pro. You might also need rules for OkHttp and Okio which are dependencies of this library.

License

Copyright 2013 Square, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Issues
  • Retrofit 2.0 API Spec (Working Draft)

    Retrofit 2.0 API Spec (Working Draft)

    This is a living spec for what will be v2 of Retrofit.

    Goals

    • Broaden the scope of how you can process successful responses and handle the results of various exceptions throughout the stack. Currently there's a mix of exceptions, wrapped types, and callbacks depending on the usage of synchronous or asynchronous API declarations.
    • Unify API declaration so that both synchronous and asynchronous consumption of a resource does not change its declaration.
    • Introduce better request and response interceptors which allow for both tweaks to existing requests or full on replacement (e.g., for signing purposes).
    • Allow simple request retry and cancel support without the need to talk to the RestAdapter or generated interface instance.
    • Completely decouple interface declaration parsing from request invocation and processing and expose the shim layer between the two as a extension-based system for customization. This will be used to (hopefully) facilitate RxJava integration as an extensions rather than being baked into the core of the library itself

    Changes

    Annotation Processors

    An annotation processor will be embedded inside the core artifact for compile-time verification of REST API interface declarations.

    A second annotation processor will be provided as a separate artifact for full code generation of a class implementation of each API interface.

    The RestAdapter will always do a read-through cached lookup for the generated classes since it has no knowledge of whether the code-gen processor was used and we don't want to place the burden on the caller either.

    Request Object

    All interface declarations will be required to return an object through which all interaction will occur. The behavior of this object will be similar to a Future and will be generic typed (T) for the success response type (ref: #231).

    @GET("/foo")
    Call<Foo> getFoo();
    

    Callers can synchronously call .execute() which will return type T. Exceptions will be thrown for any error, network error, unsuccessful response, or unsuccessful deserialization of the response body. While these exceptions will likely extend from the same supertype, it's unclear as to whether that supertype should be checked or unchecked.

    interface Api {
      @GET("/{user}/tweets")
      Call<List<Tweet>> listTweets(@Path("user") String username);
    }
    
    List<Tweet> tweets = api.listTweets("JakeWharton").execute();
    

    Callers can also supply callbacks to this object for asynchronous notification of the response. The traditional Callback<T> of the current version of Retrofit will be available. One change will be that the error object passed to failure will not be the same exception as would be thrown in synchronous execution but rather something a bit more transparent to the underlying cause.

    interface Api {
      @GET("/{user}/tweets")
      Call<List<Tweet>> listTweets(@Path("user") String username);
    }
    
    Call<List<Tweet>> c = api.listTweets("JakeWharton");
    c.execute(new Callback<List<Tweet>>() {
      @Override public void success(List<Tweet> response) { }
      // ...
    }
    

    TODO describe error handling

    public abstract class ResponseCallback<T> {
      public abstract void success(T response);
    
      public void networkError(IOException e) {
        error(ErrorType.NETWORK, -1, e);
      }
      public void processingError(Exception e) {
        error(ErrorType.PROCESSING, -1, e);
      }
      public void httpError(int status, ...) {
        error(ErrorType.HTTP, status, e);
      }
      public void error(ErrorType errorType, int status, Exception e) {
        throw new RuntimeException(.., e);
      }
    
      public enum ErrorType { NETWORK, PROCESSING, HTTP }
    }
    

    The call object is also an obvious place for handling the retry and cancelation of requests. Both are a simple, no-args method on the object which can only be called at appropriate times.

    • cancel() is a no-op after the response has been received. In all other cases the method will set any callbacks to null (thus freeing strong references to the enclosing class if declared anonymously) and render the request object dead. All future interactions with the request object will throw an exception. If the request is waiting in the executor its Future will be cancelled so that it is never invoked.
    • retry() will re-submit the request onto the backing executor without passing through any of the mutating pipeline described above. Retrying a request is only available after a network error or 5XX response. Attempting to retry a request that is currently in flight, after a non-5XX response, after an unexpected error, or after calling cancel() will throw an exception.

    Extension System

    In order to facilitate libraries which offer semantics on both synchronous and asynchronous operations without requiring a wrapper, we will introduce a system which we will tentatively refer to as extensions. An extension instance will be registered with the RestAdapter and associate itself as handling an explicit interface method return type.

    By default, Retrofit will install its own extension which handles the aforementioned Call type for both synchronous and asynchronous request execution.

    The current (albeit experimentally denoted) RxJava integration will also be provided as an opt-in extension. This extension will register itself as handling the Observable type which will allow the declaration of interfaces which return this type.

    interface FooService {
      @GET("/foos")
      Observable<Foo> getFoo();
    }
    

    And more...

    TODO!

    opened by JakeWharton 106
  • How to Upload a File using Retrofit 2.0

    How to Upload a File using Retrofit 2.0

    How to upload a file using multipart/form-data upload in Retrofit 2.0, I am unable to upload and there is no proper documentation anywhere on uploading file using retrofit 2.0, I followed the docs in retrofit website but I was not able to make it work.

    All the examples I see used TypedFile. It is not clear how to use RequestBody. Please post sample code.

    opened by vivekkiran 100
  • 1.4.0 New line in String causes retrofit.RetrofitError: java.io.EOFException

    1.4.0 New line in String causes retrofit.RetrofitError: java.io.EOFException

    Gist: https://gist.github.com/mazurio/8750846

    Description is a text taken from TextView (Android), if it contains more than one new line then it causes EOFException and insert fails. Trying to figure it out now.

    I tried to escape the string. It is the same problem.

    opened by mazurio 59
  • Allow to configure retrofit to allow API interfaces to extend interfaces

    Allow to configure retrofit to allow API interfaces to extend interfaces

    The comment:

    // Prevent API interfaces from extending other interfaces. This not only avoids a bug in
    // Android (http://b.android.com/58753) but it forces composition of API declarations which is
    // the recommended pattern.
    

    in:

    https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit/Utils.java

    Sounds rather arbitrary. I think it should be configurable at the very least.

    I for example have something very similar to the following.

    A base response class:

    class GenericResponse {
       Boolean success
    }
    

    and an interface that defines how my API needs to be accessed:

    interface GenericAPI<R extends GenericResponse> {
       void obtainData(
          String name, String version, Map<String, String> parameters, Callback<R> callback
       )
    }
    

    I latter extend that interface with some specific API like:

    interface GetUsersAPI extends GenericAPI<UsersResponse> {
       @GET("/dataAPI/{version}/{name}")
       void obtainData(
          @Path("name") String name,
          @Path("version") String version,
          @QueryMap Map<String, String> parameters,
          Callback<UsersResponse> callback
       )
    }
    

    being UsersResponse an extension of GenericResponse. With something like that, I can implement an abstract client like:

    abstract class GenericClient<R extends GenericResponse, C extends GenericConnector> implements Callback<R> { 
    
       protected void doRequest(
          Class<C> apiDefinition, 
          String name, String version, 
          Map<String, Object> params
       ) { 
          new RestAdapter.Builder().build().create(apiDefinition).obtainData(
             name, version, params, this
          )
       }
    }
    

    That client can work with any of my APIs, avoiding code duplication while having typing enforced by the compiler using generics. It can deal with any obtainData connector and with the common parts of every response. When implemented by a concrete class, thanks to the use of generics and inheritance, the data is transformed to the proper specific response class and when using an IDE with generics support (any, I guess :) is very convenient to use every method with the signature automatically transformed to the concrete classes declared by the implementor class.

    I understand that composition is better for many things, but I don't see how to work with a generic connector the way I'm doing it, being able to assume that every connector will have an obtainData with the given parameters and having that enforced by the compiler.

    I'm not saying is not possible - I'm saying I don't know how, and the restriction I'm finding is, in my opinion, quite poorly explained.

    I think something whose reason to be is something as arguable as "composition is the only possible, good way" should be configurable at least.

    Enhancement 
    opened by deigote 40
  • Handle Empty Body

    Handle Empty Body

    After updating to retrofit beta-3 I'm getting the Exception (Because of an empty body)

    java.io.EOFException: End of input at line 1 column 1
                                                                               at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1414)
                                                                               at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:553)
                                                                               at com.google.gson.stream.JsonReader.peek(JsonReader.java:429)
                                                                               at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:202)
                                                                               at com.google.gson.TypeAdapter.fromJson(TypeAdapter.java:260)
                                                                               at retrofit2.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:33)
                                                                               at retrofit2.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:23)
                                                                               at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:154)
                                                                               at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:92)
                                                                               at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133)
    

    I know that It's possible to solve this issue Using Call<Void> but is there any other way to enforce OkHttp or Gson To accept Empty body?

    Response Log:

     D/OkHttp: Date: Mon, 01 Feb 2016 08:32:10 GMT
    D/OkHttp: Server: Apache/2.4.7 (Ubuntu)
    D/OkHttp: X-Powered-By: PHP/5.5.9-1ubuntu4.13
    D/OkHttp: Expires: Thu, 19 Nov 1981 08:52:00 GMT
    D/OkHttp: Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    D/OkHttp: Pragma: no-cache
    D/OkHttp: Access-Control-Allow-Origin: https://example.com
    D/OkHttp: Access-Control-Allow-Methods: GET,POST,OPTIONS
    D/OkHttp: Access-Control-Allow-Headers: Accept,Cache-Control,Pragma,Origin,Authorization,Content-Type,X-Requested-With,Cookie,*
    D/OkHttp: Access-Control-Allow-Credentials: true
    D/OkHttp: Content-Length: 0
    D/OkHttp: Keep-Alive: timeout=5, max=99
    D/OkHttp: Connection: Keep-Alive
    D/OkHttp: Content-Type: application/json
    D/OkHttp: OkHttp-Sent-Millis: 1454315528548
    D/OkHttp: OkHttp-Received-Millis: 1454315528725
    D/OkHttp: <-- END HTTP (0-byte body)
    
    opened by roybrener 38
  • RxJava Retrofit --Fatal Exception thrown on Scheduler.Worker thread.

    RxJava Retrofit --Fatal Exception thrown on Scheduler.Worker thread.

    ApiManager.getApi().dispatchService(SPUtils.getUserId(), groupType) .observeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new ResultSubscriber() { @Override public void onSuccess(ServiceResultBean serviceResultBean) {

                        }
    
                        @Override
                        public void onError(ResultException error) {
                            name.setText(error.getMessage());
                        }
    
                        @Override
                        public void onCompleted() {
                            loading.setVisibility(View.GONE);
                        }
                    });
    

    then... E/AndroidRuntime﹕ FATAL EXCEPTION: main java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread. at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62) at android.os.Handler.handleCallback(Handler.java:615) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4745) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) at dalvik.system.NativeStart.main(Native Method) Caused by: rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:201) at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:111) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:197) at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:170) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)             at android.os.Handler.handleCallback(Handler.java:615)             at android.os.Handler.dispatchMessage(Handler.java:92)             at android.os.Looper.loop(Looper.java:137)             at android.app.ActivityThread.main(ActivityThread.java:4745)             at java.lang.reflect.Method.invokeNative(Native Method)             at java.lang.reflect.Method.invoke(Method.java:511)             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)             at dalvik.system.NativeStart.main(Native Method) Caused by: rx.exceptions.CompositeException: 2 exceptions occurred.             at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:201)             at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:111)             at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:197)             at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:170)             at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)             at android.os.Handler.handleCallback(Handler.java:615)             at android.os.Handler.dispatchMessage(Handler.java:92)             at android.os.Looper.loop(Looper.java:137)             at android.app.ActivityThread.main(ActivityThread.java:4745)             at java.lang.reflect.Method.invokeNative(Native Method)             at java.lang.reflect.Method.invoke(Method.java:511)             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)             at dalvik.system.NativeStart.main(Native Method) Caused by: rx.exceptions.CompositeException$CompositeExceptionCausalChain: Chain of Causes for CompositeException In Order Received => at android.util.Log.getStackTraceString(Log.java:314) at android.util.Slog.e(Slog.java:77) at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:72) at u.aly.n.uncaughtException(CrashHandler.java:34) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:66)             at android.os.Handler.handleCallback(Handler.java:615)             at android.os.Handler.dispatchMessage(Handler.java:92)             at android.os.Looper.loop(Looper.java:137)             at android.app.ActivityThread.main(ActivityThread.java:4745)             at java.lang.reflect.Method.invokeNative(Native Method)             at java.lang.reflect.Method.invoke(Method.java:511)             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)             at dalvik.system.NativeStart.main(Native Method) Caused by: retrofit.RetrofitError at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:395) at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220) at retrofit.RestAdapter$RestHandler$1.invoke(RestAdapter.java:265) at retrofit.RxSupport$2.run(RxSupport.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:442) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) at java.util.concurrent.FutureTask.run(FutureTask.java:137) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) at retrofit.Platform$Android$2$1.run(Platform.java:142) at java.lang.Thread.run(Thread.java:856) Caused by: java.net.SocketTimeoutException at java.net.PlainSocketImpl.read(PlainSocketImpl.java:491) at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46) at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240) at okio.Okio$2.read(Okio.java:137) at okio.AsyncTimeout$2.read(AsyncTimeout.java:211) at okio.RealBufferedSource.indexOf(RealBufferedSource.java:306) at okio.RealBufferedSource.indexOf(RealBufferedSource.java:300) at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:196) at com.squareup.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:191) at com.squareup.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80) at com.squareup.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:917) at com.squareup.okhttp.internal.http.HttpEngine.access$300(HttpEngine.java:95) at com.squareup.okhttp.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:902) at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:760) at com.squareup.okhttp.Call.getResponse(Call.java:274) at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:230) at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:201) at com.squareup.okhttp.Call.execute(Call.java:81) at retrofit.client.OkClient.exec

    opened by cuimingqiang 37
  • method POST must have a request body.

    method POST must have a request body.

    Why?

    compile 'com.squareup.okhttp:okhttp:2.3.0'
    compile 'com.squareup.retrofit:retrofit:1.9.0'
    
                         D  java.lang.IllegalArgumentException: method POST must have a request body.
                            D      at com.squareup.okhttp.Request$Builder.method(Request.java:236)
                            D      at retrofit.client.OkClient.createRequest(OkClient.java:59)
                            D      at retrofit.client.OkClient.execute(OkClient.java:53)
                            D      at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:326)
                            D      at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
                            D      at retrofit.RestAdapter$RestHandler$2.obtainResponse(RestAdapter.java:278)
                            D      at retrofit.CallbackRunnable.run(CallbackRunnable.java:42)
                            D      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
                            D      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
                            D      at retrofit.Platform$Android$2$1.run(Platform.java:142)
                            D      at java.lang.Thread.run(Thread.java:818)
    
    opened by crossle 37
  • Add ErrorHandler for customization of RetrofitError behavior

    Add ErrorHandler for customization of RetrofitError behavior

    Add a new interface, ErrorHandler, which is responsible for throwing customized exceptions during synchronous request errors, or directing to the appropriate callback method for asynchronous request errors.

    Currently, API users must catch RetrofitError and try to determine the cause of the error based on certain properties of the HTTP response. This interface allows API clients to expose semantic exceptions to their users.

    For example, if I had an API interface method to create a User, I might expose an InvalidUserException which could be declared on the createUser method, and used to indicate that something was invalid about the request.

    This implementation allows exceptions to be checked or unchecked, leaving that decision up to the API designer. It also doesn't change any existing behavior. Let me know if there's anything I can do to clean it up!

    opened by sberan 37
  • 2.0-beta2 adding extra quotes to multipart string form values

    2.0-beta2 adding extra quotes to multipart string form values

    retrofit 2.0 seems to be double quoting strings in multi part post requsts.

    for example this interface

    public interface ApiInterface {
    
        @Multipart
        @POST("user/login/")
        Call<SessionToken> userLogin(@Part("username") String username, @Part("password") String password);
    }
    

    server side will print the key value pairs as

    username : "brian"
    password : "password"
    

    instead of

    username : brian
    password : password
    

    The second way is how it would show up in retrofit 1.9 and any other rest client.

    stack link https://stackoverflow.com/questions/33205855/retrofit-2-0-beta-2-is-adding-literal-quotes-to-multipart-values?noredirect=1#comment54246402_33205855

    opened by bperin 37
  • Add a way to set filename on multipart requests

    Add a way to set filename on multipart requests

    This has already been discussed in #1063 #1092 #1096 and #1130, but I thought I'd open a separate issue to track this specifically.

    RFC2388 describes the filename header parameter of multipart/form-data requests that Retrofit now uses. This parameter is required on some backend configurations. More details and examples can be found here.

    We need a way of figuring out what the filename is. The problem with RequestBody (which is a part of OkHttp) is that it doesn't expose the filename when created with a File.

    Feature 
    opened by lukaciko 37
  • How does multipart handle large byte arrays?

    How does multipart handle large byte arrays?

    From my app I'm trying to upload a chunk of data. The app generates a byte array of the data to be uploaded and passes it to the Retrofit request. I've noticed that when I do so the data seems to be copied in memory resulting in twice the memory consumption. A 30 MB data blob turns into 60 mb in memory once it's passed to the POST request with retrofit.

    I then tried to migrate to a @Multipart request but that didnt seem to make any difference.

    Could someone enlighten me on the best way to do this? Do multipart requests make any sense in this scenario?

    opened by ardevd 0
  • Interceptor occasionally not returning callbacks of RxJava

    Interceptor occasionally not returning callbacks of RxJava

    I am having trouble with Interceptor as it does not call onError nor onSuccess on specific scenario

    This is my interface class

    interface EndpointServices {
    
        companion object {
    
    private fun interceptor(): Interceptor {
                return Interceptor { chain ->
                    val request: Request = chain.request()
                    val originalResponse: Response = chain.proceed(request)
                    val cacheControlStatus: String? = originalResponse.header("Cache-Control")
    
                    Log.wtf("INTERCEPT",
                        "ORIGINAL : CACHE-CONTROL: $cacheControlStatus")
    
                    Log.wtf("INTERCEPT",
                        "OVERWRITE CACHE-CONTROL: ${request.cacheControl} | CACHEABLE? ${
                            CacheStrategy.isCacheable(originalResponse,
                                request)
                        }")
    
                    originalResponse.newBuilder()
                        .build()
    
                }
            }
    
    
            private fun onlineOfflineHandling(): Interceptor {
                return Interceptor { chain ->
                    try {
                        Log.wtf("INTERCEPT", "FETCH ONLINE")
                        val cacheControl = CacheControl.Builder()
                            .maxAge(5, TimeUnit.SECONDS)
                            .build()
    
                        val response = chain.proceed(chain.request().newBuilder()
                            .removeHeader("Pragma")
                            .removeHeader("Cache-Control")
                            .header("Cache-Control", "public, $cacheControl")
                            .build())
    
                        Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}")
    
                        response
                    } catch (e: IOException) {
                        Log.wtf("INTERCEPT", "FALLBACK TO CACHE ${e.message}")
    
                        val cacheControl: CacheControl = CacheControl.Builder()
                            .maxStale(30, TimeUnit.DAYS)
                            .onlyIfCached() // Use Cache if available
                            .build()
    
                        val offlineRequest: Request = chain.request().newBuilder()
                            .cacheControl(cacheControl)
                            .build()
    
                        val response = chain.proceed(offlineRequest)
    
                        Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}")
    
                        response
                    }
                }
            }
    
    
            fun create(baseUrl: String, cacheDir: File): EndpointServices {
    
                // Inexact 150 MB of maximum cache size for a total of 4000 assets where 1MB/30 assets
                // The remaining available space will be use for other cacheable requests
                val cacheSize: Long = 150 * 1024 * 1024
    
                val cache = Cache(cacheDir, cacheSize)
    
                Log.wtf("CACHE DIRECTORY", cache.directory.absolutePath)
    
                for (cacheUrl in cache.urls())
                    Log.wtf("CACHE URLS", cacheUrl)
    
                Log.wtf("CACHE OCCUPIED/TOTAL SIZE", "${cache.size()}/${cache.maxSize()}")
    
                /*val interceptor = HttpLoggingInterceptor()
                interceptor.level = HttpLoggingInterceptor.Level.BODY*/
    
                val httpClient = OkHttpClient.Builder()
                    .cache(cache)
                    /*.addInterceptor(interceptor)*/
                    .callTimeout(10, TimeUnit.SECONDS)
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .addNetworkInterceptor(interceptor())
                    .addInterceptor(onlineOfflineHandling())
                    .build()
    
                val retrofit = Retrofit.Builder()
                    .addCallAdapterFactory(
                        RxJava2CallAdapterFactory.create()
                    )
                    .addConverterFactory(
                        MoshiConverterFactory.create()
                    )
                    .client(httpClient)
                    .baseUrl(baseUrl)
                    .build()
    
                return retrofit.create(EndpointServices::class.java)
    
            }
    
    }
    
    @GET("{fullPath}")
        fun getExchangeItems(
            @Path("fullPath", encoded = true) fullPath: String,
            @Query("fields") fields: String
        ):
                Single<ExchangeItemModel>
    
    }
    
    

    Fetching it with this

    private val RetroService by lazy {
            EndpointServices.create(MySingleton.assetLink, application.cacheDir)
        }
    
    RetroService.getExchangeItems(
                MySingleton.exchange.replace("{ID}", assetName.trim().replace(" ", "-")),
                MySingleton.exchangeField
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    { result ->
    
                        // Filter the item to display
                        includedAsset.value =
                            result.data.filter { it.exchangeName != null && !it.excludedFromPrice}
    
                        excludedAsset.value =
                            result.data.filter { it.exchangeName != null && it.excludedFromPrice}
    
                    },
                    { error ->
                        Log.wtf("WTF", "${error.message}")
                        FirebaseCrashlytics.getInstance().recordException(error)
                        // Only change the UI with this conditions
                        if ((includedAsset.value!!.isEmpty() && excludedAsset.value!!.isEmpty()) || (error is HttpException && error.code() == HttpURLConnection.HTTP_GATEWAY_TIMEOUT))
                            errorMessage.value = R.string.swipe_to_refresh
                    }
                ))
    

    Case 1: No network (Wifi/Mobile Data is OFF)

    • Interceptor : A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "some.host.com": No address associated with hostname

    • onError (error ->) is called if no cache

    • onSuccess (result ->) is called if cache is available

    All Good! We show error UI to user during offline mode if no cache exist else we show the list if cache is at least available.

    ========================================================================

    Case 2: Connected to network (Wifi/Mobile Data is ON and the network has INTERNET SERVICE)

    • onError (error ->) is called when response was failed such as 404, etc.

    • onSuccess (result ->) is called when response was success

    All Good! BUT due to unknown circumstances sometimes I got A/INTERCEPT: FALLBACK TO CACHE CANCELED from the Interceptor and when this happen I don't receive any callbacks neither onError nor onSuccess thus the UI for loading never ends.

    ========================================================================

    Case 3: Connected to network (Wifi/Mobile Data is ON but the network has NO INTERNET SERVICE)

    • Interceptor : A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "some.host.com": No address associated with hostname

    • onError (error ->) is not called

    • onSuccess (result ->) is not called

    As you can see, my Interceptor log here is just the same in our first case yet no callback has been return even a cache is available thus the UI for loading never ends again.

    dependencies

    implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
        implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
        implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
    
        implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
    
        implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
        // Because RxAndroid releases are few and far between, it is recommended you also
        // explicitly depend on RxJava's latest version for bug fixes and new features.
        // (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version)
        implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
    
    opened by ArcherEmiya05 0
  • Read kotlin metadata to resolve nullability of suspending functions

    Read kotlin metadata to resolve nullability of suspending functions

    Attempt at https://github.com/square/retrofit/issues/3075. Any suggestions to improve are welcome.

    opened by ghus-raba 7
  • Blocking UI thread on first initialization

    Blocking UI thread on first initialization

    I am getting a lot of hiccups Choreographer: Skipped 59 frames! The application may be doing too much work on its main thread. during first initialization of Retrofit which is noticeable when switching between fragments using navigation drawer. It will be fixed if I add delay on making request but I want to know if there is any neat solution on this? I tried to use Kotshi as it was stated on the other issue that it performs well than Moshi but no luck.

    Home Fragment extending Parent Fragment

    disposable = initRetrofit.getAssetItems(
                Singleton.assetField,
                limit
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    { result ->
    
                        swipeRefreshLayout.isRefreshing = false
    
                        swipeRefreshLayout.isEnabled = false // Swipe gesture no longer needed
    
                        adapter.submitList(result.data)
    
                        logTxt.text = null
    
                    },
                    { error ->
                        Log.wtf("WTF", "${error.message}")
                        swipeRefreshLayout.isRefreshing = false
                        if (adapter.currentList.isEmpty() || (error is HttpException && error.code() == HttpURLConnection.HTTP_GATEWAY_TIMEOUT)){
                            adapter.submitList(mutableListOf()) // Pass an empty list if no cache
                            logTxt.setText(R.string.swipe_to_refresh)
                        }
                    }
                )
    

    Parent Fragment

    protected val initRetrofit by lazy {
            EndpointServices.create(url, requireContext())
        }
    

    EndpointServices class with companion object function create(string,context)

    val httpClient = OkHttpClient.Builder()
                    .cache(cache)
                    .addInterceptor(interceptor)
                    .callTimeout(10, TimeUnit.SECONDS)
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .addNetworkInterceptor(interceptor())
                    .addInterceptor(onlineOfflineHandling())
                    .build()
    
                val retrofit = Retrofit.Builder()
                    .addCallAdapterFactory(
                        RxJava2CallAdapterFactory.create()
                    )
                    .addConverterFactory(
                        MoshiConverterFactory.create()
                    )
                    .client(httpClient)
                    .baseUrl(baseUrl)
                    .build()
    
    opened by ArcherEmiya05 2
  • Retrofit2 + OkHttp with RxJava: onNext() callback not being triggered when connected to a network that has no internet

    Retrofit2 + OkHttp with RxJava: onNext() callback not being triggered when connected to a network that has no internet

    Whenever there is an error like network related errors, we will use the last cached data from the client instead. It works but only when not connected to any network. If a client is connected to any network where internet service is not really available the callbacks is no longer working.

    So far I do not know which side this error came from base on this 3 libraries.

    Here we are using YouTube API as a sample.

    interface EndpointServices {
    
        companion object {
    
            private fun interceptor(): Interceptor {
                return Interceptor { chain ->
                    val request: Request = chain.request()
                    val originalResponse: Response = chain.proceed(request)
                    val cacheControlStatus: String? = originalResponse.header("Cache-Control")
                    if (cacheControlStatus == null || cacheControlStatus.contains("no-store") || cacheControlStatus.contains(
                            "no-cache") ||
                        cacheControlStatus.contains("must-revalidate") || cacheControlStatus.contains("max-stale=0")
                    ) {
    
                        Log.wtf("INTERCEPT", "ORIGINAL CACHE-CONTROL: $cacheControlStatus")
    
                    } else {
    
                        Log.wtf("INTERCEPT",
                            "ORIGINAL : CACHE-CONTROL: $cacheControlStatus")
    
                    }
    
                    Log.wtf("INTERCEPT",
                        "OVERWRITE CACHE-CONTROL: ${request.cacheControl} | CACHEABLE? ${
                            CacheStrategy.isCacheable(originalResponse,
                                request)
                        }")
    
                    originalResponse.newBuilder()
                        .build()
    
                }
            }
    
    
            private fun onlineOfflineHandling(): Interceptor {
            return Interceptor { chain ->
                try {
                    Log.wtf("INTERCEPT", "FETCH ONLINE")
                    val cacheControl = CacheControl.Builder()
                        .maxAge(5, TimeUnit.SECONDS)
                        .build()
    
                    val response = chain.proceed(chain.request().newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", "public, $cacheControl")
                        .build())
    
                    Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}")
    
                    response
                } catch (e: Exception) {
                    Log.wtf("INTERCEPT", "FALLBACK TO CACHE ${e.message}")
    
                    val cacheControl: CacheControl = CacheControl.Builder()
                        .maxStale(1, TimeUnit.DAYS)
                        .onlyIfCached() // Use Cache if available
                        .build()
    
                    val offlineRequest: Request = chain.request().newBuilder()
                        .cacheControl(cacheControl)
                        .build()
    
                    val response = chain.proceed(offlineRequest)
    
                    Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}")
    
                    response
                }
            }
        }
    
    
            fun create(baseUrl: String, context: Context): EndpointServices {
    
                // Inexact 150 MB of maximum cache size for a total of 4000 assets where about 1MB/30 assets
                // The remaining available space will be use for other cacheable requests
                val cacheSize: Long = 150 * 1024 * 1024
    
                val cache = Cache(context.cacheDir, cacheSize)
    
                Log.wtf("CACHE DIRECTORY", cache.directory.absolutePath)
    
                for (cacheUrl in cache.urls())
                    Log.wtf("CACHE URLS", cacheUrl)
    
                Log.wtf("CACHE OCCUPIED/TOTAL SIZE", "${cache.size()} ${cache.maxSize()}")
    
                val interceptor = HttpLoggingInterceptor()
                interceptor.level = HttpLoggingInterceptor.Level.BODY
    
                val httpClient = OkHttpClient.Builder()
                    .cache(cache)
                    .addInterceptor(interceptor)
                    .callTimeout(10, TimeUnit.SECONDS)
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .addNetworkInterceptor(interceptor())
                    .addInterceptor(onlineOfflineHandling())
                    .build()
    
                val retrofit = Retrofit.Builder()
                    .addCallAdapterFactory(
                        RxJava2CallAdapterFactory.create()
                    )
                    .addConverterFactory(
                        MoshiConverterFactory.create()
                    )
                    .client(httpClient)
                    .baseUrl(baseUrl)
                    .build()
    
                return retrofit.create(EndpointServices::class.java)
    
            }
    
        }
    
        @GET("search")
        fun getVideoItems(
            @Query("key") key: String,
            @Query("part") part: String,
            @Query("maxResults") maxResults: String,
            @Query("order") order: String,
            @Query("type") type: String,
            @Query("channelId") channelId: String,
        ):
                Single<VideoItemModel>
    
    
    }
    

    MainActivity

    EndpointServices.create(url, requireContext()).getVideoItems(
                AppUtils.videoKey,
                "id,snippet",
                "20",
                "date",
                "video",
                channelId
            )
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    { result ->
    
                        Log.wtf("RESPONSE", result.toString())
                        adapter.submitList(result.videoData)
    
                        swipeRefreshLayout.isRefreshing = false
    
                        logTxt.text = null
    
                    },
                    { error ->
                        Log.wtf("WTF", "${error.message}")
                        swipeRefreshLayout.isRefreshing = false
                        if (adapter.currentList.isEmpty() || (error is HttpException && error.code() == HttpURLConnection.HTTP_GATEWAY_TIMEOUT)){
                            adapter.submitList(mutableListOf())
                            logTxt.text = getString(R.string.swipeToRefresh)
                        }
                    }
                )
    

    FLOW BASED ON LOGS

    WHEN ONLINE

    A/CACHE DIRECTORY: /data/data/com.appname.app/cache
    A/CACHE URLS: www.api.com
    A/CACHE OCCUPIED/TOTAL SIZE: 228982 157286400
    A/INTERCEPT: FETCH ONLINE
    A/INTERCEPT: ORIGINAL : CACHE-CONTROL: private
    A/INTERCEPT: OVERWRITE CACHE-CONTROL: public, max-age=5 | CACHEABLE? true
    A/INTERCEPT: CACHE Response{protocol=http/1.1, code=200, message=, url=https://api.com} NETWORK Response{protocol=h2, code=304, message=, url=https://api.com}
    A/RESPONSE: VideoItemModel(.....) WORKING!
    

    COMPLETELY OFFLINE (Wi-Fi/Mobile Data OFF)

    A/CACHE DIRECTORY: /data/data/com.appname.app/cache
    A/CACHE URLS: www.api.com
    A/CACHE OCCUPIED/TOTAL SIZE: 228982 157286400
    A/INTERCEPT: FETCH ONLINE
    A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "api.com": No address associated with hostname
    A/INTERCEPT: CACHE Response{protocol=http/1.1, code=200, message=, url=https://api.com} NETWORK null
    A/RESPONSE: VideoItemModel(.....) WORKING!
    

    JUST CONNECTED TO A NETWORK BUT REALLY NO INTERNET SERVICE (Wi-Fi/Mobile Data ON)

    A/CACHE DIRECTORY: /data/data/com.appname.app/cache
    A/CACHE URLS: www.api.com
    A/CACHE OCCUPIED/TOTAL SIZE: 228982 157286400
    A/INTERCEPT: FETCH ONLINE
    A/INTERCEPT: FALLBACK TO CACHE Unable to resolve host "api.com": No address associated with hostname
    ???WHERE IS THE CALLBACK JUST LIKE THE PREVIOUS ONE???
    

    Also worth mentioning that neither of the line Log.wtf("INTERCEPT", "CACHE ${response.cacheResponse} NETWORK ${response.networkResponse}") is being called on this last scenario.

    Dependencies

        implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
        implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
        implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
    
        implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
    
        implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    
    opened by ArcherEmiya05 1
  • Update dependencies

    Update dependencies

    opened by Goooler 1
  • Service interface extending doesn't work with Proguard/R8

    Service interface extending doesn't work with Proguard/R8

    Hi, since latest versions now support service interface subclassing, as to facilitate versioning, I did just so

    interface ConfigApi {
        suspend fun configuration(): Response<ConfigurationResponse>
    }
    
    interface V1ConfigAp : ConfigApi {
        @GET("api/v1/config")
        override suspend fun configuration(): Response<ConfigurationResponse>
    }
    
    interface V2ConfigAp : ConfigApi {
        @GET("api/v2/config")
        override suspend fun configuration(): Response<ConfigurationResponse>
    }
    

    I'm on android. If run in debug, everything works. If run with R8 turned on, at runtime I get

    2021-03-26 18:10:06.503 22986-23211/foo.bar W/Default: HTTP method annotation is required (e.g., @GET, @POST, etc.).
            for method b.a
        java.lang.IllegalArgumentException: HTTP method annotation is required (e.g., @GET, @POST, etc.).
            for method b.a
    
    

    If I explicitly keep the class -keep class foo.bar.V2ConfigApi { *; } it works

    It appears R8 ignores the "implementing" interface V2ConfigApi (Its only reference is in a dagger module provider method, and nowhere else), and just uses the base one, which obviously doesn't have the annotations.

    I can live with the keep rule for now, but it's footgunny, since almost nothing requires app level keep rules now Is there a general rule to be extracted? (and then possibly merged into the library)

    Android AGP 4.1.2 Retrofit 2.9.0

    opened by ursusursus 1
  • About signal 11

    About signal 11

    first of all , it is a problem that can be reproduced when the phone is in a situation. i can't find out what is the situation. so , i put the information here, wonder u can help, thanks.


    • install debug apk,

    when the interface define like this

    interface Service{
        @GET("path/{path1}/{path2}/path")
        fun request(
                @Path("path1") path1: String,
                @Path("path2") path2: String,
                @QueryMap map: Map<String, String>
        ): Call<ResponseBody>
    }
    

    it failed when create a instance of call :

    val service = Retrofit.Builder()
                               .baseUrl()
                                ...
                              .build().create(Service::class.java)
    val call = service.request(...) //this make the app crash 
    

    and the back stack is

    03-22 18:27:47.770 325-325/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8
    03-22 18:27:47.890 325-325/? A/DEBUG:     r0 9b7eb808  r1 0000004c  r2 0000000e  r3 14182e50
    03-22 18:27:47.890 325-325/? A/DEBUG:     r4 00000003  r5 bed97f50  r6 00000003  r7 bed97f88
    03-22 18:27:47.890 325-325/? A/DEBUG:     r8 00000002  r9 00000003  sl bed982d0  fp 00000008
    03-22 18:27:47.890 325-325/? A/DEBUG:     ip 720b8aeb  sp bed97f40  lr bed98018  pc b412c512  cpsr 00070030
    03-22 18:27:47.980 325-325/? A/DEBUG: backtrace:
    03-22 18:27:47.980 325-325/? A/DEBUG:     #00 pc 001fb512  /system/lib/libart.so (_ZN3art11interpreter6DoCallILb0ELb1EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+509)
    

    but when i just simple the GET path, like this:

    interface Service{
        // change is : path2 is not a variable, just literal
        @GET("path/{path1}/path2/path")
        fun request(
                @Path("path1") path1: String,
                @QueryMap map: Map<String, String>
        ): Call<ResponseBody>
    }
    

    it won't be crashed anymore

    • install release apk another interface makes a similar crash the back stack like this:
    03-02 00:37:11.380 F/DEBUG   (  325): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x327b9944
    03-02 00:37:11.480 F/DEBUG   (  325):     r0 6f679784  r1 34d0b260  r2 0069a164  r3 b0c50070
    03-02 00:37:11.480 F/DEBUG   (  325):     r4 0069a164  r5 34d0b260  r6 6f678dd0  r7 6fec1cc8
    03-02 00:37:11.480 F/DEBUG   (  325):     r8 35eec5e0  r9 8a7b0100  sl 7018afd0  fp 8995852c
    03-02 00:37:11.480 F/DEBUG   (  325):     ip b0c50070  sp 899584b0  lr 734a872d  pc b3f5b488  cpsr 000d0030
    03-02 00:37:11.500 F/DEBUG   (  325): 
    03-02 00:37:11.500 F/DEBUG   (  325): backtrace:
    03-02 00:37:11.500 F/DEBUG   (  325):     #00 pc 000ea488  /system/lib/libart.so (art_quick_imt_conflict_trampoline+7)
    

    and also is crashed when creating a instance of call

    this interface has one @path , serveral @Query params. as the debug scene above, i try to just use okhttp with literal url, it's ok

    but before i finger out more infos, my teammate just restore the phone to factory setting ... and this crash is fixed


    now i can offer some infos when the crash can reproduced

    lib version : retrofit 2.4.0 device name: Galaxy J2 Prime / SM-G532G

    other maybe useful info :
    disk: total 8g / used 7.86 ( when install apk ,will 8g be fully used memory : total 1g / used nearly half

    Needs Info 
    opened by TpOut 3
  • Retrofit JAXB Converter does not work with Jakarta JAXB

    Retrofit JAXB Converter does not work with Jakarta JAXB

    Not sure if it is a bug report or feature request. In my recent project I have both old java.xml.bind and jakarta.xml.bind implementations are on classpath. My classes are annotated with jakarta.xml.bind.*. I am getting following error

    java.lang.IllegalArgumentException: Unable to create @Body converter for class com.sil.upi.jaxb.RespListAccount (parameter #3)
       for method UPIService.sendRespListAccount
           at retrofit2.Utils.methodError(Utils.java:54)
           at retrofit2.Utils.parameterError(Utils.java:60)
           at retrofit2.RequestFactory$Builder.parseParameterAnnotation(RequestFactory.java:781)
           at retrofit2.RequestFactory$Builder.parseParameter(RequestFactory.java:325)
           at retrofit2.RequestFactory$Builder.build(RequestFactory.java:206)
           at retrofit2.RequestFactory.parseAnnotations(RequestFactory.java:67)
           at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:26)
           at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202)
           at retrofit2.Retrofit$1.invoke(Retrofit.java:160)
           at com.sun.proxy.$Proxy78.sendRespListAccount(Unknown Source)
           at com.sil.upi.application.RetroClient.sendRespListAccount(RetroClient.java:101)
           at com.sil.upi.services.meta.ListAccount.run(ListAccount.java:110)
           at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
           at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
           at java.base/java.lang.Thread.run(Thread.java:832)
    Caused by: java.lang.IllegalArgumentException: Could not locate RequestBody converter for class com.sil.upi.jaxb.RespListAccount.
     Tried:
      * retrofit2.BuiltInConverters
      * retrofit2.converter.jaxb.JaxbConverterFactory
      * retrofit2.OptionalConverterFactory
           at retrofit2.Retrofit.nextRequestBodyConverter(Retrofit.java:335)
           at retrofit2.Retrofit.requestBodyConverter(Retrofit.java:293)
           at retrofit2.RequestFactory$Builder.parseParameterAnnotation(RequestFactory.java:778)
           ... 12 more
    

    I can understand retrofit jaxb converter is compiled with java.xml.bind so I am getting error on deserialization. Is there a possibility that future converter release will work for both implementations?

    Enhancement PR welcome 
    opened by KrishnaST 1
Releases(parent-2.0.0-beta3)
  • parent-2.0.0-beta3(Jan 5, 2016)

  • parent-2.0.0-beta2(Sep 28, 2015)

    • New: Using a response type of Void (e.g., Call<Void>) will ignore and discard the response body. This can be used when there will be no response body (such as in a 201 response) or whenever the body is not needed. @Head requests are now forced to use this as their response type.
    • New: validateEagerly() method on Retrofit.Builder will verify the correctness of all service methods on calls to create() instead of lazily validating on first use.
    • New: Converter is now parameterized over both 'from' and 'to' types with a single convert method. Converter.Factory is now an abstract class and has factory methods for both request body and response body.
    • New: Converter.Factory and CallAdapter.Factory now receive the method annotations when being created for a return/response type and the parameter annotations when being created for a parameter type.
    • New: callAdapter() method on Retrofit allows querying a CallAdapter for a given type. The nextCallAdapter() method allows delegating to another CallAdapter from within a CallAdapter.Factory. This is useful for composing call adapters to incrementally build up behavior.
    • New: requestConverter() and responseConverter() methods on Retrofit allow querying a Converter for a given type.
    • New: onResponse method in Callback now receives the Retrofit instance. Combined with the responseConverter() method on Retrofit, this provides a way of deserializing an error body on Response. See the DeserializeErrorBody sample for an example.
    • New: The MoshiConverterFactory has been updated for its v1.0.0.
    • Fix: Using ResponseBody for the response type or RequestBody for a parameter type is now correctly identified. Previously these types would erroneously be passed to the supplied converter.
    • Fix: The encoding of @Path values has been corrected to conform to OkHttp's HttpUrl.
    • Fix: Use form-data content disposition subtype for @Multipart.
    • Fix: Observable and Single-based execution of requests now behave synchronously (and thus requires subscribeOn() for running in the background).
    • Fix: Correct GsonConverterFactory to honor the configuration of the Gson instances (such as not serializing null values, the default).
    Source code(tar.gz)
    Source code(zip)
  • parent-1.6.0(Jun 20, 2014)

    • New: @Streaming on a Response type will skip buffering the body to a byte[] before delivering.
    • When using OkHttp, version 1.6.0 or newer (including 2.0.0+) is now required.
    • The absence of a response body and an empty body are now differentiated in the log messages.
    • Fix: If set, the RequestInterceptor is now applied at the time of Observable subscription rather than at the time of its creation.
    • Fix: Callback subtypes are now supported when using MockRestAdapter.
    • Fix: RetrofitError now contains a useful message indicating the reason for the failure.
    • Fix: Exceptions thrown when parsing the response type of the interface are now properly propagated.
    • Fix: Calling Response#getBody when null body now correctly returns instead of throwing an NPE.
    • Experimental RxJava support updated for v0.19.
    • The Content-Type and Content-Length headers are no longer automatically added to the header list on the Request object. This reverts erroneous behavior added in v1.5.0. Custom Client implementations should revert to adding these headers based on the TypedInput body of the Request.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.5.1(Jun 20, 2014)

    • New: @PartMap annotation accepts a Map of key/value pairs for multi-part.
    • Fix: MockRestAdpater uses the ErrorHandler from its parent RestAdapter.
    • Experimental RxJava support updated for v0.18 and is now lazily initialized.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.5.0(Jun 20, 2014)

    • New: Support for AppEngine's URL Fetch HTTP client.
    • New: Multipart requests of unknown length are now supported.
    • New: HTTP Content-Type can be overridden with a method-level or paramter header annotation.
    • New: Exceptions from malformed interface methods now include detailed information.
    • Fix: Support empty HTTP response status reason.
    • If an ErrorHandler is supplied it will be invoked for Callback and Observable methods.
    • HTTP PATCH method using HttpUrlConnection is no longer supported. Add the OkHttp jar to your project if you need this behavior.
    • Custom Client implementations should no longer set Content-Type or Content-Length headers based on the TypedInput body of the Request. These headers will now be added automatically as part of the standard Request header list.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.4.1(Jun 20, 2014)

  • parent-1.4.0(Jun 20, 2014)

    • New: @Query and @EncodedQuery now accept List or arrays for multiple values.
    • New: @QueryMap and @EncodedQueryMap accept a Map of key/value pairs for query parameters.
    • New: @Field now accepts List or arrays for multiple values.
    • New: @FieldMap accepts a Map of name/value pairs for form URL-encoded request bodies.
    • New: Endpoint replaces Server as the representation of the remote API root. The Endpoints utility class contains factories methods for creating instances. Server and ChangeableServer are now deprecated.
    • SimpleXmlConverter and JacksonConverter now have a default constructor.
    • Response now includes the URL.
    • Fix: Hide references to optional classes to prevent over-eager class verifiers from complaining (e.g., Dalvik).
    • Fix: Properly detect and reject interfaces which extend from other interfaces.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.3.0(Jun 20, 2014)

    • New: Converter module for SimpleXML.
    • New: Mock module which allows simulating real network behavior for local service interface implementations. See 'mock-github-client' example for a demo.
    • New: RxJava Observable support! Declare a return type of Observable<Foo> on your service interfaces to automatically get an observable for that request. (Experimental API)
    • Fix: Use ObjectMapper's type factory when deserializing (Jackson converter).
    • Multipart POST requests now stream their individual part bodies.
    • Log chunking to 4000 characters now only happens on the Android platform.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.2.2(Sep 20, 2013)

  • parent-1.2.1(Aug 30, 2013)

  • parent-1.2.0(Aug 30, 2013)

    • New: Additional first-party converters for Jackson and Protocol Buffers! These are provided as separate modules that you can include and pass to RestAdapter.Builder's setConverter.
    • New: @EncodedPath and @EncodedQuery annotations allow provided path and query params that are already URL-encoded.
    • New: @PATCH HTTP method annotation.
    • Fix: Properly support custom HTTP method annotations in UrlConnectionClient.
    • Fix: Apply RequestInterceptor during method invocation rather than at request execution time.
    • Change setDebug to setLogLevel on RestAdapter and RestAdapter.Builder and provide two levels of logging via LogLevel.
    • Query parameters can now be added in a request interceptor.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.1.1(Aug 30, 2013)

    • Fix: Ensure @Headers-defined headers are correctly added to requests.
    • Fix: Supply reasonable connection and read timeouts for default clients.
    • Fix: Allow passing null for a @Part-annotated argument to remove it from the multipart request body.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.1.0(Aug 30, 2013)

    • Introduce RequestInterceptor to replace RequestHeaders. An interceptor provided to the RestAdapter.Builder will be called for every request and allow setting both headers and additional path parameter replacements.
    • Add ErrorHandler for customizing the exceptions which are thrown when synchronous methods return non-200 error codes.
    • Properly parse responses which erroneously omit the "Content-Type" header.
    Source code(tar.gz)
    Source code(zip)
  • parent-1.0.2(Aug 30, 2013)

    • Allow uppercase letters in path replacement identifiers.
    • Fix: Static query parameters in the URL are now correctly appended with a separating '?'.
    • Fix: Explicitly allow or forbid null as a value for method parameters.
      • @Path - Forbidden
      • @Query - Allowed
      • @Field - Allowed
      • @Part - Forbidden
      • @Body - Forbidden
      • @Header - Allowed
    Source code(tar.gz)
    Source code(zip)
  • parent-1.0.1(Aug 30, 2013)

  • parent-1.0.0(Aug 30, 2013)

  • 0.6.0-rc6(Aug 30, 2013)

  • 0.6.0-rc5(Aug 30, 2013)

  • 0.6.0-rc4(Aug 30, 2013)

  • 0.6.0-rc3(Aug 30, 2013)

  • 0.6.0-rc2(Aug 30, 2013)

  • 0.6.0-rc1(Aug 30, 2013)

Feign makes writing java http clients easier

Feign makes writing java http clients easier Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal

null 6.8k Mar 13, 2021
A type-safe HTTP client for Android and the JVM

Retrofit A type-safe HTTP client for Android and Java. For more information please see the website. Download Download the latest JAR or grab from Mave

Square 38k May 1, 2021
Rest.li is a REST+JSON framework for building robust, scalable service architectures using dynamic discovery and simple asynchronous APIs.

Rest.li is an open source REST framework for building robust, scalable RESTful architectures using type-safe bindings and asynchronous, non-blocking I

LinkedIn 2.1k Mar 10, 2021
Microserver is a Java 8 native, zero configuration, standards based, battle hardened library to run Java Rest Microservices via a standard Java main class. Supporting pure Microservice or Micro-monolith styles.

Microserver A convenient modular engine for Microservices. Microserver plugins offer seamless integration with Spring (core), Jersey, Guava, Tomcat, G

AOL 929 Mar 7, 2021
Leading REST API framework for Java

Restlet Framework The leading REST API framework for Java Thanks to Restlet Framework's powerful routing and filtering capabilities, unified client an

Restlet Framework 612 Feb 28, 2021
Spring HATEOAS - Library to support implementing representations for hyper-text driven REST web services.

Spring HATEOAS This project provides some APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring and es

Spring 920 Mar 8, 2021
Spring HATEOAS - Library to support implementing representations for hyper-text driven REST web services.

Spring HATEOAS This project provides some APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring and es

Spring 920 Mar 8, 2021
A damn simple library for building production-ready RESTful web services.

Dropwizard Dropwizard is a sneaky way of making fast Java web applications. It's a little bit of opinionated glue code which bangs together a set of l

Dropwizard 7.9k Mar 12, 2021
Library for OpenAPI 3 with spring-boot

Full documentation Acknowledgements springdoc-openapi is made possible thanks to all of its contributors. Introduction The springdoc-openapi Java libr

springdoc-openapi 1.1k Mar 13, 2021
Rapidoid - Extremely Fast, Simple and Powerful Java Web Framework and HTTP Server!

Rapidoid - Simple. Powerful. Secure. Fast! Rapidoid is an extremely fast HTTP server and modern Java web framework / application container, with a str

null 1.6k Mar 10, 2021
JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.

Greetings, Java Hipster! Full documentation and information is available on our website at https://www.jhipster.tech/ Please read our guidelines befor

JHipster 18.3k May 4, 2021
Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API

Swagger Core NOTE: If you're looking for Swagger Core 1.5.X and OpenAPI 2.0, please refer to 1.5 branch. NOTE: Since version 2.1.7 Swagger Core suppor

Swagger 6.7k Mar 13, 2021