天天看點

最新源碼分析Android windowManager(懸浮視窗)的實作

關于windowManager可能很多人不是很熟悉,其實所有的window都是windowManager來管理的。當然包括的Activity的phoneWindow

這裡主要講的是android 的懸浮視窗。先來看一下案例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
 <Button
     android:id="@+id/window_button_add"
     android:text="添加"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:onClick="onAddButtonClick"/>
 <Button
     android:id="@+id/window_button_remove"
     android:text="删除"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:onClick="onRemoveButtonClick"/>
</LinearLayout>
           

布局很簡單一個添加按鈕,一個删除按鈕,看看widowManegerActivity的代碼:

/**
 * @author Gordon
 * @since 2016/8/5
 * do()
 */
public class WindowManagerActivity extends Activity implements View.OnTouchListener {
    private Button mCreateWindowButton;
    private Button mFloatingButton;
    private LayoutParams mLayoutParams;
    private WindowManager mWindowManager;
    private boolean isShow;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.window_manage_activity);
        initView();
    }

    private void initView() {
        mCreateWindowButton = (Button) findViewById(R.id.window_button_add);
        mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    }

    public void onAddButtonClick(View v) {
        if (v == mCreateWindowButton && !isShow) {
            if(mFloatingButton == null)
            mFloatingButton = new Button(this);
            mFloatingButton.setText("move me");
            mLayoutParams = new LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
                    PixelFormat.TRANSPARENT);
            mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
            mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
            mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
            mLayoutParams.x = 100;
            mLayoutParams.y = 300;
            mFloatingButton.setOnTouchListener(this);
            mWindowManager.addView(mFloatingButton, mLayoutParams);
            isShow = true;
        }
    }

    public void onRemoveButtonClick(View v) {
        if (mFloatingButton != null) {
            try {
                mWindowManager.removeViewImmediate(mFloatingButton);
                isShow = false;
            } catch (Exception e) {
            }
            ;
        }

    }

    private int x, y, moveX, moveY;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //相對于螢幕的位置
                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;

            case MotionEvent.ACTION_MOVE:
                moveX = (int) (event.getRawX() - x);
                moveY = (int) (event.getRawY() - y);
                if (Math.abs(moveX) < 5 || Math.abs(moveY) < 5) { //如果手指一動的距離很小就break
                    break;
                }
                mLayoutParams.x += moveX;
                mLayoutParams.y += moveY;
                mWindowManager.updateViewLayout(mFloatingButton, mLayoutParams);
                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }

        return false;
    }

    @Override
    protected void onDestroy() {
        try {
            mWindowManager.removeView(mFloatingButton);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        super.onDestroy();
    }

}
           

看看運作的圖,如下:

最新源碼分析Android windowManager(懸浮視窗)的實作

簡單的介紹一下,這裡點選添加按鈕生成一個懸浮窗,然後可以随意的移動懸浮窗,删除按鈕删除懸浮窗。

那麼懸浮窗是怎麼出現的呢?讓我們看下源碼,

看下WidowManegerActivity的22行,這個windowManager是怎麼來的,點進去:

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

這是Activity的getSystemService方法,因為傳入的是WINDOW_SERVICE,是以走到第二個if語句

直接傳回沒mWindowManager。看下activity的attach方法

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) {
      .........................................
      ..........................................
     ............................................
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }
           

而在mWindow的setWindowManager中:

/**
     * Set the window manager for use by this Window to, for example,
     * display panels.  This is <em>not</em> used for displaying the
     * Window itself -- that must be done by the client.
     *
     * @param wm The window manager for adding new windows.
     */
    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);
    }
           

得到了mWindowManager其實也就是windowManagerImpl。而windowManager是怎麼實作的的呢

看下WindowManagerActivity的25-41行:

關鍵的41行中,有個addview方法,而這個mWindowManager又是WindowManagerImpl,看下它的addView方法:

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
           

而最終是mGlobal的addview,看看mGlobal也就是WindowManagerGlobal.看看它的addView方法:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }
           

有些複雜,慢慢解讀 3-25行就是判斷View,display,以及parentView是否為空,做出相應的判斷。

30-67的加鎖的代碼主要就是準備添加的view,防止重複添加。

mViews存儲的是所有Window所對應的View,mRoots存儲的是所有Window所對應的ViewRootImpl,

mParams存儲的是所有Window所對應的布局參數,

而mDyingViews存儲了那些正在被删除的View對象,或者說是那些已經調用removeView方法但是還沒有删除的Window對象。

在addView方法中通過如下方式将Window的一系列對象添加到清單中。

80行的root.setView()才是真正的添加View。而ViewRootImpl又是什麼呢?

/**
 * The top of a view hierarchy, implementing the needed protocol between View
 * and the WindowManager.  This is for the most part an internal implementation
 * detail of {@link WindowManagerGlobal}.
 *
 * {@hide}
 */
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    ..........................
}
           

代碼的注釋是說ViewRootImpl此類主要是連接配接View和WindowManager的,是WIndowManagerGlobal的重要部分。

用于更新界面,看下此類的setView方法:

/**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
               ..................
                .......................
                  .........................
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
           ..............................
             ................................
                 ...................................
    }
           

這裡是主要代碼,requestLayout()是繪制并更新View,

windowSession.addDisplay是調用服務端添加到window中,看下源碼:

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

最終是調用WindowManagerServce來完成對view在window中的添加操作,這裡就不細講。

更新和删除都是這個流程,總結一下看下流程圖:

最新源碼分析Android windowManager(懸浮視窗)的實作

WindowManagerImpl implements 接口WindowManager,為WindowManager implements接口VIewManager,

在WindowManagerImpl類中調用WindowManagerGlobal單獨來處理添加,更新和删除的動作,

而在相應的方法中,核心的調用VIewRootImpl的相應方法,ViewRootImpl類本身去處理View的繪制和更新工作,

類中調用Android service端完成視窗的添加,更新和删除工作 。

關于删除的removeView()和removeViewImmediate()其實顧名思義就是阻塞和非阻塞的删除方法,具體可以看看源碼。

着了就不多說了。

下一章 繼續研究Activity的啟動過程