okhttp response.body().string() android.os.NetworkOnMainThreadExceptin [duplicate] - java

I want to use OkHttp library for networking in Android.
I started with the simple post example as written in their website:
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
With this call:
String response = post("http://www.roundsapp.com/post", json);
This call ends with NetworkOnMainThreadException.
I could wrap the call with an AsyncTask, but as far as I understand from the examples, the OkHttp library should have already taken care of that..
Am I doing something wrong?

You should use OkHttp's async method.
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
Call post(String url, String json, Callback callback) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Call call = client.newCall(request);
call.enqueue(callback);
return call;
}
And then your response would be handled in the callback (OkHttp 2.x):
post("http://www.roundsapp.com/post", json, new Callback() {
#Override
public void onFailure(Request request, Throwable throwable) {
// Something went wrong
}
#Override public void onResponse(Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Or OkHttp 3.x/4.x:
post("http://www.roundsapp.com/post", "", new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// Something went wrong
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String responseStr = response.body().string();
// Do what you want to do with the response.
} else {
// Request not successful
}
}
});
Take a look at their recipes for more examples: http://square.github.io/okhttp/recipes/

According to the OkHttp docs:
It supports both synchronous blocking calls and async calls with callbacks.
Your example is on main thread and Android since version 3.0 throws that exception if you try to do network calls on main thread
Better option is to use it together with retrofit and Gson:
http://square.github.io/retrofit/
https://code.google.com/p/google-gson/
Here are the examples:
http://engineering.meetme.com/2014/03/best-practices-for-consuming-apis-on-android/
http://heriman.net/?p=5

If you follows these steps to implement OKHTTP, then definitely you'll call multiple API on multiple screen by applying only two lines of code
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.getJsonData();
Step 1:
baseHTTPRequest = new BaseHTTPRequest();
// baseHTTPRequest.setURL("https://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demohttps://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo");
baseHTTPRequest.setURL("http://jsonparsing.parseapp.com/jsonData/moviesDemoItem.txt");
baseHTTPRequest.setRequestCode(reqType);
baseHTTPRequest.setCachedRequired(true);
UpdateListener updateListener = new UpdateListener(HitAPIActivity.this, baseHTTPRequest);
updateListener.executeRequest();
Step 2 : Create a request class
/**
* Created by Deepak Sharma on 4/7/16.
* This is a HTTP request class which has the basic parameters.
* If you wants to add some more parameters, please make a subclass of that class
* and add with your subclass. Don't modify this class.
*/
public class BaseHTTPRequest<T> {
private Context context;
private String URL;
private int requestCode;
private List<T> listParameters;
private String header;
private boolean isCachedRequired;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getURL() {
return URL;
}
public int getRequestCode() {
return requestCode;
}
public void setRequestCode(int requestCode) {
this.requestCode = requestCode;
}
public List<T> getListParameters() {
return listParameters;
}
public void setListParameters(List<T> listParameters) {
this.listParameters = listParameters;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public boolean isCachedRequired() {
return isCachedRequired;
}
public void setCachedRequired(boolean cachedRequired) {
isCachedRequired = cachedRequired;
}
}
step 4 : Create a listener class
import android.util.Log;
import com.google.gson.Gson;
import java.io.IOException;
import dxswifi_direct.com.wifidirectcommunication.base.model.request.BaseHTTPRequest;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* Created by Deepak Sharma on 4/7/16.
* #email : dpsharma.sharma1#gmail.com
* This is a Simple java class which will help you for HTTP request/response and it will
* throw the response to your correspondance activity.
*/
public class UpdateListener {
private OnUpdateViewListener onUpdateViewListener;
OkHttpClient okHttpClient = new OkHttpClient();
BaseHTTPRequest mRequestModel;
private String mURL = null;
private Request mRequest = null;
public interface OnUpdateViewListener {
void updateView(String responseString, boolean isSuccess,int reqType);
}
public UpdateListener(OnUpdateViewListener onUpdateView, final BaseHTTPRequest requestModel) {
this.mRequestModel = requestModel;
this.onUpdateViewListener = onUpdateView;
if (requestModel.isCachedRequired())
{
/*File httpCacheDirectory = new File(requestModel.getContext().getCacheDir(), "responses");
Cache cache = null;
cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024);
if (cache != null) {
okHttpClient.setCache(cache);
}*/
}
/*mURL = null;
if (requestModel.getListParameters()!=null && requestModel.getListParameters().size()>0)
{
HttpUrl.Builder urlBuilder = HttpUrl.parse(requestModel.getURL()).newBuilder();
List<RequestParameter> requestParameters = requestModel.getListParameters();
for (int i=0; i<requestParameters.size();i++)
{
urlBuilder.addQueryParameter(requestParameters.get(i).getKey(),requestParameters.get(i).getValue());
}
mURL = urlBuilder.build().toString();
}
else
{
mURL = requestModel.getURL();
}*/
mURL = requestModel.getURL();
if (mRequestModel.getListParameters()!=null && mRequestModel.getListParameters().size()>1)
{
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
mRequest = new Request.Builder()
.url(mURL)
.post(RequestBody.create(JSON, new Gson().toJson(BaseHTTPRequest.class)))
.build();
}
else
{
mRequest = new Request.Builder()
.url(mURL)
.build();
}
}
public void executeRequest()
{
Call call = okHttpClient.newCall(mRequest);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
onUpdateViewListener.updateView(NetworkException.getErrorMessage(e), false, mRequestModel.getRequestCode());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
// You can also throw your own custom exception
throw new IOException("Unexpected code " + response);
} else {
Log.i("Response:",response.toString());
Log.i("Response body:",response.body().toString());
Log.i("Response message:",response.message());
onUpdateViewListener.updateView(response.body().string(),true, mRequestModel.getRequestCode());
}
// do something wih the result
}
});
}
}
step 5 : From the activity you requesting, implement listener
public class HitAPIActivity extends AppCompatActivity implements View.OnClickListener, UpdateListener.OnUpdateViewListener{
#Override
public void updateView(final String responseString, boolean isSuccess, int reqType) {
if (isSuccess)
{
if (!responseString.contains("failure")
&& !responseString.contains("Error")) {
// Handle request on the basis of Request Type.
switch (reqType) {
case ApiConstants.GET_CONTACTS:
break;
default:
break;
}
}
}
}

Related

cannot print data from response

I'm trying to get data from api
I did response model,and apiInterface ,ApiClient etc.
It's all good,and there are no code mistakes
,but the problem is that response body is empty.
In this example I decided to know the cause of the problem ,but i didn't know.
Api: http://api.serpstack.com/search?access_key=8cdb389dedab3a1b462de83a67921de2&query=+%22cart%22%20-intitle:%22profiles%22%20-inurl:%22dir/+%22+site:linkedin.com/in/+OR+site:linkedin.com/pub/
// Logcat : I/trace: []
//MainActivity :
public class MainActivity extends AppCompatActivity {
ApiClient apiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
apiClient = new ApiClient().getInstance(MainActivity.this);
Call<Response>call = apiClient.sNewsApi.getResultSearch("8cdb389dedab3a1b462de83a67921de2","+%22cart%22%20-intitle:%22profiles%22%20-inurl:%22dir/+%22+site:linkedin.com/in/+OR+site:linkedin.com/pub/");
call.enqueue(new Callback<Response>(){
#Override
public void onResponse(Call<Response> call, retrofit2.Response<Response> response) {
if(response.isSuccessful()){
Log.i("trace"," "+response.body().getOrganicResults());
}else {
Log.i("trace","Not");
}
}
#Override
public void onFailure(Call<Response> call, Throwable t) {
Log.i("trace","Not"+t.getMessage());
}
});
}
}
// ApiInterface :
public interface ApiInterface {
#GET("search")
Call<Response>getResultSearch(#Query("access_key") String access_key,#Query("query") String query);
}
// ApiClient :
public class ApiClient {
public static final String NEWS_API_URL ="http://api.serpstack.com/";
private static final Object LOCK = new Object();
public static ApiInterface sNewsApi;
public static ApiClient sInstance;
public ApiClient() {
}
public static ApiClient getInstance(Context context) {
if (sInstance == null || sNewsApi == null) {
synchronized (LOCK) {
Cache cache = new Cache(context.getApplicationContext().getCacheDir(), 5 * 1024 * 1024);
Interceptor networkInterceptor = new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(1, TimeUnit.HOURS)
.maxStale(3, TimeUnit.DAYS)
.build();
return chain.proceed(chain.request())
.newBuilder()
.header("Cache-Control", cacheControl.toString())
.build();
}
};
// For logging
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);
// Building OkHttp client
OkHttpClient client = new OkHttpClient.Builder()
/// .cache(cache)
.addNetworkInterceptor(networkInterceptor)
.addInterceptor(loggingInterceptor)
.build();
// Configure GSON
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateDeserializer()).create();
// Retrofit Builder
Retrofit.Builder builder = new Retrofit.Builder().
baseUrl(NEWS_API_URL).client(client).addConverterFactory(GsonConverterFactory.create(gson));
// Set NewsApi instance
sNewsApi = builder.build().create(ApiInterface.class);
sInstance = new ApiClient();
}
}
return sInstance;
}
}
// Response :
public class Response{
#SerializedName("request")
private Request request;
#SerializedName("pagination")
private Pagination pagination;
#SerializedName("organic_results")
private List<OrganicResultsItem> organicResults;
#SerializedName("search_information")
private SearchInformation searchInformation;
#SerializedName("search_parameters")
private SearchParameters searchParameters;
public Request getRequest(){
return request;
}
public Pagination getPagination(){
return pagination;
}
public List<OrganicResultsItem> getOrganicResults(){
return organicResults;
}
public SearchInformation getSearchInformation(){
return searchInformation;
}
public SearchParameters getSearchParameters(){
return searchParameters;
}
}
It's simple, just add this line:
System.out.println(response.body());
and this will show what you have in the response, make sure to put this inside
if (response.isSuccessful()) {
System.out.println(response.body());
Log.i("trace"," "+response.body().getOrganicResults());
} else {
Log.i("trace","Not");
}

Is there a clean way to cancel an OkHttp 3 request when RxJava 2 Disposable is disposed?

So I've been trying to find a clean way of solving this issue for a while. Here's REST API class:
public class RestClient {
private final IJsonParser jsonParser;
private IServerUtils serverUtils;
private String baseApiUrl;
private OkHttpClient okHttpClient;
#Inject
public RestClient(OkHttpClient okHttpClient, IJsonParser jsonParser, IServerUtils serverUtils, #Named("baseApiUrl") String baseApiUrl) {
this.okHttpClient = okHttpClient;
this.jsonParser = jsonParser;
this.serverUtils = serverUtils;
this.baseApiUrl = baseApiUrl;
}
public Single<String> get(String route) {
if (serverUtils.isThereInternetConnection()) {
Request request = new Request.Builder()
.get()
.url(baseApiUrl + route)
.build();
final Call call = okHttpClient.newCall(request);
return Single.create(emitter -> {
execute(call, emitter);
});
} else {
return Single.error(new NetworkException());
}
}
public Single<String> post(String route, RequestBody requestBody) {
if (serverUtils.isThereInternetConnection()) {
Request request = new Request.Builder()
.post(requestBody)
.url(baseApiUrl + route)
.build();
final Call call = okHttpClient.newCall(request);
return Single.create(emitter -> execute(call, emitter));
} else {
return Single.error(new NetworkException());
}
}
public Single<String> postNoCache(String route, RequestBody requestBody) {
if (serverUtils.isThereInternetConnection()) {
Request request = new Request.Builder()
.post(requestBody)
.url(baseApiUrl + route)
.cacheControl(CacheControl.FORCE_NETWORK)
.build();
final Call call = okHttpClient.newCall(request);
return Single.create(emitter -> execute(call, emitter));
} else {
return Single.error(new NetworkException());
}
}
Response postRaw(String route, RequestBody requestBody) throws IOException {
Request request = new Request.Builder()
.post(requestBody)
.url(baseApiUrl + route)
.build();
return okHttpClient.newCall(request).execute();
}
public RequestBody convertHashMapToRequestBody(HashMap<String, String> params) {
FormBody.Builder builder = new FormBody.Builder();
for (String key : params.keySet()) {
builder.add(key, params.get(key));
}
return builder.build();
}
private void execute(Call call, SingleEmitter<String> emitter) throws IOException {
try {
Response response = call.execute();
String responseJson = response.body().string();
if (response.isSuccessful()) {
emitter.onSuccess(responseJson);
} else {
handleErrors(emitter, response, responseJson);
}
} catch (InterruptedIOException e) {
// This is my issue.
}
}
private void handleErrors(SingleEmitter<String> emitter, Response response, String responseJson) {
if (response.code() == 500 && responseJson != null) {
ApiError error = jsonParser.parseApiError(responseJson);
emitter.onError(new ApiException(error.getErrorMsg(), error.getError(), error.getUri()));
} else {
emitter.onError(new NetworkException("timeout"));
}
}
So in my execute() method I'd like to somehow maybe get some sort of callback when my upstream observer is destroyed, so I can cancel my network request. Currently what seems to happen is the thread that the Single is running on gets an interrupt, which throws an Exception, I'm not sure if this stops execution though. Is there a clean way of doing that? The only thing I've seen that you can do is the emitter.isDisposed(), which I can't see a normal usecase, since everything is synchronous in here and by the time I check that value it's most likely too late.
any help with this would be greatly appreciated.

Best way to use retrofit response in several activies

I have a function searchForTrips() which sends an API request and fetch some response in following way.
private void searchForTrips(){
int departurePortId = PORT_ID_LIST.get(departurePort);
int returnPortId = PORT_ID_LIST.get(returnPort);
int pax= Integer.parseInt(noOfPassengers);
String departureDatePARSED = DEPARTURE_DATE_VALUES.get(departureDate);
String returnDatePARSED = RETURN_DATE_VALUES.get(departureDate);
Call<TripSearchResponse> call = apiService.searchAvailableTrips(TripType,departurePortId,returnPortId,departureDatePARSED,returnDatePARSED,pax);
call.enqueue(new Callback<TripSearchResponse>() {
#Override
public void onResponse(Call<TripSearchResponse> call, Response<TripSearchResponse> response) {
int statusCode = response.code();
switch(statusCode){
case 200:
default:
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
break;
}
}
#Override
public void onFailure(Call<TripSearchResponse> call, Throwable t) {
Log.i(TAG, t.getMessage());
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
}
});
}
The purpose is to make this callback function reusable so I can call it from several activities and get requested data as I need. What is the best way to implement this?
try this way, its dynamic way and easy to use:
Create Retforit Interface:
public interface ApiEndpointInterface {
#Headers("Content-Type: application/json")
#POST(Constants.SERVICE_SEARCH_TRIP)
Call<JsonObject> searchForTrip(#Body TripRequest objTripRequest);
}
Create Retrofit Class:
public class AppEndPoint {
private static Retrofit objRetrofit;
public static ApiEndpointInterface getClient() {
if (objRetrofit == null){
objRetrofit = new Retrofit.Builder()
.baseUrl(Constants.SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return objRetrofit.create(ApiEndpointInterface.class);
}
}
Create this helper Classes/Interfaces to hold web service callback:
public enum ResponseState {
SUCCESS,
FAILURE,
NO_CONNECTION
}
public enum RequestType {
SEARCH_FOR_TRIP // add name for each web service
}
public class Response {
public ResponseState state;
public boolean hasError;
public RequestType requestType;
public JsonObject result;
}
public interface RestRequestInterface {
void Response(Response response);
Context getContext();
}
public class ResponseHolder { used to hold the Json response could be changed as your response
#SerializedName("is_successful")
#Expose
private boolean isSuccessful;
#SerializedName("error_message")
#Expose
private String errorMessage;
public boolean isSuccessful() {
return isSuccessful;
}
public void setSuccessful(boolean successful) {
isSuccessful = successful;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
public class AppClient {
private static ApiEndpointInterface objApiEndpointInterface;
private static Response objResponse;
private static Call<JsonObject> objCall;
// implement new method like below for each new web service
public static void searchForTrip(TripRequest objTripRequest, RestRequestInterface objRestRequestInterface) {
objResponse = new Response();
objResponse.state = ResponseState.FAILURE;
objResponse.hasError = true;
objResponse.requestType = RequestType.SEARCH_FOR_TRIP; // set type of the service from helper interface
objApiEndpointInterface = AppEndPoint.getClient();
objCall = objApiEndpointInterface.searchForTrip(objTripRequest);
handleCallBack(objRestRequestInterface);
}
private static void handleCallBack(final RestRequestInterface objRestRequestInterface) {
objCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
try {
ResponseHolder objResponseHolder = new Gson().fromJson(response.body(), ResponseHolder.class);
if (objResponseHolder.isSuccessful()) {
objResponse.state = ResponseState.SUCCESS;
objResponse.hasError = false;
objResponse.result = response.body();
} else {
objResponse.errorMessage = objResponseHolder.getErrorMessage();
}
objRestRequestInterface.Response(objResponse);
} catch (Exception objException) {
objResponse.errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
objRestRequestInterface.Response(objResponse);
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable objThrowable) {
String errorMessage = "";
if (objThrowable instanceof IOException) {
errorMessage = objRestRequestInterface.getContext().getString(R.string.no_connection_error);
} else {
errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
}
objResponse.errorMessage = errorMessage;
objRestRequestInterface.Response(objResponse);
}
});
}
}
then go to your activity of fragment and make the call like this:
public class MainActivity extends AppCompatActivity implements RestRequestInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize ids
// prepare to call web service
// 1.Initialize your object to be sent over web service
TripRequest objTripRequest = new TripRequest();
objTripRequest.id = 1;
// 2.Show loader
// 3.Make the call
AppClient.searchForTrip(objTripRequest, this);
}
#Override
public void Response(Response response) {
// hide loader
try {
if (response.state == ResponseState.SUCCESS && !response.hasError) {
// check the type of web service
if (response.requestType == RequestType.SEARCH_FOR_TRIP) {
// acces the return here from response.result
}
} else {
String errorMsg = response.hasError ? response.errorMessage : getString(R.string.no_connection_error);
// show the error to the user
}
} catch (Exception objException) {
// show the error to the user
}
}
#Override
public Context getContext() {
// do not forgit set the context here
// if fragment replace with getAcitvity();
return this;
}
}

Wrong IllegalStateException in Retrofit JSON parsing

Pleace, help me to parse JSON. I always get an object:
IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at
line 1 column 2 path $
I try to pars List<List<String>>, <List<String>> and <String> but get the same exeption.
Here is my JSON:
{"barcodes":[["1212"],["22222222222"],["22222321321"],["23565233665558488"],["2999300031242"],["6"]]}
Interface:
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
public interface RequestInterface {
#GET("barcodeinfo?getBarcodes")
Call<List<List<String>>> getBarcodeList();
}
obj:
public class SingleBarcode {
final String barcodes;
public SingleBarcode(String barcodes) {
this.barcodes = barcodes;
}
}
main:
void getRetrofitArray() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RequestInterface service = retrofit.create(RequestInterface.class);
Call<List<List<String>>> call = service.getBarcodeList();
call.enqueue(new Callback<List<List<String>>>() {
#Override
public void onResponse(Call<List<List<String>>> call, Response<List<List<String>>> response) {
try {
List<List<String>> BarcodeData = response.body();
Log.d("MyLog", BarcodeData.size()+"");
} catch (Exception e) {
Log.d("MyLog", "There is an error");
e.printStackTrace();
}
}
#Override
public void onFailure(Call<List<List<String>>> call, Throwable t) {
Log.d("MyLog", "error " + t.toString());
}
});
}
use these POJO class
public class Result {
#SerializedName("barcodes")
#Expose
private List<List<String>> barcodes = new ArrayList<List<String>>();
/**
*
* #return
* The barcodes
*/
public List<List<String>> getBarcodes() {
return barcodes;
}
/**
*
* #param barcodes
* The barcodes
*/
public void setBarcodes(List<List<String>> barcodes) {
this.barcodes = barcodes;
}
}
use interface like this ...
#GET("barcodeinfo?getBarcodes")
Call<Result> getBarcodeList();
and call like this ....
Call<Result> call = service.getBarcodeList();
call.enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result> response) {
Result r = response.body(); // you can initialize result r variable global if you have out side use of this response
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
Log.d("MyLog", "error " + t.toString());
}
});
NOTE:- You are trying to access as LIST But in response it coming simple JSON OBJECT

How to create a proper Volley Listener for cross class Volley method calling

I aim to call Volley from another class in, a very succinct, modular way ie:
VolleyListener newListener = new VolleyListener();
VolleySingleton.getsInstance().somePostRequestReturningString(getApplicationContext(), newListener);
JSONObject data = newListener.getResponse();
But am having allot of trouble getting the listener portion to work so as to be able to access the resulting data from a method such as
newListener.getResponse();
There are a few questions on this site that generally outline how to set up a volley call from another class, such as: Android Volley - How to isolate requests in another class. I have had success getting the method call to work, but to now get that data into the present class for usage has caused trouble.
I have the action within my VolleySingleton class as:
public void somePostRequestReturningString(final Context context,final VolleyListener<String> listener) {
final String URL = "http://httpbin.org/ip";
JsonObjectRequest set = new JsonObjectRequest(Request.Method.GET, URL, ((String) null),
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
listener.outPut = response.toString();
//Toast.makeText(context, response.toString(), Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("Error.Response", error.toString());
}
}
);
mRequestQueue.add(set);
}
and within the listener class:
public class VolleyListener {
public static String outPut;
private static Response.Listener<String> createSuccessListener() {
return new Response.Listener<String>() {
#Override
public void onResponse(String response) {
outPut = response;
}
};
}
}
How can I configure this to work and allow Volley calls and data retrieval from another class, particularly how to build callbacks correctly?
For your requirement, I suggest you refer to my following solution, hope it's clear and helpful:
First is the interface:
public interface VolleyResponseListener {
void onError(String message);
void onResponse(Object response);
}
Then inside your helper class (I name it VolleyUtils class):
public static void makeJsonObjectRequest(Context context, String url, final VolleyResponseListener listener) {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
listener.onResponse(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onError(error.toString());
}
}) {
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
};
// Access the RequestQueue through singleton class.
VolleySingleton.getInstance(context).addToRequestQueue(jsonObjectRequest);
}
Then, inside your Activity classes, you can call like the following:
VolleyUtils.makeJsonObjectRequest(mContext, url, new VolleyResponseListener() {
#Override
public void onError(String message) {
}
#Override
public void onResponse(Object response) {
}
});
You can refer to the following questions for more information (as I told you yesterday):
Android: How to return async JSONObject from method using Volley?
POST Request Json file passing String and wait for the response Volley
Android/Java: how to delay return in a method
Volley excels at RPC-type operations used to populate a UI, such as
fetching a page of search results as structured data. It integrates
easily with any protocol and comes out of the box with support for raw
strings, images, and JSON. By providing built-in support for the
features you need, Volley frees you from writing boilerplate code and
allows you to concentrate on the logic that is specific to your app.
How to create Common GET/POST Method Using Volley .
Create a Application Class
The Application class in Android is the base class within an Android
app that contains all other components such as activities and services
public class MyApplication extends Application {
public static final String TAG = MyApplication.class
.getSimpleName();
private RequestQueue mRequestQueue;
private static MyApplication mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized MyApplication getInstance() {
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req, String tag) {
// set the default tag if tag is empty
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
Make Sure you add this Manifest Section .
<application
.....
android:name=".MyApplication"
>
Now, You need to create Singleton Class .
Singleton Pattern says that just define a class that has only one
instance and provides a global point of access to it .
public class MySingleton
{
private static MySingleton mInstance;
private RequestQueue mRequestQueue;
private static Context mCtx;
private MySingleton(Context context)
{
mCtx = context;
mRequestQueue = getRequestQueue();
}
public static synchronized MySingleton getInstance(Context context)
{
if (mInstance == null)
{
mInstance = new MySingleton(context);
}
return mInstance;
}
public RequestQueue getRequestQueue()
{
if (mRequestQueue == null)
{
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req)
{
getRequestQueue().add(req);
}
}
Now Common Class
public class VolleyUtils {
public static void GET_METHOD(Context context, String url, final VolleyResponseListener listener)
{
// Initialize a new StringRequest
StringRequest stringRequest = new StringRequest(
Request.Method.GET,
url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
listener.onResponse(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onError(error.toString());
}
})
{
};
// Access the RequestQueue through singleton class.
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
}
public static void POST_METHOD(Context context, String url,final Map<String,String> getParams, final VolleyResponseListener listener)
{
// Initialize a new StringRequest
StringRequest stringRequest = new StringRequest(
Request.Method.POST,
url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
listener.onResponse(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onError(error.toString());
}
})
{
/**
* Passing some request headers
* */
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
getParams.put("Content-Type", "application/json; charset=utf-8");
return headers;
}
};
// Access the RequestQueue through singleton class.
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
}
}
Now You should create Interface .
A class implements an interface, thereby inheriting the abstract
methods of the interface .
/**
* Created by Intellij Amiyo on 10-06-2017.
* Please follow standard Java coding conventions.
* http://source.android.com/source/code-style.html
*/
public interface VolleyResponseListener {
void onError(String message);
void onResponse(Object response);
}
How To Call
public void _loadAPI()
{
//GET
String URL_GET = "";
VolleyUtils.GET_METHOD(MainActivity.this, URL_GET, new VolleyResponseListener() {
#Override
public void onError(String message) {
System.out.println("Error" + message);
}
#Override
public void onResponse(Object response) {
System.out.println("SUCCESS" + response);
}
});
//POST
String URL_POST=" ";
VolleyUtils.POST_METHOD(MainActivity.this, URL_POST,getParams(), new VolleyResponseListener() {
#Override
public void onError(String message) {
System.out.println("Error" + message);
}
#Override
public void onResponse(Object response) {
System.out.println("SUCCESS" + response);
}
});
}
public Map<String,String> getParams()
{
Map<String, String> params = new HashMap<String, String>();
params.put("YOUR_KEY", "VALUE");
return params;
}
For demo you should Download Volley-Common-Method
If you followed the general example from Android Volley - How to isolate requests in another class, (including the stuff regarding the singleton stuff) and looking for the parsing part (or, how to actually use the objects you receive), then this is the (again very general) addition
say you have a Json object coming in, that looks somewhat like this :
{"users":
[{"username":"Jon Doe","userid":83},
{"username":"Jane Doe",userid":84}]}
and our User object would look something like this:
public class User
{
String username;
int userid;
public String getName()
{
return username;
}
public int getId()
{
return userid;
}
}
Important: When working with Gson (you will see later), the object
fields should be named according to params you get in the Json, this
sort of reflection is how the parsing works.
then, the request itself would look something like this
(note the listener callback returning a
List<User>
object back to the caller, you'll see later):
public class NetworkManager
{
//... other stuff
public void getUsers(final SomeCustomListener<List<User>> listener)
{
final String URL = "http://httpbin.org/ip";
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>()
{
#Override
public void onResponse(String response)
{
Log.d(TAG + ": ", "getUsers Response: " + response);
List<User> users = MyJsonParser.getListObjects(response, "$.users[*]", User.class);
if(null != users)
listener.getResult(users);
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error)
{
if (null != error.networkResponse)
{
Log.d(TAG + ": ", "Error Response code: " + error.networkResponse.statusCode);
listener.getResult(null);
}
}
});
requestQueue.add(request);
// ... other stuff
}
what you would need now is that class to parse the Json string, namely the object list, in this example I use Gson (again - this is a general example, change and reorder stuff according to your needs, you could probably also optimize this some more - it's just for the explanation):
public class MyJsonParser
{
//... other stuff
public static <T> List<T> getListObjects(String json_text, String json_path, Class<T> c)
{
Gson gson = new Gson();
try
{
List<T> parsed_list = new ArrayList<>();
List<Object> nodes = JsonPath.read(json_text, json_path);
for (Object node : nodes)
{
parsed_list.add(gson.fromJson(node.toString(), c));
}
return (parsed_list);
}
catch (Exception e)
{
return (new ArrayList<>());
}
}
//... other stuff
}
So, after we have all this (and the following stuff from the pre-mentioned SO question), what you said you were looking for is the callback in your working code, well that can be achieved in a couple of ways:
A straight forward way:
just call the method and override it's callback right there, e.g:
public class SomeClass
{
private List<User> mUsers;
private void someMethod()
{
// ... method does some stuff
NetworkManager.getInstance().getUsers(new SomeCustomListener<List<User>>()
{
#Override
public void getResult(List<User> all_users)
{
if (null != allUsers)
{
mUsers = allUsers;
// ... do other stuff with our info
}
}
});
// ... method does some more stuff
}
}
Or, in an indirect way (considering the time, memory consumption, etc. ), you can save the info you got in the same Singelton (or another container), and create a get method for it, and just get the object later (looks more slick)
remember: fire the request before (considering the latency for the response), as the nature of these callbacks is to be dependent on the response which might be delayed.
It would then look like this:
private List<User> mUsers;
private void someMethod()
{
// ... method does some stuff
mUsers = NetworkManager.getInstance().getUsersObject();
// ... method does some more stuff
}
A different option entirely would be to consider using Retrofit, that does the parsing for you, uses annotations, and is supposedly a lot faster , that might be what you're looking for (for the streamlined look) - I would read up on benchmarks, especially since the new 2.0 version came out.
Hope this Helps (although somewhat late)! :)

Categories