【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数据 的相关文章

  • 如何根据运行的 jar 的结果让我的 ant 任务通过或失败?

    我正在运行 CrossCheck 无浏览器 js 单元测试 作为 ant 脚本的一部分 如果 CrossCheck 测试失败 我希望 ant 报告失败 这是 build xml 中的相关部分
  • 从手机访问本地主机[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我正在使用
  • ACCESS_BACKGROUND_LOCATION 不适用于低于 Q (29) 的 Android 版本

    我的应用程序面向 Android API 28 根据文档 https developer android com preview privacy location target android 10 我应该要求ACCESS BACKGROU
  • 使用 Guice 优化注册表

    你好 今天思考了一种优化 有一些疑问 语境 我正在使用 Guice 2 进行 Java 开发 在我的网络应用程序中 我有一个转换器注册表 可以即时转换为某种类型 转换器描述如下 public class StringToBoolean im
  • 使用嵌套的 hashmap 参数发送 volley 请求

    我正在使用 android volley 框架向我的服务器发送 jsonobject 请求 get 请求工作正常 现在我想发送一个带有请求参数的 post 请求 该请求参数是嵌套的 hashmap 我重写 getparams 方法 但它期望
  • 生成的序列以 1 开头,而不是注释中设置的 1000

    我想请求一些有关 Hibernate 创建的数据库序列的帮助 我有这个注释 下面的代码 在我的实体类中 以便为合作伙伴表提供单独的序列 我希望序列以 1000 开头 因为我在部署期间使用 import sql 将测试数据插入数据库 并且我希
  • 在另一个模块中使用自定义 gradle 插件模块

    我正在开发一个自定义插件 我希望能够在稍后阶段将其部署到存储库 因此我为其创建了一个独立的模块 在对其进行任何正式的 TDD 之前 我想手动进行某些探索性测试 因此 我创建了一个使用给定插件的演示模块 到目前为止 我发现执行此操作的唯一方法
  • 如何避免 ArrayIndexOutOfBoundsException 或 IndexOutOfBoundsException? [复制]

    这个问题在这里已经有答案了 如果你的问题是我得到了java lang ArrayIndexOutOfBoundsException在我的代码中 我不明白为什么会发生这种情况 这意味着什么以及如何避免它 这应该是最全面的典范 https me
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • 读取电子邮件的文本文件转换为 Javamail MimeMessage

    我有一个电子邮件原始来源的文本文件 直接从 gmail 复制 如果您单击 查看原始文件 您就会看到它 我想读入该文件并将其转换为 MimeMessage 如果您好奇为什么 我设置了 JavaMaildir 并且需要用电子邮件填充它的收件箱以
  • 使用架构注册表对 avro 消息进行 Spring 云合约测试

    我正在查看 spring 文档和 spring github 我可以看到一些非常基本的内容examples https github com spring cloud samples spring cloud contract sample
  • Docker 和 Eureka 与 Spring Boot 无法注册客户端

    我有一个使用 Spring Boot Docker Compose Eureka 的非常简单的演示 我的服务器在端口 8671 上运行 具有以下应用程序属性 server port 8761 eureka instance prefer i
  • Android - iphone 风格 tabhost [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Java Swing:需要一个高质量的带有复选框的开发 JTree

    我一直在寻找一个 Tree 实现 其中包含复选框 其中 当您选择一个节点时 树中的所有后继节点都会被自动选择 当您取消选择一个节点时 树中其所有后继节点都会自动取消选择 当已经选择了父节点 并且从其后继之一中删除了选择时 节点颜色将发生变化
  • 如何在Android中解析xml类型的HTTPResponse

    我有一个 Android 应用程序 我使用 POST 方法来获取响应 这是我的代码 HttpResponse httpResponse httpclient execute httppost HttpEntity resEntity htt
  • 使用按钮作为列表的渲染器

    我想使用一个更复杂的渲染器 其中包含列表的多个组件 更准确地说 类似于this https stackoverflow com questions 10840498 java swing 1 6 textinput like firefox
  • Java中HashMap和ArrayList的区别?

    在爪哇 ArrayList and HashMap被用作集合 但我不明白我们应该在哪些情况下使用ArrayList以及使用时间HashMap 他们两者之间的主要区别是什么 您具体询问的是 ArrayList 和 HashMap 但我认为要完
  • org.apache.commons.net.io.CopyStreamException:复制时捕获 IOException

    我正在尝试使用以下方法中的代码将在我的服务器中创建的一些文件复制到 FTP 但奇怪的是我随机地低于错误 我无法弄清楚发生了什么 Exception org apache commons net io CopyStreamException
  • 异步更新后更新Android Listview

    我正在将 HTTP 调用从同步调用转换为异步调用 由于连接在后台运行 因此当我最初设置列表适配器时 数据不存在 如何在 HTTP 调用后更新列表适配器 我尝试了一些方法 例如在数据发送回之前不设置适配器并再次设置适配器 但没有任何效果 这是
  • 在浏览器刷新中刷新检票面板

    我正在开发一个付费角色系统 一旦用户刷新浏览器 我就需要刷新该页面中可用的统计信息 统计信息应该从数据库中获取并显示 但现在它不能正常工作 因为在页面刷新中 java代码不会被调用 而是使用以前的数据加载缓存的页面 我尝试添加以下代码来修复

随机推荐

  • 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可以实现跨进程通讯 可以解决以上需求 下面重点分析下如何具体实现 以实