Android插件ResourcesNotFoundExcept

2018-11-30  本文已影响49人  None_Ling

背景

之前遇到过一种ResourceNotFoundException,是因为在WebView初始化的时候,AssetManager被重新创建了,所有被添加的插件APK路径都被清空了,所以导致资源找不到。

而这次遇到了两个问题,都是资源ID可以找到,而Context无法找到ID对应的资源

以及在7.0以上Asset中的资源可以找到,而8.0系统的Asset资源无法找到

WindowManagerService.applyAnimationLocked

当Activity被finish的时候,会出现Crash,而问题日志如下:

11-30 13:11:31.701 1250-1338/? A/WindowManager: Unhandled exception while laying out windows
    android.content.res.Resources$NotFoundException: Resource ID #0x7a04001d
        at android.content.res.Resources.getValue(Resources.java:1375)
        at android.content.res.Resources.loadXmlResourceParser(Resources.java:2894)
        at android.content.res.Resources.getAnimation(Resources.java:1191)
        at android.view.animation.AnimationUtils.loadAnimation(AnimationUtils.java:73)
        at com.android.server.wm.OppoAppTransition.loadAnimation(OppoAppTransition.java:192)
        at com.android.server.wm.OppoAppTransition.createOppoCustomAnimLocked(OppoAppTransition.java:376)
        at com.android.server.wm.OppoAppTransition.loadAnimation(OppoAppTransition.java:242)
        at com.android.server.wm.WindowManagerService.applyAnimationLocked(WindowManagerService.java:3804)
        at com.android.server.wm.WindowManagerService.setTokenVisibilityLocked(WindowManagerService.java:4898)
        at com.android.server.wm.WindowManagerService.handleAppTransitionReadyLocked(WindowManagerService.java:9902)
        at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedInner(WindowManagerService.java:10669)
        at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedLoop(WindowManagerService.java:9461)
        at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLocked(WindowManagerService.java:9403)
        at com.android.server.wm.WindowManagerService.access$400(WindowManagerService.java:203)
        at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:8282)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:179)
        at android.os.HandlerThread.run(HandlerThread.java:61)

跟踪问题

  1. 通过Android Studio解开插件的APK确认:

    • 资源的类型
    • 资源的ID
  2. 根据ID找到对应的资源名称确认:

    • 插件的APK中是存在这个资源的
    • AAPT将ID和资源名都打进了resources.arsc中。
  3. 搜索资源的引用:

    • 发现在Activity的Theme中的activityOpenEnterAnimation中使用了该动画
  4. 将该资源替换之后,发现还是报同样的错误

  5. 最后发现在Activity结束的时候,会在overridePendingTransition中使用该资源,导致的Crash

    overridePendingTransition

原因

因为在ActivityManagerService带过去的PackageName是主包的PackageName,而不是插件的,所以在主包的resource.arsc中找不到对应的插件资源ID,所以导致的Crash。

解决方案

将使用到的资源,放置到主包即可

Activity.setContentView

当启动Activity的时候,发生Crash,日志如下:

2018-12-05 19:45:14.068 16411-16411/? E/TAG: Crash
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.exapmle.test/com.example.test.TestActivity}: android.content.res.Resources$NotFoundException: Resource ID #0x7f0300f9
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2855)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2930)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1619)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:171)
        at android.app.ActivityThread.main(ActivityThread.java:6684)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:246)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
     Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x790300f9
        at android.content.res.ResourcesImpl.getValue(ResourcesImpl.java:211)
        at android.content.res.MiuiResourcesImpl.getValue(MiuiResourcesImpl.java:91)
        at android.content.res.Resources.loadXmlResourceParser(Resources.java:2158)
        at android.content.res.Resources.getLayout(Resources.java:1156)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:425)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:418)
        at android.app.Activity.setContentView(Activity.java:2713)

问题追踪

  1. 当Activity启动的时候,调用setContentView
  2. setContentView中创建PhoneWindow对象,在PhoneWindow对象中,会调用LayoutInflater.inflate来解析layout文件
  3. LayoutInflater.inflate函数中会通过getContext().getResources().getLayout()来获取XmlResourceParser对象
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }

        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

解决方案

只需要重写Activity中的getResources(),返回添加了路径的Resources对象即可,从添加了路径的Resources对象中获取layout文件就可以找到这个文件了。

AssetManager.open

在项目后期,发现了在Androidi 系统7.0的手机上可以找到Asset资源,而8.0手机无法找到Asset资源的问题。由于插件资源路径是通过Resource中的AssetManager反射调用addAssetPath函数加入插件APK路径直接注入的,所以如果AssetManager不对,则无法获取。

打印日志后发现:

2018-12-06 19:34:58.865 26210-26954/? E/GLSurfaceView: Context context:com.example.TestActivity@72e38ee
2018-12-06 19:34:58.865 26210-26954/? E/GLSurfaceView: Context Resource:android.content.res.MiuiResources@47759b2   ApplicationResource:android.content.res.MiuiResources@47759b2
2018-12-06 19:34:58.865 26210-26954/? E/GLSurfaceView: 
// Activity.getAsset()
Activity Asset:android.content.res.AssetManager@5d19d03  
// Activity.getResource().getAsset()
ContextResource Asset:android.content.res.AssetManager@b68f480  
// Application.getResource().getAsset()
Application Asset:android.content.res.AssetManager@b68f480
2018-12-06 19:58:22.771 7971-9450/? E/GLSurfaceView: Context context:com.example.TestActivity@17d6008
2018-12-06 19:58:22.771 7971-9450/? E/GLSurfaceView: Context Resource:android.content.res.HwResources@b16ed87   ApplicationResource:android.content.res.HwResources@b16ed87
2018-12-06 19:58:22.771 7971-9450/? E/GLSurfaceView: 
// Activity.getAsset()
Activity Asset:android.content.res.AssetManager@28c74b4  
// Activity.getResource().getAsset()
ContextResource Asset:android.content.res.AssetManager@28c74b4  
// Application.getResource().getAsset()
Application Asset:android.content.res.AssetManager@28c74b4

解决方案

只需要将Activity中获取getResources()getAsset()重写即可,将对应的Resources以及AssetManager返回正确,就不会出现FileNotFoundException了。

上一篇 下一篇

猜你喜欢

热点阅读