Android开发问题记录——悬浮窗(一) 未完待续

背景

上上周连续加班后果有点大,又快跌进短视频漩涡了,家里人又特别擅长搞我心态,做点难度不大又有些意义时事情慢慢找回对技术的热情吧。

这篇文章不介绍悬浮窗是怎么实现的,郭霖大佬13年就有悬浮窗的文章;

我只有一年多一些的APP开发经验,如果有错误的地方,还请指正。

需求

公司有一个产品,用的Android8.1,但是系统是外包出去做的,刚入职不久,还没拿到系统权限,外包那边效率慢。目前想要监听右侧一定宽度,高度match_parent的区域的长按事件,来唤出悬浮窗,别的触摸事件就给下面的APP。

思路

隐形悬浮窗

刚开始是想放一个隐形悬浮窗接收触摸事件,长按唤出主要功能的悬浮窗,但是,隐形悬浮窗一旦接收了触摸事件,下面的APP那块区域就接收不到任何的触摸事件,为了让下面的APP接收到触摸事件,就需要把我的隐形悬浮窗设置为不可触摸,这时我的隐形悬浮窗就接收不到任何的触摸事件。

kotlin 复制代码
    // 让悬浮窗接收到触摸事件
    LayoutParams.FLAG_NOT_TOUCH_MODAL or
    LayoutParams.FLAG_NOT_FOCUSABLE

    // 让悬浮窗下面接收到触摸事件
    LayoutParams.FLAG_NOT_TOUCH_MODAL or
    LayoutParams.FLAG_NOT_FOCUSABLE or
    LayoutParams.FLAG_NOT_TOUCHABLE

矛盾产生了,这时我又找到了WindowManager的一个函数

java 复制代码
updateViewLayout(View view, ViewGroup.LayoutParams params)

现在有两个个未验证的新想法:

  1. 先将隐形悬浮窗设置为可接收触摸事件,如果是长按,唤出自己的多功能悬浮窗;如果不是,将自己的隐形悬浮窗设置为不可触摸,再在原位置想办法模拟出原来的触摸事件。但是不够优雅。
  2. 在那块区域放数个小型的均匀分散的隐形悬浮窗。感觉有些问题。

无障碍服务/辅助服务/AccessibilityService

得益于系统外包的性质,我需要通过adb赋予无障碍服务权限。这块的内容也是昨天刚刚接触的,简单试了下,如果触摸位置没有view的话,无障碍服务就接收不到事件,而且view和view的体质是不一样的,有的能接收到事件有的不能接收到事件。不过无障碍服务好像能模拟一些手势。

反射

昨天下午翻文章看到说Android系统有做全局触摸监听,那么我是否可以通过反射把那个全局触摸监听弄出来

WindowManagerPolicy#PointerListener:全局触摸监听类

java 复制代码
public interface PointerEventListener {
    /**
     * 1. onPointerEvent will be called on the service.UiThread.
     * 2. motionEvent will be recycled after onPointerEvent returns so if it is needed
     * copy() must be made and the copy must be recycled.
    **/
    void onPointerEvent(MotionEvent motionEvent);
    /**
    * @see #onPointerEvent(MotionEvent)
    **/
    default void onPointerEvent(MotionEvent motionEvent, int displayId) {
        if (displayId == DEFAULT_DISPLAY) {
            onPointerEvent(motionEvent);
        }
    }
}

android.googlesource.com/platform/fr...

类找到了,对象呢?

WindowManagerService

java 复制代码
@Override 
public void registerPointerEventListener(PointerEventListener listener) { 
    mPointerEventDispatcher.registerInputEventListener(listener); 
} 

@Override 
public void unregisterPointerEventListener(PointerEventListener listener) { 
    mPointerEventDispatcher.unregisterInputEventListener(listener); 
} 

android.googlesource.com/platform/fr...

那我要是拿到了mPointerEventDispatcher是不是就能给我自己注册一个全局触摸监听了?那么WindowManager是怎么获取WindowManagerService的呢?因为我们只能接触到WindowManager对象

WindowManager

java 复制代码
@SystemService(Context.WINDOW_SERVICE) 
public interface WindowManager extends ViewManager 

android.googlesource.com/platform/fr...

一个接口类,几千行代码,Ctrl F没找到别的带WindowManager字符串的类,谁实现的呢?

WindowManagerImpl

java 复制代码
@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); 
} 

private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) { 
    // Only use the default token if we don't have a parent window. 
    if (mDefaultToken != null && mParentWindow == null) { 
        if (!(params instanceof WindowManager.LayoutParams)) { 
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 
        } 
        // Only use the default token if we don't already have a token. 
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; 
        if (wparams.token == null) { 
            wparams.token = mDefaultToken; 
        } 
    } 
} 

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

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

@Override 
public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId) { 
    IResultReceiver resultReceiver = new IResultReceiver.Stub() { 
        @Override 
        public void send(int resultCode, Bundle resultData) throws RemoteException { 
            List<KeyboardShortcutGroup> result = 
            resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY); 
            receiver.onKeyboardShortcutsReceived(result); 
        } 
    }; 
    try { 
        WindowManagerGlobal.getWindowManagerService() 
            .requestAppKeyboardShortcuts(resultReceiver, deviceId); 
    } catch (RemoteException e) { 
    } 
} 

@Override 
public Display getDefaultDisplay() { 
    return mContext.getDisplay(); 
} 

@Override 
public Region getCurrentImeTouchRegion() { 
    try { 
        return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion(); 
    } catch (RemoteException e) { 
    } 
    return null; 
} 

android.googlesource.com/platform/fr...

因为这个类就一百多行代码,就贴的多了点,主要看WindowManagerGlobal.getWindowManagerService(),至于mGlobal

java 复制代码
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

关键就在WindowManagerGlobal类了。

WindowManagerGlobal

android.googlesource.com/platform/fr...

贴个链接自己翻去吧,反正是静态方法获取的;

根据WindowManagerGlobal.getWindowManagerService()通过反射获取WindowManagerService对象,贴个刚刚让GPT3.5给我生成的代码:

kotlin 复制代码
import java.lang.reflect.Method

fun getGlobalWindowManagerService(): Any? {
    try {
        val windowManagerGlobalClass = Class.forName("android.view.WindowManagerGlobal")
        val getWindowManagerServiceMethod: Method = windowManagerGlobalClass.getDeclaredMethod("getWindowManagerService")
        getWindowManagerServiceMethod.isAccessible = true
        return getWindowManagerServiceMethod.invoke(null)
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return null
}

以上,是我周五下班前的进度,今天又刷了一个白天的手机,WindowManagerService好像涉及到一块非常大的内容,叫什么WMS,而上面的mPointerEventDispatcher又涉及到InputManagerService,被叫做IMS,等我这两天看完这方面文章,验证一下这条路的可行性再更。

另外还有几条思路,分别是DecorView做悬浮窗看看能不能绕开WindowManager的触摸管控,感觉行不通;要系统级权限,试试stackoverflow.com/questions/4... 中的老方法;要外包暴露出全局触摸接口给我用;叫老板改需求

相关推荐
烬奇小云3 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野15 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO15 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他15 小时前
Android ANR分析总结
android
PenguinLetsGo17 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博19 小时前
音频格式转换
android·音视频
音视频牛哥21 小时前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.21 小时前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农21 小时前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android