前言

最近因为公司项目需求,需要远程调度启动客户端输入法输入内容。
这就是大致的需求流程,这篇首先讲远程与服务控制端通讯。首先控制服务端定义好一个Service,且在ServiceManager注册添加服务。
在这里我讲解远程端与服务控制端通讯(主要通过C++往ServiceManager注册服务)。
首先我们得获取到服务控制端注册在ServiceManager的服务IBinder对象,通过Java反射机制获得Ibinder接口对象。
public static IBinder getRemoteBinder(){
try {
Class<?> serviceManager = Class.forName("android.os.ServiceManager");
Method getService = serviceManager.getMethod("getService", String.class);
IBinder iBinder = (IBinder) getService.invoke(serviceManager.newInstance(), "InputService");
if(iBinder==null){
Log.e(PinyinIME.TAG,"getService InputService : is empty");
printServerList();//打印系统所提供的所有服务
}
return iBinder;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
//具体源码在android.os.ServiceManager
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
获取到IBinder对象作用是跨进程,举个例子,输入法程序是怎么和应用编辑框通讯的呢?怎么通过什么控制输入法弹起隐藏的呢。也是通过这个IBinder来通讯的,不信你翻翻源码,这里不做详细介绍。
而服务控制端则是由C++层注入服务:
class IServiceManager : public IInterface
{
public:
DECLARE_META_INTERFACE(ServiceManager);
/**
* Retrieve an existing service, blocking for a few seconds
* if it doesn't yet exist.
*/
virtual sp<IBinder> getService( const String16& name) const = 0;
/**
* Retrieve an existing service, non-blocking.
*/
virtual sp<IBinder> checkService( const String16& name) const = 0;
/**
* Register a service.
*/
virtual status_t addService( const String16& name,
const sp<IBinder>& service,
bool allowIsolated = false) = 0;
/**
* Return list of all existing services.
*/
virtual Vector<String16> listServices() = 0;
enum {
GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
CHECK_SERVICE_TRANSACTION,
ADD_SERVICE_TRANSACTION,
LIST_SERVICES_TRANSACTION,
};
};
//上面C++层注册服务提供一个IBinder接口子类,需要实现onTransact方法
virtual status_t onTransact(uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0)
{
LOGD("enter MyService onTransact and the code is %d", code);
switch (code)
{
case BINDER_HANDLE:
LOGD("MyService interface handle");
reply->writeCString("handle reply");
break;
case BINDER_SET_SCREEN:
LOGD("MyService interface set screen");
reply->writeCString("set screen reply");
break;
case BINDER_SET_CHAR:
{//call cb
LOGD("MyService interface set char before");
reply->writeCString("set char reply");
cb = data.readStrongBinder();
if (cb != NULL)
{
LOGD("MyService interface set char : %s", data.readCString());
Parcel in, out;
in.writeInterfaceToken(String16(BINDER_NAME));
in.writeInt32(n++);
in.writeString16(String16("This is a string."));
cb->transact(1, in, &out, 0);
show();
}
break;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
return 0;
}
这样我们可以通过刚刚获取到IBinder对象与之通讯了,这里我只讲个例子:
当远程端设备输入法激活的时候,我将传递输入法输入类型和输入法展示的多功能键传递给服务控制端。
//当输入法被激活的时候,会调用onStartInputView(EditorInfo,boolean)
Parcel data = Parcel.obtain();
data.writeInt(editorInfo.inputType);
data.writeInt(editorInfo.imeOptions);
Log.d(TAG, "isActives:" + isActives);
if (isActives) {
if (mController != null) {
mController.startInput(data, Parcel.obtain());
} else {
isNeedActives = true;
tmp = data;
mController = new Controller(remoteBinder,this);
}
} else {
isNeedActives = true;
tmp = data;
if (mController != null) {
mController.serviceConnection();
} else {
mController = new Controller(remoteBinder,this);
}
}
//这里我将两个int参数写入到Parce对象中开始进行通讯
/**
* 开始输入
*
* @param data
* 写入输入类型和多功能
* @param reply
*/
public void startInput(final Parcel data, final Parcel reply) {
Log.d(PinyinIME.TAG, getClass().getName() + ":\t startInput");
if (!PinyinIME.isActives) {
Log.d(PinyinIME.TAG, "not yet check success , start input failure");
dealHandler.sendEmptyMessage(Constant.HANDER_RELINK);
return;
}
new Thread(new Runnable() {
@Override
public void run() {
if (remoteBinder != null && remoteBinder.isBinderAlive()) {
try {
if (remoteBinder.transact(
Constant.INPUT_METHOD_ACTIVATION, data, reply,
IBinder.FLAG_ONEWAY)) {
PinyinIME.isNeedActives = false;
Log.d(PinyinIME.TAG,
"input method to activate, notify the success");
} else {
Log.d(PinyinIME.TAG,
"input method to activate, notify the failure");
}
} catch (RemoteException e) {
e.printStackTrace();
} finally {
data.recycle();
reply.recycle();
}
}else{
dealHandler.sendEmptyMessage(Constant.HANDER_RELINK);
}
}
}).start();
}
这样我们就可以通过获取到的Ibinder对象的transact方法进行通讯。
//code必须双方定义好,否则接收数据无法正常, //第一个是我们装载的序列化数据, //第二我们可以直接传个对象,最好一个是需要返回结果的标识, //0代表需要返回内容,FLAG_ONEWAY单方面无需返回结果的标识 public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
当我们调用了ibinder.transact(int,parce,parce,int)方法,这是注册的服务中的IBinder对象的onTransact(int,parce,parce,int)方法就会被响应,这样我们就实现了远程端跟服务控制端通讯了。
到了这里,有个问题,服务控制端接收到客户端输入的内容咋办,怎通知远程端输入法输入内容到编辑框中呢。
其实也很简单,我们只需要在远程端输入法程序实现一个Ibinder对象,传递给服务控制端,这样就可以实现,具体怎么传递了?
//首先我们得让远程输入法程序拥有属于自己的ibinder类。
package com.redfinger.inputmethod.server;
import com.android.inputmethod.pinyin.PinyinIME;
import android.annotation.SuppressLint;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
public interface InputBinder extends IInterface{
public static class Stub extends Binder implements InputBinder{
private static final java.lang.String DESCRIPTOR = "com.redfinger.inputmethod.service.InputBinder";
public PinyinIME pinyinIME;
public Stub(PinyinIME pinyinIME) {
this.pinyinIME = pinyinIME;
this.attachInterface(this, DESCRIPTOR);
}
public InputBinder asInterface(IBinder obj){
if(obj == null){
return null;
}
IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR);
if(iInterface!=null&&iInterface instanceof InputBinder){
return (InputBinder)iInterface;
}
return new Stub.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@SuppressLint({ "NewApi", "Recycle" })
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
switch (code) {
case Constant.CONNECTION_HANDSHAKE2:
String dataString = data.readString();
Log.d(PinyinIME.TAG, "The second handshake start [data = "+dataString +"]");
if("CONNECTION_RESPONED".equals(dataString)){
Parcel parcel = Parcel.obtain();
parcel.writeString("CONNECTION_FINISH");
pinyinIME.getRemoteBinder().transact(Constant.CONNECTION_HANDSHAKE3, parcel, Parcel.obtain(), IBinder.FLAG_ONEWAY);
PinyinIME.isActives = true;
Log.d(PinyinIME.TAG, "The third handshake success");
if (PinyinIME.isNeedActives) {
PinyinIME.mController.startInput(pinyinIME.getTmp(), Parcel.obtain());
}
if (PinyinIME.isNeedCloseInputMethod) {
PinyinIME.mController.finishInput();
}
}else{
Log.d(PinyinIME.TAG, "The third handshake failure , agent connect ! ");
PinyinIME.mController.serviceConnection();
}
break;
case Constant.FUNCTION_INPUT:
....
switch (keyCode) {
case 14:
pinyinIME.simulateKeyEventDownUp(KeyEvent.KEYCODE_DEL);
return true;
case 28:
pinyinIME.simulateKeyEventDownUp(KeyEvent.KEYCODE_ENTER);
return true;
case 65:
pinyinIME.requestHideSelfFromClient = true;
pinyinIME.requestHideSelf(0);
break;
}
break;
case Constant.CHARACTER_INPUT:
....
return true;
case Constant.DISCONNECTION:
....
break;
case Constant.INPUT_METHOD_PLATFORM:
....
break;
}
return super.onTransact(code, data, reply, flags);
}
public static class Proxy implements InputBinder{
private android.os.IBinder mRemote;
public Proxy(android.os.IBinder mRemote) {
this.mRemote = mRemote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
}
static final int receiveChar = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
}
是不是特变像AIDL文件的内容一样,aidl其实就是Android自己给我写好的ibinder代码一样。
这样我们就可以在获取到服务控制端ibinder对象中写入我们自己ibinder对象,传递过去让他通过transact方法来与输入法程序ibinder对象通讯了。
//Parce类中提供了这样的一个方法,就是用于写入ibinder对象的。
public final void writeStrongBinder(IBinder val) {
nativeWriteStrongBinder(mNativePtr, val);
}
这样我们就可以在InputBinder类中来处理返回的数据了。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
# android
# binder
# 使用
# android的binder机制
# Android中关于Binder常见面试问题小结
# Android中Binder IPC机制介绍
# Android 图文详解Binder进程通信底层原理
# Android中的binder机制详解
# Android中Binder详细学习心得
# Android10 Binder原理概述深入解析
# 就可以
# 我们可以
# 多功能
# 我将
# 自己的
# 类中
# 象中
# 客户端
# 这是
# 就会
# 给我
# 有个
# 让他
# 第一个
# 子类
# 在这
# 这就是
# 则是
# 是怎么
# 只需
相关文章:
小自动建站系统:AI智能生成+拖拽模板,多端适配一键搭建
文字头像制作网站推荐软件,醒图能自动配文字吗?
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
北京建设网站制作公司,北京古代建筑博物馆预约官网?
电商平台网站制作流程,电商网站如何制作?
音乐网站服务器如何优化API响应速度?
如何快速打造个性化非模板自助建站?
魔方云NAT建站如何实现端口转发?
如何在Golang中使用encoding/gob序列化对象_存储和传输数据
Python路径拼接规范_跨平台处理说明【指导】
长沙做网站要多少钱,长沙国安网络怎么样?
如何在搬瓦工VPS快速搭建网站?
建站之星如何快速解决建站难题?
如何高效搭建专业期货交易平台网站?
专业网站制作服务公司,有哪些网站可以免费发布招聘信息?
如何在阿里云高效完成企业建站全流程?
西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?
怎么将XML数据可视化 D3.js加载XML
淘宝制作网站有哪些,淘宝网官网主页?
成都网站制作价格表,现在成都广电的单独网络宽带有多少的,资费是什么情况呢?
枣阳网站制作,阳新火车站打的到仙岛湖多少钱?
建站之星2.7模板:企业网站建设与h5定制设计专题
网站制作费用多少钱,一个网站的运营,需要哪些费用?
深圳网站制作费用多少钱,读秀,深圳文献港这样的网站很多只提供网上试读,但有些人只要提供试读的文章就能全篇下载,这个是怎么弄的?
如何在IIS中配置站点IP、端口及主机头?
如何确保FTP站点访问权限与数据传输安全?
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
专业公司网站制作公司,用什么语言做企业网站比较好?
如何在Windows服务器上快速搭建网站?
如何获取上海专业网站定制建站电话?
logo在线制作免费网站在线制作好吗,DW网页制作时,如何在网页标题前加上logo?
如何用IIS7快速搭建并优化网站站点?
建站之星展会模版如何一键下载生成?
建站之星备案是否影响网站上线时间?
javascript中的try catch异常捕获机制用法分析
如何在阿里云服务器自主搭建网站?
如何通过FTP服务器快速搭建网站?
香港服务器部署网站为何提示未备案?
Swift中循环语句中的转移语句 break 和 continue
建站主机SSH密钥生成步骤及常见问题解答?
宝华建站服务条款解析:五站合一功能与SEO优化设置指南
阿里云网站制作公司,阿里云快速搭建网站好用吗?
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
如何获取开源自助建站系统免费下载链接?
道歉网站制作流程,世纪佳缘致歉小吴事件,相亲网站身份信息伪造该如何稽查?
建站主机系统SEO优化与智能配置核心关键词操作指南
建站之星后台密码遗忘?如何快速找回?
如何在Golang中指定模块版本_使用go.mod控制版本号
建站之星如何实现PC+手机+微信网站五合一建站?
*请认真填写需求信息,我们会在24小时内与您取得联系。