Android源碼設(shè)計(jì)模式之筆記(陸續(xù)更新)

開篇之單例模式:
相信大家已經(jīng)很熟悉單例模式了吧甚垦,特別是在保證某個(gè)類只有一個(gè)對(duì)象的場(chǎng)景下才會(huì)使用到艰亮。那有人會(huì)問什么時(shí)候用到單例模式呢迄埃,其實(shí)如果一個(gè)對(duì)象創(chuàng)建需要消耗過多資源時(shí)侄非,正是單例模式用到之處逞怨。

看看單例模式的UML類圖:


單例模式UML圖.png
  • 單例類里面有成員變量(當(dāng)前實(shí)例自己)
  • 暴露一個(gè)public類型的getInstance方法
  • 當(dāng)前構(gòu)造器不公開叠赦,private類型

樣例代碼:

public class SingletonDemo {
    private static SingletonDemo singletonDemo;
    private SingletonDemo(){}
    //懶漢式獲取單例實(shí)例
    public static SingletonDemo getInstance() {
        if (singletonDemo == null) {
            synchronized (SingletonDemo.class) {
                if (singletonDemo == null) {
                    singletonDemo = new SingletonDemo();
                }
            }
        }
        return singletonDemo;
    }

   ***********************
    //飽漢式
    private static final SingletonDemo singletonDemo = new SingletonDemo();
    private SingletonDemo() {
    }
    public static SingletonDemo getInstance() {
        return SingletonDemo.singletonDemo;
    }
}

看到這個(gè)地方,可能大家還是覺得不好使用單例模式册踩。想想你的項(xiàng)目中那些網(wǎng)絡(luò)操作棍好、對(duì)話框的管理借笙、io文件的讀取等等單一實(shí)例的地方是不是都可以去處理呢业稼。但是單例模式由于是一直持有實(shí)例的低散,因此對(duì)于上下文(context)的地方熔号,需要注意注意內(nèi)存泄漏的情況了引镊。

Builder模式:
builder設(shè)計(jì)模式的出現(xiàn)弟头,是為了更好地為一些復(fù)雜對(duì)象進(jìn)行分離處理赴恨。通俗點(diǎn)也就是類的功能太多了伦连,將一些行為放到Builder類中間接處理惑淳。

UML類圖:


builder模式UML圖.png

樣例代碼:

public class Factory {
    Builder builder;

    public Factory() {
        builder = new Builder();
    }

    public void createEngine() {
        builder.createEngine();
    }

    public void createGearbox() {
        builder.createGearbox();
    }

    public void createSuspension() {
        builder.createSuspension();
    }

    public static class Builder {
        private Car car;

        public Builder() {
            car = new Car();
        }

        public Builder createEngine() {
            car.engine = "自然吸氣";
            return this;
        }

        public Builder createGearbox() {
            car.gearbox = "at變速箱";
            return this;
        }

        public Builder createSuspension() {
            car.suspension = "獨(dú)立懸架";
            return this;
        }
    }
}

public class Car {
    //發(fā)動(dòng)機(jī)
    public String engine;
    //變速箱
    public String gearbox;
    //懸架
    public String suspension;

    //正規(guī)寫法是提供set方法的短荐,參數(shù)都是由外部提供的忍宋,為了省事糠排,你懂的
}

上面事例代碼,也正好說明了Builder模式的特點(diǎn),將復(fù)雜對(duì)象的構(gòu)成放到我們的內(nèi)部類中進(jìn)行處理速客。

還記得之前我們顯示一個(gè)對(duì)話框的代碼嗎?

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("dialog title");
builder.setMessage("I am a dialog.");
builder.setPositiveButton("ok", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        //// TODO: 17/12/26
    }
});
builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        //// TODO: 17/12/26
    }
});
builder.show();

相信這段代碼誰都會(huì)寫了吧,以前總是一頭霧水地寫完這段代碼巢钓,然后交給經(jīng)理樂樂地說搞定了≈⑿冢現(xiàn)在不行啊背镇,對(duì)源碼得有點(diǎn)分析,下面就去看看源碼吧:

首先是生成了一個(gè)AlertDialog.Builder對(duì)象济瓢,那咋們瞧瞧吧:

public class AlertDialog extends AppCompatDialog implements DialogInterface {
    ******省略AlertDialog類中代碼******
    public static class Builder {
        private final AlertController.AlertParams P;
        private final int mTheme;
        /**
         * Creates a builder for an alert dialog that uses the default alert
         * dialog theme.
         * <p>
         * The default alert dialog theme is defined by
         * {@link android.R.attr#alertDialogTheme} within the parent
         * {@code context}'s theme.
         *
         * @param context the parent context
         */
        public Builder(@NonNull Context context) {
            this(context, resolveDialogTheme(context, 0));
        }
        /**
         * Creates a builder for an alert dialog that uses an explicit theme
         * resource.
         * <p>
         * The specified theme resource ({@code themeResId}) is applied on top
         * of the parent {@code context}'s theme. It may be specified as a
         * style resource containing a fully-populated theme, such as
         * {@link R.style#Theme_AppCompat_Dialog}, to replace all
         * attributes in the parent {@code context}'s theme including primary
         * and accent colors.
         * <p>
         * To preserve attributes such as primary and accent colors, the
         * {@code themeResId} may instead be specified as an overlay theme such
         * as {@link R.style#ThemeOverlay_AppCompat_Dialog}. This will
         * override only the window attributes necessary to style the alert
         * window as a dialog.
         * <p>
         * Alternatively, the {@code themeResId} may be specified as {@code 0}
         * to use the parent {@code context}'s resolved value for
         * {@link android.R.attr#alertDialogTheme}.
         *
         * @param context the parent context
         * @param themeResId the resource ID of the theme against which to infra
         *                   this dialog, or {@code 0} to use the parent
         *                   {@code context}'s default alert dialog theme
         */
        public Builder(@NonNull Context context, @StyleRes int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
            mTheme = themeResId;
        }
        /**
         * Returns a {@link Context} with the appropriate theme for dialogs area
         * Applications should use this Context for obtaining LayoutInflaters of
         * that will be used in the resulting dialogs, as it will cause views to
         * the correct theme.
         *
         * @return A Context for built Dialogs.
         */
        @NonNull
        public Context getContext() {
            return P.mContext;
        }
        /**
         * Set the title using the given resource id.
         *
         * @return This Builder object to allow for chaining of calls to set met
         */
        public Builder setTitle(@StringRes int titleId) {
            P.mTitle = P.mContext.getText(titleId);
            return this;
        }
        /**
         * Set the title displayed in the {@link Dialog}.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setTitle(@Nullable CharSequence title) {
            P.mTitle = title;
            return this;
        }
        /**
         * Set the message to display using the given resource id.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setMessage(@StringRes int messageId) {
            P.mMessage = P.mContext.getText(messageId);
            return this;
        }

        /**
         * Set the message to display.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setMessage(@Nullable CharSequence message) {
            P.mMessage = message;
            return this;
        }
        /**
         * Set a listener to be invoked when the positive button of the dialog is pressed.
         * @param textId The resource id of the text to display in the positive button
         * @param listener The {@link DialogInterface.OnClickListener} to use.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
            P.mPositiveButtonText = P.mContext.getText(textId);
            P.mPositiveButtonListener = listener;
            return this;
        }

        /**
         * Set a listener to be invoked when the positive button of the dialog is pressed.
         * @param text The text to display in the positive button
         * @param listener The {@link DialogInterface.OnClickListener} to use.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
            P.mPositiveButtonText = text;
            P.mPositiveButtonListener = listener;
            return this;
        }

        /**
         * Set a listener to be invoked when the negative button of the dialog is pressed.
         * @param textId The resource id of the text to display in the negative button
         * @param listener The {@link DialogInterface.OnClickListener} to use.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setNegativeButton(@StringRes int textId, final OnClickListener listener) {
            P.mNegativeButtonText = P.mContext.getText(textId);
            P.mNegativeButtonListener = listener;
            return this;
        }

        /**
         * Set a listener to be invoked when the negative button of the dialog is pressed.
         * @param text The text to display in the negative button
         * @param listener The {@link DialogInterface.OnClickListener} to use.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {
            P.mNegativeButtonText = text;
            P.mNegativeButtonListener = listener;
            return this;
        }
        /**
         * Creates an {@link AlertDialog} with the arguments supplied to this
         * builder and immediately displays the dialog.
         * <p>
         * Calling this method is functionally identical to:
         * <pre>
         *     AlertDialog dialog = builder.create();
         *     dialog.show();
         * </pre>
         */
        public AlertDialog show() {
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
        /**
         * Creates an {@link AlertDialog} with the arguments supplied to this
         * builder.
         * <p>
         * Calling this method does not display the dialog. If no additional
         * processing is needed, {@link #show()} may be called instead to both
         * create and display the dialog.
         */
        public AlertDialog create() {
            // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
            // so we always have to re-set the theme
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            if (P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }
    }
}

這里就是我們剛才顯示對(duì)話框的時(shí)候铺纽,調(diào)的幾個(gè)方法狡门∑淞螅可以看到Builder內(nèi)部類中的setTitle叛复、setMessagesetPositiveButton翘簇、
setNegativeButton方法都是給AlertController.AlertParams P變量賦值缘揪≌殷荩看來AlertParams又是AlertController內(nèi)部類了袖裕。接著就是調(diào)了show方法急鳄,show方法里面緊接著調(diào)了create方法疾宏。在create方法里面生成了一個(gè)AlertDialog坎藐,然后調(diào)用了
AlertController.AlertParamsapply方法岩馍,去看看做了些啥吧:

public void apply(AlertController dialog) {
    if (mCustomTitleView != null) {
        dialog.setCustomTitle(mCustomTitleView);
    } else {
        if (mTitle != null) {
            dialog.setTitle(mTitle);
        }
        if (mIcon != null) {
            dialog.setIcon(mIcon);
        }
        if (mIconId != 0) {
            dialog.setIcon(mIconId);
        }
        if (mIconAttrId != 0) {
            dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
        }
    }
    if (mMessage != null) {
        dialog.setMessage(mMessage);
    }
    if (mPositiveButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                mPositiveButtonListener, null);
    }
    if (mNegativeButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                mNegativeButtonListener, null);
    }
    if (mNeutralButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
                mNeutralButtonListener, null);
    }
    // For a list, the client can either supply an array of items or an
    // adapter or a cursor
    if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
        createListView(dialog);
    }
    if (mView != null) {
        if (mViewSpacingSpecified) {
            dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
                    mViewSpacingBottom);
        } else {
            dialog.setView(mView);
        }
    } else if (mViewLayoutResId != 0) {
        dialog.setView(mViewLayoutResId);
    }
    /*
    dialog.setCancelable(mCancelable);
    dialog.setOnCancelListener(mOnCancelListener);
    if (mOnKeyListener != null) {
        dialog.setOnKeyListener(mOnKeyListener);
    }
    */
}

看到了沒,這里才是將上面builder中的賦值又傳給了AlertController類,這里我們就看下AlertControllersetTitle方法:

public void setTitle(CharSequence title) {
    mTitle = title;
    if (mTitleView != null) {
        mTitleView.setText(title);
    }
}

這里就把builder中傳過來的title給了AlertController中的mTitleView佃乘。那咱們看看mTitleView是什么時(shí)候生成的吧:

private void setupTitle(ViewGroup topPanel) {
    if (mCustomTitleView != null) {
       //省略代碼
    } else {
        mIconView = (ImageView) mWindow.findViewById(android.R.id.icon);
        final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
        if (hasTextTitle && mShowTitle) {
            // Display the title if a title is supplied, else hide it.
            mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
            mTitleView.setText(mTitle);
            //省略代碼
    }
}

可以看到實(shí)際上mTitleViewwindow對(duì)象的R.id.alertTitle布局了庞呕。這里可以自己去研究該window下是怎么生成的,這里就把create的過程屢了一遍了地啰,最后就剩下了show岭埠∠郏可以看到Buildershow方法最后調(diào)用了AlertDialogshow方法馆类。

這里可以畫張流程圖更清晰:

dialog生成的流程圖.png

關(guān)于builder模式就先說這么多了乾巧,總結(jié)下來就是將復(fù)雜對(duì)象的生成放到單獨(dú)的一個(gè)類進(jìn)行處理沟于,對(duì)主類進(jìn)行分離旷太。

一直看好的工廠模式:

說到工廠模式其實(shí)大家可能沒怎么留意,而且自己在寫代碼的時(shí)候嗜傅,也很少知道自己寫的是不是工廠模式了吕嘀。工廠模式顯著的特點(diǎn)是具體產(chǎn)品類專門由一個(gè)叫工廠類專門去構(gòu)造偶房,也就是new的過程棕洋。這樣的模式好處是調(diào)用者無需關(guān)心具體構(gòu)造產(chǎn)品的過程掰盘,而且對(duì)于一些復(fù)雜對(duì)象的構(gòu)造愧捕,也起到了透明的效果次绘。
UML類圖:

工廠模式UML圖.png

事例代碼也很簡(jiǎn)單:

//抽象的產(chǎn)品
public abstract class Product {
    public abstract void function();
}
//具體的產(chǎn)品
public class ProductA extends Product {
    @Override
    public void function() {
        //// TODO: 17/12/27  
    }
}
//具體的產(chǎn)品
public class ProductB extends Product {
    @Override
    public void function() {
        //// TODO: 17/12/27  
    }
}
//抽象工廠
public abstract class Factory {
    abstract Product createProductA();

    abstract Product createProductB();
}
//具體的工廠類
public class FactoryProduct extends Factory {
    @Override
    Product createProductA() {
        return new ProductA();
    }

    @Override
    Product createProductB() {
        return new ProductB();
    }
}

所以從上面結(jié)構(gòu)看管跺,標(biāo)準(zhǔn)的工廠模式是產(chǎn)品有各種各樣的,而工廠就只有一個(gè)

下面通過另外一種方式去看下工廠類的寫法(反射來獲取具體產(chǎn)品):

//反射方式的抽象工廠類
public abstract class ReflexFactory {
    protected abstract <T extends Product> T createProduct(Class<T> cl);
}
//具體的反射工廠類
public class ReflexFactoryProduct extends ReflexFactory {
    @Override
    protected <T extends Product> T createProduct(Class<T> cl) {
        Product p = null;
        try {
            p = (Product) Class.forName(cl.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) p;
    }
}

這里和普通的工廠類相比贩绕,少了創(chuàng)建不同product的方法淑倾,通過傳入不同的class類型來獲得不同的product娇哆。但是貌似只能調(diào)用到無參的product碍讨,這里尷尬了勃黍,要是構(gòu)造器要傳入屬性就麻煩了,不就相當(dāng)于new Object()了弄息。

這里除了上面兩種工廠類之外摹量,還有種靜態(tài)工廠類的形式:

public class StaticFactory {
    enum ProductType {
        ProductA, ProductB;
    }

    public static Product createProduct(ProductType type) {
        switch (type) {
            case ProductA:
                return new ProductA();
            case ProductB:
                return new ProductB();
        }
        return null;
    }
}

靜態(tài)工廠類就一個(gè)類啊荆永,通過分支創(chuàng)建不同的Product具钥。缺點(diǎn)就是如果很多種product的話,那這個(gè)類就龐大了宁玫,而且這里如果每種product需要傳參的話欧瘪,那靜態(tài)方法的參數(shù)也是不定的佛掖。好處就是一個(gè)類搞定了啊芥被。

好了拴魄,說了幾種工廠模式后,去看下android源碼中有沒有應(yīng)用了:

AudioManager audioManager=context.getSystemService(Context.AUDIO_SERVICE)

相信大家都獲取過****Manager了吧顶捷,那咱們?nèi)タ纯催@句代碼跟工廠模式有關(guān)系沒焊切。
是不是在activity中直接有getSystemService呢专肪,那咱們?nèi)タ聪掳?

    @Override
    public Object getSystemService(@ServiceName @NonNull String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }

        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

直接看最后一行調(diào)用了父類的getSystemService方法:

Activity父類圖.png

那咱們?nèi)?code>ContextThemeWrapper中去找唄:

    @Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

看最后一行就行了伍宦,這里獲取getBaseContext后,然后調(diào)了getSystemService遇骑。咱們看下getBseContext是什么鬼落萎。又要去ContextThemeWrapper的父類去找getBaseContext了:

ContextThemeWrapper父類圖.png

好吧,父類是ContextWrapper,那咱們看下getBaseContext是什么了:

    /**
     * @return the base context as set by the constructor or setBaseContext
     */
    public Context getBaseContext() {
        return mBase;
    }

這里是ContextWrapper中的mBase變量了:

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

賦值就這兩個(gè)地方了。那咱們就知道Activity中的context實(shí)際上就是ContextWrapper中的mBase變量了绿鸣。這就要追溯到Activity創(chuàng)建的地方了枚驻,才能揭穿mBase的真面目了。這里需要知道點(diǎn)Android的應(yīng)用啟動(dòng)流程了株旷,咱們就直接看ActivityThread類了:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //省略代碼
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);
        //省略代碼

        return activity;
    }

看到最后面調(diào)用了activityattach方法再登,并且把創(chuàng)建的appContext傳進(jìn)了attach方法,那咱們看下創(chuàng)建這個(gè)context是什么了:

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
    //省略代碼
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.token, displayId, r.overrideConfig);
    appContext.setOuterContext(activity);
    Context baseContext = appContext;
    //省略代碼
    return baseContext;
}

看到這的時(shí)候晾剖,基本就知道上面說的mBase其實(shí)就是ContextImpl了锉矢。Activityattach的方法也正是把傳進(jìn)來的ContextImpl給了ContextWrapper中的mBase

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window) {
    //調(diào)用了父類ContextWrapper的attach方法
    attachBaseContext(context);
    //省略代碼
}

到這里ContextWrapper中的mBase其實(shí)是一個(gè)ContextImpl了齿尽,下面就去看下ContextImpgetSystemService方法了:

@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

好吧沽损,又是走了一層了,去看下SystemServiceRegistry中的getSystemService方法吧:

public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

這里就很清晰了循头,首先通過nameSYSTEM_SERVICE_FETCHERS中獲取一個(gè)ServiceFetcher對(duì)象绵估,然后調(diào)用了getService方法:

private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
        new HashMap<String, ServiceFetcher<?>>();

好吧,這里是個(gè)HashMap卡骂,那咱們看下什么時(shí)候put進(jìn)去的ServiceFetcher

private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

這里是在registerService的時(shí)候?qū)?code>serviceFetcherput進(jìn)去的:

static {
    registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
            new CachedServiceFetcher<AccessibilityManager>() {
        @Override
        public AccessibilityManager createService(ContextImpl ctx) {
            return AccessibilityManager.getInstance(cox);
        }});
    registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
            new CachedServiceFetcher<CaptioningManager>() {
        @Override
        public CaptioningManager createService(ContextImpl ctx) {
            return new CaptioningManager(cox);
        }});
    registerService(Context.ACCOUNT_SERVICE, AccountManager.class,
            new CachedServiceFetcher<AccountManager>() {
        @Override
        public AccountManager createService(ContextImpl ctx) {
            IBinder b = ServiceManager.getService(Context.ACCOUNT_SERVICE);
            IAccountManager service = IAccountManager.Stub.asInterface(b);
            return new AccountManager(ctx, service);
        }});
        //registerService太多了国裳,我這里就羅列上面幾個(gè)了
}

看到這的時(shí)候,才看到有工廠模式的影子啊全跨,好多小伙伴都要哭了缝左。源碼真的是藏得深啊。這里簡(jiǎn)單說下在靜態(tài)的時(shí)候,通過service的name構(gòu)造出不同的ServiceFetcher渺杉,并存儲(chǔ)在SYSTEM_SERVICE_FETCHERS中蛇数。然后在getSystemService過程中通過傳進(jìn)來的name獲取不同的ServiceFetcher,最后調(diào)用getService方法獲取相應(yīng)的Manager了是越。不難看出這里的ServiceFetcher就是產(chǎn)品抽象類耳舅,SystemServiceRegistry就是一個(gè)生產(chǎn)***Manager的靜態(tài)工廠類了。

下面畫張圖理解下ActivitygetSystemService

getSystemService流程圖.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末倚评,一起剝皮案震驚了整個(gè)濱河市挽放,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔓纠,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吗蚌,死亡現(xiàn)場(chǎng)離奇詭異腿倚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蚯妇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門敷燎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人箩言,你說我怎么就攤上這事硬贯。” “怎么了陨收?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵饭豹,是天一觀的道長。 經(jīng)常有香客問我务漩,道長拄衰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任饵骨,我火速辦了婚禮翘悉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘居触。我一直安慰自己妖混,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布轮洋。 她就那樣靜靜地躺著制市,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砖瞧。 梳的紋絲不亂的頭發(fā)上息堂,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼荣堰。 笑死床未,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的振坚。 我是一名探鬼主播薇搁,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼渡八!你這毒婦竟也來了啃洋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤屎鳍,失蹤者是張志新(化名)和其女友劉穎宏娄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逮壁,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孵坚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窥淆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卖宠。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖忧饭,靈堂內(nèi)的尸體忽然破棺而出扛伍,到底是詐尸還是另有隱情,我是刑警寧澤词裤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布刺洒,位于F島的核電站,受9級(jí)特大地震影響亚斋,放射性物質(zhì)發(fā)生泄漏作媚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一帅刊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧女揭,春花似錦栏饮、人聲如沸吧兔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灶平。三九已至箍土,卻和暖如春逢享,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吴藻。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侧但,地道東北人航罗。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓粥血,卻偏偏與公主長得像,于是被迫代替她去往敵國和親立莉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子七问,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法械巡,類相關(guān)的語法,內(nèi)部類的語法有勾,繼承相關(guān)的語法古程,異常的語法挣磨,線程的語...
    子非魚_t_閱讀 31,582評(píng)論 18 399
  • 設(shè)計(jì)模式基本原則 開放-封閉原則(OCP),是說軟件實(shí)體(類塘砸、模塊晤锥、函數(shù)等等)應(yīng)該可以拓展廊宪,但是不可修改女轿。開-閉原...
    西山薄涼閱讀 3,753評(píng)論 3 13
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用册烈、多...
    MinoyJet閱讀 3,905評(píng)論 1 15
  • 面向?qū)ο蟮牧笤瓌t 單一職責(zé)原則 所謂職責(zé)是指類變化的原因赏僧。如果一個(gè)類有多于一個(gè)的動(dòng)機(jī)被改變扭倾,那么這個(gè)類就具有多于...
    JxMY閱讀 925評(píng)論 1 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理膛壹,服務(wù)發(fā)現(xiàn),斷路器模聋,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139