编辑框架类实现Android以实现向后兼容

时间:2015-05-03 20:59:25

标签: android performance android-fragments reflection android-viewpager

我正在尝试在旧的Android 2.2应用程序中使用ViewPager.PageTransformer接口。我的目标API是8级。但是已经知道ViewPager compat库类

中的检查
if (Build.VERSION.SDK_INT >= 11) {
        final boolean hasTransformer = transformer != null;
        final boolean needsPopulate = hasTransformer != (mPageTransformer ....
    }

因此,即使有很棒的PageTransformer图书馆,这项检查NineOldAndroids也无法在旧版Androids中使用。
我知道有一些可用的解决方案,其中之一是更改ViewPager类的默认实现。此外,github上还提供了没有此检查的dafault compat库的分支 当然,我可以使用这个库,或者只是自己更改它并使用我的自定义ViewPager类 但在这种情况下会出现很多问题 最重要的是,您不能将此自定义类与其他库一起使用,例如ViewPageIndicator,因为它们是为默认的ViewPager类创建的。
首先,我想实施什么?

我的想法是为我的目的创建一些库和自定义视图,此自定义视图将包括页面指示器,视图寻呼机和一些其他视图。

因此,图书馆的用户不必直接使用ViewPager,它将仅使用自定义视图和库中的其他类。

我有不同的想法如何解决这个问题。

  1. 只需创建自定义ViewPager,使用我的自定义实现编辑依赖于ViewPager的其他库中的类,只需更改类成员的类型即可。因此,只需隐藏用户的所有脏东西,并允许他使用海关查看方法。

  2. 使用反射。起初,我认为这是一个坏主意,因为它非常昂贵,我想是这样,但我在官方Android开发博客Android Backward Compatibility 上发现了这篇文章。下面是如何使用反射以便向后比较的示例。如果在这种情况下使用反射,我可以简单地覆盖方法,而不是调用超级,更改部分,但它看起来非常沉重和丑陋。但是我们将有一个很大的优势,我们可以在任何需要默认ViewPager的地方使用此自定义类。

  3. 也许还有另一种解决这个问题的好方法。

    但是我真的很关心性能,因为这个代码应该在旧设备上运行,它应该尽可能轻量级。

    请建议在这种情况下遵循的最佳方式,只需使用默认实现实现我想做的任何事情,并使其对用户隐藏或使用反射并保存与其他类的兼容性。

    我将不胜感激任何帮助和建议。 THX。

1 个答案:

答案 0 :(得分:0)

首先,我尝试更改ViewPager的默认实现,并在库中使用它。我只是删除了API检查并开始编写库的其他部分 我很快就遇到了第一个问题,而不是另一个问题。所以我的代码变成了一些奇怪的东西 如果使用自定义ViewPager类,则兼容性会完全被破坏 我必须在我的项目中放置所有需要的库,以使其与使用默认ViewPager的其他库伪兼容。仅用于更改这些类中的参数和字段。

总而言之,我回到了思考。它看起来很丑陋并假装是一种不好的做法,但在这种情况下我找不到更好的解决方案(如果有请举例)。
我已经用反射和默认方法做了一些基准测试并测量了方法调用所需的时间 结果是。

  1. 默认方法第一次有0延迟(即使在纳秒内)。但比最大30517ns

  2. 反射看起来非常沉重。它从10000000 to 50000000 ns开始调用此方法。但是,它还会将一些(不同的)服务的ANR错误输出到调试日志中。 以下是此类消息的示例。

  3.   

    05-09 22:39:50.995 169-187 /? E / ActivityManager:ANR in   com.google.android.gms       原因:执行服务com.google.android.gms / .icing.service.IndexWorkerService       载荷:3.06 / 3.08 / 3.0       CPU使用率从5830ms到0ms前:       4.1%169 / system_server:3%用户+ 1%内核/故障:158次       0.3%123 / adbd:0%用户+ 0.3%内核/故障:15个未成年人       0.3%2137 / lib.performancecheck:0.3%用户+ 0%内核/故障:28小调       0%245 / tiwlan_wq:0%用户+ 0%内核       6%总计:3.7%用户+ 2.2%内核       CPU使用时间从513ms到1124ms .......

    每次都不会发生这种情况,但主要是在应用程序首次启动时,因此使用反射调用方法需要很长时间。

    如果这种方法不被频繁调用,也许只有一次在活动开始时或根本没有被调用,这可能是可接受的解决方案?

    我担心这个消息,有任何真正的ANR(当然因为它不应该响应更长的时间)。这条消息是否可能导致将来出现问题,我是否应该在这种情况下真正摆脱反思?也许有其他方法可以解决这个问题。

    以下是使用反射的setPageTransformer方法的代码。

    @Override
        public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
         Log.d("VIEWPAGER","Inside overridden method Reflection");
            if (Build.VERSION.SDK_INT >= 11) {
                super.setPageTransformer(reverseDrawingOrder, transformer);
            }
            else {
                final boolean hasTransformer = transformer != null;
                Class<?> clazz =this.getClass();
                while(clazz!=null && !clazz.getSimpleName().equals("ViewPager")) {
                    clazz = clazz.getSuperclass();
                }
                Log.d("VIEWPAGER", clazz.getSimpleName());
                try {
                    Field pageTransformerField = clazz.getDeclaredField("mPageTransformer");
                    pageTransformerField.setAccessible(true);
                    PageTransformer pageTransformer = (PageTransformer) pageTransformerField.get(this);
                    final boolean needsPopulate = hasTransformer != (pageTransformer != null);
                    pageTransformerField.set(this, transformer);
                    Method drawingOrderMethod = clazz.getDeclaredMethod("setChildrenDrawingOrderEnabledCompat",boolean.class);
                    drawingOrderMethod.setAccessible(true);
                    drawingOrderMethod.invoke(this,hasTransformer);
                    Field drawingOrderField = clazz.getDeclaredField("mDrawingOrder");
                    drawingOrderField.setAccessible(true);
    
                    if (hasTransformer) {
                        drawingOrderField.set(this, reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD);
                    } else {
                        drawingOrderField.set(this, DRAW_ORDER_DEFAULT);
                    }
                    if (needsPopulate) {
                        Method populateMethod = clazz.getDeclaredMethod("populate");
                        populateMethod.setAccessible(true);
                        populateMethod.invoke(this);
                    }
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
    
        }