Android模拟器中替换系统应用的方法

Android模拟器6.0版本进入系统时,桌面应用com.android.launcher3会发生随机Crash。

W/System.err( 1611): java.lang.IllegalArgumentException:
Wrong state class, expecting View State but received class
android.appwidget.AppWidgetHostView$ParcelableSparseArray instead.
This usually happens when two views of different type have the same id in the same hierarchy.
This view’s id is id/0x3. Make sure other views do not use the same id.

W/System.err( 1611): at android.view.View.onRestoreInstanceState(View.java:13772)
W/System.err( 1611): at android.widget.TextView.onRestoreInstanceState(TextView.java:3784)
W/System.err( 1611): at android.view.View.dispatchRestoreInstanceState(View.java:13748)
W/System.err( 1611): at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2894)
W/System.err( 1611): at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2894)
W/System.err( 1611): at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2894)
W/System.err( 1611): at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2894)
W/System.err( 1611): at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2894)
W/System.err( 1611): at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2894)
W/System.err( 1611): at android.view.View.restoreHierarchyState(View.java:13726)
W/System.err( 1611): at com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2009)
W/System.err( 1611): at android.app.Activity.onRestoreInstanceState(Activity.java:1074)
W/System.err( 1611): at com.android.launcher3.Launcher.onRestoreInstanceState(Launcher.java:2013)

网上找到如下解决方法:

packages/apps/Launcher3/src/com/android/launcher3/Launcher.java:

public void onRestoreInstanceState(Bundle state) {
    // Wrap the code block will throw runtinme exception in try / catch block
    // ----------------------------------------------------------------------
    //super.onRestoreInstanceState(state);
    //for (int page: mSynchronouslyBoundPages) {
    //    mWorkspace.restoreInstanceStateForChild(page);
    //}

    try {
        super.onRestoreInstanceState(state);
        for (int page: mSynchronouslyBoundPages) {
            mWorkspace.restoreInstanceStateForChild(page);
        }
    } catch (Exception e) {
        Log.e(TAG, "Exception in onRestoreInstanceState():");
        e.printStackTrace();
    }
}

编译后替换掉镜像中的文件:

/system/priv-app/Launcher3/Launcher3.apk
/system/priv-app/Launcher3/oat/x86/Launcher3.odex

重启后报了另外一个错误:

java.lang.RuntimeException: Unable to get provider com.android.launcher3.LauncherProvider: java.lang.ClassNotFoundException: Didn’t find class “com.android.launcher3.LauncherProvider” on path: DexPathList[[zip file “/system/priv-app/Launcher3/Launcher3.apk”],nativeLibraryDirectories=[/system/priv-app/Launcher3/lib/x86, /vendor/lib, /system/lib]]
at android.app.ActivityThread.installProvider(ActivityThread.java:5156)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:4748)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4688)
at android.app.ActivityThread.-wrap1(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.ClassNotFoundException: Didn’t find class “com.android.launcher3.LauncherProvider” on path: DexPathList[[zip file “/system/priv-app/Launcher3/Launcher3.apk”],nativeLibraryDirectories=[/system/priv-app/Launcher3/lib/x86, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at android.app.ActivityThread.installProvider(ActivityThread.java:5141)
… 10 more
Suppressed: java.io.IOException: No original dex files found for dex location /system/priv-app/Launcher3/Launcher3.apk
at dalvik.system.DexFile.openDexFileNative(Native Method)
at dalvik.system.DexFile.openDexFile(DexFile.java:295)
at dalvik.system.DexFile.(DexFile.java:80)
at dalvik.system.DexFile.(DexFile.java:59)
at dalvik.system.DexPathList.loadDexFile(DexPathList.java:279)
at dalvik.system.DexPathList.makePathElements(DexPathList.java:248)
at dalvik.system.DexPathList.(DexPathList.java:120)
at dalvik.system.BaseDexClassLoader.(BaseDexClassLoader.java:48)
at dalvik.system.PathClassLoader.(PathClassLoader.java:65)
at android.app.ApplicationLoaders.getClassLoader(ApplicationLoaders.java:58)
at android.app.LoadedApk.getClassLoader(LoadedApk.java:376)
at android.app.LoadedApk.makeApplication(LoadedApk.java:568)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4680)
… 8 more

猜测是因为编译时用的art库版本与模拟器中不一致,导致生成的oat文件不匹配。解决方法是把dex文件拷贝到模拟器里,然后使用dex2oat编译下。

编译出来的dex文件(Launcher3.apk中不包含dex文件)路径是:

out/target/common/obj/APPS/Launcher3_intermediates/classes.dex

使用如下命令可以在/data/local/tmp目录下生成Launcher3.odex文件,/data/local/tmp/Launcher3.dex是要输入的dex文件路径。

5.0中执行的命令

> dex2oat --runtime-arg -Xms64m --runtime-arg -Xmx512m --dex-file=/data/local/tmp/Launcher3.dex --dex-location=/system/priv-app/Launcher3/Launcher3.apk --oat-file=/data/local/tmp/Launcher3.odex --android-root=/system --instruction-set=x86 --instruction-set-features=default --include-patch-information --runtime-arg -Xnorelocate --no-include-debug-symbols

6.0中执行的命令

> dex2oat --runtime-arg -Xms64m --runtime-arg -Xmx512m --dex-file=/data/local/tmp/Launcher3.dex --dex-location=/system/priv-app/Launcher3/Launcher3.apk --oat-file=/data/local/tmp/Launcher3.odex --android-root=/system --instruction-set=x86 --instruction-set-variant=x86 --instruction-set-features=default --include-patch-information --runtime-arg -Xnorelocate --no-generate-debug-info --abort-on-hard-verifier-error

另外一种解决方法是:
修改

build/target/board/generic_x86/BoardConfig.mk

文件,注释掉:

WITH_DEXPREOPT ?= true

这样,编译后的apk中就会包含dex文件,在模拟器第一次开机时会自动进行dexopt操作。

分享