Android中HereMap离线地图无法下载,主线程任务过载问题排查
Android HereMap离线地图下载无法启动问题
我尝试在Android应用中下载HereMap离线地图,但自定义的HereMapClass初始化正常,下载却无法启动。出现的错误日志如下:
I/Choreographer: Skipped 1390 frames! The application may be doing too much work on its main thread. I/OpenGLRenderer: Davey! duration=23246ms; Flags=0, IntendedVsync=541429044228998, Vsync=541452210894738, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=541452219847929, AnimationStart=541452220213815, PerformTraversalsStart=541452220244856, DrawStart=541452273514752, SyncQueued=541452286173554, SyncStart=541452287155273, IssueDrawCommandsStart=541452287225898, SwapBuffers=541452289434856, FrameCompleted=541452291323711, DequeueBufferDuration=140261, QueueBufferDuration=1621927, GpuCompleted=0, I/OpenGLRenderer: Davey! duration=23290ms; Flags=0, IntendedVsync=541429044228998, Vsync=541452210894738, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=541452219847929, AnimationStart=541452220213815, PerformTraversalsStart=541452220244856, DrawStart=541452321017669, SyncQueued=541452332492773, SyncStart=541452332826783, IssueDrawCommandsStart=541452332910273, SwapBuffers=541452333750690, FrameCompleted=541452335195690, DequeueBufferDuration=159479, QueueBufferDuration=935885, GpuCompleted=0, V/FA: Inactivity, disconnecting from the service
主类代码
public class LoginActivity extends AppCompatActivity { ... private HereMapClass hereMap; private ProgressBar downloadProgressBar; ... } public void downloadMap() { downloadProgressBar = (ProgressBar) findViewById(R.id.ext_progressBar); hereMap = new HereMapClass(LoginActivity.getAppContext()); hereMap.onDownloadButtonClicked(downloadProgressBar); }
HereMapClass代码
public class HereMapClass { private MapEngine mapEngine; private MapLoader mapLoader; ... public HereMapClass(Context context){ this.context=context; } // listener for MapLoader private MapLoader.Listener mapLoaderHandler = new MapLoader.Listener() { @Override public void onProgress(int progress) { Log.i(TAG, "Progress " + progress + "%"); downloadProgressBar.setProgress(progress); } @Override public void onInstallationSize(long diskSize, long networkSize) { Log.i(TAG, "Map data require " + diskSize); } @Override public void onGetMapPackagesComplete(MapPackage rootMapPackage, MapLoader.ResultCode resultCode) { if (resultCode == MapLoader.ResultCode.OPERATION_SUCCESSFUL) { Log.i(TAG, "Map packages received successful: " + rootMapPackage.getTitle()); currentInstalledMaps = new ArrayList<>(1); populateInstalledMaps(rootMapPackage); } else { Log.e(TAG, "Can't retrieve map packages: " + resultCode.name()); Toast.makeText(context, "Error: " + resultCode.name(), Toast.LENGTH_SHORT).show(); return; } StringBuilder sb = new StringBuilder(); for (MapPackage pac : currentInstalledMaps) { sb.append(pac.getTitle()); sb.append("\n"); } currentInstalledMapsString = sb.toString(); } private void populateInstalledMaps(MapPackage pac) { // only take installed root package, so if e.g. Germany is installed, // don't check for children states if (pac.getInstallationState() == MapPackage.InstallationState.INSTALLED) { Log.i(TAG, "Installed package found: " + pac.getTitle() + " id " + pac.getId()); currentInstalledMaps.add(pac); } else if (pac.getChildren() != null && pac.getChildren().size() > 0) { for (MapPackage p : pac.getChildren()) { populateInstalledMaps(p); } } } @Override public void onCheckForUpdateComplete(boolean updateAvailable, String currentMapVersion, String newestMapVersion, MapLoader.ResultCode resultCode) { Log.i(TAG, "onCheckForUpdateComplete. Update available: " + updateAvailable + " current version: " + currentMapVersion); AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AppCompatAlertDialogStyle); builder.setTitle("Map version checked"); builder.setMessage("Current map version: " + currentMapVersion + "\n\n" + (updateAvailable ? "Update found to " + newestMapVersion : "No update found") + "\n\n" + "Installed maps:\n" + currentInstalledMapsString); builder.setNegativeButton("close", null); if (updateAvailable) { builder.setPositiveButton("Update", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Log.i(TAG, "Update triggered"); downloadProgressBar.setProgress(0); downloadProgressBar.setVisibility(View.VISIBLE); MapLoader.getInstance().performMapDataUpdate(); } }); } builder.show(); } @Override public void onPerformMapDataUpdateComplete(MapPackage rootMapPackage, MapLoader.ResultCode resultCode) { Log.i(TAG, "onPerformMapDataUpdateComplete"); downloadProgressBar.setVisibility(View.INVISIBLE); String message; if (resultCode == MapLoader.ResultCode.OPERATION_SUCCESSFUL) { message = "Map updated successfully"; } else { message = "Map update failed: " + resultCode.name(); } Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } @Override public void onInstallMapPackagesComplete(MapPackage rootMapPackage, MapLoader.ResultCode resultCode) { downloadProgressBar.setVisibility(View.INVISIBLE); String message; if (resultCode == MapLoader.ResultCode.OPERATION_SUCCESSFUL) { message = "Maps installed successfully"; Log.i(TAG, "Package is installed: " + rootMapPackage.getId() + " " + rootMapPackage.getTitle()); MapLoader.getInstance().getMapPackages(); } else { message = "Map installation failed: " + resultCode.name(); Log.e(TAG, "Failed to install package: " + rootMapPackage.getId() + " " + rootMapPackage.getTitle()); } Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } @Override public void onUninstallMapPackagesComplete(MapPackage rootMapPackage, MapLoader.ResultCode resultCode) { Log.i(TAG, "onUninstallMapPackagesComplete"); String message; if (resultCode == MapLoader.ResultCode.OPERATION_SUCCESSFUL) { message = "Maps removed successfully"; } else { message = "Map removal failed: " + resultCode.name(); } // Toast.makeText(HereMap.this, message, Toast.LENGTH_SHORT).show(); // update packages and get installation state MapLoader.getInstance().getMapPackages(); } }; public void onDownloadButtonClicked(ProgressBar downloadProgressBar2) { this.downloadProgressBar = downloadProgressBar2; mapEngine = MapEngine.getInstance(); ApplicationContext appContext = new ApplicationContext(context); mapEngine.init(appContext, new OnEngineInitListener() { @Override public void onEngineInitializationCompleted(Error error) { if (error == OnEngineInitListener.Error.NONE) { // Post initialization code goes here Log.d(TAG, "Downloading new map data..."); List<Integer> downloadList = new ArrayList<>(1); downloadList.add(120214); //Id:120002 Berlin Id:120214, Name: Andalucia, Size:231504 Cherche l'id avec l'application iOS map-downloader-ios-swift downloadProgressBar.setProgress(0); downloadProgressBar.setVisibility(View.VISIBLE); //START TEST AsyncTask.execute(new Runnable() { @Override public void run() { MapLoader.getInstance().installMapPackages(downloadList); }}); //=> LINE WITH PROBLEMS } else { // handle factory initialization failure } } }); }
请问我是否存在操作错误?
问题分析与修正方案
你存在几处关键操作错误,导致下载无法启动:
- 未给MapLoader设置监听器:你定义了
mapLoaderHandler但从未将其注册到MapLoader实例,因此下载的回调(进度、完成状态)都无法触发,也无法确认下载是否真正执行。 - 主线程操作UI风险:虽然你用
AsyncTask.execute执行下载,但onProgress回调会直接在子线程更新ProgressBar,这会触发UI线程异常,导致界面卡顿(日志中的Skipped 1390 frames就是这个原因)。 - MapEngine初始化后未正确获取MapLoader实例:你声明了
mapLoader变量但未初始化,直接调用MapLoader.getInstance()可能存在初始化顺序问题。
修正后的关键代码调整
1. 初始化MapLoader并设置监听器
在MapEngine初始化成功后,添加监听器注册:
mapEngine.init(appContext, new OnEngineInitListener() { @Override public void onEngineInitializationCompleted(Error error) { if (error == OnEngineInitListener.Error.NONE) { Log.d(TAG, "Downloading new map data..."); // 初始化MapLoader并设置监听器 mapLoader = MapLoader.getInstance(); mapLoader.addListener(mapLoaderHandler); List<Integer> downloadList = new ArrayList<>(1); downloadList.add(120214); downloadProgressBar.setProgress(0); downloadProgressBar.setVisibility(View.VISIBLE); AsyncTask.execute(new Runnable() { @Override public void run() { mapLoader.installMapPackages(downloadList); } }); } else { Log.e(TAG, "MapEngine初始化失败: " + error.name()); } } });
2. 修正UI更新线程问题
在onProgress回调中切换到主线程更新ProgressBar:
@Override public void onProgress(int progress) { Log.i(TAG, "Progress " + progress + "%"); // 切换到主线程更新UI ((Activity) context).runOnUiThread(new Runnable() { @Override public void run() { downloadProgressBar.setProgress(progress); } }); }
3. 避免重复初始化与资源泄漏
在HereMapClass销毁时移除监听器:
public void destroy() { if (mapLoader != null) { mapLoader.removeListener(mapLoaderHandler); } }
并在LoginActivity的onDestroy中调用:
@Override protected void onDestroy() { super.onDestroy(); if (hereMap != null) { hereMap.destroy(); } }
额外注意点
- 确保
LoginActivity.getAppContext()返回的是应用上下文而非Activity上下文,避免内存泄漏。 - 检查地图包ID
120214是否有效,可通过HereMap官方工具确认当前可用的地图包ID。 - 确认应用已申请
INTERNET、WRITE_EXTERNAL_STORAGE等必要权限,且运行时权限已授予。
内容的提问来源于stack exchange,提问作者ΩlostA
相关产品推荐
相关产品推荐

