Android版本兼容器

Android版本兼容器

随着Android版本一代代发布,碎片化的问题越来越严重,不过好在趋势上市面上的版本已经开始比较集中了。但我们终究还是要面对版本兼容问题。我们不能因为要用高版本方法而提高最低版本限制,高版本里炫酷的效果及高效的方法只会导致你的最低版本显示越来越高,而官方的解决方案(Support-v4)无疑是给了我们新的启示。

示例

首先我们抽取一个官方的版本兼容器的一部分看看:

/**
 * Helper for accessing features in {@link TextView} introduced after API level
 * 4 in a backwards compatible fashion.
 */
public final class TextViewCompat {

    // Hide constructor
    private TextViewCompat() {}

    interface TextViewCompatImpl {
        ...
        int getMaxLines(TextView textView);
        int getMinLines(TextView textView);
        void setTextAppearance(@NonNull TextView textView, @StyleRes int resId);
        Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView);
    }

    static class BaseTextViewCompatImpl implements TextViewCompatImpl {
        ...
        @Override
        public int getMaxLines(TextView textView) {
            return TextViewCompatGingerbread.getMaxLines(textView);
        }

        @Override
        public int getMinLines(TextView textView) {
            return TextViewCompatGingerbread.getMinLines(textView);
        }

        @Override
        public void setTextAppearance(TextView textView, @StyleRes int resId) {
            TextViewCompatGingerbread.setTextAppearance(textView, resId);
        }

        @Override
        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
            return TextViewCompatGingerbread.getCompoundDrawablesRelative(textView);
        }
    }

    static class JbTextViewCompatImpl extends BaseTextViewCompatImpl {
        @Override
        public int getMaxLines(TextView textView) {
            return TextViewCompatJb.getMaxLines(textView);
        }

        @Override
        public int getMinLines(TextView textView) {
            return TextViewCompatJb.getMinLines(textView);
        }
    }

    static class JbMr1TextViewCompatImpl extends JbTextViewCompatImpl {
        ...
        @Override
        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
            return TextViewCompatJbMr1.getCompoundDrawablesRelative(textView);
        }
    }

    static class JbMr2TextViewCompatImpl extends JbMr1TextViewCompatImpl {
        ...
        @Override
        public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
                @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
                @DrawableRes int bottom) {
            TextViewCompatJbMr2.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
                    start, top, end, bottom);
        }
    }

    static class Api23TextViewCompatImpl extends JbMr2TextViewCompatImpl {
        @Override
        public void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
            TextViewCompatApi23.setTextAppearance(textView, resId);
        }
    }

    static final TextViewCompatImpl IMPL;

    static {
        final int version = Build.VERSION.SDK_INT;
        if (version >= 23) {
            IMPL = new Api23TextViewCompatImpl();
        } else if (version >= 18) {
            IMPL = new JbMr2TextViewCompatImpl();
        } else if (version >= 17) {
            IMPL = new JbMr1TextViewCompatImpl();
        } else if (version >= 16) {
            IMPL = new JbTextViewCompatImpl();
        } else {
            IMPL = new BaseTextViewCompatImpl();
        }
    }
    
    ...
    public static int getMaxLines(@NonNull TextView textView) {
        return IMPL.getMaxLines(textView);
    }

    public static int getMinLines(@NonNull TextView textView) {
        return IMPL.getMinLines(textView);
    }

    public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
        IMPL.setTextAppearance(textView, resId);
    }

    public static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
        return textView.getCompoundDrawables();
    }
}

分析

首先private私有化构造函数,避免开发者去new一个出来;然后定义接口,其包含配套的需要做版本兼容的方法;接着实现基础的类来实现这些接口,再就是根据版本断层来重写这一基础类,重写这些类以达到高版本使用高级方法,在就是通过静态代码块来根据不同系统版本实例化一个兼容接口,最后用静态方法来调用这一接口达到版本兼容的效果。

官方的兼容器习惯于使用另外的一个类的静态方法来实现具体功能,好处是版本兼容器只管版本兼容不管具体实现,使代码更清晰,但我们可以直接简化到该重写方法内部实现即可。有些低版本上无法实现高版本方法的效果,但有些可以以曲线救国的方式来达到。所以,使用版本控制器还是要注意低版本上是不做任何处理还是实现了同样的效果。

官方支持库内提供了很多版本兼容器:ViewCompat、ViewGroupCompat、ViewConfigurationCompat、ViewParentCompat等等,基本使用Compat结尾命名。

使用

载入支持库后,我们可以直接使用这些版本兼容器,但是官方也并非面面俱到得提供了所有方法的版本兼容器。但我们可以套用这种模式实现自己的版本兼容器。

下面列出一个WebView的版本兼容器:

public class WebViewCompat {
    private WebViewCompat() {
    }

    /**
     * A callback interface used to provide values asynchronously.
     */
    public interface ValueCallbackCompat {
        /**
         * Invoked when the value is available.
         *
         * @param value The value.
         */
        void onReceiveValue(String value);
    }

    private interface WebViewCompatImpl {
        boolean isSupportEvaluateJavascript();

        void evaluateJavascript(WebView webView, String script, ValueCallbackCompat callback);
    }

    private static class BaseWebViewCompatImpl implements WebViewCompatImpl {
        @Override
        public boolean isSupportEvaluateJavascript() {
            return false;
        }

        @Override
        public void evaluateJavascript(WebView webView, String script, ValueCallbackCompat callback) {
            // do nothing
        }
    }

    @TargetApi(19)
    private static class KitKatWebViewCompatImpl extends BaseWebViewCompatImpl implements ValueCallback<String> {

        private WeakReference<ValueCallbackCompat> callbackCompatWeakReference;

        @Override
        public boolean isSupportEvaluateJavascript() {
            return true;
        }

        @Override
        public void evaluateJavascript(WebView webView, String script, ValueCallbackCompat callback) {
            callbackCompatWeakReference = new WeakReference<>(callback);
            webView.evaluateJavascript(script, this);
        }

        @Override
        public void onReceiveValue(String value) {
            try {
                callbackCompatWeakReference.get().onReceiveValue(value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static final WebViewCompatImpl IMPL;

    static {
        final int version = android.os.Build.VERSION.SDK_INT;
        if (version >= 19) {
            IMPL = new KitKatWebViewCompatImpl();
        } else {
            IMPL = new BaseWebViewCompatImpl();
        }
    }

    public static boolean isSupportEvaluateJavascript() {
        return IMPL.isSupportEvaluateJavascript();
    }

    public static void evaluateJavascript(WebView webView, String script, ValueCallbackCompat callback) {
        IMPL.evaluateJavascript(webView, script, callback);
    }
}

值得注意的是我这里定义了另一个回调接口,因为我将版本限制放置在4,而ValueCallback是在API 7才引入,所以在最低版本限制在7以上的,也就可以直接使用ValueCallback,不需要重新定义另一个接口了。@TargetApi(int)用于避免编译器报错,一旦用到android.os.Build.VERSION.SDK_INT,那么版本限制就一定要放在4了,从4开始才有这个参数。

最后再给出一个我用该方式构建的一个控件ShapeImageView,高低版本呈现的效果一致,但是使用的是不同的方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,079评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,947评论 4 60
  • 改天请你吃饭! 你说过这句话吗?你说到办到了吗?别以为小题大做,从一些小事,可以看出一个人的人品。 一个中国留学生...
    羽佳成长故事阅读 440评论 0 0
  • 今年只有十几岁,游戏也同样玩了十几年,老游戏玩家一枚。 16年是游戏大作井喷的一年,但是不知道为什么,我努力融入游...
    图九阅读 413评论 7 4