如何在不使用sleep的情况下,等待异步任务获取数据库数据后再加载地图?
解决方案:移除Sleep,异步协调地图加载与数据获取
核心问题分析
你的代码问题根源在于异步任务并行执行时的时序混乱,以及错误地在后台线程操作UI组件:
AsyncTask是后台线程,主线程调用getJSON()后立刻执行sleep,但后台请求仍在异步进行,导致日志顺序错乱;doInBackground()中直接操作gMap(清除标记、添加标记)属于UI操作,会触发线程安全异常;- 固定时长的
sleep完全不可靠——网络慢时10秒不够,网络快时纯浪费时间,还会阻塞主线程导致UI卡顿甚至ANR。
优化方案:异步任务协调(无Sleep)
通过标志位+回调的方式,等待「地图加载完成」和「数据获取完成」两个异步操作都就绪后,再统一更新地图。
完整优化代码
import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.app.restaurant.bowzers.R; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MarkerOptions; import org.json.JSONArray; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import android.os.AsyncTask; public class TruckFragment2 extends Fragment { private double mylocationLong = -82.83; private double mylocationLat = 39.9; private GoogleMap gMap; // 标志位:记录地图和数据是否就绪 private boolean isMapReady = false; private boolean isDataLoaded = false; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_truck, container, false); // 1. 初始化地图并监听就绪状态 SupportMapFragment supportMapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.truck_map); supportMapFragment.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(@NonNull GoogleMap googleMap) { gMap = googleMap; isMapReady = true; Log.i("STEP 5", mylocationLong + "," + mylocationLat); // 检查是否可以更新地图 updateMapIfReady(); } }); // 2. 异步获取服务器数据 getJSON("https://bowzershotdogstand.com/app/get_location.php"); return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } private void getJSON(final String urlWebService) { class GetJSON extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... voids) { Log.i("STEP 1", urlWebService); try { URL url = new URL(urlWebService); HttpURLConnection con = (HttpURLConnection) url.openConnection(); StringBuilder sb = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream())); String json; while ((json = bufferedReader.readLine()) != null) { sb.append(json).append("\n"); } Log.i("STEP 2", sb.toString()); return sb.toString().trim(); } catch (Exception e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(String s) { super.onPostExecute(s); if (s != null) { try { JSONArray JA = new JSONArray(s); if (JA.length() > 0) { JSONObject JO = JA.getJSONObject(0); mylocationLong = Double.parseDouble(JO.getString("longitude")); mylocationLat = Double.parseDouble(JO.getString("lattitude")); Log.i("STEP 3", mylocationLat + "," + mylocationLong); isDataLoaded = true; // 检查是否可以更新地图 updateMapIfReady(); } } catch (Exception e) { e.printStackTrace(); } } } } new GetJSON().execute(); } // 核心协调方法:只有地图和数据都就绪时才更新地图 private void updateMapIfReady() { if (isMapReady && isDataLoaded) { Log.i("STEP 4", mylocationLong + "," + mylocationLat); LatLng latLng = new LatLng(mylocationLat, mylocationLong); MarkerOptions markerOptions = new MarkerOptions() .position(latLng) .title(latLng.latitude + ": " + latLng.longitude); gMap.clear(); gMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16)); gMap.addMarker(markerOptions); } } }
优化说明
- 移除Sleep:彻底删除主线程的
sleep调用,避免UI阻塞; - 线程安全修正:将地图操作(添加标记、移动相机)移到UI线程执行,
doInBackground()只负责网络请求和数据解析; - 异步协调:用
isMapReady和isDataLoaded两个标志位,只有两者都为true时,才执行地图更新逻辑; - 时序可控:日志顺序会变为
STEP1→STEP2→STEP3→STEP5→STEP4(或地图先就绪则STEP5先出现),最终都会触发地图更新。
进阶优化:用协程替代AsyncTask
由于 AsyncTask 已被官方废弃,推荐使用 Kotlin协程(Java也可调用)实现更简洁的异步逻辑:
// 需先添加协程依赖 import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.Dispatchers; import kotlinx.coroutines.launch; import kotlinx.coroutines.withContext; // 替换原getJSON方法 private void getJSON(final String urlWebService) { CoroutineScope(Dispatchers.IO).launch { Log.i("STEP 1", urlWebService); try { URL url = new URL(urlWebService); HttpURLConnection con = (HttpURLConnection) url.openConnection(); StringBuilder sb = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream())); String json; while ((json = bufferedReader.readLine()) != null) { sb.append(json).append("\n"); } Log.i("STEP 2", sb.toString()); final String response = sb.toString().trim(); // 切换回主线程处理UI和数据解析 withContext(Dispatchers.Main) { try { JSONArray JA = new JSONArray(response); if (JA.length() > 0) { JSONObject JO = JA.getJSONObject(0); mylocationLong = Double.parseDouble(JO.getString("longitude")); mylocationLat = Double.parseDouble(JO.getString("lattitude")); Log.i("STEP 3", mylocationLat + "," + mylocationLong); isDataLoaded = true; updateMapIfReady(); } } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } } }
协程版本避免了 AsyncTask 的生命周期泄漏问题,代码结构更清晰,是目前Android异步编程的推荐方案。
内容的提问来源于stack exchange,提问作者Shawn Mulligan
相关产品推荐
相关产品推荐

