Rxjava and Volley Requests - android

My question should sounds something like stupid, but i am just jumping from Asynktask to RxJava.
So:
Is possible to use RxJava Observable with Volley Requests?, it means,
using future requests.
I ask this, because another httpClient like retrofit uses RxJava very well, but personally love Volley, so is it possible?
Edit
Base on the first answer, i get that it is possible.
Could you share
some sample showing how to do that?

THIS CODE WOKS WITH THIS LIBRARY
api 'com.netflix.rxjava:rxjava-android:0.16.1'
Finally and thanks for your answer i find a solution that want to share:
In my case i use Activities, but for fragment should be more or less equal.
And i want o get a JsonObject in response, but could be your custom Volley implementation.
public class MyActivity extends BaseActivityWithoutReloadCustomer implements Observer<JSONObject>
{
private CompositeSubscription mCompositeSubscription = new CompositeSubscription();
private Activity act;
/**
* #use handle response from future request, in my case JsonObject.
*/
private JSONObject getRouteData() throws ExecutionException, InterruptedException {
RequestFuture<JSONObject> future = RequestFuture.newFuture();
String Url=Tools.Get_Domain(act.getApplicationContext(), Global_vars.domain)+ PilotoWs.wsgetRoutesbyUser+ Uri.encode(routeId);
final Request.Priority priority= Request.Priority.IMMEDIATE;
Estratek_JSONObjectRequest req= new Estratek_JSONObjectRequest(Request.Method.GET, Url,future,future,act,priority);
POStreet_controller.getInstance().addToRequestQueue(req);
return future.get();
}
/**
*#use the observable, same type data Jsob Object
*/
public Observable<JSONObject> newGetRouteData() {
return Observable.defer(new Func0<Observable<JSONObject>>() {
#Override
public Observable<JSONObject> call() {
Exception exception;
try {
return Observable.just(getRouteData());
} catch (InterruptedException | ExecutionException e) {
Log.e("routes", e.getMessage());
return Observable.error(e);
}
}
});
};
#Override
public void onCreate(Bundle instance) {
super.onCreate(instance);
setContentView(R.layout.yourLayout);
act = this;
/**
* #condition: RxJava future request with volley
*/
mCompositeSubscription.add(newGetRouteData()
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(this));
}
#Override
public void onCompleted() {
System.out.println("Completed!");
}
#Override
public void onError(Throwable e) {
VolleyError cause = (VolleyError) e.getCause();
String s = new String(cause.networkResponse.data, Charset.forName("UTF-8"));
Log.e("adf", s);
Log.e("adf", cause.toString());
}
#Override
public void onNext(JSONObject json) {
Log.d("ruta", json.toString());
}
For me, it just works. Hope helps some one.
Edit
Estratek_JSONObjectRequest.java
public class Estratek_JSONObjectRequest extends JsonObjectRequest{
Activity Act;
Priority priority;
public Estratek_JSONObjectRequest(int method, String url,
JSONObject jsonRequest, Listener<JSONObject> listener,
ErrorListener errorListener,Activity act, Priority p) {
super(method, url, jsonRequest, listener, errorListener);
this.Act=act;
this.priority=p;
}
public Estratek_JSONObjectRequest(int method, String url,
Listener<JSONObject> listener,
ErrorListener errorListener,Activity act, Priority p) {
super(method, url, null, listener, errorListener);
this.Act=act;
this.priority=p;
}
#Override
public Map<String, String> getHeaders() {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json; charset=utf-8");
headers.put("Authorization", "Bearer "+Tools.mySomeBearerToken);
return headers;
}
//it make posible send parameters into the body.
#Override
public Priority getPriority(){
return priority;
}
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String je = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
if (je.equals("null")){
je="{useInventAverage:0}";
return Response.success(new JSONObject(je), HttpHeaderParser.parseCacheHeaders(response));
}
else
return Response.success(new JSONObject(je), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException var3) {
return Response.error(new ParseError(var3));
} catch (JSONException var4) {
return Response.error(new ParseError(var4));
}
}
}
This is like Volley constructors, but i make my own custom, to send some headers, like bearer token, content-type, send priority, etc.
Otherwise is the same.
FOR THE RXJAVA2 LIBRARY, THIS IS THE WAY:
> build.gradle should have something like this:
api "io.reactivex.rxjava2:rxandroid:2.0.2":
public class MainActivity extends AppCompatActivity {
CompositeDisposable mCompositeDisposable = new CompositeDisposable();
#Override
public void onCreate(Bundle instancia) {
super.onCreate(instancia);
setContentView(R.layout.sale_orders_list);
// disposable that will be used to subscribe
DisposableSubscriber<JSONObject> d = new DisposableSubscriber<JSONObject>() {
#Override
public void onNext(JSONObject jsonObject) {
onResponseVolley(jsonObject);
}
#Override
public void onError(Throwable t) {
// todo
}
#Override
public void onComplete() {
System.out.println("Success!");
}
};
newGetRouteData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(d);
}
#Override
public void onDestroy(){
super.onDestroy();
/**
* #use: unSubscribe to Get Routes
*/
if (mCompositeDisposable != null){
mCompositeDisposable.clear();
}
}
/**
* #condition: RxJava future request with volley
*/
private JSONObject getRouteData() throws ExecutionException, InterruptedException,RuntimeException {
RequestFuture<JSONObject> future = RequestFuture.newFuture();
String Url = "https//miapirest.com/api";
JSONObjectRequest req= new JSONObjectRequest(Request.Method.GET, Url,future,future,act,priority);
VolleyInstance.addToRequestQueue(req);
return future.get();
}
/**
* #condition: this function create a new Observable object and return that if success or
*/
public Flowable<JSONObject> newGetRouteData() {
return Flowable.defer(new Callable<Publisher<? extends JSONObject>>() {
#Override
public Publisher<? extends JSONObject> call() throws Exception {
return Flowable.just(getRouteData());
}
});
};
}

Good news is that Mr.张涛 kymjs modified Google Volley into RxVolley, Removed the HttpClient and RxJava support.
RxVolley = Volley + RxJava + OkHttp
Complete documentation available at http://rxvolley.mydoc.io/
P.S:
I'm currently thinking of how to use RxVolley with support for multiple custom JSON converters like retrofit!

Related

How to return the result from get (okhttp3)

I get a result by using okhttp3 get method.
And Now, I want to return the result to MainActivity.
I tried using intent, but I'm failed.
Also I read this okhttp3 how to return value from async GET call. But I confused about where I have to write that code.
public interface GetLastIdCallback {
void lastId(String id);
}
my MainActivity:
getMaskInfo info = new getMaskInfo(this);
info.requestGet(latitude, longitude);
getMaskInfo Activity (I want to return JSONObject or JSONArray):
package com.example.buymaskapp;
public class getMaskInfo {
OkHttpClient client = new OkHttpClient();
public static Context mContext;
public getMaskInfo(Context context){
mContext = context;
}
public void requestGet(double lat, double lng){
String url = "https://8oi9s0nnth.apigw.ntruss.com/corona19-masks/v1/storesByGeo/json";
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
urlBuilder.addEncodedQueryParameter("lat", Double.toString(lat));
urlBuilder.addEncodedQueryParameter("lng", Double.toString(lng));
urlBuilder.addEncodedQueryParameter("m", "1000");
String requestUrl = urlBuilder.build().toString();
Request request = new Request.Builder().url(requestUrl).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.d("error", "Connect Server Error is " + e.toString());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try{
JSONObject jsonObject = new JSONObject(response.body().string());
JSONArray totalStore = jsonObject.getJSONArray("stores");
System.out.println(jsonObject);
}catch (JSONException e){
//
}
}
});
}
}
Instead of returning void from requestGet() method, return a LiveData
public LiveData<JSONObject> requestGet(double lat, double lng) {
LiveData<JSONObject> result = MutableLiveData<JSONObject>();
/* reqeust builder & url builder code here */
client.newCall(request).enqueue(new Callback() {
/* override other methods here */
public void onResponse(Call call, Response response) throws IOException {
try{
JSONObject jsonObject = new JSONObject(response.body().string());
((MutableLiveData) result).postValue(jsonObject);
}catch (JSONException e){
/* catch and do something */
}
}
});
return result;
}
Observe the livedata in mainactivity
info.requestGet(latitude, longitude).observe(getViewLifeCycleOwner, new Observer() {
#Override
public void onCanged(JSONObject result) {
/* code to use result */
}
});
Otherwise, you can also implement interface on mainactivity and use its instance in getMaskInfo or in requestGet method to send back data.
Create a callback in MainActivity:
public void onResult(JSONArray stores)
or whatever you want to return from the call. Since you now know that your mContext is actually MainActivity, you can make a cast and call that method
((MainActivity)mContext).onResult(totalStore).
If you need to use getMaskInfo with other activities as well, you can put method onResult into an interface, make MainActivity implement that interface and pass the interface as an argument to getMaskInfo.
Interface class
public interface GetLastIdCallback {
void lastId(String id);
void getJSONCallback(JSONObject object);
}
Update the onResponse function
#Override
public void onResponse(Call call, Response response) throws IOException {
try{
JSONObject jsonObject = new JSONObject(response.body().string());
JSONArray totalStore = jsonObject.getJSONArray("stores");
System.out.println(jsonObject);
((GetLastIdCallback )(mContext)).getJSONCallback(jsonObject); //Return here
}catch (JSONException e){
//
}
}
});
Calling activity must implement GetLastIdCallback interface
public class Main2Activity extends AppCompatActivity implements GetLastIdCallback{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
#Override
public void lastId(String id) {
}
#Override
public void getJSONCallback(JSONObject object) {
//Here you can use response according to your requirements
}
}

Cancelling long running task inside Observable when a subscription is disposed

I am writing a reactive wrapper over volley library to send http request easily in my app. Here is the class:
/**
* Used to send a http GET/POST request.
*/
public class BasicRequest {
public static final String LOG_TAG = "BasicRequest";
public static final int GET_REQUEST = Request.Method.GET;
public static final int POST_REQUEST = Request.Method.POST;
private final int mRequestType;
private final String mServiceLocation;
private final Map<String, String> mParams;
/**
* Keeps track of all the request for this object. Will be helpful when we need to cancel
* the request when someone disposes the subscription.
*/
private List<StringRequest> mStringRequests = new ArrayList<>();
private Context mContext;
private int mRequestTimeout = BASIC_REQUEST_DEFAULT_TIMEOUT;
public BasicRequest(Context context,
String serviceLocation,
int requestType,
final Map<String, String> params) {
mContext = context;
mRequestType = requestType;
mServiceLocation = serviceLocation;
mParams = params;
}
private void fireRequest(final SingleEmitter<String> e) {
StringRequest stringRequest;
if(mRequestType == GET_REQUEST) {
stringRequest = new StringRequest(Request.Method.GET, mServiceLocation,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
e.onSuccess(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
e.onError(error);
}
});
} else {
stringRequest = new StringRequest(Request.Method.POST, mServiceLocation,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
e.onSuccess(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
e.onError(error);
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
return mParams;
}
};
}
mStringRequests.add(stringRequest);
stringRequest.setRetryPolicy(new DefaultRetryPolicy(
mRequestTimeout,
ConnectionUtils.BASIC_REQUEST_DEFAULT_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
VolleyInstance.getInstance(mContext).addToRequestQueue(stringRequest);
}
/**
* Returns a Single observable for results. Queues the request on Subscription. Must be
* called only once during the lifetime of object. Calling multiple times will return null.
* Expect to get VolleyException in case of error.
* #return Single observable for String results. If it's is used for second time, it will
* return null.
*/
#Nullable
public Single<String> get() {
return Single.create(new SingleOnSubscribe<String>() {
#Override
public void subscribe(#NonNull SingleEmitter<String> e) throws Exception {
fireRequest(e);
}
}).doOnDispose(new Action() {
#Override
public void run() throws Exception {
for (StringRequest stringRequest: mStringRequests) {
stringRequest.cancel();
}
}
});
}
/**
* Set the request timeout for this request.
* #param requestTimeout time in milliseconds.
*/
public void setRequestTimeout(int requestTimeout) {
mRequestTimeout = requestTimeout;
}
Now the problem is when somebody disposes a subscription all the request corresponding to all the subscription will be stopped. Is there a way I can only stop the request for which subscription is disposed?
I know once way achieving it would to enforce that only one subscription can be maintained and if someone calls get again, cache'd observer will be returned. Is there a better way of disposing http request based on subscription disposal?
You don't need to manage it outside fireRequest, SingleEmitter has setCancellable method exactly for that, do the cancellation there, and RxJava will make sure to call it when someone dispose the Observable.
add at fireRequest() method, and remove the doOnDispose :
e.setCancellable(()-> stringRequest.cancel());

How to implement a login in Android using Volley?

I have a LoginActitvity with two textfields for the username and password and a login-button. When the user presses the button, the app starts an async task. The async task implements my VolleyHandler. It checks if the login parameters are correct and fetches some user data (using volley and json). While doing this, a ProgressDialog appears. When the async task is finished, it starts an intent to the MainActivity in the onPostExecute method.
Now to my question: Is it a good idea to make volley-requests in the async task, or do you have a better solution?
Thanks.
You cannot use asynctask. Volley care about it. You can use callback for work with data and ui.
Looks like this:
public class LoginActivity extends SinglePaneActivity implements DownloadCallback {
//...
public void sendRequest(){
Downloader download = new Download(this);
downloader.download(userName, password);
progresbar.show();
}
public void requestFinish(){
progersbar.dismis();
//... continue
}
}
callback:
public interface DownloadCallback {
void requestFinish();
}
in class downloader
private RequestQueue requestQueue;
DownloadCallback mcallback;
public void Downloader(DownloadCallback callback){
mCallback = callback;
requestQueue = Volley.newRequestQueue(mContext);
initVolleyListeners();
}
private void initVolleyListeners() {
mSuccessListener = new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
mCallback.requestFinish();
}
};
mErrorListener = new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
mCallback.requestFinish();
}
};
public void download(String user, String pass){
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, url, createJson(user, pass), mSuccessListener , mErrorListener ) {
//header for send JSON to server
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json; charset=utf-8");
return headers;
}
};
requestQueue.add(jsonObjectRequest );
}
And one point. Don't send user name in json. You send it as param in header. Use this application/x-www-form-urlencoded and set up pass an username as params in header.
Update:
Now It will work. Sorry I wrote it in a hurry.
Article about how callback work

Retrofit and Centralized Error Handling

Each request to the server may return error_code. I want to handle these error in one place
when I was using AsyncTask I had a BaseAsyncTask like that
public abstract class BaseAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected Context context;
private ProgressDialog progressDialog;
private Result result;
protected BaseAsyncTask(Context context, ProgressDialog progressDialog) {
this.context = context;
this.progressDialog = progressDialog;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(Result result) {
super.onPostExecute(result);
HttpResponse<ErrorResponse> response = (HttpResponse<ErrorResponse>) result;
if(response.getData().getErrorCode() != -1) {
handleErrors(response.getData());
}else
onResult(result);
}
private void handleErrors(ErrorResponse errorResponse) {
}
public abstract void onResult(Result result);
}
But, using retrofit each request has its error handling callback:
git.getFeed(user,new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
});
}
});
How can I handle all errors in one place?
If you need to get some 'logic' error, then you need some Java logic since it's not a Retrofit feature so basically:
Create a Your implementation Callback that implements the Retrofit Callback
Create a base object that define the method 'isError'
Modify Retrofit RestAdapter in order to get your Callback instead of the Retrofit One
MyCallback.java
import android.util.Log;
import retrofit.Callback;
import retrofit.client.Response;
public abstract class MyCallback<T extends MyObject> implements Callback<T> {
#Override
public final void success(T o, Response response) {
if (o.isError()) {
// [..do something with error]
handleLogicError(o);
}
else {
handleSuccess(o, response);
}
}
abstract void handleSuccess(T o, Response response);
void handleLogicError(T o) {
Log.v("TAG", "Error because userId is " + o.id);
}
}
MyObject.java (the base class for all your objects you get from Retrofit)
public class MyObject {
public long id;
public boolean isError() {
return id == 1;
}
}
MyRealObject.java - a class that extends the base object
public class MyRealObject extends MyObject {
public long userId;
public String title;
public String body;
}
RetroInterface.java - the interface used by retrofit you should be familiar with
import retrofit.http.GET;
import retrofit.http.Path;
public interface RetroInterface {
#GET("/posts/{id}")
void sendGet(#Path("id") int id, MyCallback<MyRealObject> callback);
}
And finally the piece of code where you use all the logic
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint("http://jsonplaceholder.typicode.com")
.build();
RetroInterface itf = adapter.create(RetroInterface.class);
itf.sendGet(2, new MyCallback<MyRealObject>() {
#Override
void handleSuccess(MyRealObject o, Response response) {
Log.v("TAG", "success");
}
#Override
public void failure(RetrofitError error) {
Log.v("TAG", "failure");
}
});
If you copy and paste this code, you'll get an error when you'll execute the itf.sendGet(1, new MyCallback..) and a success for itf.sendGet(2, new MyCallback...)
Not sure I understood it correctly, but you could create one Callback and pass it as a parameter to all of your requests.
Instead of:
git.getFeed(user,new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
}
});
First define your Callback:
Callback<gitmodel> mCallback = new Callback<gitmodel>() {
#Override
public void success(gitmodel gitmodel, Response response) {
}
#Override
public void failure(RetrofitError error) {
// logic to handle error for all requests
}
};
Then:
git.getFeed(user, mCallback);
In Retrofit you can specify ErrorHandler to all requests.
public class ApiErrorHandler implements ErrorHandler {
#Override
public Throwable handleError(RetrofitError cause) {
//here place your logic for all errors
return cause;
}
}
Apply it to RestAdapter
RestAdapter.Builder()
.setClient(client)
.setEndpoint(endpoint)
.setErrorHandler(errorHandler)
.build();
I think that it is what you asked for.
In Retrofit2 you can't set an ErrorHandler with the method .setErrorHandler(), but you can create an interceptor to fork all possible errors centralised in one place of your application.
With this example you have one centralised place for your error handling with Retrofit2 and OkHttpClient. Just reuse the Retrofit object (retrofit).
You can try this standalone example with a custom interceptor for network and server errors. These both will be handled differently in Retrofit2, so you have to check the returned error code from the server over the response code (response.code()) and if the response was not successful (!response.isSuccessful()).
For the case that the user has no connection to the network or the server you have to catch an IOException of the method Response response = chain.proceed(chain.request()); and handle the network error in the catch block.
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
try {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful()) {
Log.e("tag", "Failure central - response code: " + response.code());
Log.e("tag", "central server error handling");
// Central error handling for error responses here:
// e.g. 4XX and 5XX errors
switch (response.code()) {
case 401:
// do something when 401 Unauthorized happened
// e.g. delete credentials and forward to login screen
// ...
break;
case 403:
// do something when 403 Forbidden happened
// e.g. delete credentials and forward to login screen
// ...
break;
default:
Log.e("tag", "Log error or do something else with error code:" + response.code());
break;
}
}
return response;
} catch (IOException e) {
// Central error handling for network errors here:
// e.g. no connection to internet / to server
Log.e("tag", e.getMessage(), e);
Log.e("tag", "central network error handling");
throw e;
}
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://10.0.2.2:8000/api/v1/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
UserRepository backendRepository = retrofit.create(UserRepository.class);
backendRepository.getUser("userId123").enqueue(new Callback<UserModel>() {
#Override
public void onResponse(Call<UserModel> call, retrofit2.Response<UserModel> response) {
Log.d("tag", "onResponse");
if (!response.isSuccessful()) {
Log.e("tag", "onFailure local server error handling code:" + response.code());
} else {
// its all fine with the request
}
}
#Override
public void onFailure(Call<UserModel> call, Throwable t) {
Log.e("tag", "onFailure local network error handling");
Log.e("tag", t.getMessage(), t);
}
});
UserRepository example:
public interface UserRepository {
#GET("users/{userId}/")
Call<UserModel> getUser(#Path("userId") String userId);
}
UserModel example:
public class UserModel implements Parcelable {
#SerializedName("id")
#Expose
public String id = "";
#SerializedName("email")
#Expose
public String mail = "";
public UserModel() {
}
protected UserModel(Parcel in) {
id = in.readString();
mail = in.readString();
}
public static final Creator<UserModel> CREATOR = new Creator<UserModel>() {
#Override
public UserModel createFromParcel(Parcel in) {
return new UserModel(in);
}
#Override
public UserModel[] newArray(int size) {
return new UserModel[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(mail);
}
}
Fairly simply Retrofit custom error handling example. Is set up so that you don't need to do much work in the 'failure' handler of a retrofit call to get the user-visible error message to show. Works on all endpoints. There's lots of exception handling as our server folks like to keep us on our toes by sending all kinds of random stuff..!
// on error the server sends JSON
/*
{ "error": { "data": { "message":"A thing went wrong" } } }
*/
// create model classes..
public class ErrorResponse {
Error error;
public static class Error {
Data data;
public static class Data {
String message;
}
}
}
//
/**
* Converts the complex error structure into a single string you can get with error.getLocalizedMessage() in Retrofit error handlers.
* Also deals with there being no network available
*
* Uses a few string IDs for user-visible error messages
*/
private static class CustomErrorHandler implements ErrorHandler {
private final Context ctx;
public CustomErrorHandler(Context ctx) {
this.ctx = ctx;
}
#Override
public Throwable handleError(RetrofitError cause) {
String errorDescription;
if (cause.isNetworkError()) {
errorDescription = ctx.getString(R.string.error_network);
} else {
if (cause.getResponse() == null) {
errorDescription = ctx.getString(R.string.error_no_response);
} else {
// Error message handling - return a simple error to Retrofit handlers..
try {
ErrorResponse errorResponse = (ErrorResponse) cause.getBodyAs(ErrorResponse.class);
errorDescription = errorResponse.error.data.message;
} catch (Exception ex) {
try {
errorDescription = ctx.getString(R.string.error_network_http_error, cause.getResponse().getStatus());
} catch (Exception ex2) {
Log.e(TAG, "handleError: " + ex2.getLocalizedMessage());
errorDescription = ctx.getString(R.string.error_unknown);
}
}
}
}
return new Exception(errorDescription);
}
}
// When creating the Server...
retrofit.RestAdapter restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(apiUrl)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.setErrorHandler(new CustomErrorHandler(ctx)) // use error handler..
.build();
server = restAdapter.create(Server.class);
// Now when calling server methods, get simple error out like this:
server.postSignIn(login,new Callback<HomePageResponse>(){
#Override
public void success(HomePageResponse homePageResponse,Response response){
// Do success things!
}
#Override
public void failure(RetrofitError error){
error.getLocalizedMessage(); // <-- this is the message to show to user.
}
});

Android UI, Networking and async code

I have a serious problem which I can't a solution to.
I need to authenticate a token in order to let the user login into my app, the problem is that even though I'm using the AsyncTask, and probably because of it, I can't authenticate it in time. Other problem that sometimes accurs is that I get the NetworkOnMainThreadException error... I'm really hopeless.
Here's the flow -
Check for existsing token -> Validate -> Move to next activity
And here's my code -
public boolean validateToken(TokenAccess token) {
new IsValid().execute(token);
return isValid;
}
private class IsValid extends AsyncTask<TokenAccess, Void, Boolean> {
#Override
protected Boolean doInBackground(TokenAccess... params) {
TokenAccess token = params[0];
switch (token.getSource().getSource()) {
case 'M':
new UrlDownloader(new UrlDownloader.DownloadListener() {
#Override
public void setRequest(HttpRequest request) {}
#Override
public void onRecive(String content) {
if (content.contains("stats")) {
isValid = true;
} else {
isValid = false;
}
}
#Override
public void onError(Exception e) {}
}, UrlDownloader.RequestType.GET)
.execute("https://api.meetup.com/dashboard?access_token="
+ token.getToken());
}
return isValid;
}
}
That's is the URLDownloader class -
public class UrlDownloader extends AsyncTask<String, Void, HttpResponse> {
public static final String TAG = "net.ytsweb.socigo.assests.UrlDownloader";
public enum RequestType {
GET, POST;
}
private RequestType type;
private DownloadListener listener;
public UrlDownloader(DownloadListener listener, RequestType type) {
this.type = type;
this.listener = listener;
}
#Override
protected HttpResponse doInBackground(String... params) {
HttpClient httpClient = new DefaultHttpClient();
HttpUriRequest request;
HttpResponse response;
if (type == RequestType.GET) {
request = new HttpGet(params[0]);
} else {
request = new HttpPost(params[1]);
}
listener.setRequest(request);
try {
response = httpClient.execute(request);
} catch (Exception e) {
listener.onError(e);
return null;
}
return response;
}
#Override
protected void onPostExecute(HttpResponse response) {
try {
Log.d(TAG, response.getAllHeaders()[0].getValue() + "");
listener.onRecive(EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
listener.onError(e);
}
}
public interface DownloadListener {
public void onRecive(String content);
public void onError(Exception e);
public void setRequest(HttpRequest request);
}
}
You need to use onPostExecute for handling the result of the IsValid AsyncTask. What I don't understand: why two AsyncTasks? One would be enough, do everything in there, and handle the result in the one and only onPostExecute.
Whatever happens in doInBackground is in a separate thread, onPostExecute happens on the UI-thread again. One AsyncTask is enough, but don't fetch a result in your validateToken method. In there, just execute your AsyncTask and whatever you need to do with the result you have to initiate in the onPostExecute.
As a basic example of what I mean:
public boolean validateToken(TokenAccess token) {
new YourAsyncTask().execute(token);
// DON'T rely on a result here
}
public class YourAsyncTask extends AsyncTask<?, ?, ?> {
#Override
protected ? doInBackground(?) {
// do networking in background-task
return result;
}
#Override
protected void onPostExecute(? response) {
// handle result here.. call a method in your main class, a listener with the result, or start an Activity directly
}
}

Categories

Resources