안 드 로 이 드 학습 소개 Binder 의 간단 한 사용

11871 단어 androidbinder
머리말
최근 회사 프로젝트 수요 로 원 격 스케줄 링 시작 클 라 이언 트 입력 법 입력 내용 이 필요 합 니 다.

이것 이 바로 대체적인 수요 절차 이다.이 편 은 먼저 원 격 과 서비스 제어 단 통신 을 말한다.먼저 제어 서버 에서 서 비 스 를 정의 하고 ServiceManager 에 가입 하여 서 비 스 를 추가 합 니 다.
여기 서 저 는 원 격 단 과 서비스 제어 단 통신(주로 C++를 통 해 ServiceManager 에 서 비 스 를 등록 합 니 다)을 설명 합 니 다.
우선 서비스 제어 단 이 ServiceManager 에 등 록 된 서비스 IBinder 대상 을 얻 고 자바 반사 체 제 를 통 해 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 파일 의 내용 과 같 지 않 습 니까?에 이 드 는 사실 안 드 로 이 드 가 직접 써 준 ibinder 코드 와 같 습 니 다.
이렇게 하면 우 리 는 서비스 제어 단 ibinder 대상 에 우리 자신의 ibinder 대상 을 기록 하고 과거 에 그 가 transact 방법 을 통 해 입력 법 프로그램 ibinder 대상 과 통신 하도록 전달 할 수 있다.

 //Parce            ,      ibinder   。
 public final void writeStrongBinder(IBinder val) {
 nativeWriteStrongBinder(mNativePtr, val);
 }
이렇게 하면 우 리 는 InputBinder 류 에서 되 돌아 오 는 데 이 터 를 처리 할 수 있다.
총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 면 댓 글 을 남 겨 주 십시오.

좋은 웹페이지 즐겨찾기