How to run a retrofit in the background thread? - java

Interface:
I enter the word to text and then click on the button and in the output the result
public interface Link {
#FormUrlEncoded//аннотация
#POST("https://translate.yandex.net/api/v1.5/tr.json/translate")
Call<Object> translate(#FieldMap Map<String,String> map);
}
How to run a retrofit in the background thread?
I enter the word to text and then click on the button and in the output the result,simple translator, I test this library

call.execute(); is a synchronous network call which should not be performed on the main thread.
As they have pointed out to you, the best option you have is to use call.enqueue() like below:
call.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
response = response.body();
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
}
If the call is successful, you get your response in onResponse else in onFailure

With com.squareup.retrofit2:retrofit:2.1.0
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call1, retrofit2.Response<ResponseBody> response) {
BufferedReader reader = null;
StringBuilder sb = new StringBuilder();
reader = new BufferedReader(new InputStreamReader(response.body().byteStream()));
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
result = sb.toString();
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("Tag","onResponse onFailure" );
}
});

Related

Google Directions API using ExecutorService instead of AsyncTask

Every tutorial I find seems to use AsyncTask (depreciated) instead of ExecutorService. I took a java course on Udemy and they used AsyncTask for everything as well. Here is one class I'm working with:
public class FetchURL extends AsyncTask<String, Void, String> {
Context mContext;
String directionMode = "driving";
public FetchURL(Context mContext) {
this.mContext = mContext;
}
#Override
protected String doInBackground(String... strings) {
// For storing data from web service
String data = "";
directionMode = strings[1];
try {
// Fetching the data from web service
data = downloadUrl(strings[0]);
Log.d("mylog", "Background task data " + data.toString());
} catch (Exception e) {
Log.d("Background Task", e.toString());
}
return data;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
PointsParser parserTask = new PointsParser(mContext, directionMode);
// Invokes the thread for parsing the JSON data
parserTask.execute(s);
}
private String downloadUrl(String strUrl) throws IOException {
String data = "";
InputStream iStream = null;
HttpURLConnection urlConnection = null;
try {
URL url = new URL(strUrl);
// Creating an http connection to communicate with url
urlConnection = (HttpURLConnection) url.openConnection();
// Connecting to url
urlConnection.connect();
// Reading data from url
iStream = urlConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
StringBuffer sb = new StringBuffer();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
data = sb.toString();
Log.d("mylog", "Downloaded URL: " + data.toString());
br.close();
} catch (Exception e) {
Log.d("mylog", "Exception downloading URL: " + e.toString());
} finally {
iStream.close();
urlConnection.disconnect();
}
return data;
}
}
and I'd really like to use ExecutorService like here instead of AsyncTask. I'm beating my head against the wall and I can't seem to get the proper arguments in and this thing working.
Replace your AsyncTask with a Runnable:
public class FetchUrl implements Runnable {
public interface Callback {
void onSuccess(String data);
void onFailure(Exception e);
}
private String url;
private WeakReference<Callback> callbackWeakReference;
public FetchUrl(String url, Callback callback) {
this.url = url;
this.callbackWeakReference = new WeakReference<>(callback);
}
#Override
public void run() {
try {
String data = downloadUrl(url);
Callback callback = callbackWeakReference.get();
if (callback != null) {
callback.onSuccess(data);
}
} catch (Exception e) {
Callback callback = callbackWeakReference.get();
if (callback != null) {
callback.onFailure(e);
}
}
}
... // include your downloadUrl function
}
Then create and submit it to the ExecutorService:
FetchUrl.Callback callback = new FetchUrl.Callback() {
#Override
public void onSuccess(String data) {
// handle your data
}
#Override
public void onFailure(Exception e) {
// handle the exception
}
};
Runnable job = new FetchUrl(url, callback);
ExecutorService executorService = Executors.newFixedThreadPool(4);
executorService.submit(job);
Notice I used a WeakReference<Callback>, because code in your callback is holding a reference to Context and would cause Context leaks.
The submit() function returns a Future to control your submitted job. It's handy if you want to cancel the job or want to wait for its completion (blocking the current thread). The latter usecase would perhaps favor using Callable<Result> instead of Runnable, because the calling thread can handle the exception and there would be no use for a callback making your code more concise.
Also don't forget to call shutdown() on your ExecutorService when you no longer need it.

RxJava Android: Observer incompatible types error

I have the following Async network call I'd like to make by using RxJava and RxAndroid. I included the latest RxJava (3.0.1) and RxAndroid (3.0.0) in the project dependencies, but still get the following error in the Subscription subscription = .... block:
Incompatible types
Required: org.reactivestreams.Subscription
Found: void
Why is that? Can anyone tell me how to make this RxJava observable work?
Any help apprechiated, thanks.
Subscription subscription = getServerResponse(et_id.getText().toString())// error highlighted in this whole block
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(String bitmap) {
// Handle result of network request
}
#Override
public void onError(#NonNull Throwable e) {
// Update user interface to handle error
}
#Override
public void onComplete() {
// Update user interface if needed
}
});
public Observable<String> getServerResponse(String string) {
return Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
BufferedReader inFromServer = null;
String response = "";
Socket clientSocket = null;
try {
clientSocket = new Socket(serverHostname, serverPort);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
outToServer.writeBytes(string + "\n");
response = inFromServer.readLine();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
response = MainActivity.this.getString(R.string.result_serverError);
}
return response;
}
});
}
All my imports for reactive java:
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
try using a composite disposable. It would be something like this:
private CompositeDisposable compositeDisposable = CompositeDisposable();
compositeDisposable.add(getServerResponse(et_id.getText().toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(String bitmap) {
// Handle result of network request
}
#Override
public void onError(#NonNull Throwable e) {
// Update user interface to handle error
}
#Override
public void onComplete() {
// Update user interface if needed
}
}));
Dont forget to do a compositeDisposable.dispose() on the onDestroy method of your activity.
Let me know if it works for you!

Android, Volley Request, the response is blocking main thread

Something bad is happening when using Volley to treat a large response:
String url = AppHelper.DOMAIN + "/service/pages/profile_update.json";
this.infoTextView.setText(getString(R.string.profile_info_updating));
final StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject json = new JSONObject(response);
if (json.getBoolean("success")) {
// manage JSON object here
} else {
Toast.makeText(ProfileActivity.this,
getString(R.string.connection_problem_server),
Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
ProfileActivity.this.infoTextView.setText(
getString(R.string.profile_info_updating_error));
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
ProfileActivity.this.infoTextView.setText(
getString(R.string.profile_info_updating_error));
if (error.networkResponse != null && error.networkResponse.statusCode == 401) {
Toast.makeText(ProfileActivity.this,
getString(R.string.connection_problem_permission),
Toast.LENGTH_LONG).show();
}
new android.os.Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (ProfileActivity.this.swipeRefreshLayout != null) {
ProfileActivity.this.swipeRefreshLayout.setRefreshing(false);
}
}
}, 1000);
error.printStackTrace();
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("auth_token", ProfileActivity.this.defaultUser.getAuthenticationToken());
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.putAll(super.getHeaders());
params.put("Accept-Encoding", "gzip,deflate");
return params;
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
StringBuilder output = new StringBuilder();
try {
GZIPInputStream gStream = new GZIPInputStream(new ByteArrayInputStream(response.data));
InputStreamReader reader = new InputStreamReader(gStream);
BufferedReader in = new BufferedReader(reader, 16384);
String read;
while ((read = in.readLine()) != null) {
output.append(read).append("\n");
}
reader.close();
in.close();
gStream.close();
} catch (IOException error) {
error.printStackTrace();
return Response.error(new ParseError());
}
return Response.success(output.toString(), HttpHeaderParser.parseCacheHeaders(response));
}
};
stringRequest.setRetryPolicy(new RetryPolicy() {
#Override
public int getCurrentTimeout() {
// 40 seconds
return 40000;
}
#Override
public int getCurrentRetryCount() {
return DefaultRetryPolicy.DEFAULT_MAX_RETRIES;
}
#Override
public void retry(VolleyError error) throws VolleyError {
throw error;
}
});
Volley.newRequestQueue(this).add(stringRequest);
this code block the main thread, freezing the application.
Additionally, I set some header values to allow gzip response and a code to handle the data. But it is not the piece that make the undesired behavior. The application freeze only when onResponse(String response) starts.
What can I do to avoid this?
onResponse and onErrorResponse is called on UI thread hence any heavy operation done inside these methods will make you application less responsive. I guess you are trying to parse the response in onResponse() which is incorrect.
You have to move to parsing logic to parseNetworkResponse since this
is the method which is called in background thread. Refer the below link for more details :
https://developer.android.com/training/volley/request-custom.html
If it help someone , try to create a new thread in a method inside onResponse , inside that thread execute your parsing data. I hope the answer works for you

Sending POST Request and Receive Json Responds using Retrofit Unable to use Responds data I need to pass it to another activity

I am using Retrofit for sending POST request.Server return's a JSON Response and i am able to parse the response in the callback method. I need to pass the data from server to another activity. But i can't use the response data outside.
api.LoginUser(
Email.getText().toString(), // passing value to interface of retrofit
Password.getText().toString(),
new Callback<Response>() {
#Override
public void success(Response result, Response response) {
BufferedReader reader = null;
String output = "";
try {
reader = new BufferedReader(new InputStreamReader(result.getBody().in()));
output = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
//Json PArsing
try {
JSONObject mainObject = new JSONObject(output);
JSONObject dataObj = mainObject.getJSONObject("data");
String id = dataObj.getString("id");
String name = dataObj.getString("name");
n=name;
Log.d("jsontext", n); //This works
}
catch(JSONException e)
{
e.printStackTrace();
}
Toast.makeText(MainActivity.this, output, Toast.LENGTH_LONG).show();
}
#Override
public void failure(RetrofitError error) {
//If any error occured displaying the error as toast
Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show();
}
}
);
I can't use this when it executes App crashes.its ok now there is no value in the variable.how to get values out of the callback Responds OnSuccess method???
Log.d("outer",n);
Intent dash = new Intent(this,Dashboard.class);
dash.putExtra("Value",fi);
startActivity(dash);
}
You can create an object and implement Serializable:
class User implements Serializable {
...
}
Then put object User to bundle, add to intent:
Bundle bundle = new Bundle();
bundle.putSerializable("data", user);
Intent intent = new Intent(this, YourClass.class);
intent.putExtras(bundle);
startActivity(intent);
Hope it help you.
Hold all data in a string and using intent app another activity and parse it;
You can do it as follows
public void onSuccess(int statusCode, Header[] headers,
byte[] response) {
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(new ByteArrayInputStream(
response)));
String st = "";
String st1 = "";
while ((st = br.readLine()) != null) {
st1 = st1 + st;
}
showStoreData(st1);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onFailure(int statusCode, Header[] headers,
byte[] errorResponse, Throwable e) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
Log.e("FAIL", "FAIl" + statusCode);
}
#Override
public void onRetry(int retryNo) {
// called when request is retried
}
});
after that
public void showStoreData(String st) {
Intent intent = new Intent(this, YourClass.class);
intent.putExtras(st);
startActivity(intent);
}
You should use an interface that you initialize from the calling method and pass as a parameter into you request class, that way you can call the requests from anywhere and get the callback response back to where you called it from, an example would be:
A general interface, separated in another file:
public interface SomeCustomListener<T>
{
public void getResult(T object);
}
In the class holding your call (complete the stuff you need):
public void someRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
//here you initialize what you need... it's your stuff
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...");
}
}
});
}
Then from where you're calling the request (could be anywhere, Fragments, onClicks, etc):
public class BlaBla
{
//.....
public void someMethod()
{
NetworkManager.getInstance().someRequestReturningString(someObject, new SomeCustomListener<String>()
{
#Override
public void getResult(String result)
{
if (!result.isEmpty())
{
//do what you need with the result...
}
}
});
}
}
If you need more context, you can refer to this SO thread.
Hope this helps!

trying to call join on a worker thread

I have an onClickListener that uses Okhttp to asynchronously get some stuff in the background. Here's the OnClickListener:
mGetChartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String companyName = mSymbolValue.getText().toString();
getRequest(companyName, "chart");
Log.i(TAG, mChartProfile.getSizeDates()+""); // Null exception happens here
}
});
And here is the Okhttp snippet:
OkHttpClient client = new OkHttpClient();
Request request = new Request
.Builder()
.url(completeUrl)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
String jsonData = response.body().string();
Log.v(TAG, jsonData);
if (response.isSuccessful()) {
if (requestType.equals("quote")) {
isValidSearch = getQuote(jsonData);
runOnUiThread(new Runnable() {
#Override
public void run() {
if (isValidSearch) {
updateDisplay();
}
toggleFacts(isValidSearch);
}
});
}
else{
getChartInfo(jsonData);
}
} else {
alertUserAboutError();
}
} catch (IOException e) {
Log.e(TAG, "Exception caught: ", e);
} catch (JSONException e) {
Log.e(TAG, "JSONException caught: ", e);
Toast.makeText(BuyActivity.this, "oops!", Toast.LENGTH_LONG).show();
}catch (ParseException e){
Log.e(TAG, "Unable to parse", e);
}
}
});
// How do I do a thread.join() here?
private void getChartInfo(String jsonData) throws JSONException, ParseException{
JSONObject wholeChartData = new JSONObject(jsonData);
JSONArray dates = wholeChartData.getJSONArray("Dates");
mChartProfile = new ChartProfile();
// ChartProfile contains ArrayList of ChartDate and ArrayList of ChartValue
for (int i = 0; i < dates.length(); i++){
ChartDate chartDate = new ChartDate(dates.getString(i));
mChartProfile.addToDates(chartDate);
}
JSONArray values = close.getJSONArray("values");
for (int i = 0; i < values.length(); i++){
ChartValue chartValue = new ChartValue(values.getDouble(i));
mChartProfile.addToValues(chartValue);
}
}
Right now, I'm getting an error of thread exiting with uncaught exception. And this is caused by a null exception because when calling mChartProfile.getSizeDates(), the values haven't been written in yet. My intuition is that the call to getChartInfo(jsonData) doesn't finish and the main UI thread is already returning from the getRequest() function. Hence, it will continue next line, and try to access an empty array that has not been initialized. Hence, I get a null exception. My solution is to have the main thread wait on the worker thread by calling thread.join() but I am not sure of how to do this through this Okhttp interface. Any help is deeply appreciated.

Categories

Resources