I have a general sendRequest() method that I use to get a json result back from the server. I'm parsing the result in an instance of the callback right now, but instead I'd much rather just pass in a type to the sendRequest() method so I can parse and handle exceptions before the callback. How can I do this? I've included the relevant code and some comments below.
public void sendRequest(int method, String url, final RequestListener listener) {
StringRequest postRequest = new StringRequest(method, url,
new Response.Listener<String>()
{
#Override
public void onResponse(String response) {
listener.gotResponse(response, null);
// Instead of parsing this into a user object, I'd like to parse it into
// a type that I pass into the sendRequest() method.
try {
User user = User.parseJson(new JSONObject(response));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
First of all please note that out of the box you have JsonObjectRequest which will convert the response directly into a JSONObject which seems to be better than StringRequest in your case, but as you want to go a little bit further I would propose to create a custom request based on a generic unmarshaller.
public interface JsonUnmarshaller<T> {
T parse(JSONObject object) throws JSONException;
}
The custom request could be something like this:
public class JSONRequest extends Request<T> {
private final Listener<T> mListener;
private final JsonUnmarshaller<T> unmarshaller;
public JSONRequest(int method, String url, Listener<T> listener,
ErrorListener errorListener, JsonUnmarshaller<T> unmarshaller) {
super(method, url, errorListener);
mListener = listener;
this.unmarshaller = unmarshaller;
}
#Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(
response.data, HttpHeaderParser.parseCharset(response.headers)
);
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
try {
return Response.success(
unmarshaller.parse(new JSONObject(parsed)),
HttpHeaderParser.parseCacheHeaders(response)
);
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
}
Then your method would be:
public <T> void sendRequest(int method, String url, final RequestListener
listener, JsonUnmarshaller<T> unmarshaller) {
JSONRequest postRequest = new JSONRequest(...
Wrote a little something for you here, I think you will be able to apply this to your example:
import java.util.*;
public class Cat{
public static void main(String... args){
Cat cat = new Cat();
cat.send(Dog.class);
cat.send(Mouse.class);
}
public <T extends Parseable> void send(Class<T> clazz){
try{
T t = clazz.newInstance();
t.parse();
} catch(Exception e){
}
}
}
interface Parseable{
void parse();
}
class Dog implements Parseable{
public void parse(){
System.out.println("Dog parse");
}
}
class Mouse implements Parseable{
public void parse(){
System.out.println("Mouse parse");
}
}
Your method is static so you probably can just do T.parseJson(). Not sure if there won't be any problems with transfering generic type to anonymous listener, but I hope it will work.
Related
In my first android project i made an onClick event to call a function:
public void doSomething(View v) {
String result = authenticate();
[...]
}
This function calls the method:
private String authenticate() {
OkHttpClient client = new OkHttpClient();
[...]
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
[...]
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseString = response.body().string();
try {
JSONObject responseObject = new JSONObject(responseString);
String responseObjectAccessToken = responseObject.getString("accesstoken");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
}
now i would like to return the responseObjectAccessToken to my doSomething function. A detailed explanation would be great since I am new to Java and Android Studio.
One suggestion is to handle that async response through a callback which is passed as an argument to your authenticate method. Here I'm using Java 8 lambda for that one method interface.
public class MyActivity extends Activity {
public interface AuthCallback {
void onAuthResult(String token);
}
private String authenticate(AuthCallback callback) {
// ...
String responseObjectAccessToken = responseObject.getString("accesstoken");
callback.onAuthResult(responseObjectAccessToken)
// ...
}
public void doSomething(View v) {
authenticate((token) -> {
// do something with token
});
}
// ...
}
As you can see that callback could be stored if you wanted to:
// ...
AuthCallback cb = new AuthCallback() {
#Override
public void onAuthResult(String token) {
// do something in the view/fragment/activity
}
}
// then pass it as argument
or your class could implement this interface and pass itself into the method:
public class MyActivity extends Activity implements AuthCallback {
#Override
public void onAuthResult(String token) {
// do something
}
// ...
public void doSomething(View v) {
authenticate(MyActivity.this); // <-- pass itself
}
}
One important point here is that networking happens on a separate thread, so if you want to have some UI changes after your API responds you could use a helper method runOnUiThread to do changes on main ui thread instead:
authenticate((token) -> {
runOnUiThread(() -> {
// do something with UI here
})
});
I'm trying to build a library that basically wraps our api. Basically, the structure im going for is something like this:
MySDK mySDK = new MySDK("username", "password");
mySDK.getPlaylistInfo("3423", 2323, new CustomCallback<>(){
//on response
//on failure
});
So with vanilla Retrofit, an api call usually looks something like the following:
ApiService api = retrofit.create(ApiService.class);
Call<Response> call = api.getPlaylistInfo()
call.enqueue(new Callback<Response>() {
#Override
public void onResponse(Call<Response> call, Response<Response> response) {
//handle response
}
#Override
public void onFailure(Call<Response> call, Throwable t) {
//handle failure
}
});
Basically, how would I wrap retrofits callback system into my own? Note, the reason for needing to do this is to preprocess the data returned from the api before delivering the final response.
I've written something similar so it might help you getting started, this follows an implementation I'v written for Volley, and re-used when I migrated to Retrofit2 so it resembles it (this SO question).
Create a global object (what you would refer to as MySDK) as a singelton class that handles your requests:
create a singleton class, which you instatiate when you're application comes up:
public class NetworkManager
{
private static final String TAG = "NetworkManager";
private static NetworkManager instance = null;
private static final String prefixURL = "http://some/url/prefix/";
//for Retrofit API
private Retrofit retrofit;
private ServicesApi serviceCaller;
private NetworkManager(Context context)
{
retrofit = new Retrofit.Builder().baseUrl(prefixURL).build();
serviceCaller = retrofit.create(ServicesApi.class);
//other stuf if you need
}
public static synchronized NetworkManager getInstance(Context context)
{
if (null == instance)
instance = new NetworkManager(context);
return instance;
}
//this is so you don't need to pass context each time
public static synchronized NetworkManager getInstance()
{
if (null == instance)
{
throw new IllegalStateException(NetworkManager.class.getSimpleName() +
" is not initialized, call getInstance(...) first");
}
return instance;
}
public void somePostRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
String url = prefixURL + "this/request/suffix";
Map<String, Object> jsonParams = new HashMap<>();
jsonParams.put("param1", param1);
Call<ResponseBody> response;
RequestBody body;
body = RequestBody.create(okhttp3.MediaType.parse(JSON_UTF), (new JSONObject(jsonParams)).toString());
response = serviceCaller.thePostMethodYouWant("someUrlSufix", body);
response.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
// do what you want with it and based on that...
//return it to who called this method
listener.getResult("someResultString");
}
catch (Exception e)
{
e.printStackTrace();
listener.getResult("Error1...");
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
try
{
// do something else in case of an error
listener.getResult("Error2...");
}
catch (Exception e)
{
throwable.printStackTrace();
listener.getResult("Error3...");
}
}
});
}
public void someGetRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
// you need it all to be strings, lets say id is an int and name is a string
Call<ResponseBody> response = serviceCaller.theGetMethodYouWant
(String.valueOf(param1.getUserId()), param1.getUserName());
response.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
// do what you want with it and based on that...
//return it to who called this method
listener.getResult("someResultString");
}
catch (Exception e)
{
e.printStackTrace();
listener.getResult("Error1...");
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
try
{
// do something else in case of an error
listener.getResult("Error2...");
}
catch (Exception e)
{
throwable.printStackTrace();
listener.getResult("Error3...");
}
}
});
}
}
This works with your interface (example with POST and GET request, GET could be without params):
public interface BelongServicesApi
{
#POST("rest/of/suffix/{lastpart}") // with dynamic suffix example
Call<ResponseBody> thePostMethodYouWant(#Path("lastpart") String suffix, #Body RequestBody params);
#GET("rest/of/suffix") // with a fixed suffix example
Call<ResponseBody> theGetMethodYouWant(#Query("userid") String userid, #Query("username") String username);
}
when your application comes up:
public class MyApplication extends Application
{
//...
#Override
public void onCreate()
{
super.onCreate();
NetworkManager.getInstance(this);
}
//...
}
a simple listener interface for your callback (seperate file would do good):
public interface SomeCustomListener<T>
{
public void getResult(T object);
}
and finally, from wherever you want, the context is already in there, just call:
public class BlaBla
{
//.....
public void someMethod()
{
//use the POST or GET
NetworkManager.getInstance().somePostRequestReturningString(someObject, new SomeCustomListener<String>()
{
#Override
public void getResult(String result)
{
if (!result.isEmpty())
{
//do what you need with the result...
}
}
});
}
}
you can use any object with the listener, just parse the response string to a corresponding object, depending on what you need to receive and you can call that from everywhere (onClicks, etc.), just remember the objects need to match between methods.
Hope this Helps!
I'm new to retrofit and i am trying te get a json response to an object called RootObject. The error that i am stuck with is :
"Error:(21, 44) error: incompatible types: NewsController cannot be
converted to Callback>"
Does someone now my mistake here? thanks in regards!
public class NewsController {
public void getNews(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("apilink").addConverterFactory(GsonConverterFactory.create()).build();
GetNewsService service = retrofit.create(GetNewsService.class);
try {
service.GetNewsItems().enqueue(this); //asynchronous
Response<List<RootObject>> response = service.GetNewsItems().execute(); //synchronous
}
catch (IOException e){
}
}
}
class to put the data:
public class RootObject implements Serializable {
public ArrayList<Result> results ;
public int nextId;
public ArrayList<Result> getResults() { return results; }
public int getNextId() { return nextId; }
public String toString() {
return String.format("JEEJ" + nextId);
}
}
Interface:
public interface GetNewsService {
#GET("/Articles")
Call<List<RootObject>> GetNewsItems();
}
First of all,
change your interface to this:
public interface GetNewsService {
#GET("/Articles")
void GetNewsItems(Callback<List<RootObject>> cb);
}
Also change your newsController class.
public class NewsController {
private RestAdapter restAdapter;
static final String API_URL = "[Enter your API base url here]";
public void getNews(){
OkHttpClient mOkHttpClient = new OkHttpClient();
mOkHttpClient.setConnectTimeout(15000,TimeUnit.MILLISECONDS);
mOkHttpClient.setReadTimeout(15000,TimeUnit.MILLISECONDS);
restAdapter = new RestAdapter.Builder().setEndpoint(API_URL).setClient(new OkClient(mOkHttpClient)).setLogLevel(RestAdapter.LogLevel.FULL) .build();
GetNewsService service = restAdapter.create(GetNewsService.class);
Callback<List<RootObject> cb = new Callback<List<RootObject>>() {
#Override
public void success(List<RootObject> rootObjectList, Response response) {
//whatever you want to do with the fetched news items
}
#Override
public void failure(RetrofitError error) {
//whatever you want to do with the error
}
};
service.GetNewsItems(cb);
}
}
You'll need to add the following dependencies in your build.gradle:
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.google.code.gson:gson:2.3.1'
compile 'com.squareup.okhttp:okhttp:2.4.0'
#megh vidani's answer works, but he had you switch your code from Retrofit 2 to Retrofit 1. Here is how to do it in Retrofit 2. You would need to go back to your original gradle settings, etc. --
public class NewsController {
public void getNews(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("apilink").addConverterFactory(GsonConverterFactory.create()).build();
GetNewsService service = retrofit.create(GetNewsService.class);
service.GetNewsItems().enqueue(new Callback<List<RootObject>>() {
#Override
public void onResponse(Response<List<RootObject>> response) {
// Handle your response
// Note HTTP errors are delivered here, you can check
// response.isSuccess() or response.code() to determine
// HTTP failures
}
#Override
public void onFailure(Throwable t) {
// Network errors
}
});
}
}
How do I return a list from AsyncTask to a class instead of a method?
I have this AsyncTask which connects to a webpage, reads the page into a string and then splits it all into variables. This is the page
This is the AsyncTask:
public class sellableIds_AsyncTask extends AsyncTask<String, Void, List> {
String bodyHtml;
List<String> items;
private Context context;
private sellableIdsFilter sellableIdsFilter;
public sellableIds_AsyncTask(Context context) {
this.context = context;
}
public void setSellableIdsFilter(sellableIdsFilter sellableIdsFilter) {
this.sellableIdsFilter = sellableIdsFilter;
}
#Override
protected List doInBackground(String... params) { //Incompatible return type
try {
HttpClient httpClient = new DefaultHttpClient();
HttpGet get = new HttpGet(params[0]);
HttpResponse response = httpClient.execute(get);
bodyHtml = EntityUtils.toString(response.getEntity()).replace("[", "");
;
items = Arrays.asList(bodyHtml.split("\\s*,\\s*"));
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return items;
}
#Override
protected void onPostExecute(List result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
this.sellableIdsFilter.sellableIdsFilter(result);
}
public interface sellableIdsFilter {
public void sellableIdsFilter(List result);
}
}
And called in the activity:
sellableIds_AsyncTask sellableIds_AsyncTask = new sellableIds_AsyncTask(this);
sellableIds_AsyncTask.setSellableIdsFilter(this);
sellableIds_AsyncTask.execute(sellableIdsUrl);
How can I return this list to the activity where I call it from and have the list able to use anywhere within the class? instead to the method sellableIdsFilter();
Create a listener interface
public interface OnSellableIdsFilterListener {
void onSellableIdsFilterSuccess(List<Integer> ids);
}
make the class you need to get the ids implement that interface
public class SomeClassThatNeedsTheIDs implements OnSellableIdsFilterListener {
void someVoid() {
//Pass the instance of this class to the constructor of the asynctask as a listener
new YourAsyncTask(this, this).execute();
}
#Override
public void onGetSellableIdsFilterSuccess(List<Integer> ids) {
// do whatever you want with the ids
}
}
Change your asynctask adding a listener to the constructor
public class YourAsyncTask extends AsyncTask< , , > {
Context context;
OnSellableIdsFilterListener listener;
public YourAsyncTask(Context context, OnSellableIdsFilterListener listener) {
this.listener = listener;
this.context = context;
}
// In onPostExecute check if listener!=null and invoke the method, passing the ids
public void onPostExecute() {
if(listener!=null) {
listener.onSellableIdsFilterSuccess(ids);
}
}
}
You can add a method which returns List 'items' in your 'sellableIds_AsyncTask' class. After calling asyncTasks execute , call get() to ensure async task has finished its task. Then call the getter function to get your List 'items'. Hope this helps.
I have APIHelper class, whose static methods asynchronously make request to server, receive json string, parse and return Object or ArrayList:
...
public static ArrayList<Item> getItemsInCategory(int id_category) throws ExecutionException, InterruptedException, JSONException {
DoRequest doRequest = new DoRequest();
String jsonString = doRequest.execute(API_PATH + PRODUCT_SEARCH + CATEGORY_ID + id_category).get();
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("products");
return Item.fromJson(jsonArray);
}
public static Item getItem(int id_item) throws ExecutionException, InterruptedException, JSONException {
DoRequest doRequest = new DoRequest();
String jsonString = doRequest.execute(API_PATH + PRODUCT_GET_INFO + id_item).get();
JSONObject jsonObject = new JSONObject(jsonString);
return Item.fromJson(jsonObject);
}
...
Now I want to make methods without calling get() method from AsyncTask class DoRequest.
My DoRequest class:
public class DoRequest extends AsyncTask<String, Void, String> {
ResultListener mResultListener;
public abstract interface ResultListener{
Object onResultAvailable(String result) throws JSONException;
}
DoRequest(ResultListener resultListener){
mResultListener = resultListener;
}
#Override
protected String doInBackground(String... URL) {
ServiceHandler serviceHandler = new ServiceHandler();
String jsonStr = serviceHandler.makeServiceCall(URL[0]);
return jsonStr;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
try {
mResultListener.onResultAvailable(result);
} catch (JSONException e) {
e.printStackTrace();
}
}
Help me change my methods in APIHelper class that they return values after callback from DoRequest.
You can use an event bus like otto to have the async task publish an event to the event bus once in onPostExecute and then have the whoever the Helper class is returning its results to, listen to the event on the bus and then handle the callback there.
http://square.github.io/otto/
An example of using this would be:
First you are going to have to use a custom post method to be able to post to threads from the background.
public class MainThreadBus extends Bus {
private final Handler handler = new Handler(Looper.getMainLooper());
#Override public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
post(event);
}
});
}
}
}
Now that we have that set up, inside of the class that calls the helper class we create a register on the bus and call the helper method:
class someClass(){
///some stuff
public void performRestCall(int id_category){
bus.register(this);
ApiHelper.getItemsInCategory(id_category);
}
#Subscribe
public void restCallCompleted(GetCategoryEvent e){
ArrayList<Item> list = e.getList();
//do whatever else you need to
bus.unRegister(this);
}
}
Now in the asyncTask onPostExecute we perform all the work we were doing after the asyncTask finished and tell everyone on the bus that this event has completed. We pass the list in the object instead of return it from a method.:
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
bus.register(this);
try {
JSONObject jsonObject = new JSONObject(jsonString);
bus.post(new GetCategoryEvent( Item.fromJson(jsonObject));
} catch (JSONException e) {
e.printStackTrace();
}
}
Your solution will end up being something along these lines.