热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android学习之介绍Binder的简单使用

BInder方面的资料虽然感觉看的比较多,但是真正用的时候才发现有很多地方模棱两棵的,所以,打算用一个实例再来巩固一下binder的使用方法。这篇文章主要介绍了Android中Binder的简单使用,文中给出详细的示例代码,需要的朋友可以参考下

前言

最近因为公司项目需求,需要远程调度启动客户端输入法输入内容。


这就是大致的需求流程,这篇首先讲远程与服务控制端通讯。首先控制服务端定义好一个Service,且在ServiceManager注册添加服务。

在这里我讲解远程端与服务控制端通讯(主要通过C++往ServiceManager注册服务)。

首先我们得获取到服务控制端注册在ServiceManager的服务IBinder对象,通过Java反射机制获得Ibinder接口对象。

 public static IBinder getRemoteBinder(){
 try {
 Class<&#63;> 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 null 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 getService( const String16& name) cOnst= 0;

 /**
 * Retrieve an existing service, non-blocking.
 */
 virtual sp checkService( const String16& name) cOnst= 0;

 /**
 * Register a service.
 */
 virtual status_t addService( const String16& name,
   const sp& service,
   bool allowIsolated = false) = 0;

 /**
 * Return list of all existing services.
 */
 virtual Vector 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类中来处理返回的数据了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。


推荐阅读
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 在 Android 开发中,通过 Intent 启动 Activity 或 Service 时,可以使用 putExtra 方法传递数据。接收方可以通过 getIntent().getExtras() 获取这些数据。本文将介绍如何使用 RoboGuice 框架简化这一过程,特别是 @InjectExtra 注解的使用。 ... [详细]
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • 本文探讨了在 ASP.NET MVC 5 中实现松耦合组件的方法。通过分离关注点,应用程序的各个组件可以更加独立且易于维护和测试。文中详细介绍了依赖项注入(DI)及其在实现松耦合中的作用。 ... [详细]
  • 网易严选Java开发面试:MySQL索引深度解析
    本文详细记录了网易严选Java开发岗位的面试经验,特别针对MySQL索引相关的技术问题进行了深入探讨。通过本文,读者可以了解面试官常问的索引问题及其背后的原理。 ... [详细]
  • 本文将深入探讨如何在不依赖第三方库的情况下,使用 React 处理表单输入和验证。我们将介绍一种高效且灵活的方法,涵盖表单提交、输入验证及错误处理等关键功能。 ... [详细]
  • 探索电路与系统的起源与发展
    本文回顾了电路与系统的发展历程,从电的早期发现到现代电子器件的应用。文章不仅涵盖了基础理论和关键发明,还探讨了这一学科对计算机、人工智能及物联网等领域的深远影响。 ... [详细]
  • 科研单位信息系统中的DevOps实践与优化
    本文探讨了某科研单位通过引入云原生平台实现DevOps开发和运维一体化,显著提升了项目交付效率和产品质量。详细介绍了如何在实际项目中应用DevOps理念,解决了传统开发模式下的诸多痛点。 ... [详细]
  • 深入理解 .NET 中的中间件
    中间件是插入到应用程序请求处理管道中的组件,用于处理传入的HTTP请求和响应。它在ASP.NET Core中扮演着至关重要的角色,能够灵活地扩展和自定义应用程序的行为。 ... [详细]
  • 本文介绍如何在Spring Boot项目中集成Redis,并通过具体案例展示其配置和使用方法。包括添加依赖、配置连接信息、自定义序列化方式以及实现仓储接口。 ... [详细]
  • 本文深入探讨了SQL数据库中常见的面试问题,包括如何获取自增字段的当前值、防止SQL注入的方法、游标的作用与使用、索引的形式及其优缺点,以及事务和存储过程的概念。通过详细的解答和示例,帮助读者更好地理解和应对这些技术问题。 ... [详细]
  • 云函数与数据库API实现增删查改的对比
    本文将深入探讨使用云函数和数据库API实现数据操作(增删查改)的不同方法,通过详细的代码示例帮助读者更好地理解和掌握这些技术。文章不仅提供代码实现,还解释了每种方法的特点和适用场景。 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 本章详细介绍SP框架中的数据操作方法,包括数据查找、记录查询、新增、删除、更新、计数及字段增减等核心功能。通过具体示例和详细解析,帮助开发者更好地理解和使用这些方法。 ... [详细]
author-avatar
lailai
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有