Retrofit 2.0-Beta1 Read Response from Observable - android

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());
}
}

Related

Get the result of the Webservice request using Retrofit 2.0

I'm using Retrofit and I'm in touch with my webservice but I can not get an answer and the field I want. Example: campo1 and assign to a variable and print it.
I think I'm not calling onResponse properly, as it's coming as null
Send JSON:
{
"select":"select id || ' | ' ||senha_terminal,nome,login_web,null,5,null n2,7,senha_terminal,senha_web,data_inclusao from usuario where id in (3257) order by id desc"
}
Expected response:
[
{
"campo1": "3257 | 74327",
"campo2": "Sidnei",
"campo3": "sidnei01",
"campo4": null,
"campo5": 5,
"campo6": null,
"campo7": 7,
"campo8": "74327",
"campo9": "56c07af798f309dbd75822a849ce47b6",
"campo10": "2012-02-08T11:00:06"
}
]
I would like to get field1 and assign in a variable response.
Main:
package com.example.romeu.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import org.json.JSONException;
import org.json.JSONObject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class MainActivity extends AppCompatActivity implements Callback<SelectModel> {
public Button btnConsultar;
Call<SelectModel> call;
Response<SelectModel> response;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiInterface.URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiInterface apiInterface = retrofit.create(ApiInterface.class);
try
{
JSONObject paramObject = new JSONObject();
paramObject.put("select", "select id || ' | ' ||senha_terminal,nome,login_web,null,5,null n2,7,senha_terminal,senha_web,data_inclusao from usuario where id in (3257) order by id desc");
onResponse(call, response);
}
catch (JSONException e)
{
e.printStackTrace();
}
}
#Override
public void onResponse(Call<SelectModel> call, Response<SelectModel> response)
{
String resposta = response.body().getCampo1();
Log.i("MSG", resposta);
}
#Override
public void onFailure(Call<SelectModel> call, Throwable t) {
}
}
ApiInterface:
package com.example.romeu.myapplication;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
/**
* Created by romeu on 01/03/18.
*/
public interface ApiInterface {
String URL_BASE = "https://services-dev.redetendencia.com.br/api-rest";
#Headers("Content-Type: application/json")
#POST("select")
Call<SelectModel> getSelect(#Body String body);
}
Model:
package com.example.romeu.myapplication;
/**
* Created by romeu on 01/03/18.
*/
class SelectModel {
public String campo1,campo2,campo3,campo4,campo5,campo6,campo7,campo8,campo9,campo10;
public SelectModel(){}
//Getters
public String getCampo1() {
return campo1;
}
public String getCampo2() {
return campo2;
}
public String getCampo3() {
return campo3;
}
public String getCampo4() {
return campo4;
}
public String getCampo5() {
return campo5;
}
public String getCampo6() {
return campo6;
}
public String getCampo7() {
return campo7;
}
public String getCampo8() {
return campo8;
}
public String getCampo9() {
return campo9;
}
public String getCampo10() {
return campo10;
}
}
I think you are trying to achieve an async request. But the callback does not work this way:
onResponse(call, response);
you are actually just invoking onResponse( .. ) directly with null response. Also, you do not construct Call at all it is also null. Replace above line with:
Call<SelectModel> call = apiInterface.getSelect(paramObject.toString());
call.enqueue(MainActivity.this);
Note: paramObject.toString() is only a suggestion. I am not sure how it is supposed to be attached to the request body. Maybe you can put only that string without any JsonObject.
That enqueues async request which when gets response, will invoke onResponse( .. ).

Update TextView on UiThread from Async - Android

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);
}

Save the response from OkHttp -- Android --

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);
}
}
}

org.json.JSONException: End of input at character 0 of

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())".

Using volley library inside a library

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.

Categories

Resources