8

Filing bug to YouTube Android Player API Library engineers: see android-youtube-api tag

Over the course of the past week and a half, I've noticed this weird BadParcelableException steadily increasing in our app and have nailed it down to YouTube's new release on android.

This crash will occur if your app is playing a youtube video, bringing your app to background, force stopping the Youtube app, and resuming your app again. Crash reproducible on Youtube version 12.19.56. Also tested on an older YouTube version 12.05.21 and the crash was not there.

Stack trace:

main Exception: Unable to start activity ComponentInfo{com.myapp.MainActivity}: 
android.os.BadParcelableException: ClassNotFoundException when unmarshalling: asc 
Stack: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.MainActivity}: 
android.os.BadParcelableException: ClassNotFoundException when unmarshalling: asc 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2666) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727) 
at android.app.ActivityThread.-wrap12(ActivityThread.java) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1478) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:6121) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779) Caused by: 
android.os.BadParcelableException: ClassNotFoundException when unmarshalling: asc 
at android.os.Parcel.readParcelableCreator(Parcel.java:2536) 
at android.os.Parcel.readParcelable(Parcel.java:2462) 
at android.os.Parcel.readValue(Parcel.java:2365) 
at android.os.Parcel.readSparseArrayInternal(Parcel.java:2813) 
at android.os.Parcel.readSparseArray(Parcel.java:2068) 
at android.os.Parcel.readValue(Parcel.java:2422) 
at android.os.Parcel.readArrayMapInternal(Parcel.java:2732) 
at android.os.BaseBundle.unparcel(BaseBundle.java:269) 
at android.os.Bundle.getSparseParcelableArray(Bundle.java:934) 
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1208) 
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1528) 
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1595) 
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:2893) 
at android.support.v4.app.FragmentController.dispatchCreate(FragmentController.java:190) 
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:353) 
at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:85) 
at com.myapp.BaseActivity.onCreate(BaseActivity.java:36) 
at com.myapp.MainActivity.onCreate(MainActivity.java:190) 
at android.app.Activity.performCreate(Activity.java:6682) 
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2619) ... 9 more

Caused by: java.lang.ClassNotFoundException: Didn't find class "asc" on path: DexPathList[[zip file "/data/app/com.myapp-naA-_cCrz-w81rqx98ipcQ==/base.apk"],nativeLibraryDirectories=[/data/app/com.myapp-naA-_cCrz-w81rqx98ipcQ==/lib/arm64, /system/lib64, /vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:93)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 32 more

Some more info:

@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    super.onCreate(savedInstanceState); // Crashing here MainActivity.java:190
    ...
}
mco
  • 1,809
  • 15
  • 31
  • 1
    Based on stacktrace, it seems like the FragmentManager is trying to fetch some of it's internal state from Bundle during initialization, Bundle attempts to unparcel itself, but that fails, because Bundle contents are broken in a subtle way. Could you post contents of Activity state Bundle at the end of call to `Activity#onSaveInstanceState`? Override the method, set breakpoint before returning, then copy *entire* Bundle contents from debugger (including all nested objects with their contents!) and paste it somewhere for us to see (Github Gist etc). Pay special attention to SparseArrays. – user1643723 Jun 06 '17 at 03:19
  • Note, that bugs in Bundle aside, your own code is likely to be somewhat guilty here. Make sure, that you haven't somehow corrupted Parcel data by making mistake in one of your own Parcelables. – user1643723 Jun 06 '17 at 03:23
  • @user1643723 I tried, but how do you copy something from debugger to list out all of its contents in text format? – mco Jun 06 '17 at 19:39
  • Maybe have a look at IntelliJ custom renderers? There shouldn't be *that* much information anyway, as I said, just look at contents of `SparseArray`s. – user1643723 Jun 07 '17 at 01:22
  • Also you can just use LogCat to recursively log everything inside Bundle during onSaveInstanceState (just don't log it in single line to make sure that it fits). – user1643723 Jun 07 '17 at 01:28
  • @mco Did you found any solution yet ? – Shadab Ansari Aug 02 '17 at 21:26
  • @ShadabAnsari yea, I just surrounded the `super.onCreate(...)` with a try catch block and caught the `BadParcelableException`. – mco Aug 02 '17 at 22:36

4 Answers4

4

The problem in view state saving and restoring. Youtube app have a bug and store view state which contains instance of class asc from youtube apk. So our app can not restore it because known nothing about this class. My solution is preventing view state saving for YoutubePlayerView in YouTubePlayerSupportFragment by next code:

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

    // disable view state saving to prevent saving states from youtube apk which cannot be restored.
    View view = getView();
    if (view instanceof ViewGroup) {
        ViewGroup viewGroup = ((ViewGroup) view);
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            viewGroup.getChildAt(i).setSaveFromParentEnabled(false);
        }
    }
}

This code should be added to your subclass of YouTubePlayerSupportFragment. This solution does not remove youtube player state from bundle. So youtube player will be restored successfully.

TalkLittle
  • 8,866
  • 6
  • 54
  • 51
xkor
  • 646
  • 5
  • 11
0

Use following code in your onCreate:

@Override
public void onCreate(Bundle b) {
  if (b != null) {
    final ClassLoader contextCl = getClassLoader();
    b.setClassLoader(contextCl);
  }
  super.onCreate(b);
}

The bug is probably due to YouTube library code storing custom Parcelable inside the state Bundle (YouTube library classes are probably obfuscated by Proguard, hence weird names like "asc").

If the suggestion above does not work, follow along the lines of @Fitz's suggestion and drop the state Bundle. Don't try to mess with it in onCreate (that won't work), instead it is best to override onSaveInstanceState to return Bundle.EMPTY if your app is suspended when you have a playback in progress.


Note, that the bug in question is pretty tricky in multiple ways. If you (or YouTube app) store nested Bundles inside each other, those also need to have proper ClassLoader set… Someone should tell Google to get some common sense and just use Thread#getContextClassLoader inside Bundle to fix that problem once and for all.

user1643723
  • 4,109
  • 1
  • 24
  • 48
  • I tried this on my MainActivity and no luck, still getting the same crash. I have the MainActivity, then a fragment with a ViewPager and one of the items of the ViewPager is the fragment containing the YouTubePlayerSupportFragment – dtrejogo Jun 20 '17 at 21:26
0

I found a solution that works for me. In my onCreate method I catch the RuntimeException that gets thrown due to the ClassNotFoundException in the youtube player. The video loses its state. Pressing play causes it to start over. Otherwise business as usual.

Ben Dauer
  • 1
  • 1
  • 3
-1

I just ended up using a try catch block that worked for me.

try {
    super.onCreate(savedInstanceState);

} catch (BadParcelableException e) {
    // Maybe put an analytic here to see if it is still being thrown.
    // So when youtube fixes the issue in the future we can remove this 
    // ugly try catch.
    Log.e(TAG, e.toString);
}
mco
  • 1,809
  • 15
  • 31