Reputation: 13
I have a method which calls an external API using okhttp library on android, I'm able to access the data that comes back inside that method/thread but I'm not able to return the data or use it somewhere else. What's the problem?
I have tried putting the data in another class (extended from AsyncTask) and it still didn't work.
public class DisplayImage extends AppCompatActivity {
ImageView imageView;
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_image);
imageView = findViewById(R.id.mImageView);
textView = findViewById(R.id.textView);
Bitmap bitmap = BitmapFactory.decodeFile(getIntent().getStringExtra("image_path"));
imageView.setImageBitmap(bitmap);
String imagePath = getIntent().getStringExtra("image_path");
try {
//map returned here
HashMap<String, double[]> map = getCropInfo(imagePath);
//This text view doesn't update
textView.setText(String.valueOf(map.get("ID")[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
HashMap getCropInfo(String imageUri) throws Exception {
final HashMap<String, double[]> map = new HashMap<>();
OkHttpClient client = new OkHttpClient();
MediaType MEDIA_TYPE_PNG = MediaType.parse("image/jpg");
File file = new File(imageUri);
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("image", file.getName(), RequestBody.create(MEDIA_TYPE_PNG, file))
.build();
Request request = new Request.Builder()
.header("Prediction-Key", "") //predictionkey hidden
.header("Content-Type", "application/octet-stream")
.url("https://westeurope.api.cognitive.microsoft.com/customvision/v3.0/Prediction/7f5583c8-36e6-4598-8fc3-f9e7db218ec7/detect/iterations/Iteration1/image")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
public void onResponse(Call call, final Response response) throws IOException {
// Read data on the worker thread
final String responseData = response.body().string();
// Run view-related code back on the main thread
DisplayImage.this.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
JSONObject jsonObject = new JSONObject(responseData);
JSONArray jsonArray = jsonObject.getJSONArray("predictions");
double highestIDProbability = 0;
double highestVoltageProbability = 0;
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject tempObject = jsonArray.getJSONObject(i);
if(tempObject.getString("tagName").equals("ID") && tempObject.getDouble("probability") > highestIDProbability) {
highestIDProbability = tempObject.getDouble("probability");
map.put("ID", getCoordinatesPixels(tempObject));
}
else if(tempObject.getString("tagName").equals("Voltage") && tempObject.getDouble("probability") > highestVoltageProbability) {
highestVoltageProbability = tempObject.getDouble("probability");
map.put("Voltage", getCoordinatesPixels(tempObject));
}
}
//setting text view works from here.
//textView.setText(String.valueOf(map.get("ID")[0]));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
//I am returning map
return map;
}
static double[] getCoordinatesPixels(JSONObject object) {
double[] arr = new double[4];
try {
JSONObject innerObject = object.getJSONObject("boundingBox");
arr[0] = innerObject.getDouble("left");
arr[1] = innerObject.getDouble("top");
arr[2] = innerObject.getDouble("width");
arr[3] = innerObject.getDouble("height");
} catch (JSONException e) {
e.printStackTrace();
}
return arr;
}
}
I need the map to return so I can use the data externally.
Upvotes: 0
Views: 2256
Reputation: 462
I believe you're running into an issue related to the asynchronous nature of OkHttp and network requests in general. When you do a new call, that call is queued and handled asynchronously. This means that the code will most likely execute return map;
before the asynchronous call has completed and before the callback modifies the map. If you need access to the map outside of the scope of the callback you have two main options.
Make the call blocking. This essentially means that you will have to force the function to stall until the OkHttp callback is triggered before return map;
occurs. I would absolutely not recommend doing this as it defeats the entire purpose of moving long running tasks to other threads.
Call a function inside the onResponse()
callback. Construct the map
inside the callback itself, then just call a function with that map as a parameter to handle any operations you need to do on that map
. Alternatively you could also make the map
a global variable so you can access it from practically anywhere.
On a sidenote, if this data is going to be used to propagate changes back to the UI or other program state, I would recommend using a ViewModel (it's a model object that holds data and can outlive Activity lifecycles) paired with something like MutableLiveData (which is a data wrapper that makes basically anything observable).
Using such a setup, you would have your map
object inside your ViewModel. You would then register an observer from any context (Activity, Fragment, etc) where you need to know about updates on the map
. Finally, in the callback, you would just need to update the ViewModel's map
. This would automatically notify any registered observers.
Good luck!
Upvotes: 1