Okhttp3 - IndexOutOfBoundsException after calls to API - java

I'm new to android development but I'm stuck on why I can make calls to my API, but it doesn't populate my class in time for the recycler view to populate. I get IndexOutOfBoundsException because the mData.getDataFeeds() returns null. If I debug this application and walk through it slowly, it works.
ListFeedAdapter listFeedAdapter = new ListFeedAdapter(mData.getDataFeeds());
I have an Activity that gets a Fragment.
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
RecyclerView recyclerView = view.findViewById(R.id.listRecyclerView);
try {
login();
getFeed();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
ListFeedAdapter listFeedAdapter = new ListFeedAdapter(mData.getDataFeeds());
recyclerView.setAdapter(listFeedAdapter);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
return view;
}
Then I call login()
private void login() throws IOException {
String user = "user";
String password = "pass";
String loginUrl = getString(R.string.jsonLogin);
OkHttpClient client = new OkHttpClient.Builder()
.build();
JSONObject credentials = new JSONObject();
JSONObject session = new JSONObject();
try {
credentials.put("email", user);
credentials.put("password", password);
session.put("session", credentials);
} catch (JSONException e) {
e.printStackTrace();
}
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, session.toString());
Request request = new Request.Builder()
.url(loginUrl)
.post(body)
.addHeader("Content-Type", mediaType.toString())
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
String jsonData = response.body().string();
String jsonHead = response.headers("Set-Cookie").toString();
if(response.isSuccessful()) {
for (String setCookie : response.headers("Set-Cookie")) {
cookies.add(Cookie.parse(response.request().url(), setCookie));
}
}
}
});
The getFeed()
private void getFeed() throws IOException, JSONException {
String loginUrl = "http://testurlhere";
OkHttpClient client = new OkHttpClient.Builder()
.build();
MediaType mediaType = MediaType.parse("application/json");
Request request = new Request.Builder()
.url(loginUrl)
.get()
.addHeader("Content-Type", mediaType.toString())
.addHeader("_session", cookies.get(0).toString())
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
String jsonData = response.body().string();
String jsonHead = response.headers("Set-Cookie").toString();
Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mData = parseDataFeed(jsonData);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
updateDisplay();
}
});
}
}
catch (IOException e) {
Log.e(TAG, "Exception caught: ", e);
}
catch (JSONException e) {
Log.e(TAG, "Exception caught: ", e);
}
}
});
}

okhttp is an asynchronous operation, and you should use mData after onResponse ()
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
String jsonData = response.body().string();
String jsonHead = response.headers("Set-Cookie").toString();
Log.v(TAG, jsonData);
if (response.isSuccessful()) {
mData = parseDataFeed(jsonData);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
ListFeedAdapter adapter = new ListFeedAdapter(mData.getDataFeeds());
rexyxlerView.setAdapter(adapter);
updateDisplay();
}
});
}
}
catch (IOException e) {
Log.e(TAG, "Exception caught: ", e);
}
catch (JSONException e) {
Log.e(TAG, "Exception caught: ", e);
}
}
});

Related

Launch specific method from argument/parameter in Java

I would like to launch a specific method from an argument/parameter of another method in Java.
Consider following code:
void getResponse(String getUrl, final Activity activity) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(getUrl)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
JSONObject json = new JSONObject(myResponse);
//txtString.setText("First Name: "+json.getJSONObject("data").getString("first_name") + "\nLast Name: " + json.getJSONObject("data").getString("last_name"));
ReturnedString(json.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
}
The public void run() launches ReturnedString(json.toString());
But I would like to be "ReturnedString" a parameter of the getResponse method, so I can reuse getResponse.
This would mean that getResponse becomes something like:
void getResponse(String getUrl, final Activity activity, Method method) throws IOException {
And
method(json.toString());
But it seems not to be working this way.
Albert
You can use java.util.function.Consumer as an parameter of the method.
void getResponse(String getUrl, final Activity activity, final Consumer<String> consumer) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(getUrl)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
JSONObject json = new JSONObject(myResponse);
//txtString.setText("First Name: "+json.getJSONObject("data").getString("first_name") + "\nLast Name: " + json.getJSONObject("data").getString("last_name"));
//ReturnedString(json.toString());
consumer.accept(json.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
}
and the call of this method would be:
getResponse(url, activity, new Consumer<String>(){
#Override
public void accept(String s) {
ReturnedString(s);
}
});
You can use a Function<String, Void> as third parameter:
Initialize it as follows:
import androidx.arch.core.util.Function;
Function<String, Void> function = new Function<String, Void>() {
#Override
public Void apply(String input) {
ReturnedString(input);
}
}
Your method with the Function parameter:
void getResponse(String getUrl, final Activity activity, final Function<String, Void> function) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(getUrl)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
JSONObject json = new JSONObject(myResponse);
function.apply(json.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
}
Let's assume you need another Function, say
Function<String, Void> otherFunction = new Function<String, Void>() {
#Override
public Void apply(String input) {
Log.i("TEST", input);
}
}
Then you can use the two Functions as follows:
for(String day: daysOfWeek){
if(itIsTuesday(day)){
getResponse(urlString, activity, function);
}
else{
getResponse(urlString, activity, otherFunction);
}
}

How can I display information from an API that requires two URLs?

The first URL returns a list of names and IDs of farmers markets:
https://search.ams.usda.gov/farmersmarkets/v1/data.svc/locSearch?lat=" + latitude + "&lng=" + longitude
Then, the ID from the above URL must be used to get further information from each farmers market:
"https://search.ams.usda.gov/farmersmarkets/v1/data.svc/mktDetail?id=" + id
I want to display the name of the farmers markets from the first URL, and then the address of each farmers market from the second URL. How can I do this so that it all proceeds in the correct order?
Here is my MainActivity:
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
private ListView mListView;
GPSTracker gps;
Context mContext;
String marketAddress;
ArrayList<String> marketAddressArrayList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
mListView = (ListView) findViewById(R.id.list_view);
double latitude = 45.496481;
double longitude = -122.573462;
gps = new GPSTracker(mContext, MainActivity.this);
if (gps.canGetLocation()) {
latitude = gps.getLatitude();
longitude = gps.getLongitude();
}
else {
gps.showSettingsAlert();
}
final String marketUrl = "https://search.ams.usda.gov/farmersmarkets/v1/data.svc/locSearch?lat=" + latitude + "&lng=" + longitude;
Log.d(TAG, String.valueOf(latitude));
Log.d(TAG, String.valueOf(longitude));
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(marketUrl)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
e.printStackTrace();
Log.d(TAG, "failure");
}
#Override
public void onResponse(Response response) throws IOException {
try {
final String jsonData = response.body().string();
Log.v(TAG, "THIS IS MY JSONDATA " + jsonData);
if (response.isSuccessful()) {
Log.d(TAG, marketUrl);
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
getCurrentDetails(jsonData);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
Log.v(TAG, jsonData);
}
}
catch (IOException e) {
Log.e(TAG, "Exception caught: ", e);
}
}
});
Log.d(TAG, "Main UI code is running!");
}
private void getCurrentDetails(String jsonData) throws JSONException {
JSONObject usdaJSON = new JSONObject(jsonData);
JSONArray resultsJSON = usdaJSON.getJSONArray("results");
Market[] markets = new Market[resultsJSON.length()];
for(int i = 0; i < resultsJSON.length(); i++){
final JSONObject marketJSON = resultsJSON.getJSONObject(i);
String marketname = marketJSON.getString("marketname");
String id = marketJSON.getString("id");
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://search.ams.usda.gov/farmersmarkets/v1/data.svc/mktDetail?id=" + id)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
e.printStackTrace();
Log.d(TAG, "failure");
}
#Override
public void onResponse(Response response) throws IOException {
try {
final String marketDetailsJsonData = response.body().string();
Log.v(TAG, "THIS IS MY JSONDATA " + marketDetailsJsonData);
if (response.isSuccessful()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
JSONObject detailsJSON = new JSONObject(marketDetailsJsonData);
JSONObject marketDetailsJSON = detailsJSON.getJSONObject("marketdetails");
marketAddress = marketDetailsJSON.getString("Address");
marketAddressArrayList.add(marketAddress);
//marketAddressArrayList.get(0);
//updateMarketAddress(); call this method for each market found - it will run 20 times if there are 20 markets
Log.d(TAG, "this is marketadress"+ marketAddress);
} catch (JSONException e) {
e.printStackTrace();
Log.d(TAG, "broken");
}
}
});
}
}
catch (IOException e) {
Log.e(TAG, "Exception caught: ", e);
}
}
});
Log.d(TAG, "outside of the loop"+ marketname);
Market market = new Market(marketname, id, marketAddress);
markets[i] = market;
//markets[i].setAddress(marketAddressArrayList.get(i));
}
MarketAdapter adapter = new MarketAdapter(this, markets);
mListView.setAdapter(adapter);
for(int i = 0; i < resultsJSON.length(); i++) {
Log.d(TAG, markets[i].getMarketname());
Log.d(TAG, markets[i].getId());
// Log.d(TAG, markets[i].getMarketAddress());
}
}
}
First, extract out all the JSON parsing (for example, use Retrofit instead of OkHTTP) and UI updating to a separate method.
Then, hit the first URL, from that onResponse, hit the second URL.
Basically, what you are doing now,
if (response.isSuccessful()) {
Log.d(TAG, marketUrl);
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
getCurrentDetails(jsonData); // Call the second URL
But it doesn't need to be on the UI thread if you aren't updating the UI
Your for loop at the end must be within the second onResponse body, where the adapter/list would be populated.
Also, MarketAdapter should probably use List<Market> instead of ArrayList<String>

Activity keeps restarting when I leave the activity and come back to it

I have two activities, When I will move from activity A to B, B keeps restarting or "refreshing", when i go back from B to A, it also keeps restarting. The code is very big, here I am posting area where I think problem causes :
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
deviceStatus();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
this is deviceStatus();
public void deviceStatus(){
try {
RequestQueue requestQueue = Volley.newRequestQueue(InActivate.this);
String URL = "http://gickuwait-dev.com/electionapi/api/DeviceStatus";
JSONObject jsonBody = new JSONObject();
jsonBody.put("device_PK", device_ID2);
final String requestBody = jsonBody.toString();
StringRequest stringRequest = new StringRequest(Request.Method.POST, URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
if(response.equals("true")){
Intent intent = new Intent(InActivate.this, Vote.class);
startActivity(intent);
finish();
}else if(response.equals("false")) {
}
// Toast.makeText(getApplicationContext(), response.toString(), Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("VOLLEY", error.toString());
}
}) {
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() throws AuthFailureError {
try {
return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
return null;
}
}
#Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String responseString;
String json = null;
try {
json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
responseString = String.valueOf(json).trim();
ArrayList<DeviceStatusResponse> list = new ArrayList<DeviceStatusResponse>();
Type listType = new TypeToken<List<DeviceStatusResponse>>() {}.getType();
list = new Gson().fromJson(responseString, listType);
device_Status = list.get(0).getIsActive().toString();
// Toast.makeText(getApplicationContext(), ""+device_Status+" null ", Toast.LENGTH_LONG).show();
return Response.success(device_Status, HttpHeaderParser.parseCacheHeaders(response));
}
};
requestQueue.add(stringRequest);
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
in Activity B, i have the same code to check the device status from the database, any help would be appreciated
You can use Handle to check the repeated task.
private Handler delayHandler = new Handler();
private Runnable runnable = new Runnable() {
#Override
public void run() {
deviceStatus();
driverDelayHandler.postDelayed(runnable, 1000);
}
};
Don't forgot to cancel on onStop method.
delayHandler.removeCallbacks(runnable);

Android:Method Callback is not working

I am trying to implement method callback using relection but it is not calling the desired method.Here is my main activity
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RetrofitCalls retrofitCalls = new RetrofitCalls();
retrofitCalls.requestAccesstoken();
retrofitCalls.requestDataFromServer(MainActivity.this,"onCountryListReceived");
}
public void onCountryListReceived(JsonObject jsonObject)
{
String temp = "hello";
}
}
and here is the requestDataServer method
public void requestDataFromServer(final Activity activity, final String callBackMethod){
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HeaderInterceptor())
.build();
retrofit = new Retrofit.Builder()
.baseUrl(Constants.ROOT_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
Api api = retrofit.create(Api.class);
Call<JsonObject> call = api.getDataFromServer(Constants.COUNTRY_LIST);
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
if(response.errorBody() != null){
try {
String j1 = response.errorBody().string();
String temp = "hello";
} catch (IOException e) {
e.printStackTrace();
}
}else if(response.body() != null) {
JsonObject jsonObject = response.body();
JsonArray jsonArray = jsonObject.getAsJsonArray("Data");
String temp = "hello";
try {
String x = activity.getClass().toString();
Method method = activity.getClass().getDeclaredMethod(callBackMethod.trim(),Object.class);
method.invoke(activity,jsonObject);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
String temp = "hello";
}
});
}
why the onCountryListReceived is not invoked? It is giving NoSuchMethodException. What's the problem?
Instead of Object.class i passed JsonObject.class in getDeclaredMethod as it is the parameter of the method to be called.
getDeclaredMethod accepts two parameters 1.callback method name 2.parameters of that method.

UI does not update after Face Book request...why?

I am trying to set text in TextView userEmail, after calling FaceBook request for getting Email.
public TextView userEmail;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_profile);
//user_profile_name
userName = (TextView)findViewById(R.id.user_profile_name);
userEmail = (TextView)findViewById(R.id.user_profile_short_bio);
userbday = (TextView) findViewById(R.id.user_bday);
getMyFBProfileRequest();
}
public void getMyFBProfileRequest() {
GraphRequest request = GraphRequest.newMeRequest(
AccessToken.getCurrentAccessToken(),
new GraphRequest.GraphJSONObjectCallback() {
#Override
public void onCompleted(JSONObject object, GraphResponse response) {
// Application code
try {
Log.i("Response",response.toString());
Toast.makeText(MyProfileActivity.this,object.getString("email") ,
Toast.LENGTH_SHORT).show();
// Application code
String email = object.getString("email");
String birthday = object.getString("birthday");
userEmail.setText(email);
} catch (JSONException e) {
e.printStackTrace();
Log.i("Error","");
//profileView.showToast("Error");
}
}
});
// GraphRequest.GraphJSONObjectCallback objectCallback = new JSONObjectCallback();
// GraphRequest request = GraphRequest.newMeRequest(accessToken, objectCallback);
Bundle parameters = new Bundle();
parameters.putString("fields", "email,name,first_name,last_name,gender");
request.setParameters(parameters);
request.executeAsync();
}
And I get a fine response, which I see in toast.
But nothing change in textView. UI does not update… why? I do not know what to do. I used Broadcast Receiver. lost a lot of time. It does not work… Help me, please, anybody.
FB answer has field "birthday"! But my request did't get birthday. It is correct for get it: params.putString("fields", "birthday")
params.putString("fields","email,birthday,picture.type(large)");
2) To surround with try catch all operation with JSONObject:
try {
userModel.setEmail( data.getString("email"));
} catch (JSONException e) {
e.printStackTrace();
userModel.setEmail("");
}
try {
userModel.setBday(data.getString("birthday"));
} catch (JSONException e) {
e.printStackTrace();
userModel.setBday("");
}
and set "" in cath if result null;
So, now my request looks like:
Bundle params = new Bundle();
params.putString("fields", "email,birthday,picture.type(large)");
new GraphRequest(AccessToken.getCurrentAccessToken(), "/me/", params, HttpMethod.GET,
new GraphRequest.Callback() {
public ImageLoader imageLoader;
public ImageView mImageView;
public UserInfo userModel;
#Override
public void onCompleted( GraphResponse response) {
saveDataInSingletone(response);
profileView.setInfoToView();
}
private void saveDataInSingletone(GraphResponse response) {
JSONObject data = response.getJSONObject();
userModel = UserInfo.getInstance();
String lastName, firstName;
String profilePicUrl;
if (data.has("picture")) {
try {
profilePicUrl = data.getJSONObject("picture").getJSONObject("data").getString("url");
// getFacebookProfilePicture(profilePicUrl);
// imageView = (ImageView) findViewById(R.id.pic);
// imageView.setScaleType(ImageView.ScaleType.FIT_XY);
userModel.setAvatar(profilePicUrl);
//mImageView.setImageBitmap(profilePic);
// userModel.setAvatar(profilePic);
} catch (JSONException e) {
e.printStackTrace();
}
}
try {
userModel.setEmail( data.getString("email"));
} catch (JSONException e) {
e.printStackTrace();
userModel.setEmail("");
}
try {
userModel.setBday(data.getString("birthday"));
} catch (JSONException e) {
e.printStackTrace();
userModel.setBday("");
}}).executeAsync();

Categories