Android源碼設(shè)計(jì)模式學(xué)習(xí)筆記-橋接模式

當(dāng)一個類存在兩個獨(dú)立變化的緯度谬莹,且這兩個緯度都需要進(jìn)行擴(kuò)展,我們可以使用橋接模式帅涂。下面來看看橋接模式的UML


image.png

Abstraction和Implementor就是兩個獨(dú)立緯度變化的類田绑,Implementor相對于Abstraction是一個聚合的關(guān)系,也就是Abstraction可能擁有多個Implementor彼念,下面我們來舉個例子.


image.png

咖啡一般分為4種,大杯加糖浅萧,大杯不加糖逐沙,小杯加糖和小杯不加糖。對于大杯和小杯洼畅,加糖和不加糖其實(shí)是兩個相對獨(dú)立緯度的變化.下面先定一個咖啡類
public abstract class Coffee {
    protected CoffeeAdditives impl;

    public Coffee(CoffeeAdditives impl) {
        this.impl = impl;
    }

    /**
     * 咖啡具體是什么樣的由子類決定
     */
    public abstract void makeCoffee();
}

CoffeeAdditives是一種橋接的方式吩案,咖啡分為大杯和小杯,下面繼續(xù)看看大杯咖啡和小杯咖啡的定義

public class LargeCoffee extends Coffee{

    public LargeCoffee(CoffeeAdditives impl) {
        super(impl);
    }

    @Override
    public void makeCoffee() {
        System.out.println("大杯的"+impl.addSomething()+"咖啡");
    }
}
public class SmallCoffee extends Coffee{
    public SmallCoffee(CoffeeAdditives impl) {
        super(impl);
    }

    @Override
    public void makeCoffee() {
        System.out.println("小杯的"+impl.addSomething()+"咖啡");
    }
}

至于加糖不加糖我們通過CoffeeAdditives這種橋接方式定義

public abstract class CoffeeAdditives {
    public abstract String addSomething();
}
public class Ordinary extends CoffeeAdditives {
    @Override
    public String addSomething() {
        return "原味";
    }
}
public class Sugar extends CoffeeAdditives {
    @Override
    public String addSomething() {
        return "加糖";
    }
}

最終調(diào)用:

public class Test {
    public static void main(String[] args){
        //原汁原味
        Ordinary ordinary = new Ordinary();
        //準(zhǔn)備糖類
        Sugar sugar = new Sugar();

        //大杯咖啡 原味
        LargeCoffee largeCoffeeOrdinary = new LargeCoffee(ordinary);
        largeCoffeeOrdinary.makeCoffee();
        //小杯咖啡 原味
        SmallCoffee smallCoffeeOrdinary = new SmallCoffee(ordinary);
        smallCoffeeOrdinary.makeCoffee();
        //大杯咖啡 加糖
        LargeCoffee largeCoffeeSugar = new LargeCoffee(sugar);
        largeCoffeeSugar.makeCoffee();
        //小杯咖啡 加糖
        SmallCoffee smallCoffeeSugar = new SmallCoffee(sugar);
        smallCoffeeSugar.makeCoffee();
    }
}

總結(jié)
這里Coffee對應(yīng)uml圖中的Abstraction, CoffeeAdditives對應(yīng)的是Implementor這個類帝簇,這種橋接模式很好的獨(dú)立了大杯和小杯徘郭,加糖和不加糖兩個緯度靠益,你也可以添加另外一個緯度,比如說加奶不加奶残揉,這樣就是3個緯度進(jìn)行橋接胧后,當(dāng)然你也可以使用繼承來實(shí)現(xiàn),不過總的來說橋接模式更加靈活抱环。

Android源碼中的橋接模式

比較典型的是Window與WindowManager之間的關(guān)系壳快,它們就用到了橋接這種模式


image.png

在framework中Window和PhoneWindow構(gòu)成窗口的抽象部分,其中Window類為該抽象部分的抽象接口镇草,PhoneWindow為抽象部分具體的實(shí)現(xiàn)及擴(kuò)展眶痰。而WindowManager則為實(shí)現(xiàn)部分的基類,WindowManagerImpl為實(shí)現(xiàn)部分具體的邏輯實(shí)現(xiàn)陶夜,其使用WindowManagerGlobal通過IWindowManager接口與WindowManagerService進(jìn)行交互(簡稱WMS)凛驮,并由WMS完成具體的窗口管理工作. 如下是Window與WindowManager橋梁搭建的主要代碼.

public abstract class Window {
    //代碼省略...
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
        setWindowManager(wm, appToken, appName, false);
    }

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
    //代碼省略...
}

關(guān)于WindowManagerService

毫不夸張的說Android中的framework層主要就是由它與另外一個系統(tǒng)服務(wù)AMS還有View構(gòu)成裆站,這三個模塊穿插交互在整個framework中条辟。
wms是由SystemServer啟動

private void startOtherServices() {
            //通過wms的靜態(tài)方法main獲取一個WindowManagerService對象
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
           //將wms添加到ServiceManager中
           ServiceManager.addService(Context.WINDOW_SERVICE, wm);
}

在WindowManagerService的main中通過runWithScissors執(zhí)行一個同步的task構(gòu)造wms實(shí)例

public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }

WindowManagerService的構(gòu)造方法大部分是一些窗口管理使用到的成員變量進(jìn)行初始化.

private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {
        installLock(this, INDEX_WINDOW);
        mRoot = new RootWindowContainer(this);
        mContext = context;
        mHaveInputMethods = haveInputMethods;
        mAllowBootMessages = showBootMsgs;
        mOnlyCore = onlyCore;
        mLimitedAlphaCompositing = context.getResources().getBoolean(
                com.android.internal.R.bool.config_sf_limitedAlpha);
        mHasPermanentDpad = context.getResources().getBoolean(
                com.android.internal.R.bool.config_hasPermanentDpad);
        mInTouchMode = context.getResources().getBoolean(
                com.android.internal.R.bool.config_defaultInTouchMode);
        mDrawLockTimeoutMillis = context.getResources().getInteger(
                com.android.internal.R.integer.config_drawLockTimeoutMillis);
        mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
        mMaxUiWidth = context.getResources().getInteger(
                com.android.internal.R.integer.config_maxUiWidth);
        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mDisplaySettings = new DisplaySettings();
        mDisplaySettings.readSettingsLocked();

        mWindowPlacerLocked = new WindowSurfacePlacer(this);
        mPolicy = policy;
        mTaskSnapshotController = new TaskSnapshotController(this);

        LocalServices.addService(WindowManagerPolicy.class, mPolicy);

        if(mInputManager != null) {
            final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
            mPointerEventDispatcher = inputChannel != null
                    ? new PointerEventDispatcher(inputChannel) : null;
        } else {
            mPointerEventDispatcher = null;
        }

        mFxSession = new SurfaceSession();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        mDisplays = mDisplayManager.getDisplays();
        for (Display display : mDisplays) {
            createDisplayContentLocked(display);
        }

        mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);

        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);

        if (mPowerManagerInternal != null) {
            mPowerManagerInternal.registerLowPowerModeObserver(
                    new PowerManagerInternal.LowPowerModeListener() {
                @Override
                public int getServiceType() {
                    return ServiceType.ANIMATION;
                }

                @Override
                public void onLowPowerModeChanged(PowerSaveState result) {
                    synchronized (mWindowMap) {
                        final boolean enabled = result.batterySaverEnabled;
                        if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
                            mAnimationsDisabled = enabled;
                            dispatchNewAnimatorScaleLocked(null);
                        }
                    }
                }
            });
            mAnimationsDisabled = mPowerManagerInternal
                    .getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled;
        }
        mScreenFrozenLock = mPowerManager.newWakeLock(
                PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
        mScreenFrozenLock.setReferenceCounted(false);

        mAppTransition = new AppTransition(context, this);
        mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);

        final AnimationHandler animationHandler = new AnimationHandler();
        animationHandler.setProvider(new SfVsyncFrameCallbackProvider());
        mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
                AnimationThread.getHandler(), animationHandler);

        mActivityManager = ActivityManager.getService();
        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
        AppOpsManager.OnOpChangedInternalListener opListener =
                new AppOpsManager.OnOpChangedInternalListener() {
                    @Override public void onOpChanged(int op, String packageName) {
                        updateAppOpsState();
                    }
                };
        mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null, opListener);
        mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null, opListener);

        // Get persisted window scale setting
        mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
                Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
        mTransitionAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
                Settings.Global.TRANSITION_ANIMATION_SCALE,
                context.getResources().getFloat(
                        R.dimen.config_appTransitionAnimationDurationScaleDefault));

        setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(),
                Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting));

        IntentFilter filter = new IntentFilter();
        // Track changes to DevicePolicyManager state so we can enable/disable keyguard.
        filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
        // Listen to user removal broadcasts so that we can remove the user-specific data.
        filter.addAction(Intent.ACTION_USER_REMOVED);
        mContext.registerReceiver(mBroadcastReceiver, filter);

        mSettingsObserver = new SettingsObserver();

        mHoldingScreenWakeLock = mPowerManager.newWakeLock(
                PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
        mHoldingScreenWakeLock.setReferenceCounted(false);

        mAnimator = new WindowAnimator(this);

        mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);


        LocalServices.addService(WindowManagerInternal.class, new LocalService());
        initPolicy();

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);

        openSurfaceTransaction();
        try {
            createWatermarkInTransaction();
        } finally {
            closeSurfaceTransaction();
        }

        showEmulatorDisplayOverlayIfNeeded();
    }

WMS主要功能分為兩方面滓侍,一是對窗口的管理;二是對事件的管理和分發(fā)。其接口方法以AIDL的方式定義在IWindowManager.aidl文件中歹鱼,編譯后會生成一個IWindowManager.java接口文件,這個接口文件定義了WMS絕大部分的功能方法铃绒,大家有興趣可以自行查看定血。作為窗口的管理承擔(dān)者灾票,WMS中定義了許多各種不同的窗口濒析,它們被定義在WMS的成員變量中.
下面列出一部分相關(guān)的成員變量

 /**
     * List of window tokens that have finished starting their application,
     * and now need to have the policy remove their windows.
     */
    final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();

    /**
     * List of window tokens that have finished drawing their own windows and
     * no longer need to show any saved surfaces. Windows that's still showing
     * saved surfaces will be cleaned up after next animation pass.
     */
    final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();

    /**
     * List of app window tokens that are waiting for replacing windows. If the
     * replacement doesn't come in time the stale windows needs to be disposed of.
     */
    final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();

    /**
     * Windows that are being resized.  Used so we can tell the client about
     * the resize after closing the transaction in which we resized the
     * underlying surface.
     */
    final ArrayList<WindowState> mResizingWindows = new ArrayList<>();

    /**
     * Windows whose animations have ended and now must be removed.
     */
    final ArrayList<WindowState> mPendingRemove = new ArrayList<>();

    /**
     * Used when processing mPendingRemove to avoid working on the original array.
     */
    WindowState[] mPendingRemoveTmp = new WindowState[20];

    /**
     * Windows whose surface should be destroyed.
     */
    final ArrayList<WindowState> mDestroySurface = new ArrayList<>();

    /**
     * Windows with a preserved surface waiting to be destroyed. These windows
     * are going through a surface change. We keep the old surface around until
     * the first frame on the new surface finishes drawing.
     */
    final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();

    /**
     * Windows that have lost input focus and are waiting for the new
     * focus window to be displayed before they are told about this.
     */
    ArrayList<WindowState> mLosingFocus = new ArrayList<>();

WMS維護(hù)上述的各個成員的變量值庭惜,可以看到大量線性表的應(yīng)用,不同的窗口或同一個窗口在不同的狀態(tài)階段有可能位于不同的表中。雖然窗口的狀態(tài)種類繁多,但是雏胃,對于Android來講窗口的類型主要只有兩種固棚,一種是應(yīng)用窗口,我們常見的Activity所處的窗口,應(yīng)用對話框窗口,應(yīng)用彈出的窗口等都屬于該類,與該應(yīng)用相關(guān)的Window類主要是PhoneWindow, 其主要應(yīng)用于手機(jī),PhoneWindow繼承于Window,其核心是DecorView, 應(yīng)用窗口的添加主要就是通過WindowManager的addView方法將一個DecorView添加到WindowManager中暖释。另一種是系統(tǒng)窗口袭厂,常見的屏幕頂部狀態(tài)欄纹磺,底部的導(dǎo)航欄橄杨,桌面窗口等都是系統(tǒng)窗口瞬痘,系統(tǒng)窗口沒有針對性的封裝類,只需要直接通過WindowManager的addView方法將一個View添加到WindowManager中即可.

wms和其它系統(tǒng)服務(wù)一樣由SystemServer啟動拆撼,其運(yùn)行在系統(tǒng)進(jìn)程里筒严,當(dāng)一個應(yīng)用需要創(chuàng)建窗口時通過IPC請求wms生成一個窗口,爾后再由wms向應(yīng)用返回和窗口交互的消息筋岛。addView的實(shí)質(zhì)上是由WindowManagerGlobal的addView方法實(shí)現(xiàn)具體的邏輯娶视,輾轉(zhuǎn)多次后最終調(diào)用到ViewRootImpl的setView方法,在該方法通過addToDisplay方法向wms發(fā)起一個Session請求睁宰,這里要注意的是肪获,IWindowSession方法也是一個AIDL接口文件,需要將其編譯后才生成IWindowSession.java接口柒傻,這里addToDisplay方法最終會調(diào)用到Session中對應(yīng)方法.

public class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
}

最終我們回到了wms的addWindow方法.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孝赫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子红符,更是在濱河造成了極大的恐慌青柄,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件预侯,死亡現(xiàn)場離奇詭異致开,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)萎馅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門双戳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人糜芳,你說我怎么就攤上這事飒货。” “怎么了峭竣?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵塘辅,是天一觀的道長。 經(jīng)常有香客問我邪驮,道長莫辨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮沮榜,結(jié)果婚禮上盘榨,老公的妹妹穿的比我還像新娘。我一直安慰自己蟆融,他們只是感情好草巡,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著型酥,像睡著了一般山憨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上郁竟,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機(jī)與錄音由境,去河邊找鬼棚亩。 笑死,一個胖子當(dāng)著我的面吹牛虏杰,可吹牛的內(nèi)容都是我干的讥蟆。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼纺阔,長吁一口氣:“原來是場噩夢啊……” “哼瘸彤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笛钝,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤质况,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后婆翔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拯杠,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年啃奴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雄妥。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡最蕾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出老厌,到底是詐尸還是另有隱情瘟则,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布枝秤,位于F島的核電站醋拧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丹壕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一庆械、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧菌赖,春花似錦缭乘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邑时,卻和暖如春奴紧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晶丘。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工黍氮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铣口。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓滤钱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親脑题。 傳聞我的和親對象是個殘疾皇子件缸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

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