I am using OkHttp3 to do a GET API call, which works, however I would like to update a TextView (jsonTextView) in the UiThread with the result of the GET call.
I have tried 10 different ways but it does not work. Always tells me that can't resolve this or that therefore I ask that if you decide to help me, please take into account posting any dependencies on your answer such as import or declaration.
I left a line in my rest class: "CODE TO UPDATE jsonTextView IN UITHREAD". In your opinion what is the best way to achieve this?
Thank you so much in advance, I already lost hours on this.
This is my activity
package it.test.test;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.EditText;
import android.os.*;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
Button fetchUpdateButton;
TextView jsonTextView;
EditText assetCode;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fetchUpdateButton = (Button) findViewById(R.id.fetchUpdateButton);
fetchUpdateButton.setOnClickListener(fetchAssetUpdateClick);
jsonTextView = (TextView) findViewById(R.id.jsonTextView);
assetCode = (EditText) findViewById(R.id.assetCode);
}
View.OnClickListener fetchAssetUpdateClick = new View.OnClickListener() {
#Override
public void onClick(View v) {
REST r = new REST();
try {
r.getAssetUpdates(assetCode.getText().toString());
} catch (Exception e) {
e.printStackTrace();
jsonTextView.setText("API Fetch Failed");
}
}
};
}
This is my REST class
package it.test.test;
import android.util.Log;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* Created by fabio on 25/02/2017.
*/
public class REST {
private final OkHttpClient client = new OkHttpClient();
public void getAssetUpdates(String assetCode) throws Exception {
Request request = new Request.Builder()
.url("http://10.0.0.3:8080/api/api/assets/getAsset?networkAssetCode=" + assetCode)
.build();
client.newCall(request).enqueue(new Callback() {
#Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override public void onResponse(Call call, Response response) throws IOException {
try (final ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
System.out.println(responseBody.string());
CODE TO UPDATE jsonTextView IN UITHREAD
}
}
});
}
}
You can't update the other class UI from the REST class unless you put the callback into the method definition.
public void getAssetUpdates(String assetCode, Callback callback) {
...
client.newCall(request).enqueue(callback);
}
it's simply moving around some variables
Then, in the onClick define your callback so you can access your TextView in that Activity class ,
#Override
public void onClick(View v) {
...
r.getAssetUpdates(assetCode.getText().toString(),
new Callback() {
#Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override public void onResponse(Call call, Response response) throws IOException {
try (final ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
...
// update UI in here
}
}
);
}
And I do think Okhttp requires a usage of runOnUiThread.
OkHTTP Update UI from enqueue callback
Note that if you have JSON and REST, then Retrofit is going to be better than pure Okhttp.
One solution is to give an Activity to REST and call runOnUiThread().
Create own listener & pass the listener object from your activity..
And return the call back from Rest to your Activity
Step 1: Create interface
public interface MyListener
{
void restResult(String result);
}
Step 2:Implement this listener to your activity
YourActivity implements MyListener
Step 3:Pass the listener to Rest class
Rest rest=new Rest(this);//this-- your listener object
Step 4:Modify Rest Constructor
Private MyListener myListener;
Rest(MyListener myListener){
this.myListener=myListener;
}
//After your Rest completed pass the result
CODE TO UPDATE jsonTextView IN UITHREAD
myListener.restResult(responseBody.string());
Step 5: In your Activity get your result
Update the textview using
#override
restResult(String result){
yourtextview.settext(result);
}
Related
I'm currently try to implement a two factor authentication system on a project i'm working on using twilio as a sms gateway service to request a random login token and then send it to the user as a text message. I followed the tutorial found here "https://www.twilio.com/blog/2016/05/how-to-send-an-sms-from-android.html" to test the service out. Following the tutorial I hosted the backend on Heroku. The app works just fine and says that the sms has been sent. However I never receive it. Any help would great.
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.content.Context;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private EditText mTo;
private EditText mBody;
private Button mSend;
private OkHttpClient mClient = new OkHttpClient();
private Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTo = (EditText) findViewById(R.id.txtNumber);
mBody = (EditText) findViewById(R.id.txtMessage);
mSend = (Button) findViewById(R.id.btnSend);
mSend.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
try {
post(" https://cryptic-shore-79857.herokuapp.com", new
Callback(){
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response)
throws IOException {
runOnUiThread(new Runnable() {
#Override
public void run() {
mTo.setText("");
mBody.setText("");
Toast.makeText(getApplicationContext(),"SMS Sent!",Toast.LENGTH_SHORT).show();
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
mContext = getApplicationContext();
}
Call post(String url, Callback callback) throws IOException {
RequestBody formBody = new FormBody.Builder()
.add("To", mTo.getText().toString())
.add("Body", mBody.getText().toString())
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Call response = mClient.newCall(request);
response.enqueue(callback);
return response;
}
}
I'm thinking the URL that connects to Heroku is incorrect but I have no idea what it should be.
Twilio developer evangelist here.
You're POSTing your request to the wrong URL. Currently your code does:
try {
post("https://cryptic-shore-79857.herokuapp.com", new
Callback(){
But the path for the action that sends the SMS should be:
try {
post("https://cryptic-shore-79857.herokuapp.com/sms", new
Callback(){
Note, the /sms path.
Let me know if that helps at all.
I wanna to save the result from OnResponse method to use it for updating the UI
i tried to save the result into String var then call it into main thread but it doesn't work .
here's my code with some comments ,
any help ?
package com.example.blacknight.testokhttp;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import static android.R.string.ok;
public class MainActivity extends AppCompatActivity {
public final String URL_MOVIE = "http://api.themoviedb.org/3/movie/popular?api_key=" + API_KEY;
String res_120 ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL_MOVIE)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.v("BK-201 URL: " , response.body().string());
// wanna save the result to update UI
res_120 = response.body().string();
}
});
// just for test : if the result has been saved or not
Log.i("BK-111 : " , res_120);
}
}
Let's say you want to update a TextView element in you UI with the response in a String format. You could do something like this. I keeped your test log to help you follow the code, just in case.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL_MOVIE)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.v("BK-201 URL: " , response.body().string());
// wanna save the result to update UI
res_120 = response.body().string();
updateUI(response.body().string());
}
});
}
void updateUI(String string) {
textView.setText(string);
Log.i("BK-111 : " , res_120);
}
Here's a working code for anyone have the same problem or new on using OkHttp , Unfortunately i'm using AsyncTask
Thaks to Jofre Mateu
package com.example.blacknight.testokhttp;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
public final String URL_MOVIE = "http://api.themoviedb.org/3/movie/popular?api_key=" + API_KEY ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MovieTask().execute();
}
public class MovieTask extends AsyncTask<String , Void , String>
{
#Override
protected String doInBackground(String... params) {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL_MOVIE)
.build();
Response response = client.newCall(request).execute();
String res_120 = response.body().string();
return res_120;
} catch (Exception e ){
return null;
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
TextView textView = (TextView) findViewById(R.id.testView_test);
textView.setText(s);
}
}
}
I use the JSONObject forecast = new JSONObject(response.body().string()) to get the weather information, but android monitor always show "org.json.JSONException: End of input at character 0 of ".
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Debug;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(isconnected()){
String url = "https://api.forecast.io/forecast/2d0f15adaeff2c8e1343201418843890/37.8267,-122.423";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
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()) {
Log.d(TAG, response.body().string());
String a=response.body().string();
try {
JSONObject forecast = new JSONObject(a);
} catch (JSONException e) {
e.printStackTrace();
}
} else {
alertUserAboutError();
}
}
});
}
else{
Toast.makeText(MainActivity.this,"LOST CONNECTION",Toast.LENGTH_SHORT).show();
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().hide();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
private boolean isconnected() {
ConnectivityManager cm =
(ConnectivityManager)MainActivity.this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
return isConnected;
}
private void alertUserAboutError() {
AlertDialogFragment alertDialogFragment = new AlertDialogFragment();
alertDialogFragment.show(getSupportFragmentManager(), "error");
}
}
I use the Log.d(TAG, response.body().string()) to test it is a string, but when in JSONObject forecast = new JSONObject(response.body().string()), it will not work.
For some weird reason if you use .string() >1 time, response turns out to be empty. Try to use it only once when parsing Retrofit response.
Someone, who knows why it happens like this, please improve my answer.
Answer : Response object should of Generic type - ResponseBody.
See below Correct code for reference.
Now response.body() method will return object ResponseBody
i.e.
ResponseBody rb = response.body();
rb.string();
Here ResponseBody have method string() which returns String object but internally string() method calls Util.closeQuietly(source); which makes response empty once method string() gets called.
Just remove Log.d(TAG, response.body().string()); and follow below code.
Reference - okhttp3.ResponseBody.java
error : org.json.JSONException: End of input at character 0
Correct code :
#Override
public void onResponse(Call call, Response<ResponseBody> response) throws IOException {
if (response.isSuccessful()) {
String remoteResponse=response.body().string();
Log.d(TAG, remoteResponse);
try {
JSONObject forecast = new JSONObject(remoteResponse);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Sometimes the probleme is th URL, if your website forces HTTPS, you have to make sure that you are using an HTTPS URL in your APP, and vice-versa if your website doesn't support HTTPS, you have to use an HTTP URL.
I have solved this problem after moving the "String a=response.body().string()" before "if (response.isSuccessful())".
I am trying to read the httpstatus code e body in the success case of a request. So I created the code below to test but I failed to get the onNext called, I tried to use the okhttp (com.squareup.okhttp.Response) and retrofit Response (retrofit.Response) class, but I couldn't make it work.
Can someone help me to read the body and httpstatus code here? I would like to keep using the Observables.
Thanks in advance.
package com.app.rest;
import com.squareup.okhttp.Response;
import retrofit.GsonConverterFactory;
import retrofit.Retrofit;
import retrofit.RxJavaCallAdapterFactory;
import retrofit.http.GET;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class TestApiClient {
public interface Test {
#GET("/posts")
Observable<Response> getPosts();
}
public TestApiClient() {
new Retrofit.Builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build().create(Test.class).getPosts().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Response>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.toString();
}
#Override
public void onNext(Response response) {
response.toString();
}
});
}
}
I got the answer.
import com.squareup.okhttp.ResponseBody;
import retrofit.Response;
...
public Observable<Response<ResponseBody>> xxx(){}
...
Playlist playlist = Playlist.parse(((ResponseBody)response.body()).byteStream());
Actually, response.body() is a Object, You can cast it to another type.In this case, it is ResponseBody.
I got it working with the code below, it was to needed to use the generics of retrofit.Response with ResponseBody:
package com.app.rest;
import com.squareup.okhttp.ResponseBody;
import retrofit.GsonConverterFactory;
import retrofit.Response;
import retrofit.Retrofit;
import retrofit.RxJavaCallAdapterFactory;
import retrofit.http.GET;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class TestApiClient {
public interface Test {
#GET("/posts")
Observable<Response<ResponseBody>> getPosts();
}
public TestApiClient() {
new Retrofit.Builder()
.baseUrl("http://jsonplaceholder.typicode.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build().create(Test.class).getPosts().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Response<ResponseBody>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.toString();
}
#Override
public void onNext(Response<ResponseBody> response) {
response.toString();
}
});
}
}
You need to define your interface's Observable as
#GET("/posts")
Observable<Result<MyPost>>
Then you can easily get the response body in onNext() like this:
#Override
public void onNext(Result<MyPost> result) {
result.response().body();
}
Sometimes the response.toString() or response.body() gives you unreadable string, I had to get the bytes from the response body and construct a new string from it.
#Override
public void onNext(Response<ResponseBody> response) {
try {
String responseStr = new String(response.body().bytes());
Log.d("responseBodyResponse", responseStr);
} catch (Exception e) {
Log.d("responseBodyResponse Exception", e.getMessage());
}
}
I want to make a library to reduce my duplicate network works on every android projects or even give my jar to some other developers to using my methods for network communications.
So i build this:
import java.util.Map;
import org.json.JSONObject;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request.Method;
import com.android.volley.Request.Priority;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
public class RequestResp {
private final static String WEB_SERVICE_URL = "http://blabla/api";
private final Priority priorityImmediatelly = Priority.IMMEDIATE;
private final Priority priorityHigh = Priority.HIGH;
private final Priority priorityNORMAL = Priority.NORMAL;
private String tag_req_default = "tag_req_default";
VolleyCustomRequest mVolleyCustomReq;
DefaultRetryPolicy drp = new DefaultRetryPolicy(15000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
public /*JSONObject*/ void sendParamsAsHighPriority(Map<String, String> params) {
mVolleyCustomReq = new VolleyCustomRequest(Method.POST,
WEB_SERVICE_URL, params, new Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
if (response != null) {
}
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(tag_req_default, error.getMessage());
}
}) {
#Override
public Priority getPriority() {
return priorityHigh;
}
};
mVolleyCustomReq.setRetryPolicy(drp);
VolleyController.getInstance().addToRequestQueue(mVolleyCustomReq,
tag_req_default);
/*return response; ?!?!?*/
}
}
But how to return response?! Cause if server was busy or down or something that make response a little late, developers in their applications get null!(i guess).
How to make a such this?! Build a jar library that has a class that has a method that give parameters and send it on specific URL, with volley library?
Define Interface like
public interface OntaskCompleted {
public void onSuccess(JSONObject response);
public void onError(String message);
}
Now Your activity should implement this interface and you have to override these method.
Now in you Volley class do this.
if (response != null) {
ontaskCompleted.onSuccess(JSONObject);
}
and
public void onErrorResponse(VolleyError error) {
VolleyLog.d(tag_req_default, error.getMessage());
ontaskCompleted.onError( error.getMessage());
}
Now your activity will get the result of error or success.
Hope it helps you.