编辑框架 class 实现 Android 以实现向后兼容性
Edit framework class implementation Android for backward compatibility
我正在尝试在旧的 Android 2.2 应用程序中使用 ViewPager.PageTransformer
界面。我的目标API是8级。但是在 ViewPager
compat library class
中有已知的检查
if (Build.VERSION.SDK_INT >= 11) {
final boolean hasTransformer = transformer != null;
final boolean needsPopulate = hasTransformer != (mPageTransformer ....
}
所以因为这个检查 PageTransformer
在旧的 Android 中不起作用,即使有很棒的 NineOldAndroids
库。
我知道有一些可用的解决方案,其中之一是更改 ViewPager
class 的默认实现。此外,在 github.
上可以使用没有此检查的 dafault compat 库的分支
当然我可以使用这个库或者自己修改它并使用我的自定义 ViewPager
class.
但是这样的话就出现了很多问题。
最重要的是,您不能将此自定义 class 与其他库一起使用,例如 ViewPageIndicator
,因为它们是为默认 ViewPager
class.
创建的
首先,我想实现什么?
我的想法是为我的目的创建一些库和自定义视图,这个自定义视图将包括页面指示器、视图分页器和一些其他视图。
因此,库的用户不必直接使用 ViewPager
,它将仅使用自定义视图和库中的其他一些 classes。
我有不同的想法如何解决这个问题。
只需创建自定义 ViewPager
,使用我的自定义实现从依赖于 ViewPager
的其他库编辑 classes,只是为了更改 [=62= 的类型] 成员。因此,只需向用户隐藏所有这些肮脏的东西,并允许他使用自定义视图的方法。
使用反射。起初,我认为这是个坏主意,因为它相当昂贵,我是这么认为的,但后来我在官方 Android 开发博客 Android Backward Compatibility 上找到了这篇文章。下面是如何使用反射来实现向后兼容的示例。如果在这种情况下使用反射,我可以简单地覆盖方法而不是调用 super,更改部分,但它看起来真的很笨重和丑陋。但是我们将有一个很大的优势,我们可以在任何需要默认 ViewPager
的地方使用这个自定义 class。
也许还有另一种解决这个问题的好方法。
但我真的很关心这个代码在旧设备上应该 运行 的性能,它应该尽可能轻量级。
请建议在这种情况下遵循的最佳方法,只需使用默认实现实现我想做的任何事情的库,并使其对用户隐藏或使用反射并保存与其他 classes 的兼容性。
如有任何帮助和建议,我将不胜感激。
谢谢
首先我尝试更改 ViewPager
的默认实现并在库中使用它。我只是删除了 API 检查并开始编写库的其他部分。
很快我就陷入了第一个问题,而不是另一个问题。所以我的代码变成了一些奇怪的东西。
如果使用自定义 ViewPager
class,兼容性将完全破坏。
我必须将所有需要的库放入我的项目中,以使其与其他使用默认 ViewPager
的库伪兼容。仅用于更改这些 classes 中的参数和字段。
总之我回到了反思。它看起来非常丑陋并且假装是一种不好的做法,但在这种情况下我找不到更好的解决方案(如果有请举个例子)。
我已经完成了一些基准测试并测量了使用反射和默认方法调用方法所需的时间。
结果是。
默认方法第一次有 0 延迟(甚至以纳秒为单位)。但最多花费 30517ns
。
Reflection 看起来非常 heavy.It 从 10000000 to 50000000 ns
调用此方法。但此外,它将来自某些(不同)服务的 ANR 错误打印到调试日志中。
这是此类消息的示例。
05-09 22:39:50.995 169-187/? E/ActivityManager﹕ ANR in
com.google.android.gms
Reason: Executing service com.google.android.gms/.icing.service.IndexWorkerService
Load: 3.06 / 3.08 / 3.0
CPU usage from 5830ms to 0ms ago:
4.1% 169/system_server: 3% user + 1% kernel / faults: 158 minor
0.3% 123/adbd: 0% user + 0.3% kernel / faults: 15 minor
0.3% 2137/lib.performancecheck: 0.3% user + 0% kernel / faults: 28 minor
0% 245/tiwlan_wq: 0% user + 0% kernel
6% TOTAL: 3.7% user + 2.2% kernel
CPU usage from 513ms to 1124ms later.......
这种情况不是每次都会发生,但主要是在应用程序首次启动时发生,因此使用反射调用方法需要花费很多时间。
也许这是可以接受的解决方案,只要此方法不会被频繁调用,可能只在 activity 启动时调用一次,或者根本不调用?
我担心这个消息,有任何真正的 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();
}
}
}
我正在尝试在旧的 Android 2.2 应用程序中使用 ViewPager.PageTransformer
界面。我的目标API是8级。但是在 ViewPager
compat library class
if (Build.VERSION.SDK_INT >= 11) {
final boolean hasTransformer = transformer != null;
final boolean needsPopulate = hasTransformer != (mPageTransformer ....
}
所以因为这个检查 PageTransformer
在旧的 Android 中不起作用,即使有很棒的 NineOldAndroids
库。
我知道有一些可用的解决方案,其中之一是更改 ViewPager
class 的默认实现。此外,在 github.
上可以使用没有此检查的 dafault compat 库的分支
当然我可以使用这个库或者自己修改它并使用我的自定义 ViewPager
class.
但是这样的话就出现了很多问题。
最重要的是,您不能将此自定义 class 与其他库一起使用,例如 ViewPageIndicator
,因为它们是为默认 ViewPager
class.
创建的
首先,我想实现什么?
我的想法是为我的目的创建一些库和自定义视图,这个自定义视图将包括页面指示器、视图分页器和一些其他视图。
因此,库的用户不必直接使用 ViewPager
,它将仅使用自定义视图和库中的其他一些 classes。
我有不同的想法如何解决这个问题。
只需创建自定义
ViewPager
,使用我的自定义实现从依赖于ViewPager
的其他库编辑 classes,只是为了更改 [=62= 的类型] 成员。因此,只需向用户隐藏所有这些肮脏的东西,并允许他使用自定义视图的方法。使用反射。起初,我认为这是个坏主意,因为它相当昂贵,我是这么认为的,但后来我在官方 Android 开发博客 Android Backward Compatibility 上找到了这篇文章。下面是如何使用反射来实现向后兼容的示例。如果在这种情况下使用反射,我可以简单地覆盖方法而不是调用 super,更改部分,但它看起来真的很笨重和丑陋。但是我们将有一个很大的优势,我们可以在任何需要默认
ViewPager
的地方使用这个自定义 class。
也许还有另一种解决这个问题的好方法。
但我真的很关心这个代码在旧设备上应该 运行 的性能,它应该尽可能轻量级。
请建议在这种情况下遵循的最佳方法,只需使用默认实现实现我想做的任何事情的库,并使其对用户隐藏或使用反射并保存与其他 classes 的兼容性。
如有任何帮助和建议,我将不胜感激。 谢谢
首先我尝试更改 ViewPager
的默认实现并在库中使用它。我只是删除了 API 检查并开始编写库的其他部分。
很快我就陷入了第一个问题,而不是另一个问题。所以我的代码变成了一些奇怪的东西。
如果使用自定义 ViewPager
class,兼容性将完全破坏。
我必须将所有需要的库放入我的项目中,以使其与其他使用默认 ViewPager
的库伪兼容。仅用于更改这些 classes 中的参数和字段。
总之我回到了反思。它看起来非常丑陋并且假装是一种不好的做法,但在这种情况下我找不到更好的解决方案(如果有请举个例子)。
我已经完成了一些基准测试并测量了使用反射和默认方法调用方法所需的时间。
结果是。
默认方法第一次有 0 延迟(甚至以纳秒为单位)。但最多花费
30517ns
。Reflection 看起来非常 heavy.It 从
10000000 to 50000000 ns
调用此方法。但此外,它将来自某些(不同)服务的 ANR 错误打印到调试日志中。 这是此类消息的示例。
05-09 22:39:50.995 169-187/? E/ActivityManager﹕ ANR in com.google.android.gms Reason: Executing service com.google.android.gms/.icing.service.IndexWorkerService Load: 3.06 / 3.08 / 3.0 CPU usage from 5830ms to 0ms ago: 4.1% 169/system_server: 3% user + 1% kernel / faults: 158 minor 0.3% 123/adbd: 0% user + 0.3% kernel / faults: 15 minor 0.3% 2137/lib.performancecheck: 0.3% user + 0% kernel / faults: 28 minor 0% 245/tiwlan_wq: 0% user + 0% kernel 6% TOTAL: 3.7% user + 2.2% kernel CPU usage from 513ms to 1124ms later.......
这种情况不是每次都会发生,但主要是在应用程序首次启动时发生,因此使用反射调用方法需要花费很多时间。
也许这是可以接受的解决方案,只要此方法不会被频繁调用,可能只在 activity 启动时调用一次,或者根本不调用?
我担心这个消息,有任何真正的 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();
}
}
}