Android 实现简单的悬浮窗按钮(二)

这篇主要分析 Window 的工作机制,WindowManager 添加 Window (View) 的主要流程

以下是Android9.0 的源码

首先看一下 WindowManager.java

xref: /frameworks/base/core/java/android/view/WindowManager.java

package android.view;
......

@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
......

    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
        
        /**
         * X position for this window.  With the default gravity it is ignored.
         * When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link                 Gravity#RIGHT} or
         * {@link Gravity#END} it provides an offset from the given edge.
         */
        @ViewDebug.ExportedProperty
        public int x;

        /**
         * Y position for this window.  With the default gravity it is ignored.
         * When using {@link Gravity#TOP} or {@link Gravity#BOTTOM} it provides
         * an offset from the given edge.
         */
        @ViewDebug.ExportedProperty
        public int y;
        
        ......

    }
......
}

WindowManager 是一个接口,该接口继承了 ViewManager,内部类 LayoutParams 继承了 ViewGroup 的 LayoutParams

通过注释可以看到,当我们使用 WindowManager.LayoutParams 的 x、y参数时需要通过类似 mParams.gravity=Gravity.LEFT | Gravity.TOP;为其指明 gravity 参数,否则会出现问题(比如无法拖动悬浮按钮)

看一下 ViewManager 接口

xref: /frameworks/base/core/java/android/view/ViewManager.java

package android.view;

/** Interface to let you add and remove child views to an Activity. To get an instance
  * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
  */
public interface ViewManager
{
    /**
     * Assign the passed LayoutParams to the passed View and add the view to the window.
     * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
     * errors, such as adding a second view to a window without removing the first view.
     * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
     * secondary {@link Display} and the specified display can't be found
     * (see {@link android.app.Presentation}).
     * @param view The view to be added to this window.
     * @param params The LayoutParams to assign to view.
     */
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

WindowManager 是一个接口,谁实现了这个接口呢(可以搜索关键字 "implements WindowManager")

xref: /frameworks/base/core/java/android/view/WindowManagerImpl.java

package android.view;
......
public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Context context) {
        this(context, null);
    }
    ......

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

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

    ......

        @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

添加、删除 View的操作是通过WindowManagerGlobal来实现的,进入 WindowManagerGlobal

xref: /frameworks/base/core/java/android/view/WindowManagerGlobal.java

package android.view;
......

public final class WindowManagerGlobal {
    private static final String TAG = "WindowManager";
    
    ......


    private final Object mLock = new Object();

    /*
       以下几个变量比较重要
       mViews 存储所有 Winow 所对应的 View
       mRoots 存储所有 Window 所对应的 ViewRootImpl
       mParams 存储所有 Window 对应的布局参数
       mDyingViews 存储正在被删除的 View 对象(已经调用 removeView 方法但还未完成删除)
    */
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

    //单例模式
    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

    //获取 WindowManagerService 服务
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }
    
    //获取 WindowSession
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
    ......

    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;
        ......
        
        //定义ViewRootImpl
        ViewRootImpl root;
        View panelParentView = null;
         
        ......

        int index = findViewLocked(view, false);//获取要添加的View的索引
        //这里检查索引值,因为如果是第一次添加,返回的索引应该为 -1
        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.
        }


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

          view.setLayoutParams(wparams);//为View设置布局参数

            //将Window一系列对象添加到列表
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

               
            //通过ViewRootImpl的 setView 方法来更新界面,完成Window的添加过程
            // 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.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

进入 ViewRootImpl

xref: /frameworks/base/core/java/android/view/ViewRootImpl.java

package android.view;

......

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    private static final String TAG = "ViewRootImpl";

    ......


    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        //在构造方法中获取WindowSession实例
        mWindowSession = WindowManagerGlobal.getWindowSession();
        mDisplay = display;
        ......
        mFirst = true; // true for the first time the view is added
        mAdded = false;
        ......
    }


    ......


     /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;//getView方法中会直接返回mView,可以通过getView直接获取该View

                mAttachInfo.mDisplayState = mDisplay.getState();
                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);

                mAttachInfo.mDisplayState = mDisplay.getState();
                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);


                ......

                if (panelParentView != null) {
                    mAttachInfo.mPanelParentWindowToken
                            = panelParentView.getApplicationWindowToken();
                }
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                //通过requestLayout()来完成异步刷新请求
                requestLayout();
                ......
                
                 try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //通过WindowSession最终完成 Window 的添加过程
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout,  mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
               ......

mWindowSession 是 IWindowSession,是一个 Binder 对象,addToDisplay 是Window添加过程中的一次IPC调用

看一下IWindowSession是由谁来实现的(通过搜索 "IWindowSession.Stub")

可以看到 从 frameworks/base/core 进入了 frameworks/base/services/core

xref: /frameworks/base/services/core/java/com/android/server/wm/Session.java

位于 com.android.server.wm 包下

package com.android.server.wm;

......

/**
 * This class represents an active client session.  There is generally one
 * Session object per process that is interacting with the window manager.
 */
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
  ......

  final WindowManagerService mService;//定义了WindowManagerService
  final IWindowSessionCallback mCallback;

  //构造函数中初始化 mService
  public Session(WindowManagerService service, IWindowSessionCallback callback,
            IInputMethodClient client, IInputContext inputContext) {
        mService = service;
  
  ......

  }

  ......


  @Override
  public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
            Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {

        //最终通过 WindowMangerService 添加Window
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,    outFrame,outContentInsets, outStableInsets, outOutsets, outDisplayCutout,  outInputChannel);

    }

......

}

WindowManagerService 也和 Session在同一路径下

xref: /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值