【问题标题】:BadParcelableException in google maps code谷歌地图代码中的 BadParcelableException
【发布时间】:2012-12-16 10:17:54
【问题描述】:

运行稍加修改的 Google 地图示例会在 Google 地图代码中抛出 BadParcelableExceptionLatLng 类是 parcelable 但找不到。谷歌地图代码似乎试图解开未被它包裹的对象。什么情况下的问题?

package com.example.mapdemo;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import android.os.Bundle;

public class RawMapViewDemoActivity extends android.support.v4.app.FragmentActivity {
    private MapView mMapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.raw_mapview_demo);

        mMapView = (MapView) findViewById(R.id.map);
        mMapView.onCreate(savedInstanceState);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mMapView.onSaveInstanceState(outState);

        outState.putParcelable("marker", new LatLng(0, 0));
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        LatLng ll = savedInstanceState.getParcelable("marker");
    }
}

...

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity 
    ComponentInfo{com.example.mapdemo/com.example.mapdemo.RawMapViewDemoActivity}: 
    android.os.BadParcelableException: ClassNotFoundException when unmarshalling: 
    com.google.android.gms.maps.model.LatLng
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
   at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:2832)
   at android.app.ActivityThread.access$1600(ActivityThread.java:117)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
   at android.os.Handler.dispatchMessage(Handler.java:99)
   at android.os.Looper.loop(Looper.java:130)
   at android.app.ActivityThread.main(ActivityThread.java:3683)
   at java.lang.reflect.Method.invokeNative(Native Method)
   at java.lang.reflect.Method.invoke(Method.java:507)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
   at dalvik.system.NativeStart.main(Native Method)

Caused by: android.os.BadParcelableException: 
ClassNotFoundException when unmarshalling: com.google.android.gms.maps.model.LatLng
   at android.os.Parcel.readParcelable(Parcel.java:1958)
   at android.os.Parcel.readValue(Parcel.java:1846)
   at android.os.Parcel.readMapInternal(Parcel.java:2083)
   at android.os.Bundle.unparcel(Bundle.java:208)
   at android.os.Bundle.getBundle(Bundle.java:1078)
   at com.google.android.gms.maps.internal.MapStateHelper
       .getParcelableFromMapStateBundle(MapStateHelper.java:41)
   at maps.y.ae.a(Unknown Source)
   at maps.y.bm.onCreate(Unknown Source)
   at com.google.android.gms.maps.internal.IMapViewDelegate$Stub
       .onTransact(IMapViewDelegate.java:66)
   at android.os.Binder.transact(Binder.java:279)
   at com.google.android.gms.maps.internal.IMapViewDelegate$a$a
       .onCreate(Unknown Source)
   at com.google.android.gms.maps.MapView$b.onCreate(Unknown Source)
   at com.google.android.gms.internal.c$3.a(Unknown Source)
   at com.google.android.gms.internal.i.b(Unknown Source)
   at com.google.android.gms.maps.MapView$a.a(Unknown Source)
   at com.google.android.gms.maps.MapView$a.a(Unknown Source)
   at com.google.android.gms.internal.c.a(Unknown Source)
   at com.google.android.gms.internal.c.onCreate(Unknown Source)
   at com.google.android.gms.maps.MapView.onCreate(Unknown Source)
   at com.example.mapdemo.RawMapViewDemoActivity
       .onCreate(RawMapViewDemoActivity.java:40)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
   ... 12 more

【问题讨论】:

  • 代码中的 RawMapViewDemoActivity.java:40 行在哪里
  • mMapView.onCreate(savedInstanceState);抱歉,为了简洁起见,我编辑了代码。
  • 如果您评论mMapView.onCreate(savedInstanceState); 行,那么它是否有效?
  • 初始化地图需要这一行。没有它,代码会引发其他异常并最终删除更多代码,最终会出现空白屏幕。
  • 这个运气好吗?在我介绍这些新地图之前,我在实现 Parcelable 并习惯于解组的自定义类中遇到了同样的问题。我最终添加了一个空的 onConfigurationChanged 覆盖,以防止在更改设备方向时发生这种情况。当活动被系统关闭后恢复时,我还将 null 传递给 MapView.onCreate。这会导致一些 Markers 等消失,但也不例外,当用户再次开始与地图交互时,一切都会恢复。

标签: android google-maps google-maps-android-api-2


【解决方案1】:

只是为了在此处添加现有答案,在调用 mapView.onCreate 之前,我一直在将我的包裹从保存状态中删除,它工作正常。

但是,在我的 Fragment 中添加了 ViewPager 之后,我没有意识到 BadParcelableException 已经返回并且代码已投入生产。事实证明,ViewPager 也保存了它的状态,因为它是支持库的一部分,谷歌地图无法找到解包它的类。

所以我选择了反转这个过程,而不是从我知道的 Bundle 中删除 Parcel,我选择为地图创建一个新的 Bundle,只复制地图的状态。

private final static String BUNDLE_KEY_MAP_STATE = "mapData";

@Override
public void onSaveInstanceState(Bundle outState) {
    // Save the map state to it's own bundle
    Bundle mapState = new Bundle();
    mapView.onSaveInstanceState(mapState);
    // Put the map bundle in the main outState
    outState.putBundle(BUNDLE_KEY_MAP_STATE, mapState);
    super.onSaveInstanceState(outState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_map, container, false);
    mapView = (MapView) view.findViewById(R.id.mapView);
    mapView.getMapAsync(this);

    Bundle mapState = null;
    if (savedInstanceState != null) {
        // Load the map state bundle from the main savedInstanceState
        mapState = savedInstanceState.getBundle(BUNDLE_KEY_MAP_STATE);
    }

    mapView.onCreate(mapState);
    return view;
}

【讨论】:

【解决方案2】:

我尝试了前两个答案都没有成功,但我找到了另一种解决方法:

保存状态时,在将数据添加到 Bundle 之前小心转发 MapView.onSaveInstanceState 调用(你做得很好):

@Override
public void onSaveInstanceState(Bundle outState) {

    // Forward the call BEFORE adding our LatLng array, else it will crash :
    _mapView.onSaveInstanceState(outState);

    // Put your Parcelable in the bundle:
    outState.putParcelableArray("myLatLng", new LatLng(0, 0) );
}

在onCreate/onRestore中恢复状态时,检查bundle是否不为null,如果是,获取你的parcel,然后在转发调用前将其从bundle中移除:

@Override
public void onCreate(Bundle savedInstanceState) {

    // If the bundle is not null, get your Parcelable :
    LatLng myLatLng = null;
    if(savedInstanceState != null) {

        myLatLng = (LatLng) savedInstanceState.getParcelable("myLatLng");

        // Remove your Parcelable from the bundle, else it will crash :
        savedInstanceState.remove("myLatLng");
    }

    // Forward the call :
    _mapView.onCreate(savedInstanceState);
    setUpMapIfNeeded();
}

【讨论】:

    【解决方案3】:

    此问题已在 code.google.com here 上提交,以防您想给它加注星标。

    与此同时,我通过简单地将我的 Parcelable 添加到 Bundle 中,然后将其添加到最终的 onSaveInstanceState Bundle 中来解决这个问题。这是因为 Bundle 是 MapView 的内部 ClassLoader 的已知类。

    我创建了两个非常小的 util 方法来为我执行此操作。这是代码

    public static Parcelable unbundleParcelable(String key, Bundle src) {
        Bundle b = src.getBundle(key);
        if (b != null) {
            return b.getParcelable("bundle_parcelable_util_key");
        }
        return null;
    }
    
    public static void bundleParcelable(String key, Bundle dest, Parcelable parcelable) {
        Bundle b = new Bundle();
        b.putParcelable("bundle_parcelable_util_key", parcelable);
        dest.putBundle(key, b);
    }
    

    我已修改上一篇文章中的代码以使用我的临时解决方案。以下是我的使用方法。

    @Override
    public void onSaveInstanceState(Bundle outState) {
    
        // Forward the call BEFORE adding our LatLng array, else it will crash :
        _mapView.onSaveInstanceState(outState);
    
        // Put your Parcelable in the bundle:
        bundleParcelable("myLatLng", outState, new LatLng(0, 0));
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Forward the call :
        _mapView.onCreate(savedInstanceState);
    
        LatLng myLatLng = null;
        if(savedInstanceState != null) {
            // Extract your Parcelable
            myLatLng = (LatLng) unbundleParcelable("myLatLng", savedInstanceState);
        }
    
        setUpMapIfNeeded();
    }
    

    这应该适用于您在项目中使用的任何自定义 Parcelable。

    【讨论】:

    • 我很高兴代码对某人有用。干杯!
    【解决方案4】:

    我找到了一种解决方法(有点)。在它发生的地方,试着第二次做。它总是捕获异常,并且第二次问题似乎没有在这个地方发生。这对我有用。

    try {
        mapIntent.putExtra(Intents.EXTRA_LATLNG, new LatLng(
            store.getLatitude(), store.getLongitude()));
    } catch (BadParcelableException e) {
        mapIntent.putExtra(Intents.EXTRA_LATLNG, new LatLng(
            store.getLatitude(), store.getLongitude()));
    }
    

    【讨论】:

      【解决方案5】:

      我找到了一个更简单的解决方法。在执行任何操作之前,将您的 Fragment 或 Activity 的类加载器提供给包:

      savedInstanceState.setClassLoader(getClass().getClassLoader());
      

      当直接传递到 MapFragment 或 MapView 的 onCreateView 或 onCreate 时,这似乎不起作用,因此您必须手动分配地图的 CameraPosition 并将空包传递给这些函数。

      【讨论】:

      • 您有您描述的解决方法的示例吗?
      【解决方案6】:

      当我离开我的应用程序时,我也遇到了 BadParcelableException(带有标签的 FragmentActivity,每个标签中都有一个 Fragment)。我通过先调用mapmpvMap.onSaveInstanceState(outState); 然后调用super.onSaveInstanceState(outState); 解决了这个问题

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-30
        • 2016-07-25
        • 2015-01-16
        • 1970-01-01
        相关资源
        最近更新 更多