如何从okhttp调用取回数据?

时间:2019-04-14 17:41:38

标签: java android

我有一个使用android上的okhttp库调用外部API的方法,我能够访问该方法/线程中返回的数据,但无法返回该数据或在其他地方使用它。有什么问题吗?

我尝试将数据放入另一个类(从AsyncTask扩展),但仍然无法正常工作。

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

}

我需要返回地图,以便可以在外部使用数据。

1 个答案:

答案 0 :(得分:0)

我认为您通常会遇到与OkHttp和网络请求的异步性质有关的问题。进行新呼叫时,该呼叫将排队并异步处理。这意味着代码最有可能在异步调用完成之前之前之前修改回调,return map;执行。如果您需要在回调范围之外访问地图,则有两个主要选项。

  1. 阻止通话。本质上,这意味着您必须强制该函数停止运行,直到return map;发生之前,先触发OkHttp回调。我绝对不建议这样做,因为这样做会破坏将长期运行的任务移至其他线程的整个目的。

  2. onResponse()回调中调用一个函数。在回调本身内部构造map,然后仅调用以该映射为参数的函数来处理您需要对该map进行的任何操作。另外,您也可以将map设置为全局变量,以便几乎可以从任何地方访问它。

在旁注中,如果要使用此数据将更改传播回UI或其他程序状态,我建议使用ViewModel(这是一个模型对象,可以保存数据,并且可以超出Activity生命周期)与MutableLiveData之类的东西(这是一个数据包装器,基本上可以观察到的东西)配对。

使用这种设置,您可以将map对象放在ViewModel中。然后,您可以从需要了解map上的更新的任何上下文(活动,片段等)中注册观察者。最后,在回调中,您只需要更新ViewModel的map。这会自动通知所有注册的观察员。

祝你好运!