Android 首次启动 Activity with Google Maps 非常慢
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26178212/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
First launch of Activity with Google Maps is very slow
提问by nonahex
I want to have SupportMapFragment in one of my Activity. I add this fragment directly to layout xml and this layout set as content view. But when Activity is launched for the first time, it takes too long (over 1 second). Next launches are ok and take few milliseconds.
我想在我的一个活动中使用 SupportMapFragment。我将此片段直接添加到布局 xml 并将此布局设置为内容视图。但是第一次启动Activity时,耗时太长(超过1秒)。下一次发射没问题,需要几毫秒。
I tried:
我试过:
- remove any initialization
- use MapFragment instead of SupportMapFragment
- add MapFragment programatically
- 删除任何初始化
- 使用 MapFragment 而不是 SupportMapFragment
- 以编程方式添加 MapFragment
but nothing helped. Map is shown without any problem or suspicious log.
但没有任何帮助。地图显示没有任何问题或可疑日志。
Do you have any suggestion, what it causes and how to improve it?
你有什么建议,它是什么原因造成的以及如何改进它?
edit:I have a ListView and when user clicks on Item, it launches DetailActivity with MapFragment. After click on item there is a noticeable delay before DetailActivity shows up. Only method onCreate, where I call setContentView, runs over 1 second. And while activity is in onCreate method, there is no visible content from this activity. This delay between click and showing content is not very user friendly.
编辑:我有一个 ListView,当用户单击 Item 时,它会使用 MapFragment 启动 DetailActivity。单击项目后,在 DetailActivity 出现之前会有明显的延迟。只有我调用 setContentView 的方法 onCreate 运行超过 1 秒。虽然活动在 onCreate 方法中,但此活动没有可见内容。点击和显示内容之间的这种延迟对用户来说不是很友好。
Thank you
谢谢
采纳答案by clocksmith
The reason why the first load takes so long is because the Play Services APIs have to load as seen in log lines:
第一次加载需要这么长时间的原因是播放服务 API 必须加载,如日志行所示:
I/Google Maps Android API﹕ Google Play services client version: 6587000
I/Google Maps Android API﹕ Google Play services package version: 6768430
Unfortunately, the "package" one takes about a second to load and using the MapsInitializer only will get you the "client." So here is a a not so pretty work around: Initialize a dummy map in your main launcher activity.
不幸的是,加载“包”大约需要一秒钟,而使用 MapsInitializer 只会让您成为“客户端”。所以这里有一个不太好的解决方法:在您的主启动器活动中初始化一个虚拟地图。
mDummyMapInitializer.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
Log.d(TAG, "onMapReady");
}
});
Now when you load your actual map later on, it shouldn't have to initialize the Play services APIs. This shouldn't cause any delays in your main activity either because the async method executes off the main thread.
现在,当您稍后加载实际地图时,它不应该初始化 Play 服务 API。这不应该在您的主要活动中造成任何延迟,因为异步方法在主线程之外执行。
Since you have to do the initialization somewhere no matter what, I think it makes sense to do it right when the app starts, so that when you load an activity that actually needs a map, you do not have to wait at all.
由于无论如何都必须在某处进行初始化,因此我认为在应用程序启动时正确进行初始化是有意义的,这样当您加载实际需要地图的活动时,您根本不必等待。
Note: mDummyMapInitializer
must be a MapFragment
or SupportMapFragment
and must be added to the activity, or else the Play Services APIs won't be loaded. The getMapAsync
method itself must also be called from the main thread.
注意:mDummyMapInitializer
必须是MapFragment
orSupportMapFragment
并且必须添加到活动中,否则不会加载 Play 服务 API。该getMapAsync
方法本身也必须从主线程调用。
回答by cYrixmorten
Ok so I just had the same issue and think, after viewing this question, that there is no 'nice' solution.
好的,所以我遇到了同样的问题,并认为在查看了这个问题后,没有“好的”解决方案。
My current hack is to delay adding the fragment, giving the Activity a chance to render everything else before adding the map.
我目前的技巧是延迟添加片段,让活动有机会在添加地图之前呈现其他所有内容。
Now, I am embedding the map as a childfragment, so my code looks like this:
现在,我将地图作为子片段嵌入,因此我的代码如下所示:
// inside onCreateView
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (isAdded()) {
FragmentManager fm = getChildFragmentManager();
GoogleMapFragment mapFragment = GoogleMapFragment
.newInstance();
fm.beginTransaction()
.replace(R.id.mapContainer, mapFragment).commit();
}
}
}, 1000);
if adding directly to Activity it might look like this:
如果直接添加到 Activity,它可能如下所示:
// inside onCreate
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (!isFinishing()) {
FragmentManager fm = getFragmentManager();
GoogleMapFragment mapFragment = GoogleMapFragment
.newInstance();
fm.beginTransaction()
.replace(R.id.mapContainer, mapFragment).commit();
}
}
}, 1000);
Nevertheless, a check inside the Runnable is needed to ensure that we are not trying to add the map to some non-existing Activity or Fragment.
然而,需要在 Runnable 内部进行检查,以确保我们不会尝试将映射添加到某些不存在的 Activity 或 Fragment。
I am not a fan of hardcoded delays like this, so will return if I come up with something better. 1 second should be plenty though and could probably be even less.
我不喜欢这样的硬编码延迟,所以如果我想出更好的东西,我会回来的。1 秒应该足够了,甚至可能更少。
回答by Fidel Montesino
I solved it by using MapsInitializerin my Application.onCreate():
我通过在 Application.onCreate() 中使用MapsInitializer解决了这个问题:
MapsInitializer.initialize(this);
Good results and cleaner (and not hacky) solution!
良好的结果和更清洁(而不是 hacky)的解决方案!
回答by Wookie
I've been fighting this issue too, and have found considerable improvements by doing the following:
我也一直在解决这个问题,并通过执行以下操作发现了相当大的改进:
1) Unplug your USB cable (or otherwise disconnect your debugging session) and try it again. Google Maps in an app is muchslower when a debugging session is active. Disconnect the debugger and it gets a lot faster... it's still certainly not the fastest, but it's at least acceptable.
1) 拔下 USB 电缆(或以其他方式断开调试会话)并重试。当调试会话处于活动状态时,应用程序中的 Google 地图会慢得多。断开调试器,它会变得更快……它肯定不是最快的,但至少可以接受。
2) Don't call setMapType() unless you've already called getMapType() and confirmed that it's different from what you want to set it to. Multiple calls for the same Map Type will still cause it to reset each time, which can take time.
2) 不要调用 setMapType() 除非您已经调用了 getMapType() 并确认它与您想要设置的不同。多次调用同一个 Map Type 仍然会导致它每次都重置,这可能需要时间。
3) Add the Map fragment programmatically, similar to what @cYrixmorten posted, but I do it from a background thread started at the end of my onResume(), which then waits 50ms and then runs it on the UI thread. This keeps it from hitting the UI thread right away, so that gives the Activity time to load and display; you should at least be on screen while the map is possibly choking everything up.
3) 以编程方式添加 Map 片段,类似于@cYrixmorten 发布的内容,但我是从 onResume() 末尾开始的后台线程中添加的,然后等待 50 毫秒,然后在 UI 线程上运行它。这可以防止它立即命中 UI 线程,从而使 Activity 有时间加载和显示;你至少应该在屏幕上,而地图可能会窒息一切。
The catch here is that you want to create a new MapFragment instance only once per Activity, not every time the screen orientation is rotated. What I do is call "getFragmentManager().findFragmentById(R.id.mapContainer)", which will either give me the map fragment handle from last time, or a null if this is the first time (in which case I'll create the map fragment and do the FragmentManager.replace() ).
这里的问题是您只想为每个 Activity 创建一个新的 MapFragment 实例,而不是每次旋转屏幕方向时。我所做的是调用“getFragmentManager().findFragmentById(R.id.mapContainer)”,它要么给我上次的地图片段句柄,要么给我一个 null 如果这是第一次(在这种情况下我将创建地图片段并执行 FragmentManager.replace() )。
回答by cVoronin
I have a "main" activity - and an activity with mapView. When that activity-with-mapView starts for a first time, it is really slow.
我有一个“主要”活动 - 和一个带有 mapView 的活动。当那个activity-with-mapView第一次启动时,它真的很慢。
clocksmith's postgave me an idea to start initialization from main activity in a separate thread. And it really solves the problem.
clocksmith 的帖子给了我一个想法,可以在单独的线程中从主要活动开始初始化。它确实解决了问题。
Here is my code from "main" activity:
这是我来自“主要”活动的代码:
public void onCreate(Bundle savedInstanceState) {
...
Runnable initMap = () -> {
BaseApplication.d("Start init mapView");
MapView mapView = new MapView(MainActivity.this);
mapView.onCreate(null);
BaseApplication.d("... done");
};
new Thread(initMap).start();
}
mapView is never used - it's only for initialization purpose.
mapView 从未使用过 - 它仅用于初始化目的。
And here is a stack trace - just for info:
这是一个堆栈跟踪 - 仅供参考:
12-09 19:31:54.442 17172-17341/my.app D/XXX: Start init mapView
12-09 19:31:54.525 17172-17341/my.app I/zzy: Making Creator dynamically
12-09 19:31:55.007 17172-17341/my.app D/ChimeraCfgMgr: Reading stored module config
12-09 19:31:55.153 17172-17341/my.app D/ChimeraCfgMgr: Loading module com.google.android.gms.maps from APK /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/MapsModule.apk
12-09 19:31:55.154 17172-17341/my.app D/ChimeraModuleLdr: Loading module APK /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/MapsModule.apk
12-09 19:31:55.262 17172-17341/my.app D/ChimeraFileApk: Primary ABI of requesting process is armeabi-v7a
12-09 19:31:55.271 17172-17341/my.app D/ChimeraFileApk: Classloading successful. Optimized code found.
12-09 19:31:55.316 17172-17341/my.app W/System: ClassLoader referenced unknown path: /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/native-libs/armeabi-v7a
12-09 19:31:55.317 17172-17341/my.app W/System: ClassLoader referenced unknown path: /data/user/0/com.google.android.gms/app_chimera/chimera-module-root/module-71c764a6f3cb92bdc5525a965b589e7c5ed304f3/native-libs/armeabi
12-09 19:31:55.618 17172-17341/my.app I/Google?Maps?Android?API: Google Play services client version: 7571000
12-09 19:31:55.630 17172-17341/my.app I/Google?Maps?Android?API: Google Play services package version: 8489438
12-09 19:31:55.969 17172-17341/my.app I/e: Token loaded from file. Expires in: 423267993 ms.
12-09 19:31:55.969 17172-17341/my.app I/e: Scheduling next attempt in 422967 seconds.
12-09 19:31:56.338 17172-17341/my.app D/XXX: ... done
As we can see, it realy takes a lot of time...
正如我们所看到的,这真的需要很多时间......
回答by sagits
For me it was way slower than 1sec beause i was using:
对我来说,它比我使用的 1 秒慢得多:
mapFragment.getMap();
Then i changed to:
然后我改为:
mapFragment.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
}
});
Using the getMapAsync() wont block the ui, so your activity will load before the map. Its still slow, but for my purposes it was ok with just showing a loading message.
使用 getMapAsync() 不会阻塞用户界面,因此您的活动将在地图之前加载。它仍然很慢,但就我的目的而言,只显示加载消息就可以了。
回答by Joao Sousa
Similar to the other solutions here, but using RxJava + RxAndroid.
类似于这里的其他解决方案,但使用 RxJava + RxAndroid。
Just call this snippet from the launcher activity onCreate
.
只需从启动器活动中调用此代码段即可onCreate
。
Observable.fromCallable(new Callable<Void>() {
@Override
public Void call() {
MapView mapView = new MapView(LaunchActivity.this); // Replace LaunchActivity with appropriate activity's name
mapView.onCreate(null);
return null;
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(
new Action1<Void>() {
@Override
public void call(Void ignorable) {
Log.i("Google Maps", "Initialised Google Maps.");
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable ignorable) {
Log.w("Google Maps", "[EXPECTED] Initialized Google Maps but got: " + ignorable.getMessage());
}
});
回答by Vahn84
I had the same issue and the MapsInitializer trick didn't work for me.
我遇到了同样的问题,MapsInitializer 技巧对我不起作用。
The best solution to the problem, in my humble opinion, is to load by hand the Map Fragment as described from other users. It's not an hacky solution, you just have to deal yourself with the fragment instance
以我的拙见,该问题的最佳解决方案是按照其他用户的描述手动加载 Map Fragment。这不是一个笨拙的解决方案,您只需要处理片段实例
mMapFragment = MapFragment.newInstance();
fragmentManager.beginTransaction().replace(R.id.map_fragment_container, fragment, FRAGMENT_GOOGLEMAPS_TAG).commit();