【问题标题】:How to execute onMapReady() callback after all the AsyncTasks are completed?所有 AsyncTask 完成后如何执行 onMapReady() 回调?
【发布时间】:2016-08-16 18:19:20
【问题描述】:

我的代码有问题,我有我的 Activity,我用它来调用 google api 并检索 json,反序列化它们并使用它的折线在地图上绘制。

问题是 getMapAsync() 发送 onMapReady() 回调(用于创建地图)在执行我的异步任务后立即执行,该任务检索创建地图所​​需的数据。

如何在不停止 UI 线程的情况下实现这一点?我尝试调用 .execute.get() 来冻结 UI 线程。但如果我这样做,我将无法使用 ProgressDialog 通知用户从服务器获取数据的延迟,在任务完成之前,他们将暴露于冻结的 UI。我该怎么做?

public class RouteAssistantActivity extends Activity implements OnMapReadyCallback{

public GoogleMapsDirectionsResponse dirRes;
public GoogleMapsDistanceResponse disRes;

public String jsonString;
private String mapsAPIKey;
private String directionsBaseURL;
private String distanceBaseURL;

MapFragment mapFragment;
private ProgressDialog progress;

public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_ra_route_assisstant);

    mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.ra_map);
    progress = new ProgressDialog(RouteAssistantActivity.this);
    progress.setTitle("Please Wait");
    progress.setMessage("Retrieving Data from the Server");
    progress.setIndeterminate(true);

    try {
        ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);

        if (appInfo.metaData != null) {
            mapsAPIKey = appInfo.metaData.getString("com.google.android.maps.v2.API_KEY");
            directionsBaseURL = appInfo.metaData.getString("com.google.android.maps.directions.baseURL");
            distanceBaseURL = appInfo.metaData.getString("com.google.android.maps.distance.baseURL");
        }

    } catch (PackageManager.NameNotFoundException e) {
        Log.e("Meta Error", "Meta Data not found. Please check the Manifest and the Meta Data Package Names");
        e.printStackTrace();
    }

    //Test
    String directionsURL = directionsBaseURL+"origin=6.948109,79.858191&destination=6.910176,79.894347&key="+mapsAPIKey;
    String distanceURL = distanceBaseURL+"units=metric&origins=6.948109,79.858191&destinations=6.910176,79.894347&key="+mapsAPIKey;

    Log.e("CA Debug","URL : " + directionsURL);
    Log.e("CA Debug","URL : " + distanceURL);

    new configurationSyncTask().execute(distanceURL,"distance");
    new configurationSyncTask().execute(directionsURL, "direction");

    mapFragment.getMapAsync(this);

}

@Override
public void onMapReady(GoogleMap googleMap) {
    LatLng rajagiriya = new LatLng(6.910176, 79.894347);

    String points = dirRes.getRoutes().get(0).getOverviewPolyline();
    List<LatLng> list = PolyUtil.decode(points);

    googleMap.setMyLocationEnabled(true);
    googleMap.getUiSettings().setRotateGesturesEnabled(true);
    googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(rajagiriya, 13));

    googleMap.addMarker(new MarkerOptions()
            .title("Rajagiriya")
            .snippet("My Place")
            .position(rajagiriya));

    googleMap.addPolyline(new PolylineOptions()
            .geodesic(false)
            .addAll(list)
            .color(Color.RED)
            .width(25));
}

private class configurationSyncTask extends AsyncTask<String, Void, String> {

    @Override
    protected void onPreExecute() {
        progress.show();
    }

    @Override
    protected String doInBackground(String... params) {

        String url = params[0];
        String type = params[1];

        Log.d("CA Debug", getClass().getSimpleName() + " --> Real URL : " + url);
        Log.d("CA Debug", getClass().getSimpleName() + " --> doInBackground requesting content");

        jsonString = requestContent(url);

        // if the output is null, stop the current task
        if (jsonString == null) {
            Log.d("CA Debug", getClass().getSimpleName() + " --> Stopping Async Task");
            this.cancel(true);
            Log.d("CA Debug", getClass().getSimpleName() + " --> Async Task Stopped");
        }

        return type;
    }

    @Override
    protected void onPostExecute(String types) {

        if (types.equalsIgnoreCase("distance")) {
            disRes = GMapsDistanceResponseJSONDeserializer.deserialize(jsonString);
        } if (types.equalsIgnoreCase("directions")) {
            dirRes = GMapsDirectionsResponseJSONDeserializer.deserialize(jsonString);
        }

        progress.dismiss();
    }


}

public String requestContent(String url) {

    Log.d("CA Debug",getClass().getSimpleName()+" --> URL : "+url);

    try {
        URL urlObj = new URL(url);
        HttpsURLConnection con = (HttpsURLConnection) urlObj.openConnection();
        con.setChunkedStreamingMode(0);
        con.setRequestMethod("GET");

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null,null, new SecureRandom());
        con.setSSLSocketFactory(sc.getSocketFactory());

        InputStream clientResponse;
        String jsonString;
        int status = con.getResponseCode();

        if(status >= HttpURLConnection.HTTP_BAD_REQUEST){
            Log.d("CA Debug", getClass().getSimpleName()+" --> Bad Request");
            jsonString = null;
        } else {
            Log.d("CA Debug", getClass().getSimpleName()+" --> converting Stream To String");
            clientResponse = con.getInputStream();
            jsonString = convertStreamToString(clientResponse);
        }

        Log.d("CA Debug", getClass().getSimpleName()+" --> JSON STRING : " + jsonString);

        return jsonString;
    } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {

        Log.d("CA Debug", getClass().getSimpleName()+" --> Error when creating an Input Stream");
        e.printStackTrace();

    }
    return null;
}

public String convertStreamToString(InputStream is) {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuilder sb = new StringBuilder();
    String line = null;

    try {
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
    } catch (IOException e) {
    } finally {
        try {
            is.close();
        } catch (IOException e) {
        }
    }

    return sb.toString();
}

}

【问题讨论】:

  • 您对 configurationSyncTask() 有疑问。为什么要两次调用 configurationSyncTask()?
  • @darwin 我正在传递两个不同的参数。参数是指向不同 API 的 URL。有更好的方法吗?欢迎提出建议。
  • onMapReady() 完成后执行任务?
  • 而且这些任务不是并行的。第二个任务将在第一个任务完成后开始。
  • @k9yosh 你想要 mapFragment.getMapAsync(this);只有在完成这两个语句后才能工作?新配置SyncTask().execute(distanceURL,"distance");新配置SyncTask().execute(directionsURL, "direction");

标签: java android android-asynctask


【解决方案1】:

快速但有点脏的解决方案是在单个 AsyncTask 上执行两个 AsyncTask,然后在其 onPostExecute 代码调用 getMapAsync 上执行。这样您就可以确保在处理地图准备就绪之前完成任务。

【讨论】:

  • 我会试一试,让你知道。谢谢。
  • 能否以嵌套方式执行异步任务?它说应该在主线程中调用 execute()。
  • 这不起作用。我只能从主线程调用工作线程。因此,即使我在前一个工作线程中创建新的工作线程,我也必须调用 onPostExecute() 中的 execute() 方法,该方法会返回主 UI 线程。所以最终在执行这两个新的工作线程之后,它将立即执行 getMapAsync() 而无需等待这两个新线程,这会导致 NullPointerException,就像我原来的情况一样。无论如何,感谢您的帮助。
  • 您可以在单个任务的回调中使用任务回调中的代码(通过将实际工作复制到一个函数并使用不同的参数使用它两次),只需重新设计您的代码即可。
【解决方案2】:
  • 首先,在 onMapReady 之后运行任务,因为您将摆脱 担心现成的地图。
  • 您的异步任务不是并行的,它们在后台工作,但第二个将在第一个完成后执行,请查看link
  • 将 onMapReady 的某些部分移至 onPostExecute,如下所示

移动

  @Override
  protected void onPostExecute(String types) {
      if (types.equalsIgnoreCase("distance")) {
          disRes = GMapsDistanceResponseJSONDeserializer.deserialize(jsonString);
      }if (types.equalsIgnoreCase("directions")) {
          dirRes = GMapsDirectionsResponseJSONDeserializer.deserialize(jsonString);
          String points = dirRes.getRoutes().get(0).getOverviewPolyline();
          List<LatLng> list = PolyUtil.decode(points);
          googleMap.addPolyline(new PolylineOptions()
             .geodesic(false)
             .addAll(list)
             .color(Color.RED)
             .width(25)
          );
      }
  }

【讨论】:

    【解决方案3】:

    AsyncTask.SERIAL_EXECUTOR 用于强制 AsyncTask 以串行方式执行。

    对于你的情况,一个小技巧就可以完成这项工作。

    1. 为同一个 AsyncTask 创建回调,并传递不同的参数来区分函数。

    2. 现在在第二个回调中发起 mapFragment.getMapAsync(this);


    public class MainFragment ...
    {
        DataDownloader dataDownloader;
        int processCount=1;
    
    
        void initiateProcessFirst(){
            new DataDownloader(this,processCount ).execute();
        }
        public void initiateSecondProcess(){
            processCount++;
            new DataDownloader(this,processCount ).execute();
        }
    
    
         public void secondProcessCompleted(){
                mapFragment.getMapAsync(this);
         }
      }
    

    AsyncTask 逻辑如下所示

    public class DataDownloader extends AsyncTask<Void,Void,Boolean> {
    
        MainFragment context;
        int processCount;
        public DataDownloader(MainFragment context ,int processCount){
            this.context=context;
            this.processCount=processCount;
    
        }
        @Override
        protected Boolean doInBackground(Void... params) {
            boolean status=false;
            // Do logic according to the Process Count
    
            return status;
        }
    
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            if(processCount==1)
                context.initiateSecondProcess();
            else
                context.secondProcessCompleted();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-30
      • 2022-11-01
      相关资源
      最近更新 更多