【Android11系统开发】上层app通过AIDL监听framework数据

2023-11-20

一、适用场景:

在Android系统开发中,需要监听按键、触摸、或者可见窗口大小变化等需求时,你会考虑什么方法来实现呢?通过广播的方式可以实现,但是效果可能并不好。AIDL可以实现跨进程通讯,可以解决以上需求。下面重点分析下如何具体实现,以实现按键监听功能为例,代码按实现后的调用先后来说明。

二、代码说明以及分析实现:

大致的流程图如下

 

1、在frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java文件中添加监听接口。在interceptKeyBeforeDispatching()方法中获取event传递给自定义监听类SxListenerManager中。

@Override
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
int policyFlags) {
    *****
    *****
    if (mSystemBooted) {
        //SxListenerManager使用单例,使用前判断是否为null
        if(SxListenerManager.getInstance() != null) {
            //通过系统原生接口截取event事件,传递给自定义SxListenerManager
            boolean status = SxListenerManager.getInstance().postKeyEventToUser(event);
            if(status){
                return -1;
            }
        }
    }
    *****
    *****
}

2、SxListenerManager.java类主要用于绑定service和定义接口。IBinder.DeathRecipient用于检测远程对象是否存活,由于服务端经常意外停止,我们需要重启service,可以使用linkToDeath 机制。

public class SxListenerManager {
    private final static String TAG = "SxListenerManager";

    private static SxListenerManager mInstance = null;
	private static ISxInputListener mSxListenerService;
	
    private SxListenerManager() {
		bindSxListenerService();
    }
	
	public static SxListenerManager getInstance() {
		if (mInstance == null) {
            synchronized (SxListenerManager.class) {
                if (mInstance == null) {
                    mInstance = new SxListenerManager();
                }
            }
        }
        return mInstance;
    }
	
	private static void bindSxListenerService() {
		
        IBinder service = ServiceManager.getService("Sx_service");
        try {
            mSxListenerService = ISxService.Stub.asInterface(service).getListener();
            service.linkToDeath(deathRecipient, 0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
	
    private static IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.e("SxListenerManager", "===deathRecipient binderDied===");
            if (mSxListenerService != null) {
                //注销监听和回收资源
                mSxListenerService.asBinder().unlinkToDeath(this, 0);
                mSxListenerService = null;
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
				//service死掉后重启service
                bindSxListenerService();
            }
        }
    };
	public boolean postKeyEventToUser(KeyEvent event){
		boolean ret = false;
		try {
			ret = mSxListenerService.postKeyEventToUser(event);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		return ret;
	}
	
}

3、ISxInputListener.aidl为AIDL接口类,只定义一个postKeyEventToUser接口,用于传递event事件。

interface ISxInputListener { 
    boolean postKeyEventToUser(in KeyEvent event);
}

4、SxListenerService.java类继承ISxInputListener,实现postKeyEventToUser方法。该方法调用KeyEventListener类中的sendKeyEventToListener方法。

public class SxListenerService extends ISxInputListener.Stub {
	private static final String TAG = "SxListenerService";

	public SxListenerService(Context context) {
		Log.d(TAG, "====SxListenerService==="); 
	}
	
	@Override
	public boolean postKeyEventToUser(KeyEvent event){
		boolean ret = false;
		Log.d(TAG, "post Key EventToUser = "+event);
		if(KeyEventListener.getInstance() != null){
			ret = KeyEventListener.getInstance().sendKeyEventToListener(event);
		}else{
			Log.d(TAG, "KeyEventListener = null ");
		}
		return ret;
	}
}

5、KeyEventListener类


public class KeyEventListener {
	private static final String TAG = "KeyEventListener";
	private static KeyEventListener mListener = null;
	private Context mContext = null;
	private RemoteCallbackList<ISxKeyEventClient> mCallbackList = null;
	private HashMap<IBinder, ISxKeyEventClient> mMap = null;
	
	private KeyEventListener() {
		mCallbackList = new RemoteCallbackList<ISxKeyEventClient>() {
			@Override
			public void onCallbackDied(ISxKeyEventClient client) {
				mMap.remove(client.asBinder());
			}
		};
		mMap = new HashMap<IBinder, ISxKeyEventClient>();
	}
	
	public static KeyEventListener getInstance() {
		if (mListener == null) {
			synchronized (KeyEventListener.class) {
				if (mListener == null) {
					try {
						mListener = new KeyEventListener();
					} catch (Exception e) {
						e.printStackTrace();
						return null;
					}
				}
			}
		}
		return mListener;
	}

	public void setClient(IBinder client) {
		if (client != null) {
			ISxKeyEventClient keyEventClient = ISxKeyEventClient.Stub.asInterface(client);
			mCallbackList.register(keyEventClient);
			mMap.put(client, keyEventClient);
		}
	}

	public void releaseClient(IBinder client) {
		if (client != null && mMap.size() > 0) {
			ISxKeyEventClient keyEventClient = mMap.get(client);
			mCallbackList.unregister(keyEventClient);
			mMap.remove(client);
		}
	}

	public void setContext(Context context) {
		this.mContext = context;
	}

	public boolean sendKeyEventToListener(KeyEvent arg0) {
		boolean ret = false;
		int count = mCallbackList.beginBroadcast();
		while (count > 0) {
			count--;
			try {
				ret = mCallbackList.getBroadcastItem(count).onKeyEvent(arg0);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
		mCallbackList.finishBroadcast();
		return ret;
	}

}

6、ISxKeyEventClient.aidl为AIDL接口类

interface ISxKeyEventClient {
    boolean onKeyEvent(in KeyEvent key);
}

7、KeyEventManager.java类,实现最终event事件监听。

public class KeyEventManager{
	private static final String TAG = "KeyEventManager";

	public OnKeyEventListener mSxKeyEventListener = null;

	
	public KeyEventManager(Context context) {
		mSxKeyEventListener = new SxKeyEventListener();
		SxKeyListenerManager.getInstance().registerOnKeyEventListener(mSxKeyEventListener);
	}

	private class SxKeyEventListener implements OnKeyEventListener {
		@Override
		public boolean onKeyEvent(KeyEvent event) {
            //按键监听,所有的event监听均在此处处理
			final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
			final boolean canceled = event.isCanceled();
			int KeyCode = event.getKeyCode();
		
			switch(KeyCode){
				case KeyEvent.KEYCODE_HOME: 
					break;
				case KeyEvent.KEYCODE_BACK:
					if(!down ){
					}
					break;
			}
			return true;
		}
	}
}

8、SxKeyListenerManager.java类绑定service,注册listener。


public class SxKeyListenerManager {
	private final static String TAG = "SxKeyListenerManager";

	static volatile SxKeyListenerManager mInstance = null;
	private static ISxKeyListener mKeyListenerService = null;

	private SxKeyListenerManager() {
		bindSxSourceService();
	}

	public static SxKeyListenerManager getInstance() {

		if (mInstance == null) {
			synchronized (SxKeyListenerManager.class) {
				if (mInstance == null) {
					mInstance = new SxKeyListenerManager();
				}
			}
		}
		return mInstance;
	}
	private static void bindSxSourceService() {
		IBinder service = ServiceManager.getService("Sx_service");
		try {
			mKeyListenerService = ISxService.Stub.asInterface(service).getKeyListener();
			service.linkToDeath(deathRecipient, 0);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	private static IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
		@Override
		public void binderDied() {
			Log.e("SxKeyListenerManager", "------------deathRecipient binderDied------------");
			if (mKeyListenerService != null) {
				//注销监听和回收资源
				mKeyListenerService.asBinder().unlinkToDeath(this, 0);
				mKeyListenerService = null;
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				bindSxSourceService();
			}
		}
	};

	private Client mClient;
	private ArrayList<OnKeyEventListener> mListeners = new ArrayList<OnKeyEventListener>();

	private class Client extends ISxKeyEventClient.Stub {
		@Override
		public boolean onKeyEvent(KeyEvent arg0) throws RemoteException {
			boolean ret = false;
			for (OnKeyEventListener l : mListeners) {
				ret = l.onKeyEvent(arg0);
			}
			return ret;
		}
	}

	public boolean registerOnKeyEventListener(OnKeyEventListener listener) {
		if (mClient == null) {
			mClient = new Client();
			try {
				if(mKeyListenerService != null) {
					mKeyListenerService.addClient("KeyEventListener", mClient);
				}
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
		mListeners.add(listener);
		return true;
	}
	
	private final Object LOCK = new Object();

	public boolean unregisterOnKeyEventListener(OnKeyEventListener listener) {
		synchronized (LOCK)
		{
			mListeners.remove(listener);
			if (mListeners.size() == 0 && mClient != null) {
				try {
					if(mKeyListenerService != null) {
						mKeyListenerService.removeClient("KeyEventListener", mClient);
					}
					mClient = null;
				} catch (RemoteException e) {
					Log.e(TAG, e.getMessage());
				}
			}
			return true;
		}
	}
}

9、ISxKeyListener.aidl为AIDL接口类,定义相关的接口。

interface ISxKeyListener {
	IBinder getClient(String name);
	void addClient(String name,IBinder client);
	void removeClient(String name,IBinder client);
}

10、SxKeyListenerService.java类实现相关的接口。

public class SxKeyListenerService extends ISxKeyListener.Stub {
	private static final String TAG = "SxKeyListenerService";

	public SxKeyListenerService(Context context) {
	}

	@Override
	public IBinder getClient(String name) {
		return null;
	}

	@Override
	public void addClient(String name, IBinder client) {
		if (name.equals("KeyEventListener")) {
			KeyEventListener.getInstance().setClient(client);
		}
	}

	@Override
	public void removeClient(String name, IBinder client) {
		if (name.equals("KeyEventListener")) {
			KeyEventListener.getInstance().releaseClient(client);
		}
	}
}

11、OnKeyEventListener.java为AIDL接口类。

public interface OnKeyEventListener {

    public boolean onKeyEvent(KeyEvent arg0);

}

12、ISxService.aidl类为aidl接口类。

interface ISxService {
	ISxInputListener getListener(); 
	ISxKeyListener getKeyListener();
}

13、SxService.java类用于启动所有service。

public class SxService extends Service {
	private SxListenerService mSxListenerService = null;
	private SxKeyListenerService mSxKeyListenerService = null;
	private Context mContext;

	public SxService() {
	}

	@Override
	public void onCreate() {
		mContext = this;
		super.onCreate();
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		android.os.ServiceManager.addService("Sx_service", mBinder);
		return Service.START_STICKY;
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
	}


	private final ISxService.Stub mBinder = new ISxService.Stub() {
		@Override
		public ISxInputListener getListener() throws RemoteException {
			if (mSxListenerService == null) {
				mSxListenerService = new SxListenerService(getApplicationContext());
			}
			return mSxListenerService;
		}

		@Override
		public ISxKeyListener getKeyListener() throws RemoteException {
			DebugLog.debug("");
			if (mSxKeyListenerService == null) {
				mSxKeyListenerService = new SxKeyListenerService(getApplicationContext());
			}
			return mSxKeyListenerService;
		}
	}
}

以上内容全部代码基本已实现,除了监听按键,其他需求也是类似的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Android11系统开发】上层app通过AIDL监听framework数据 的相关文章

  • Toast 消息消失后​​完成活动吗?

    有谁知道 是否有可能对 Toast 消息执行某些操作 在我的情况下完成活动 将被关闭 您只需创建一个Thread持续时间只要Toast显示 然后您就可以完成您的Activity public void onCreate Bundle sav
  • FragmentActivity 无法解析为类型

    我正在尝试来自的应用程序这个博客 http android developers blogspot com 2011 02 android 30 fragments api html 在延长的同时FragmentActivity 我收到以下
  • 如何使用双重调度来分析图形基元的交集?

    我正在分析图形基元 矩形 直线 圆形等 的交互并计算重叠 相对方向 合并等 这被引用为双重调度的一个主要示例 例如维基百科 http en wikipedia org wiki Double dispatch 自适应碰撞算法通常要求 不同的
  • 如何在 IntelliJ IDEA 中运行 akka actor

    来自 Akka 网站文档 然后 这个主要方法将创建所需的基础设施 运行演员 启动给定的主要演员并安排 一旦主要参与者终止 整个应用程序就会关闭 因此 您将能够使用类似于以下的命令运行上面的代码 下列的 java classpath akka
  • 无需递归即可对可观察结果进行分页 - RxJava

    我有一个非常标准的 API 分页问题 您可以通过一些简单的递归来处理 这是一个捏造的例子 public Observable
  • AndEngine MenuScene - 无法单击按钮

    我有一个关于 android 和 andengine 的小问题 这是我的主菜单的源代码 AbstractScene is extending Scene public class MainMenuScene extends Abstract
  • 使用 Proguard 通过 Dropbox.com 库混淆 Android 应用程序

    我刚刚创建了一个需要 Dropbox com API 库的 Android 应用程序 我现在尝试在 发布 模式下构建应用程序 并希望在代码上运行混淆器以对其进行混淆 但是 每当我尝试运行 Proguard 时 都会收到以下错误 Progua
  • java实现excel价格、收益率函数[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 确定 JavaFX 中是否消耗了事件

    我正在尝试使用 JavaFX 中的事件处理来做一些非滑雪道的事情 我需要能够确定手动触发事件后是否已消耗该事件 在以下示例中 正确接收了合成鼠标事件 但调用 Consumer 不会更新该事件 我对此进行了调试 发现 JavaFX 实际上创建
  • TabLayout 的不同 tabMode

    我正在使用 ViewPager 和 TabLayout 如果选项卡可以放置在显示 tabMode 上 则它们必须是 app tabMode fixed else app tabMode scrollable 我怎样才能做到这一点 我不明白你
  • Android AppWidgetManager 方法 updateAppWidget 无法设置意图、加载数据。而且它是随机发生的

    我的小部件由 2 个按钮和一个显示数据的列表视图组成 大多数时候 当调用小部件提供程序的 onUpdate 方法时 一切都会正常加载 每个人都很高兴 但是我注意到有时在调用更新方法后 小部件完全无法加载其数据 列表视图为空 所有按钮均无响应
  • 动态更改按钮上的图像视图

    在我的应用程序中 我有按钮和ImageView 当我按下按钮时我想改变ImageView 我的可绘制文件夹中有 5 张图像 按下按钮时 ImageView 根据按钮单击一张一张地更改图像 我想要它的解决方案 感谢任何可以提供帮助的人 维护一
  • 带 getClassLoader 和不带 getClassLoader 的 getResourceAsStream 有什么区别?

    我想知道以下两者之间的区别 MyClass class getClassLoader getResourceAsStream path to my properties and MyClass class getResourceAsStre
  • Android:透明活动问题

    最近 在我们的一款生产应用程序上 透明活动已停止工作 我的意思是它变成了黑色背景而不是透明背景 当我将活动的背景颜色设置为纯色 即红色 绿色等 时 它的应用不会出现问题 该问题可能是由于迁移到 AndroidX 引起的 但我没有这方面的证据
  • 在数组列表中过滤 Filterable 不取消之前的过滤

    我看过过滤器方法文档 其中显示调用过滤器会取消所有先前未执行的过滤请求 并发布一个稍后将执行的新过滤请求 但我收到的实际回调有些不同 在我的实现中 它不会取消先前的过滤器请求并调用publishResults 最近一次搜索条件后的上一次搜索
  • 检测到 JVM 正在关闭

    我有一个使用 addShutdownHook 处理 Ctrl C 的 Swing 应用程序 它工作正常 直到我的关闭任务之一调用一个在正常情况下更改 JLabel 文本的函数 此时它挂起 我认为问题是 Swing EDT 已终止或正在等待某
  • Java 的“&&”与“&”运算符

    我使用的示例来自 Java Herbert Schildt 的完整参考文献 第 12 版 Java 是 14 他给出了以下 2 个示例 如果阻止 第一个是好的 第二个是错误的 因此发表评论 public class PatternMatch
  • 在会话即将到期之前调用方法

    我的网络应用程序有登录的用户 有一个超时 在会话过期之前 我想执行一个方法来清理一些锁 我已经实现了sessionListener但一旦我到达public void sessionDestroyed HttpSessionEvent eve
  • Android 的代码覆盖率[重复]

    这个问题在这里已经有答案了 可能的重复 Android测试代码覆盖率 Eclipse https stackoverflow com questions 3282702 android test code coverage eclipse
  • GAE 无法部署到 App Engine

    我正在尝试从 Eclipse 发布 Web 应用程序 我在 GAE 上创建了四个项目 可以通过登录我的帐户并查看控制台来查看它们 我已经改变了appengine web xml到项目的应用程序 ID 如果我将其更改为 GAE 上第一个创建的

随机推荐

  • Linux TCP链接查看和调整

    查看Linux的TCP连接数的方法如下 统计80端口连接数 netstat nat grep i 80 wc l 统计httpd协议连接数 ps ef grep httpd wc l 统计已连接上的 状态为 established 的TCP
  • Java终止线程的三种方式

    停止一个线程通常意味着在线程处理任务完成之前停掉正在做的操作 也就是放弃当前的操作 在 Java 中有以下 3 种方法可以终止正在运行的线程 使用退出标志 使线程正常退出 也就是当 run 方法完成后线程中止 使用 stop 方法强行终止线
  • R----dplyr包介绍学习

    dplyr包 plyr包的替代者 专门面对数据框 将ddplyr转变为更易用的接口 gt 来自dplyr包的管道函数 其作用是将前一步的结果直接传参给下一步的函数 从而省略了中间的赋值步骤 可以大量减少内存中的对象 节省内存 可惜的是应用范
  • 【理解springboot自动装配原理】

    理解springboot自动装配原理 最近读了小马哥 mercyblitz Springboot编程思想 核心篇 有了一些心得和感悟 分享给大家 1 官网介绍了激活自动装配的方法 文档提到激活自动化装配的注解 EnableAutoConfi
  • DAS、SAN、NAS存储连接方式详解

    1 直接访问存储DAS Direct Access Storage DAS将存储设备通过SCSI接口或光纤通道直接连接到一台计算机上 代表为磁盘阵列柜RAID 磁盘阵列柜是由多个硬盘按照不同的方式组合成一个大型的磁盘组 利用个别磁盘提供数据
  • Spring的xml文档配置

    1基于XML的注解配置
  • webpack 收集依赖、打包输出精简实现

    文章目录 安装babel插件 读取文件信息 获取当前js文件的依赖关系 广度遍历获取所有依赖图 生成浏览器可执行代码 安装babel插件 由于ES6转ES5中需要用到babel 所以要用到一下插件 npm install babel cor
  • MATLAB-DL6

    MATLAB DL6 步骤 交互式迁移貌似2020a才有 学会用analyze network 命令行式 迁移学习 冻结 freezeWeights createLgraphUsingConnections 数据增强 学习参数 函数大杂烩
  • SQL求解用户连续登录天数

    数据分析面试过程中 一般都逃不掉对SQL的考察 可能是笔试的形式 也可能是面试过程中面试官当场提问 当场在纸上写出 或者简单说一下逻辑 今天 就来分享一道面试中常常被问到的一类SQL问题 连续问题 无论是什么样的场景 只要是 连续 问题 那
  • TCP/IP协议之服务器端——华清远见

    咳咳咳 今天也是认真学习的一天 一 TCP IP协议是什么 TCP协议是一种以固连线为基础的协议 它提供两台计算机之间可靠的数据传送 TCP可以保证从一端数据传至连接的另一端时 数据能够确实送达 TCP协议适合可靠性比较高的场合 就像拨打电
  • 队列的几种实现方式

    队列简介 队列是一种特殊的线性表 特殊之处在于它只允许在表的前端 front 进行删除操作 而在表的后端 rear 进行插入操作 和栈一样 队列是一种操作受限制的线性表 进行插入操作的端称为队尾 进行删除操作的端称为队头 队列是一种最常用的
  • Android10(Q)系统源码编译

    Android10系统编译 一 硬件环境 二 软件环境 三 开始编译 四 遇到问题 一 硬件环境 在ubuntu18 04系统中下载编译android10 Q 源码需要如下条件 1 至少4G内存 小于4G内存编译源码期间的等待将会是很痛苦的
  • 【数学建模】数据处理问题

    一 插值与拟合 常用于数据的补全以及趋势分析 1 插值 总的思想 就是利用函数f x 若干已知点的函数值 求出适当的特定函数g x 这样f x 其他未知点上的值 就可以用g x 在这一点的值来近似 这种通过已知求未知的方法称为 插值 插值方
  • mysql知识系列:查看用户密码、修改用户密码,对网上“update user set authentication_string=‘123456’ where user=‘root’;”纠错

    说明 博主用的是mysql8 0 18 网上在找回mysql密码 清一色的教程都是修改root用户的密码 并且使用 update user set authentication string 123456 where user root 博
  • Keycloak概述

    这里写自定义目录标题 Keycloak概述 Single Sign On Kerberos 社交登录 用户合并 客户端适配 管理控制台 用户管理控制台 标准协议 授权服务 Getting Started Keycloak概述 keycloa
  • FPN网络详解

    1 特征金字塔 特征金字塔 Feature Pyramid Networks FPN 的基本思想是通过构造一系列不同尺度的图像或特征图进行模型训练和测试 目的是提升检测算法对于不同尺寸检测目标的鲁棒性 但如果直接根据原始的定义进行FPN计算
  • mysql报错ERROR 1356 (HY000): View ‘mysql.user‘ references invalid table(s) or column(s) or function(s)

    当您在使用 UPDATE user SET password PASSWORD newpassword WHERE User root 命令时提示 ERROR 1356 HY000 View mysql user references in
  • c语言数组下标和指针,C语言 数组 下标与指针 效率解析

    以字符串拷贝函数为例 解析数组中下标与指针的效率情况 指针的效率至少和下标相同 原因参考C下标的实现原理 注意编译器差异 因为部分编译器针对下标设置了特殊汇编指令 不做考虑 define SIZE 50 int x SIZE int y S
  • SQL中join group by having max() 时转Linq

    本来开发时有一个分组聚合的脚本 比较复杂 为了笔记效果 所以将脚本做一个简化 本来库里有两个表TableA和TableB 两个表的主键做如下关联 TableA的主键ID为TableB的外键Aid SELECT a Id a Name b I
  • 【Android11系统开发】上层app通过AIDL监听framework数据

    一 适用场景 在Android系统开发中 需要监听按键 触摸 或者可见窗口大小变化等需求时 你会考虑什么方法来实现呢 通过广播的方式可以实现 但是效果可能并不好 AIDL可以实现跨进程通讯 可以解决以上需求 下面重点分析下如何具体实现 以实