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

前言

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

这就是大致的需求流程,这篇首先讲远程与服务控制端通讯。首先控制服务端定义好一个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类中来处理返回的数据了。

总结

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

(0)

相关推荐

  • Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路

    在前面一篇文章浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路中,介绍了Service Manager是如何成为Binder机制的守护进程的.既然作为守护进程,Service Manager的职责当然就是为Server和Client服务了.那么,Server和Client如何获得Service Manager接口,进而享受它提供的服务呢?本文将简要分析Server和Client获得Service Manager的过程. 在阅读本文之前,希望读者

  • Android进程间通信(IPC)机制Binder简要介绍

    在Android系统中,每一个应用程序都是由一些Activity和Service组成的,这些Activity和Service有可能运行在同一个进程中,也有可能运行在不同的进程中.那么,不在同一个进程的Activity或者Service是如何通信的呢?这就是本文中要介绍的Binder进程间通信机制了. 我们知道,Android系统是基于Linux内核的,而Linux内核继承和兼容了丰富的Unix系统进程间通信(IPC)机制.有传统的管道(Pipe).信号(Signal)和跟踪(Trace),这三项

  • 理解Android系统Binder机制

    一.Binder机制概述 在Android开发中,很多时候我们需要用到进程间通信,所谓进程间通信,实现进程间通信的机制有很多种,比如说socket.pipe等,Android中进程间通信的方式主要有三种: 1.标准Linux Kernel IPC 接口: 2.标准D-BUS接口: 3.Binder接口. 其中,Binder机制是使用最且最被认可的,因为Binder机制有以下优点: 1.相对于其它IPC机制,Binder机制更加简洁和快速: 2.消耗的内存相对更少: 3.传统的IPC机制可能会增加

  • Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    在前面一篇文章Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路中,介绍了在Android系统中Binder进程间通信机制中的Server角色是如何获得Service Manager远程接口的,即defaultServiceManager函数的实现.Server获得了Service Manager远程接口之后,就要把自己的Service添加到Service Manager中去,然后把自己启动起来,等待Client的请求.

  • Android深入浅出之Binder机制

    Android深入浅出之Binder机制 一 说明 Android系统最常见也是初学者最难搞明白的就是Binder了,很多很多的Service就是通过Binder机制来和客户端通讯交互的.所以搞明白Binder的话,在很大程度上就能理解程序运行的流程. 我们这里将以MediaService的例子来分析Binder的使用: ServiceManager,这是Android OS的整个服务的管理程序 MediaService,这个程序里边注册了提供媒体播放的服务程序MediaPlayerServic

  • Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

    在前面几篇文章中,我们详细介绍了Android系统进程间通信机制Binder的原理,并且深入分析了系统提供的Binder运行库和驱动程序的源代码.细心的读者会发现,这几篇文章分析的Binder接口都是基于C/C++语言来实现的,但是我们在编写应用程序都是基于Java语言的,那么,我们如何使用Java语言来使用系统的Binder机制来进行进程间通信呢?这就是本文要介绍的Android系统应用程序框架层的用Java语言来实现的Binder接口了. 熟悉Android系统的读者,应该能想到应用程序框架

  • Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析

    在上一篇文章中,我们分析了Android系统进程间通信机制Binder中的Server在启动过程使用Service Manager的addService接口把自己添加到Service Manager守护过程中接受管理.在这一篇文章中,我们将深入到Binder驱动程序源代码去分析Client是如何通过Service Manager的getService接口中来获得Server远程接口的.Client只有获得了Server的远程接口之后,才能进一步调用Server提供的服务. 这里,我们仍然是通过A

  • 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路

    上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划简要介绍了Android系统进程间通信机制Binder的总体架构,它由Client.Server.Service Manager和驱动程序Binder四个组件构成.本文着重介绍组件Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能. 既然Service Manager组件是用来管理Server并且向Client提

  • android IPC之binder通信机制

    Binder通信机制说来简单,但是在使用的过程的遇到了一些问题,最后终于解决了,在这总结一下,一并分享给大家: 1.要使用Binder通信,首先要定义接口,然后实现服务端BnInterface***和客户端BpInterface***,说到底一个是把参数解包,一个是把参数打包. 2.服务端要能够接收Binder调用请求,要具备两个条件:一个是实现Bn接口,另一个是调用IPCProcess()->self->startThreadPool() IPCThread()->Self->j

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

    前言 最近因为公司项目需求,需要远程调度启动客户端输入法输入内容. 这就是大致的需求流程,这篇首先讲远程与服务控制端通讯.首先控制服务端定义好一个Service,且在ServiceManager注册添加服务. 在这里我讲解远程端与服务控制端通讯(主要通过C++往ServiceManager注册服务). 首先我们得获取到服务控制端注册在ServiceManager的服务IBinder对象,通过Java反射机制获得Ibinder接口对象. public static IBinder getRemot

  • Android学习之Broadcast的简单使用

    本文实例为大家分享了Android学习之Broadcast的使用方法,供大家参考,具体内容如下 实现开机启动提示网络的广播 package com.example.luobo.broadcasttest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; impor

  • Android学习之Intent中显示意图和隐式意图的用法实例分析

    本文实例讲述了Android学习之Intent中显示意图和隐式意图的用法.分享给大家供大家参考,具体如下: Intent(意图)主要是解决Android应用的各项组件之间的通讯. Intent负责对应用中一次操作的动作.动作涉及数据.附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用. 因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦. 例如,在一个联系人

  • Android学习项目之简易版微信为例(二)

    1 概述 从这篇开始,正式进入简易版微信的开发.深入学习前,想谈谈个人对Android程序开发一些理解,不一定正确,只是自己的一点想法.Android程序开发不像我们在大学时候写C控制台程序那样,需要从main开始写代码逻辑,大部分逻辑控制代码都由自己来实现.事实上,Android已经为我们提供了一个程序运行的框架,我们只需要往框架中填入我们所需的内容即可,这里的内容主要是:四大组件--Activity.Service.ContentProvider.BroadCast.在这四大组件中,可以实现

  • Android学习项目之简易版微信为例(一)

    这是"Android学习之路"系列文章的开篇,可能会让大家有些失望--这篇文章中我们不介绍简易版微信的实现(不过不是标题党哦,我会在后续文章中一步步实现这个应用程序的).这里主要是和广大朋友们聊聊一个非Java程序员对Android操作系统的理解以及一个Android工程的目录结构,为进一步学习做准备. 1 缘起 智能手机的出现与普及为人们的生活.工作带来了极大的便利,我们可以用手机随时随地.随心所欲地购物.玩游戏.聊天.听音乐等等.一个个精心设计.体验良好的移动客户端应用,让用户们爱

  • Android开发之android_gps定位服务简单实现

    前言 gps定位服务的学习是这段时间gps课程的学习内容,之前老师一直在将概念,今天终于是实践课(其实就是给了一个案例,让自己照着敲).不过在照着案列敲了两遍之后,发现老师的案例是在是太老了,并且直接照着案例敲,也无法理解其中很多类,方法的作用. 于是自己在网上查看了其他实现的方法,并尝试敲案列,期间的挫折一言难尽. (网上找的案例也并不信息,使得我在给予权限,和权限检查方面一直报错,因为我使用的是最新的As和java11,在经过数遍从基础理解到实例编写的过程和不知多少遍google之后,终于完

  • Android 图文详解Binder进程通信底层原理

    之前了解到进程与多进程,涉及多进程不可避免的遇到了进程间通信,说到进程间通信,Binder 成了一道绕不过的坎.接下来咱们逐一了解.

  • Android学习之Span的使用方法详解

    目录 Span集合 段落类Span 其他Span 展示效果 小试牛刀 小结 Span集合 段落类Span BulletSpan 为段落开头增加项目符号并支持大小.颜色.弧度 span.append(SpannableString("BulletSpan").also { it.setSpan(BulletSpan(40, Color.RED), 0, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) }) QuoteSpan 为段落开头增加垂直引用线 sp

  • Android ViewDragHelper使用介绍

    ViewDragHelper是support.v4下提供的用于处理拖拽滑动的辅助类,查看Android的DrawerLayout源码,可以发现,它内部就是使用了该辅助类来处理滑动事件的. public DrawerLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setDescendantFocusability(ViewGroup.FOCUS_AFTER_DE

  • Android学习笔记(二)App工程文件分析

    App工程文件分析 关于如何创建一个最简单的Android App请参照链接: < Android学习笔记(一)环境安装及第一个hello world > http://www.jb51.net/article/52593.htm 创建完的工程文件如下图所示,本文对一些主要的文件进行分析. src文件分析 App源文件如图: 打开源文件 MainActivity.java 可看到如下代码: 源码主要功能如下: App源文件目录 package com.example.firstapp; 导入A

随机推荐