{"action":"created","issue":{"url":"https://api.github.com/repos/square/retrofit/issues/297","labels_url":"https://api.github.com/repos/square/retrofit/issues/297/labels{/name}","comments_url":"https://api.github.com/repos/square/retrofit/issues/297/comments","events_url":"https://api.github.com/repos/square/retrofit/issues/297/events","html_url":"https://github.com/square/retrofit/issues/297","id":17790787,"number":297,"title":"Retrofit 2.0 API Spec (Working Draft)","user":{"login":"JakeWharton","id":66577,"avatar_url":"https://avatars.githubusercontent.com/u/66577?v=3","gravatar_id":"","url":"https://api.github.com/users/JakeWharton","html_url":"https://github.com/JakeWharton","followers_url":"https://api.github.com/users/JakeWharton/followers","following_url":"https://api.github.com/users/JakeWharton/following{/other_user}","gists_url":"https://api.github.com/users/JakeWharton/gists{/gist_id}","starred_url":"https://api.github.com/users/JakeWharton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JakeWharton/subscriptions","organizations_url":"https://api.github.com/users/JakeWharton/orgs","repos_url":"https://api.github.com/users/JakeWharton/repos","events_url":"https://api.github.com/users/JakeWharton/events{/privacy}","received_events_url":"https://api.github.com/users/JakeWharton/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/square/retrofit/milestones/2","html_url":"https://github.com/square/retrofit/milestones/2.0","labels_url":"https://api.github.com/repos/square/retrofit/milestones/2/labels","id":374620,"number":2,"title":"2.0","description":"","creator":{"login":"JakeWharton","id":66577,"avatar_url":"https://avatars.githubusercontent.com/u/66577?v=3","gravatar_id":"","url":"https://api.github.com/users/JakeWharton","html_url":"https://github.com/JakeWharton","followers_url":"https://api.github.com/users/JakeWharton/followers","following_url":"https://api.github.com/users/JakeWharton/following{/other_user}","gists_url":"https://api.github.com/users/JakeWharton/gists{/gist_id}","starred_url":"https://api.github.com/users/JakeWharton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JakeWharton/subscriptions","organizations_url":"https://api.github.com/users/JakeWharton/orgs","repos_url":"https://api.github.com/users/JakeWharton/repos","events_url":"https://api.github.com/users/JakeWharton/events{/privacy}","received_events_url":"https://api.github.com/users/JakeWharton/received_events","type":"User","site_admin":false},"open_issues":13,"closed_issues":13,"state":"open","created_at":"2013-07-10T00:10:48Z","updated_at":"2015-04-20T04:38:17Z","due_on":null,"closed_at":null},"comments":83,"created_at":"2013-08-08T06:32:57Z","updated_at":"2015-04-30T07:19:38Z","closed_at":null,"body":"This is a living spec for what will be v2 of Retrofit.\r\n \r\n## Goals\r\n\r\n * 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.\r\n * Unify API declaration so that both synchronous and asynchronous consumption of a resource does not change its declaration.\r\n * Introduce better request and response interceptors which allow for both tweaks to existing requests or full on replacement (e.g., for signing purposes).\r\n * Allow simple request retry and cancel support without the need to talk to the `RestAdapter` or generated interface instance.\r\n * 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\r\n\r\n## Changes\r\n\r\n### Annotation Processors\r\n\r\nAn annotation processor will be embedded inside the core artifact for compile-time verification of REST API interface declarations.\r\n\r\nA second annotation processor will be provided as a separate artifact for full code generation of a class implementation of each API interface.\r\n\r\nThe `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.\r\n\r\n### Request Object\r\n\r\nAll 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).\r\n```java\r\n@GET(\"/foo\")\r\nCall<Foo> getFoo();\r\n```\r\n\r\nCallers 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.\r\n```java\r\ninterface Api {\r\n  @GET(\"/{user}/tweets\")\r\n  Call<List<Tweet>> listTweets(@Path(\"user\") String username);\r\n}\r\n```\r\n```java\r\nList<Tweet> tweets = api.listTweets(\"JakeWharton\").execute();\r\n```\r\n\r\nCallers 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.\r\n```java\r\ninterface Api {\r\n  @GET(\"/{user}/tweets\")\r\n  Call<List<Tweet>> listTweets(@Path(\"user\") String username);\r\n}\r\n```\r\n```java\r\nCall<List<Tweet>> c = api.listTweets(\"JakeWharton\");\r\nc.execute(new Callback<List<Tweet>>() {\r\n  @Override public void success(List<Tweet> response) { }\r\n  // ...\r\n}\r\n```\r\n\r\nTODO describe error handling\r\n```java\r\npublic abstract class ResponseCallback<T> {\r\n  public abstract void success(T response);\r\n\r\n  public void networkError(IOException e) {\r\n    error(ErrorType.NETWORK, -1, e);\r\n  }\r\n  public void processingError(Exception e) {\r\n    error(ErrorType.PROCESSING, -1, e);\r\n  }\r\n  public void httpError(int status, ...) {\r\n    error(ErrorType.HTTP, status, e);\r\n  }\r\n  public void error(ErrorType errorType, int status, Exception e) {\r\n    throw new RuntimeException(.., e);\r\n  }\r\n\r\n  public enum ErrorType { NETWORK, PROCESSING, HTTP }\r\n}\r\n```\r\n\r\nThe 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.\r\n\r\n * `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. \r\n\r\n * `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.\r\n\r\n\r\n### Extension System\r\n\r\nIn 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.\r\n\r\nBy default, Retrofit will install its own extension which handles the aforementioned `Call` type for both synchronous and asynchronous request execution.\r\n\r\nThe 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.\r\n```java\r\ninterface FooService {\r\n  @GET(\"/foos\")\r\n  Observable<Foo> getFoo();\r\n}\r\n```\r\n\r\n### And more...\r\n\r\nTODO!"},"comment":{"url":"https://api.github.com/repos/square/retrofit/issues/comments/97691904","html_url":"https://github.com/square/retrofit/issues/297#issuecomment-97691904","issue_url":"https://api.github.com/repos/square/retrofit/issues/297","id":97691904,"user":{"login":"mwajeeh","id":6572067,"avatar_url":"https://avatars.githubusercontent.com/u/6572067?v=3","gravatar_id":"","url":"https://api.github.com/users/mwajeeh","html_url":"https://github.com/mwajeeh","followers_url":"https://api.github.com/users/mwajeeh/followers","following_url":"https://api.github.com/users/mwajeeh/following{/other_user}","gists_url":"https://api.github.com/users/mwajeeh/gists{/gist_id}","starred_url":"https://api.github.com/users/mwajeeh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mwajeeh/subscriptions","organizations_url":"https://api.github.com/users/mwajeeh/orgs","repos_url":"https://api.github.com/users/mwajeeh/repos","events_url":"https://api.github.com/users/mwajeeh/events{/privacy}","received_events_url":"https://api.github.com/users/mwajeeh/received_events","type":"User","site_admin":false},"created_at":"2015-04-30T07:19:38Z","updated_at":"2015-04-30T07:19:38Z","body":"@YuriHeupa I know you are asking for a proper cancel mechanism, I am waiting for it too. But for now this works perfectly for me, I am sure you might be doing something similar, if not, have a look at this. Just extend from this `NetworkingFragment`.\r\n\r\n\r\n\tpublic class NetworkingFragment extends BaseFragment {\r\n\t\tprotected final CallbacksManager callbacksManager = new CallbacksManager();\r\n\r\n\t\t@Override\r\n\t\tpublic void onResume() {\r\n\t\t\tsuper.onResume();\r\n\t\t\tcallbacksManager.resumeAll();\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void onDestroyView() {\r\n\t\t\tcallbacksManager.pauseAll();\r\n\t\t\tsuper.onDestroyView();\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void onDestroy() {\r\n\t\t\tcallbacksManager.cancelAll();\r\n\t\t\tsuper.onDestroy();\r\n\t\t}\r\n\r\n\t\tprotected Service apiFor(CallbacksManager.CancelableCallback<?> callback) {\r\n\t\t\tcallbacksManager.addCallback(callback);\r\n              // return your retrofit API\r\n\t\t\treturn Api.getService();\r\n\t\t}\r\n\t}\r\n\r\nCallBackManager:\r\n\r\n\tpublic class CallbacksManager {\r\n\t\tprivate final Set<CancelableCallback> callbacks = new HashSet<>();\r\n\r\n\t\tpublic void cancelAll() {\r\n\t\t\tfor (CancelableCallback callback : callbacks) {\r\n\t\t\t\t// false to avoid java.util.ConcurrentModificationException alternatively we can use\r\n\t\t\t\t// iterator\r\n\t\t\t\tcallback.cancel(false);\r\n\t\t\t}\r\n\t\t\tcallbacks.clear();\r\n\t\t}\r\n\r\n\t\tpublic void resumeAll() {\r\n\t\t\tfinal Iterator<CancelableCallback> iterator = callbacks.iterator();\r\n\t\t\twhile (iterator.hasNext()) {\r\n\t\t\t\tboolean remove = iterator.next().resume();\r\n\t\t\t\tif (remove) {\r\n\t\t\t\t\titerator.remove();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void pauseAll() {\r\n\t\t\tfor (CancelableCallback callback : callbacks) {\r\n\t\t\t\tcallback.pause();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic void addCallback(CancelableCallback<?> callback) {\r\n\t\t\tcallbacks.add(callback);\r\n\t\t}\r\n\r\n\t\tprivate void removeCallback(CancelableCallback<?> callback) {\r\n\t\t\tcallbacks.remove(callback);\r\n\t\t}\r\n\r\n\t\tpublic abstract class CancelableCallback<T> implements Callback<T> {\r\n\t\t\tprivate boolean canceled;\r\n\t\t\tprivate boolean paused;\r\n\r\n\t\t\tprivate T pendingT;\r\n\t\t\tprivate Response pendingResponse;\r\n\t\t\tprivate RetrofitError pendingError;\r\n\r\n\t\t\tpublic CancelableCallback() {\r\n\t\t\t\tthis.canceled = false;\r\n\t\t\t}\r\n\r\n\t\t\tpublic void pause() {\r\n\t\t\t\tpaused = true;\r\n\t\t\t}\r\n\r\n\t\t\tpublic boolean resume() {\r\n\t\t\t\tpaused = false;\r\n\t\t\t\t// if callback was cancelled then no need to post pending results\r\n\t\t\t\tif (canceled) {\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tif (pendingError != null) {\r\n\t\t\t\t\tonFailure(pendingError);\r\n\t\t\t\t\t// to make sure not to post it again\r\n\t\t\t\t\tpendingError = null;\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t} else if (pendingT != null) {\r\n\t\t\t\t\tonSuccess(pendingT, pendingResponse);\r\n\t\t\t\t\t// to make sure not to post it again\r\n\t\t\t\t\tpendingT = null;\r\n\t\t\t\t\tpendingResponse = null;\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tpublic void cancel(boolean remove) {\r\n\t\t\t\tcanceled = true;\r\n\t\t\t\tif (remove) {\r\n\t\t\t\t\tremoveCallback(this);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t@Override\r\n\t\t\tpublic void success(T t, Response response) {\r\n\t\t\t\tif (canceled) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif (paused) {\r\n\t\t\t\t\tpendingT = t;\r\n\t\t\t\t\tpendingResponse = response;\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tonSuccess(t, response);\r\n\t\t\t\tremoveCallback(this);\r\n\t\t\t}\r\n\r\n\t\t\t@Override\r\n\t\t\tpublic void failure(RetrofitError error) {\r\n\t\t\t\tif (canceled) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif (paused) {\r\n\t\t\t\t\tpendingError = error;\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tonFailure(error);\r\n\t\t\t\tremoveCallback(this);\r\n\t\t\t}\r\n\r\n\t\t\tprotected abstract void onSuccess(T t, Response response);\r\n\r\n\t\t\tprotected abstract void onFailure(RetrofitError error);\r\n\t\t}\r\n\t}\r\n\r\nUsage:\r\n\r\n\tprivate void fetchUser(String id) {\r\n        final CallbacksManager.CancelableCallback<User> callback = callbacksManager.new CancelableCallback<User>() {\r\n            @Override\r\n            protected void onSuccess(User user, Response response) {\r\n            }\r\n\r\n            @Override\r\n            protected void onFailure(RetrofitError error) {\r\n            }\r\n        };\r\n\t\t// this is only drawback of this approach, you need to pass your callback to your service interface as well as to \r\n\t\t// Networking fragment for life cycle handling\r\n        apiFor(callback).getData(id, callback);\r\n    }\r\n\r\n"}}