Android 프로 세 스 통신 의 Messenger 와 AIDL 사용 에 대한 자세 한 설명

14412 단어 Androidmessengeraidl
1.머리말
언급 된 프로 세 스 간 통신(IPC:Inter-Process Communication)은 안 드 로 이 드 시스템 에서 하나의 프로 세 스 가 다른 프로 세 스 의 메모리 에 직접 접근 할 수 없 기 때문에 서로 다른 프로 세 스 간 에 통신 하 는 메커니즘 을 제공 해 야 합 니 다.안 드 로 이 드 는 공식 적 으로 AIDL(Android Interface Definition Language)을 출시 했 습 니 다.이것 은 Binder 메커니즘 을 바탕 으로 합 니 다.
위 에서 언급 한 구성 요소 와 Service 통신 방법 은 세 가지 가 있다.
  • IBinder 실현
  • Messenger
  • AIDL
  • 다음 두 가 지 는 프로 세 스 를 뛰 어 넘 을 수 있 는 통신 방식 으로 Binder 체 제 를 바탕 으로 하 는 통신 방식 입 니 다.
    2.사용 시기
    어떤 메커니즘 을 사용 할 것 인 지 를 확정 하기 전에 먼저 응용 장면 을 이해한다.Android 시스템 에서 구성 요소 와 서비스 통신 이 같은 프로 세 스 라면 첫 번 째 방식 을 사용 합 니 다.크로스 프로 세 스 통신 이 라면 두 번 째 와 세 번 째 를 사용 합 니 다.메 신 저 는 다 중 스 레 드 병행 요청 을 처리 할 수 없습니다.
    3.AIDL 사용
    AIDL 은 다 중 스 레 드 접근 요청 을 처리 할 수 있 기 때문에 AIDL 을 실현 하려 면 먼저 스 레 드 안전 을 확보 해 야 합 니 다.
  • .aidl 파일 을 만 들 고 인 터 페 이 스 를 정의 합 니 다
  • 코드 에서 인 터 페 이 스 를 실현 합 니 다.Android SDK 는 adl 파일 에 따라 인 터 페 이 스 를 생 성 합 니 다.인터페이스 내부 에 Stub 내부 추상 류 라 는 이름 이 있 습 니 다.이 종 류 는 Binder 류 를 계승 하고 adl 파일 에서 정 의 된 인 터 페 이 스 를 실현 하 는 데 사 용 됩 니 다.우 리 는 Stub 류 를 확대 하고 안의 추상 적 인 방법
  • 을 실현 해 야 합 니 다.
  • 서비스의 onBind()를 복사 하여 Stub 류 의 실현 을 되 돌려 줍 니 다.
  • 3.1.aidl 파일 만 들 기
    Android Studio 의 프로젝트 디 렉 터 리 에서 new->AIDL->AIDL FIle 을 거꾸로 누 르 면 에 이 드 파일 을 새로 만 들 수 있 습 니 다.컴 파 일 러 는 app(껍질 프로젝트)/src/main/디 렉 터 리 에 에 에 이 드 파일 을 자동 으로 새로 만 들 고 기본적으로 에 이 드 파일 이 있 는 디 렉 터 리 로 폴 더 를 새로 만 들 수 있 습 니 다.
    디 렉 터 리 구 조 는 다음 과 같 습 니 다.

    그림-1 aidl 파일 디 렉 터 리 구조
    수 동 으로 만 들 수도 있 고,aidl 인터페이스 가 정의 하 는 패키지 이름 도 프로젝트 패키지 이름과 다 를 수 있 습 니 다.aidl 파일 문법 은 자바 문법 과 비슷 합 니 다.aidl 이 정의 하 는 인터페이스 이름 은 파일 이름과 일치 해 야 하 며,사용자 정의 데이터 형식 을 전달 하 는 것 을 지원 하기 때문에 parcelable 인 터 페 이 스 를 실현 해 야 합 니 다.
    IRemoteService.aidl
    
    package com.demo.aidl;
    
    import com.demo.aidl.ParcelableData;
    
    interface IRemoteService {
     /**
     *        pid
     */
     int getPid();
     /**
     *         
     */
     String getServiceName();
     /**
     *            
     */
     void handleData(in ParcelableData data);
    }
    
    
    ParcelableData.aidl
    
    package com.demo.aidl;
    
    /**
     *         
     */
    parcelable ParcelableData;
    
    IRemoteServiceCallBack.aidl
    
    package com.demo.aidl;
    
    oneway interface IRemoteServiceCallBack {
     void valueChanged(int value);
    }
    
    aidl 파일 이 정의 하 는 인터페이스 지원 데이터 형식 은 다음 과 같 습 니 다.
  • 자바 의 8 가지 기본 데이터 형식(byte,int,short,long,float,double,char,boolean)
  • String
  • CharSequence
  • List,List 의 모든 요 소 는 adsl 파일 이 지원 하 는 데이터 형식 이 어야 합 니 다.예 를 들 어 List
  • Map,Map 의 모든 요 소 는 adl 파일 이 지원 하 는 데이터 형식 이 어야 합 니 다.
  • 기타 aidl 이 정의 한 인 터 페 이 스 는 import
  • 를 수 동 으로 추가 해 야 합 니 다.
  • 기타 aidl 파일 에 명 시 된 클래스 는 import 를 수 동 으로 추가 해 야 합 니 다
  • adl 파일 에서 정의 하 는 방법 으로 받 아들 이 는 매개 변 수 는 자바 의 기본 데이터 형식 과 adl 이 정의 하 는 인 터 페 이 스 를 제외 하고 다른 매개 변 수 는 데이터 의 방향 을 표시 해 야 합 니 다.in/out/inout,기본 데이터 형식 과 adl 이 정의 하 는 인 터 페 이 스 를 매개 변수 로 하고 기본 값 은 in 입 니 다.
  • in 은 입력 파 라 메 터 를 표시 하고 클 라 이언 트 는 서버 에 파 라 메 터 를 전달 합 니 다.
  • out 은 출력 파 라 메 터 를 표시 하고 클 라 이언 트 는 서버 에 파 라 메 터 를 전달 하여 채 운 다음 에 스스로 처리 합 니 다.
  • inout 레이 블 은 출력 파 라 메 터 를 입력 하고 해당 하 는 값 을 전송 하 며 되 돌려 받 습 니 다.
  • 키워드 oneway 는 사용자 가 해당 기능 을 요청 할 때 응답 을 기다 리 지 않 고 직접 호출 할 수 있 음 을 표시 합 니 다.차단 효과 가 아 닙 니 다.이 키 워드 는 인터페이스 나 성명 방법 을 설명 할 수 있 습 니 다.인터페이스 성명 에 oneway 키 워드 를 사용 하면 이 인터페이스 성명 의 모든 방법 은 oneway 방식 을 사용 합 니 다.
    aidl 파일 을 새로 만 든 후,rebuild 프로젝트 나 gradle assemble Debug(또는 gradle assemble Release)명령 으로 프로젝트 를 컴 파일 하여 구체 적 인 자바 코드 를 생 성 합 니 다.케이스 프로젝트/build/generated/aidl/디 렉 터 리 의 debug 또는 release 폴 더 에서 build 의 유형 에 따라 정 합 니 다.그림:

    그림-2 adil 생 성 코드 디 렉 터 리 그림
    AIDL 인터페이스 가 처음 발 표 된 후에 그 어떠한 수정 도 뒤로 호환성 을 유지 하고 클 라 이언 트 가 서비스 에 대한 사용 을 중단 하지 않도록 해 야 합 니 다.'aidl 파일 을 다른 응용 프로그램 에 복사 해 야 다른 응용 프로그램 이 서비스 에 접근 할 수 있 기 때문에 원본 인터페이스 에 대한 지원 을 유지 해 야 합 니 다.
    3.2 실현 인터페이스
    Android SDK 는.aidl 파일 에 따라 같은 이름 의 자바 파일 을 생 성 합 니 다.생 성 된 인터페이스 에는 Stub 의 추상 적 인 하위 클래스 가 있 습 니 다.이 클래스 는(implements)aidl 이 정의 하 는 인 터 페 이 스 를 실현 하 는 동시에 Binder 를 계승 합 니 다.
    구체 적 인 코드 는 다음 과 같다.
    
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
     @Override
     public int getPid() throws RemoteException {
     return Process.myPid();
     }
    
     @Override
     public String getServiceName() throws RemoteException {
     return RemoteService.this.getClass().getSimpleName();
     }
    
     @Override
     public void handleData(ParcelableData data) throws RemoteException {
     Toast.makeText(RemoteService.this, "num is " + data.num, Toast.LENGTH_SHORT).show();
     }
    
     @Override
     public void registerCallback(IRemoteServiceCallBack cb) throws RemoteException {
     if(cb != null) {
     mCallBacks.register(cb);
     }
     }
    
     @Override
     public void unregisterCallback(IRemoteServiceCallBack cb) throws RemoteException {
     if(cb != null) {
     mCallBacks.unregister(cb);
     }
     }
    };
    
    
    현재 mBinder 는 Stub 류 의 인 스 턴 스 이자 Binder 입 니 다.서비스 정의 RPC 서비스 로 onBind()방법의 반환 대상 인 스 턴 스 입 니 다.
    AIDL 인터페이스 구현 주의사항:
  • AIDL 은 다 중 스 레 드 병행 을 처리 할 수 있 기 때문에 실현 과정 에서 스 레 드 안전 을 확보 해 야 한다
  • 기본 적 인 상황 에서 RPC 호출 은 동기 화 되 지만 서버 에서 시간 이 걸 릴 수 있 습 니 다.클 라 이언 트 는 메 인 스 레 드 에서 서 비 스 를 호출 하지 않 는 것 이 좋 습 니 다
  • 서버 에서 발생 하 는 어떠한 이상 도 클 라 이언 트 에 게 되 돌아 오지 않 습 니 다.
  • 3.3 클 라 이언 트 에 인터페이스 노출
    인 터 페 이 스 를 실현 한 후 클 라 이언 트 에 게 인 터 페 이 스 를 노출 시 켜 클 라 이언 트 가 사용 할 수 있 도록 해 야 합 니 다.Stub 의 실례 화 대상 을 Service 의 onBind()방법의 반환 대상 으로 합 니 다.
    
    public class RemoteService extends Service {
     /**
     *     
     */
     private final RemoteCallbackList<IRemoteServiceCallBack> mCallBacks = new RemoteCallbackList<>();
     /**
     * aidl      
     */
     private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
     @Override
     public int getPid() throws RemoteException {
     return Process.myPid();
     }
    
     @Override
     public String getServiceName() throws RemoteException {
     return RemoteService.this.getClass().getSimpleName();
     }
    
     @Override
     public void handleData(ParcelableData data) throws RemoteException {
     Toast.makeText(RemoteService.this, "num is " + data.num, Toast.LENGTH_SHORT).show();
     }
    
     @Override
     public void registerCallback(IRemoteServiceCallBack cb) throws RemoteException {
     if(cb != null) {
     mCallBacks.register(cb);
     }
     }
    
     @Override
     public void unregisterCallback(IRemoteServiceCallBack cb) throws RemoteException {
     if(cb != null) {
     mCallBacks.unregister(cb);
     }
     }
     };
    
     @Nullable
     @Override
     public IBinder onBind(Intent intent) {
     return mBinder;
     }
    
     @Override
     public void onDestroy() {
     //       
     mCallBacks.kill();
     }
    }
    
    
    3.4 클 라 이언 트 호출
    서 비 스 는 제3자 응용 프로그램 에 제공 되 고 다른 응용 프로그램 은 인터페이스 클래스 가 있어 야 하 며 클 라 이언 트 에서 같은 aidl 파일 을 만들어 야 한다(직접 복사 할 수 있다).
    핵심 연결 원 격 서비스 코드:
    
    /**
     *     
     */
    private IRemoteService mService;
    
    private ServiceConnection mConnection = new ServiceConnection() {
     /**
     *          
     * 
     * @param className
     * @param service
     */
     public void onServiceConnected(ComponentName className, IBinder service) {
     mService = IRemoteService.Stub.asInterface(service);
     }
    
     /**
     *                  
     * 
     * @param className
     */
     public void onServiceDisconnected(ComponentName className) {
     mService = null;
     }
    };
    
    /**
     *     
     */
    private void doBindService() {
     isBound = true;
     Intent intent = new Intent(BindRemoteServiceActivity.this, RemoteService.class);
     bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    
    /**
     *     
     */
    private void doUnbindService() {
     if(isBound && mService != null) {
     isBound = false;
     try {
     mService.unregisterCallback(mCallback);
     } catch (RemoteException e) {
     e.printStackTrace();
     }
    
     unbindService(mConnection);
     }
    }
    
    /**
     *         
     */
    private void doSendMsg() {
     if(!isBound || mService == null) {
     return;
     }
     ParcelableData data = new ParcelableData(1);
     try {
     mService.handleData(data);
     } catch (RemoteException e) {
     e.printStackTrace();
     }
    }
    
    /**
     *            
     */
    private void doGetServiceInfo() {
     if(!isBound || mService == null) {
     return;
     }
     try {
     String info = mService.getServiceName();
    
     mInfoTv.setText("Service info :" + info);
     } catch (RemoteException e) {
     e.printStackTrace();
     }
    }
    
    
    상세 코드 가 길 게 붙 어 있 습 니 다.공사 주 소 를 붙 여 주세요!!
    4.Messenger 사용
    Messenger 의 사용 은 AIDL 에 비해 훨씬 편리 합 니 다.Messenger 는 Android 시스템 에서 자체 적 으로 가지 고 있 는 클래스 이기 때문에 서버 와 클 라 이언 트 는 AIDL 파일 을 만 들 필요 가 없습니다.
    Messenger 는 받 은 정 보 를 처리 하고 서버 에서 승객 과 Messenger 를 통 해 쌍방 통신 을 실현 하 는 Handler 를 가지 고 있 습 니 다.
    4.1 서버
    코드 인 스 턴 스:
    
    public class MessengerService extends Service {
    
     public static final int MSG_REGISTER_CLIENT = 0X001;
     public static final int MSG_UNREGISTER_CLIENT = 0X010;
     public static final int MSG_HANDLE = 0X100;
    
     private ArrayList<Messenger> mClients = new ArrayList<>();
    
     private final Messenger mMessenger = new Messenger(new IncomingHandler());
    
     @Nullable
     @Override
     public IBinder onBind(Intent intent) {
     return mMessenger.getBinder();
     }
    
     @Override
     public void onDestroy() {
     Toast.makeText(this, "Remote Service Destroy", Toast.LENGTH_SHORT).show();
     }
    
     private class IncomingHandler extends Handler {
     @Override
     public void handleMessage(Message msg) {
     switch (msg.what) {
     case MSG_REGISTER_CLIENT:
      mClients.add(msg.replyTo);
      break;
     case MSG_UNREGISTER_CLIENT:
      mClients.remove(msg.replyTo);
      break;
     case MSG_HANDLE:
      for (Messenger mClient : mClients) {
      try {
      mClient.send(Message.obtain(null, MSG_HANDLE, msg.arg1, 0));
      } catch (RemoteException e) {
      e.printStackTrace();
      mClients.remove(mClient);
      }
      }
      break;
     default:
      super.handleMessage(msg);
     }
     }
     };
    }
    
    
    4.2 클 라 이언 트
    핵심 코드:
    
    /**
     *        messenger
     */
    private Messenger mServiceWrapper;
    /**
     *             
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());
    
    private ServiceConnection mConnection = new ServiceConnection() {
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
     mServiceWrapper = new Messenger(service);
    
     mInfoTv.setText("Connected Service");
    
    
     try {
     //       
     Message msg = Message.obtain(null, MessengerService.MSG_REGISTER_CLIENT);
     msg.replyTo = mMessenger;
     mServiceWrapper.send(msg);
     } catch (RemoteException e) {
     e.printStackTrace();
     }
     }
    
     @Override
     public void onServiceDisconnected(ComponentName name) {
     mServiceWrapper = null;
     mInfoTv.setText("Disconnected");
     }
    };
    
    /**
     *     
     */
    private void doBindService() {
     if(!isBound) {
     bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
    
     isBound = true;
    
     mInfoTv.setText("Binding...");
     }
    }
    
    /**
     *          
     */
    private void doUnbindService() {
     if(isBound) {
     if(mServiceWrapper != null) {
     try {
     Message msg = Message.obtain(null, MessengerService.MSG_UNREGISTER_CLIENT);
     msg.replyTo = mMessenger;
     mServiceWrapper.send(msg);
     } catch (RemoteException e) {
     e.printStackTrace();
     }
     }
    
     unbindService(mConnection);
     isBound = false;
     mInfoTv.setText("Unbinding...");
     }
    }
    
    /**
     *         
     */
    private void doSendMsg() {
     if(mServiceWrapper != null) {
     try {
     Message msg = Message.obtain(null,
      MessengerService.MSG_HANDLE, this.hashCode(), 0);
     mServiceWrapper.send(msg);
     } catch (RemoteException e) {
     e.printStackTrace();
     }
     }
    }
    
    
    4.3 클 라 이언 트 가 메 시 지 를 보 냅 니 다.
    Messenger 를 사용 하여 서버 에 메 시 지 를 보 냅 니 다.Messenger.send(Message)방법 을 사용 합 니 다.이 방법 은 구체 적 으로 다음 과 같 습 니 다.
    
    public void send(Message message) throws RemoteException {
     mTarget.send(message);
    }
    방법 내부 에서 mTarget.send(Message)방법 을 호출 합 니 다.Messenger 에서 mTarget 은 구조 방법 에서 할당 되 고 두 개의 구조 함수 가 있 습 니 다.
    
    public Messenger(Handler target) {
     mTarget = target.getIMessenger();
    }
    
    public Messenger(IBinder target) {
     mTarget = IMessenger.Stub.asInterface(target);
    }
    
    첫 번 째 구조 함 수 는 이해 하기 쉽 습 니 다.mTarget.send(Message)는 실제 적 으로 Message 를 구조 함수 가 들 어 오 는 Handler 의 메시지 대기 열 에 넣 었 습 니 다.Demo 프로젝트 에서 서비스 측 이 승객 측 에 메 시 지 를 보 내 는 것 이 바로 이런 방법 입 니 다.
    두 번 째 구조 함 수 는 낯 이 익 지 않 습 니까?이것 이 바로 AIDL 정 의 를 얻 는 인터페이스 가 아 닙 니까?!!한 바퀴 돌 고 위 에 있 는 AIDL 로 돌 아 왔 습 니 다.클 라 이언 트 가 서버 에 메 시 지 를 보 내 는 것 은 사실상 AIDL 을 통 해 이 루어 졌 습 니 다.안 드 로 이 드 시스템 이 우 리 를 도와 포장 을 해 주 었 을 뿐 입 니 다.
    마지막 으로 데모 첨부:demo
    5.총화
    여기 서 Android 에서 자주 사용 하 는 프로 세 스 통신 이 모두 정리 되 었 습 니 다.끝 난 셈 입 니 다.꽃 을 뿌리 는 셈 입 니 다!!여러분 의 학습 에 도움 이 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.

    좋은 웹페이지 즐겨찾기