版權(quán)聲明:本文為博主原創(chuàng)文章姜性,未經(jīng)博主允許不得轉(zhuǎn)載恐锣。http://www.reibang.com/p/a08d754944c4
轉(zhuǎn)載請標明出處:
http://www.reibang.com/p/a08d754944c4
本文出自 AWeiLoveAndroid的博客
【前言】過年回家忙著干活茅主,忙著給親戚的孩子發(fā)紅包诀姚,好累,忙里偷閑打開studio看了一下v4包赫段,之前看過幾個類矢赁,這次是每個類都看了一下糯笙,原來Android的v4包的源碼也有一些是寫的不是那么友好的,還有很多改善空間坯台。
下面就拿其中的android.support.v4.text
這個包里面的 TextUtilsCompat
和 TextUtilsCompatJellybeanMr1
這兩個類來做一個具體講解炬丸。
本文源碼同步發(fā)布在github瘫寝,詳情請點擊 https://github.com/AweiLoveAndroid/refactor-android-support-v4
一蜒蕾、首先看一下Androidv4包下面的
TextUtilsCompat
和TextUtilsCompatJellybeanMr1
源碼:
(一)TextUtilsCompat 源碼:
public final class TextUtilsCompat {
private static class TextUtilsCompatImpl {
TextUtilsCompatImpl() {
}
@NonNull
public String htmlEncode(@NonNull String s) {
StringBuilder sb = new StringBuilder();
char c;
for (int i = 0; i < s.length(); i++) {
c = s.charAt(i);
switch (c) {
case '<':
sb.append("<"); //$NON-NLS-1$
break;
case '>':
sb.append(">"); //$NON-NLS-1$
break;
case '&':
sb.append("&"); //$NON-NLS-1$
break;
case '\'':
//http://www.w3.org/TR/xhtml1
// The named character reference ' (the apostrophe, U+0027) was
// introduced in XML 1.0 but does not appear in HTML. Authors should
// therefore use ' instead of ' to work as expected in HTML 4
// user agents.
sb.append("'"); //$NON-NLS-1$
break;
case '"':
sb.append("""); //$NON-NLS-1$
break;
default:
sb.append(c);
}
}
return sb.toString();
}
public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
if (locale != null && !locale.equals(ROOT)) {
final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale);
if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale);
// This is intentionally limited to Arabic and Hebrew scripts, since older
// versions of Android platform only considered those scripts to be right-to-left.
if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
return ViewCompat.LAYOUT_DIRECTION_RTL;
}
}
return ViewCompat.LAYOUT_DIRECTION_LTR;
}
/**
* Fallback algorithm to detect the locale direction. Rely on the first char of the
* localized locale name. This will not work if the localized locale name is in English
* (this is the case for ICU 4.4 and "Urdu" script)
*
* @param locale
* @return the layout direction. This may be one of:
* {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
* {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) {
switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
return ViewCompat.LAYOUT_DIRECTION_RTL;
case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
default:
return ViewCompat.LAYOUT_DIRECTION_LTR;
}
}
}
private static class TextUtilsCompatJellybeanMr1Impl extends TextUtilsCompatImpl {
TextUtilsCompatJellybeanMr1Impl() {
}
@Override
@NonNull
public String htmlEncode(@NonNull String s) {
return TextUtilsCompatJellybeanMr1.htmlEncode(s);
}
@Override
public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
return TextUtilsCompatJellybeanMr1.getLayoutDirectionFromLocale(locale);
}
}
private static final TextUtilsCompatImpl IMPL;
static {
final int version = Build.VERSION.SDK_INT;
if (version >= 17) { // JellyBean MR1
IMPL = new TextUtilsCompatJellybeanMr1Impl();
} else {
IMPL = new TextUtilsCompatImpl();
}
}
/**
* Html-encode the string.
* @param s the string to be encoded
* @return the encoded string
*/
@NonNull
public static String htmlEncode(@NonNull String s) {
return IMPL.htmlEncode(s);
}
/**
* Return the layout direction for a given Locale
*
* @param locale the Locale for which we want the layout direction. Can be null.
* @return the layout direction. This may be one of:
* {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
* {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
return IMPL.getLayoutDirectionFromLocale(locale);
}
public static final Locale ROOT = new Locale("", "");
static String ARAB_SCRIPT_SUBTAG = "Arab";
static String HEBR_SCRIPT_SUBTAG = "Hebr";
private TextUtilsCompat() {}
}
(二)TextUtilsCompatJellybeanMr1 源碼:
/**
* Jellybean MR1 - specific TextUtils API access.
*/
@RequiresApi(17)
@TargetApi(17)
class TextUtilsCompatJellybeanMr1 {
@NonNull
public static String htmlEncode(@NonNull String s) {
return TextUtils.htmlEncode(s);
}
public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
return TextUtils.getLayoutDirectionFromLocale(locale);
}
}
二、分析一下以上源碼的一些需要改進的問題(僅個人理解焕阿,如有不同意見咪啡,歡迎提出):
通過以上源碼來看,看起來確實有點不是很舒服暮屡。
(一)排列順序有點亂撤摸,我格式化了一下,如下褒纲,看的稍微清楚了一些:
/**
* 格式化之后的TextUtilsCompat類
*/
public class TextUtilsCompat {
private static final TextUtilsCompatImpl IMPL;
public static final Locale ROOT = new Locale("", "");
static String ARAB_SCRIPT_SUBTAG = "Arab";
static String HEBR_SCRIPT_SUBTAG = "Hebr";
private TextUtilsCompat() {}
static {
final int version = Build.VERSION.SDK_INT;
if (version >= 17) { // JellyBean MR1
IMPL = new TextUtilsCompatJellybeanMr1Impl();
} else {
IMPL = new TextUtilsCompatImpl();
}
}
/**
* Html-encode the string.
* @param s the string to be encoded
* @return the encoded string
*/
@NonNull
public static String htmlEncode(@NonNull String s) {
return IMPL.htmlEncode(s);
}
/**
* Return the layout direction for a given Locale
*
* @param locale the Locale for which we want the layout direction. Can be null.
* @return the layout direction. This may be one of:
* {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
* {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
return IMPL.getLayoutDirectionFromLocale(locale);
}
private static class TextUtilsCompatImpl {
TextUtilsCompatImpl() {}
@NonNull
public String htmlEncode(@NonNull String s) {
StringBuilder sb = new StringBuilder();
char c;
for (int i = 0; i < s.length(); i++) {
c = s.charAt(i);
switch (c) {
case '<':
sb.append("<"); //$NON-NLS-1$
break;
case '>':
sb.append(">"); //$NON-NLS-1$
break;
case '&':
sb.append("&"); //$NON-NLS-1$
break;
case '\'':
//http://www.w3.org/TR/xhtml1
// The named character reference ' (the apostrophe, U+0027) was
// introduced in XML 1.0 but does not appear in HTML. Authors should
// therefore use ' instead of ' to work as expected in HTML 4
// user agents.
sb.append("'"); //$NON-NLS-1$
break;
case '"':
sb.append("""); //$NON-NLS-1$
break;
default:
sb.append(c);
}
}
return sb.toString();
}
public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
if (locale != null && !locale.equals(ROOT)) {
final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale);
if (scriptSubtag == null) {
return getLayoutDirectionFromFirstChar(locale);
}
// This is intentionally limited to Arabic and Hebrew scripts, since older
// versions of Android platform only considered those scripts to be right-to-left.
if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
return ViewCompat.LAYOUT_DIRECTION_RTL;
}
}
return ViewCompat.LAYOUT_DIRECTION_LTR;
}
/**
* Fallback algorithm to detect the locale direction. Rely on the first char of the
* localized locale name. This will not work if the localized locale name is in English
* (this is the case for ICU 4.4 and "Urdu" script)
*
* @param locale
* @return the layout direction. This may be one of:
* {@link ViewCompat#LAYOUT_DIRECTION_LTR} or
* {@link ViewCompat#LAYOUT_DIRECTION_RTL}.
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) {
switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
return ViewCompat.LAYOUT_DIRECTION_RTL;
case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
default:
return ViewCompat.LAYOUT_DIRECTION_LTR;
}
}
}
private static class TextUtilsCompatJellybeanMr1Impl extends TextUtilsCompatImpl {
TextUtilsCompatJellybeanMr1Impl() {
}
@Override
@NonNull
public String htmlEncode(@NonNull String s) {
return TextUtilsCompatJellybeanMr1.htmlEncode(s);
}
@Override
public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
return TextUtilsCompatJellybeanMr1.getLayoutDirectionFromLocale(locale);
}
}
}
(二)TextUtilsCompat 這個類里面有兩個內(nèi)部類准夷,一個是TextUtilsCompatImpl
,一個是TextUtilsCompatJellybeanMr1Impl
,TextUtilsCompatJellybeanMr1Impl
是繼承自TextUtilsCompatImpl
的莺掠。
(三)從靜態(tài)代碼塊看出衫嵌,api 大于17 使用 new TextUtilsCompatJellybeanMr1Impl(); api小于17 使用TextUtilsCompatImpl。TextUtilsCompat彻秆,TextUtilsCompatImpl和TextUtilsCompatJellybeanMr1Impl里面都有 htmlEncode
方法和 getLayoutDirectionFromLocale
方法楔绞。
靜態(tài)代碼塊里面通過TextUtilsCompatImpl IMPL這個常量來判斷,當(dāng)api大于17用TextUtilsCompatJellybeanMr1Impl唇兑,否則用TextUtilsCompatImpl酒朵,然后htmlEncode方法調(diào)用了對應(yīng)內(nèi)部類里面的htmlEncode方法,getLayoutDirectionFromLocale調(diào)用了對應(yīng)內(nèi)部類里面的getLayoutDirectionFromLocale方法扎附。
(四)TextUtilsCompatImpl和TextUtilsCompatJellybeanMr1Impl里面都有 htmlEncode
方法和 getLayoutDirectionFromLocale
方法蔫耽,看看它們的區(qū)別针肥。
(1)TextUtilsCompatJellybeanMr1Impl這個內(nèi)部類的方法解析:
-
htmlEncode(@NonNull String s)
方法 返回的是:
TextUtilsCompatJellybeanMr1.htmlEncode(s); ==> 調(diào)用了TextUtils.htmlEncode(s);
-
getLayoutDirectionFromLocale(@Nullable Locale locale)
方法返回的是:
TextUtilsCompatJellybeanMr1.getLayoutDirectionFromLocale(locale); ==> 調(diào)用了TextUtils.getLayoutDirectionFromLocale(locale);
(2)TextUtilsCompatImpl這個內(nèi)部類的方法解析:
-
htmlEncode(@NonNull String s)
方法 返回的是:
在這個方法內(nèi)部寫了一遍,跟TextUtils.htmlEncode(s);方法里面的一模一樣。
-
getLayoutDirectionFromLocale(@Nullable Locale locale)
方法返回的是:
重新寫了一遍,這個方法是真正有所區(qū)別的地方汪厨。
三蜂厅、根據(jù)我做過項目用到的MVP的開發(fā)模式掘猿,我把共同的htmlEncode方法和getLayoutDirectionFromLocale方法抽取出一個接口,然后分別用兩個實現(xiàn)類去實現(xiàn)接口衬衬,然后用TextUtilsCompat這個類去判斷調(diào)用哪個實現(xiàn)類的方法滋尉,這樣看起來更直觀一些狮惜。具體步驟如下:
(一)抽取公共接口ITextUtilsCompat
/**
* 抽取公用的接口
*/
public interface ITextUtilsCompat {
/**
* Html-encode the string.
* @param s the string to be encoded
* @return the encoded string
*/
public String htmlEncode(@NonNull String s);
/**
* Return the layout direction for a given Locale
*
* @param locale the Locale for which we want the layout direction. Can be null.
* @return the layout direction. This may be one of:
* {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} or
* {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL}.
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
public int getLayoutDirectionFromLocale(@Nullable Locale locale);
}
(二)寫一個TextUtilsCompatJellybeanMr1實現(xiàn)ITextUtilsCompat 接口碾篡,然后把之前TextUtilsCompatJellybeanMr1類里面的復(fù)制過來开泽,具體如下:
/**
* 兼容Android 17+ 版本
* Jellybean MR1 - specific TextUtils API access.
*/
@RequiresApi(17)
@TargetApi(17)
class TextUtilsCompatJellybeanMr1 implements ITextUtilsCompat{
@Override
@NonNull
public String htmlEncode(@NonNull String s) {
return TextUtils.htmlEncode(s);
}
@Override
public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
return TextUtils.getLayoutDirectionFromLocale(locale);
}
}
(三)寫一個類TextUtilsCompatImpl實現(xiàn)ITextUtilsCompat 接口眼姐,然后把之前TextUtilsCompat類里面的有關(guān)代碼復(fù)制過來佩番,具體如下:
/**
* Android 17以下版本使用這個類
*/
class TextUtilsCompatImpl implements ITextUtilsCompat{
public static final Locale ROOT = new Locale("", "");
static String ARAB_SCRIPT_SUBTAG = "Arab";
static String HEBR_SCRIPT_SUBTAG = "Hebr";
@Override
@NonNull
public String htmlEncode(@NonNull String s) {
StringBuilder sb = new StringBuilder();
char c;
for (int i = 0; i < s.length(); i++) {
c = s.charAt(i);
switch (c) {
case '<':
sb.append("<"); //$NON-NLS-1$
break;
case '>':
sb.append(">"); //$NON-NLS-1$
break;
case '&':
sb.append("&"); //$NON-NLS-1$
break;
case '\'':
//http://www.w3.org/TR/xhtml1
// The named character reference ' (the apostrophe, U+0027) was
// introduced in XML 1.0 but does not appear in HTML. Authors should
// therefore use ' instead of ' to work as expected in HTML 4
// user agents.
sb.append("'"); //$NON-NLS-1$
break;
case '"':
sb.append("""); //$NON-NLS-1$
break;
default:
sb.append(c);
}
}
return sb.toString();
}
@Override
public int getLayoutDirectionFromLocale(@Nullable Locale locale) {
if (locale != null && !locale.equals(ROOT)) {
final String scriptSubtag = ICUCompat.maximizeAndGetScript(locale);
if (scriptSubtag == null) {
return getLayoutDirectionFromFirstChar(locale);
}
// This is intentionally limited to Arabic and Hebrew scripts, since older
// versions of Android platform only considered those scripts to be right-to-left.
if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
return ViewCompat.LAYOUT_DIRECTION_RTL;
}
}
return ViewCompat.LAYOUT_DIRECTION_LTR;
}
/**
* Fallback algorithm to detect the locale direction. Rely on the first char of the
* localized locale name. This will not work if the localized locale name is in English
* (this is the case for ICU 4.4 and "Urdu" script)
*
* @param locale
* @return the layout direction. This may be one of:
* {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} or
* {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL}.
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
private static int getLayoutDirectionFromFirstChar(@NonNull Locale locale) {
switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
return ViewCompat.LAYOUT_DIRECTION_RTL;
case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
default:
return ViewCompat.LAYOUT_DIRECTION_LTR;
}
}
}
(四)封裝TextUtilsCompat給調(diào)用者使用,具體如下:
/**
* v4包下面的TextUtilsCompat的簡單優(yōu)化
* 這里使用的是策略模式利朵,根據(jù)不同api版本調(diào)用不同的接口實現(xiàn)類
* 這樣寫更好維護。
*/
public final class TextUtilsCompat {
private static final ITextUtilsCompat IMPL;
private TextUtilsCompat() {}
static {
final int version = Build.VERSION.SDK_INT;
// JellyBean MR1 大于等于17
if (version >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
IMPL = new TextUtilsCompatJellybeanMr1();
} else {
IMPL = new TextUtilsCompatImpl();
}
}
/**
* Html-encode the string.
* @param s the string to be encoded
* @return the encoded string
*/
@NonNull
public static String htmlEncode(@NonNull String s) {
return IMPL.htmlEncode(s);
}
/**
* Return the layout direction for a given Locale
*
* @param locale the Locale for which we want the layout direction. Can be null.
* @return the layout direction. This may be one of:
* {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_LTR} or
* {@link android.support.v4.view.ViewCompat#LAYOUT_DIRECTION_RTL}.
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
public static int getLayoutDirectionFromLocale(@Nullable Locale locale) {
return IMPL.getLayoutDirectionFromLocale(locale);
}
}