Framework基于WMS添加独立窗口

这篇博客详细介绍了如何在Android系统中添加一个独立窗口,涉及SampleWindow类的编写、Android.mk编译脚本、sw.sh启动器的使用,以及窗口服务的交互过程,包括WindowManager.LayoutParams的配置、IWindowSession的获取、Choreographer的运用和InputHandler的创建,展示了如何在Android系统层面创建并管理自定义窗口。

Framework基于WMS添加独立窗口

参考链接

https://www.cnblogs.com/wzjhoutai/p/6873790.html

https://www.cnblogs.com/zhaojietec/p/4975840.html

添加三个文件

SampleWindow.java 主程序源码。

Android.mk 编译脚本。

sw.sh 启动器。

//frameworks/base/services/core/java/com/android/server/wm/samplewindow/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := samplewindow
include $(BUILD_JAVA_LIBRARY)
//frameworks/base/services/core/java/com/android/server/wm/samplewindow/SampleWindow.java
package com.android.server.wm.samplewindow;
public class SampleWindow {
    public static final String TAG = "SampleWindow";
    
    public static void main(String[] args) {
        try {
            //SampleWindow.Run()是这个程序的主入口
            new SampleWindow().Run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //IWindowSession 是client向WMS请求窗体操作的中间代理,而且是进程唯一的
    IWindowSession mSession = null;

    //InputChannel 是窗体接收用户输入事件的管道。
    InputChannel mInputChannel = new InputChannel();

    // 下面的三个Rect保存了窗体的布局结果。
    //当中mFrame表示了窗体在屏幕上的位置与尺寸
    Rect mInsets = new Rect();
    Rect mFrame = new Rect();
    Rect mVisibleInsets = new Rect();
    Configuration mConfig = new Configuration();

    // 窗体的Surface,在此Surface上进行的绘制都将在此窗体上显示出来
    Surface mSurface = new Surface();
    SurfaceControl mSurfaceControl = new SurfaceControl();

    // 用于在窗体上进行画图的画刷
    Paint mPaint = new Paint();

    // 加入窗体所需的令牌
    IBinder mToken = new Binder();

    // 一个窗体对象。本例演示了怎样将此窗体加入到WMS中,并在其上进行绘制操作
    MyWindow mWindow = new MyWindow();

    //WindowManager.LayoutParams定义了窗体的布局属性,包括位置、尺寸以及窗体类型等
    WindowManager.LayoutParams mLp = new WindowManager.LayoutParams();
    Choreographer mChoreographer = null;

    //InputHandler 用于从InputChannel接收按键事件做出响应
    InputHandler mInputHandler = null;

    boolean mContinueAnime = true;

    public void Run() throws Exception {
        Looper.prepare();

        // 获取WMS服务
        IWindowManager wms = IWindowManager.Stub.asInterface(
                ServiceManager.getService(Context.WINDOW_SERVICE));

        // 通过WindowManagerGlobal获取进程唯一的IWindowSession实例。它将用于向WMS发送请求。
        //注意这个函数在较早的Android版本号(如4 .1)位于ViewRootImpl类中
//        mSession = WindowManagerGlobal.getWindowSession(Looper.myLooper());
//        mSession = WindowManagerGlobal.getWindowSession();
        // 改用以下方法
        synchronized (WindowManagerGlobal.class) {
            try {
                mSession = wms.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        });
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

        // 获取屏幕分辨率
        IDisplayManager dm =
                IDisplayManager.Stub.asInterface(ServiceManager.getService(Context.DISPLAY_SERVICE));
        DisplayInfo di = dm.getDisplayInfo(Display.DEFAULT_DISPLAY);
        Point scrnSize = new Point(di.appWidth, di.appHeight);

        // 初始化WindowManager.LayoutParams
        initLayoutParams(scrnSize);

        // 将新窗体加入到WMS
        installWindow(wms);

        // 初始化Choreographer的实例。此实例为线程唯一。这个类的使用方法与Handler
        // 相似。只是它总是在VSYC同步时回调。所以比Handler更适合做动画的循环器[1]
        mChoreographer = Choreographer.getInstance();

        // 開始处理第一帧的动画
        scheduleNextFrame();

        // 当前线程陷入消息循环,直到Looper.quit()
        Looper.loop();

        // 标记不要继续绘制动画帧
        mContinueAnime = false;

        // 卸载当前Window
        uninstallWindow(wms);
    }


    public void initLayoutParams(Point screenSize) {
        // 标记即将安装的窗体类型为SYSTEM_ALERT。这将使得窗体的ZOrder顺序比較靠前
        mLp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        mLp.setTitle("SampleWindow");

        // 设定窗体的左上角坐标以及高度和宽度
        mLp.gravity = Gravity.LEFT | Gravity.TOP;
        mLp.x = screenSize.x / 4;
        mLp.y = screenSize.y / 4;
        mLp.width = screenSize.x / 2;
        mLp.height = screenSize.y / 2;

        // 和输入事件相关的Flag,希望当输入事件发生在此窗体之外时,其它窗体也能够接受输入事件
        mLp.flags = mLp.flags | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
    }

    public void installWindow(IWindowManager wms) throws Exception {
        Log.i(TAG, "installWindow");
        // 首先向WMS声明一个Token,不论什么一个Window都须要隶属与一个特定类型的Token
//        wms.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        wms.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                Display.DEFAULT_DISPLAY);

        // 设置窗体所隶属的Token
        mLp.token = mToken;

        // 通过IWindowSession将窗体安装进WMS,注意,此时仅仅是安装到WMS。本例的Window
        // 眼下仍然没有有效的Surface。只是,经过这个调用后。mInputChannel已经能够用来接受输入事件了
        mSession.addToDisplay(mWindow, 0, mLp, View.VISIBLE, 0,
                mInsets, mInsets, mInsets, mInsets,
                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT),
                mInputChannel,
                new InsetsState());
//        mSession.add(mWindow, 0, mLp, View.VISIBLE, mInsets, mInputChannel);

        // 通过IWindowSession要求WMS对本窗体进行又一次布局。经过这个操作后。WMS将会为窗体创建一块用于绘制
        // 的Surface并保存在參数mSurface中。同一时候。这个Surface被WMS放置在LayoutParams所指定的位置上
        mSession.relayout(mWindow, 0, mLp, mLp.width, mLp.height, View.VISIBLE, 0, 7,
                mFrame, mFrame, mFrame, mVisibleInsets, mFrame, mFrame, mFrame,
                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT),
                new MergedConfiguration(),
                mSurfaceControl,
                new InsetsState());
//        mSession.relayout(mWindow, 0, mLp, mLp.width, mLp.height, View.VISIBLE,
//                0, mFrame, mInsets, mVisibleInsets, mConfig, mSurface);

        if (mSurfaceControl.isValid()) {
            mSurface.copyFrom(mSurfaceControl);
        } else {
            destroySurface();
            throw new RuntimeException("Failed creating Surface.");
        }

        // 基于WMS返回的InputChannel创建一个Handler,用于监听输入事件
        //mInputHandler一旦被创建,就已经在监听输入事件了
        mInputHandler = new InputHandler(mInputChannel, Looper.myLooper());
    }

    private void destroySurface() {
        mSurface.release();
        mSurfaceControl.release();
    }


    public void uninstallWindow(IWindowManager wms) throws Exception {
        Log.i(TAG, "installWindow");
        // 从WMS处卸载窗体
        mSession.remove(mWindow);

        // 从WMS处移除之前加入的Token
//        wms.removeWindowToken(mToken);
        wms.removeWindowToken(mToken, Display.DEFAULT_DISPLAY);
    }

    public void scheduleNextFrame() {
        // 要求在显示系统刷新下一帧时回调mFrameRender。注意,仅仅回调一次
        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mFrameRender, null);
    }

    // 这个Runnable对象用于在窗体上描绘一帧
    public Runnable mFrameRender = new Runnable() {
        @Override
        public void run() {
            try {
                // 获取当期时间戳
                long time = mChoreographer.getFrameTime() % 1000;

                // 画图
                if (mSurface.isValid()) {
                    Canvas canvas = mSurface.lockCanvas(null);
                    canvas.drawColor(Color.DKGRAY);
                    canvas.drawRect(
                            2 * mLp.width * time / 1000 - mLp.width,
                            0,
                            2 * mLp.width * time / 1000,
                            mLp.height,
                            mPaint);
                    mSurface.unlockCanvasAndPost(canvas);
                    mSession.finishDrawing(mWindow);
                }
                if (mContinueAnime) {
                    scheduleNextFrame();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    /**
     * 定义一个类继承InputEventReceiver。用以在其onInputEvent()函数中接收窗体的输入事件
     */
    class InputHandler extends InputEventReceiver {
        Looper mLooper = null;

        public InputHandler(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
            mLooper = looper;
        }

        @Override

        public void onInputEvent(InputEvent event) {
            if (event instanceof MotionEvent) {
                MotionEvent me = (MotionEvent) event;
                if (me.getAction() == MotionEvent.ACTION_UP) {
                    // 退出程序
                    mLooper.quit();
                }
            }
            super.onInputEvent(event);
        }
    }


    /**
     * 实现一个继承自IWindow.Stub的类MyWindow。
     */
    class MyWindow extends IWindow.Stub {
        // 保持默认的实现就可以

        @Override
        public void executeCommand(String command, String parameters, ParcelFileDescriptor descriptor) throws RemoteException {

        }

        @Override
        public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration newMergedConfiguration, Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException {

        }

        @Override
        public void locationInParentDisplayChanged(Point offset) throws RemoteException {

        }

        @Override
        public void insetsChanged(InsetsState insetsState) throws RemoteException {

        }

        @Override
        public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) throws RemoteException {

        }

        @Override
        public void moved(int newX, int newY) throws RemoteException {

        }

        @Override
        public void dispatchAppVisibility(boolean visible) throws RemoteException {

        }

        @Override
        public void dispatchGetNewSurface() throws RemoteException {

        }

        @Override
        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) throws RemoteException {

        }

        @Override
        public void closeSystemDialogs(String reason) throws RemoteException {

        }

        @Override
        public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync) throws RemoteException {

        }

        @Override
        public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync) throws RemoteException {

        }

        @Override
        public void dispatchDragEvent(DragEvent event) throws RemoteException {

        }

        @Override
        public void updatePointerIcon(float x, float y) throws RemoteException {

        }

        @Override
        public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue, int localChanges) throws RemoteException {

        }

        @Override
        public void dispatchWindowShown() throws RemoteException {

        }

        @Override
        public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) throws RemoteException {

        }

        @Override
        public void dispatchPointerCaptureChanged(boolean hasCapture) throws RemoteException {

        }
    }
}

编译生成 jar 包

cd root
source build/envsetup.sh
lunch
cd frameworks/base/services/core/java/com/android/server/wm
mm #编译 得到 root\out\target\product\rk3399_smirror\system\framework\sampleWindow.jar

su
adb root
adb remount
adb push 到机器的 system/framework 目录下
sync

运行 jar

// frameworks/base/services/core/java/com/android/server/wm/samplewindow/sw.sh
base=/system
export CLASSPATH=$base/framework/samplewindow.jar
exec app_process $base/bin com.android.server.wm.samplewindow.SampleWindow "$@"

adb root
adb remount
# 拷贝 sw.sh 到机器的 data/local/tmp 目录下
chmod 777 sw.sh
./sw.sh
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值