Editing the Framework Class Interface for Backward Compatibility

I am trying to use the frontend ViewPager.PageTransformer

in an old Android 2.2 app. My target API is 8 levels. But there is a known check in ViewPager

the compatibility library class

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

      

Therefore, due to this check, PageTransformer

does not work in older androids even with a large library NineOldAndroids

.
I know there are some solutions available, one of them is to change the standard implementation of the class ViewPager

. Moreover, a fork of the dafault compat library without this check is available on github.
Of course I can use this library or just change it myself and use my custom class ViewPager

.
But in this case, there are many problems.
Most importantly, this custom class cannot be used with other libraries, for example ViewPageIndicator

, because they are created for the ViewPager

default class .
First, what do I want to implement?

My idea is to create some library and custom view for my purposes, this custom view will include page indicator, pager view and some other views.

Thus, the user of the library does not need to work with ViewPager

, he will only use the custom view and some other classes from the library.

I have different ideas how I can solve this problem.

  • Just create a custom ViewPager

    , edit classes from other dependent libraries ViewPager

    with my custom implementation just to change the class member type. So just hide all this dirty stuff from the user and let him use the custom vision methods.

  • Use reflection. At first I thought it was a bad idea because it is quite expensive, I thought, but I found an article on the official Android Development blog Android Backward Compatibility . Here's an example of how you can use reflection for backward compatibility. If using reflection in this case, I can just override the method and not call the super, change the part, but it looks really heavy and ugly. But we will have one big advantage, we can use this custom class wherever the default is required ViewPager

    .

Perhaps there is another good way to deal with this problem.

But I am really worried about performance as this code should work on older devices, it should be as lightweight as possible.

Please suggest a better way to follow in this case, just implement a library that does whatever I need with a default implementation and keep it hidden from users, or use reflection and keep compatibility with other classes.

I would be grateful for any help and advice. thank.

+3


source to share


1 answer


I first tried to change the standard implementation ViewPager

and use it in the library. I just removed the API validation and started writing another part of the library.
Very soon I faced the first problem than the other. So my code turns into something weird.
In the case of using a custom class, ViewPager

compatibility is completely broken.
I have to put all the libraries I need in my project to make it pseudo-compatible with the other libraries I use by default ViewPager

. Just to change the arguments and fields in these classes.

In general, I returned to thinking. This looks very ugly and pretends to be bad practice, but I can't seem to find a better solution in this case (if any, please provide an example).
I did some tests and measured the time it takes to call a method with reflection and default method.
Results.

  • The default method had 0 latency the first time (even in nanoseconds). But than he took the maximum 30517ns

    .

  • The reflection looks very heavy. Calling this method requires 10000000 to 50000000 ns

    . But apart from that, it prints ANR error from some (different) services in the debug log. Here is an example of such a message.

05-09 22: 39: 50.995 169-187 /? E / ActivityManager: ANR at com.google.android.gms Cause: Service execution com.google.android.gms / .icing.service.IndexWorkerService Load: 3.06 / 3.08 / 3.0 CPU usage 5830ms to 0ms ago: 4.1% 169 / system_server: 3% user + 1% core / errors: 158 minors 0.3% 123 / adbd: 0% user + 0.3% core / errors: 15 minors 0.3% 2137 / lib.performancecheck: 0.3% user + 0% core / errors: 28 low 0% 245 / tiwlan_wq: 0% user + 0% core 6% TOTAL: 3.7% users + 2.2% core CPU usage from 513 ms to 1124 ms later.

This does not happen every time, but mostly when the application is launched first, so it takes a long time to call the method with reflection.



Maybe this might be an acceptable solution since this method won't be called often, maybe only once at startup, or not running at all?

I'm worried about this post, there was some kind of real ANR (of course, because it shouldn't react for more time). Could this post cause problems in the future, should I really get rid of the reflection in this case? Perhaps there are alternative ways to solve this problem.

Here is the code for the method setPageTransformer

using reflection.

@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();
            }
        }

    }

      

+1


source







All Articles