【问题标题】:How to get data back from an okhttp call?如何从 okhttp 调用中取回数据?
【发布时间】:2019-04-14 17:41:38
【问题描述】:

我有一个使用 okhttp 库在 android 上调用外部 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;
    }

}

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

【问题讨论】:

    标签: java android


    【解决方案1】:

    我相信您遇到了与 OkHttp 的异步特性和一般网络请求相关的问题。当您进行新呼叫时,该呼叫将排队并异步处理。这意味着代码很可能会执行return map;before异步调用完成并且before回调修改地图。如果您需要访问回调范围之外的地图,您有两个主要选择。

    1. 阻止呼叫。这实质上意味着您必须强制函数停止,直到在 return map; 发生之前触发 OkHttp 回调。我绝对不建议这样做,因为它违背了将长时间运行的任务转移到其他线程的全部目的。

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

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

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

    祝你好运!

    【讨论】:

    • 感谢您的深入解释!
    • 我怎样才能按照您的第一个选项中的建议阻止呼叫?
    • @bronkers 我对 OkHttp 不是特别熟悉,但this 或许能帮到你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多