서비스 숙련도


이 섹션 소개:

이 섹션에서는 서비스 구성요소를 계속해서 학습합니다. 이 섹션에서는 Android의 AIDL 크로스 프로세스 통신에 대해 알아봅니다. 개념은 소스 코드 수준까지 깊이 들어가지 않습니다. 단지 그것이 무엇인지 알고 사용하는 방법만 알면 됩니다! 이 섹션을 시작하세요~ 이 섹션은 공식 문서인 Binder


1. 바인더 메커니즘에 대한 첫 소개


1) IBinder와 Binder가 무엇인가요?

공식 문서 내용을 살펴보겠습니다.

1.png

중국어 번역:

IBinder는 원격 개체의 기본 인터페이스이자 고성능을 위해 설계된 경량 원격 호출 메커니즘의 핵심 부분입니다. 하지만 그는 원격 호출뿐만 아니라 진행 중인 호출에도 적용됩니다. 이 인터페이스는 원격 개체와의 상호 작용을 위한 프로토콜을 정의합니다. 하지만 직접 구현하지는 마세요. 대신 이 인터페이스는 상속(확장)Binder입니다.

IBinder의 주요 API는 transact()이고 해당 API는 Binder.onTransact()입니다. 전자를 통해 다음을 수행할 수 있습니다. 원격 개체가 들어오는 호출에 응답할 수 있도록 원격 IBinder 개체에 호출을 보냅니다. IBinder의 API는 상대방의 Binder.onTransact() 메서드가 호출될 때까지 transact()와 같이 모두 Syncronous 실행됩니다. 나중에야 그는 돌아왔다. 이는 의심할 여지 없이 프로세스 내에서 호출이 발생하는 경우이고 프로세스 간 호출이 IPC의 도움으로 동일한 효과를 얻을 때 발생합니다.

transact()를 통해 전송되는 데이터는 Parcel입니다. Parcel은 데이터 외에 일반 버퍼도 포함합니다. 내용을 설명하는 일부 메타데이터입니다. 메타데이터는 버퍼가 한 프로세스에서 다른 프로세스로 이동할 수 있도록 IBinder 개체에 대한 참조를 관리하는 데 사용됩니다. 다른 프로세스로 이동할 때 이러한 참조를 저장하십시오. 이렇게 하면 IBinder가 Parcel에 기록되어 다른 프로세스로 전송될 때 다른 프로세스가 동일한 IBinder에 대한 참조를 원래 프로세스로 다시 보내면 원래 프로세스는 보낸 참조를 받을 수 있습니다. 해당 IBinder에 대한 참조입니다. 이 메커니즘을 통해 IBinder 및 Binder는 고유 식별자와 같은 프로세스 전반에 걸쳐 관리될 수 있습니다.

시스템은 대화형 스레드를 저장하기 위해 각 프로세스마다 스레드 풀을 유지 관리합니다. 이러한 대화형 스레드는 다른 프로세스에서 전송된 모든 IPC를 디스패치하는 데 사용됩니다. 부르다. 예를 들어 IPC가 프로세스 A에서 프로세스 B로 전송되면 A의 호출 스레드(스레드 풀에 있어서는 안 됨)가 차단됩니다. transact()에서. 프로세스 B의 대화형 스레드 풀에 있는 스레드는 이 호출을 수신하고 Binder.onTransact()를 호출하고 완료 후 결과로 Parcel을 반환합니다. 그런 다음 프로세스 A의 대기 스레드 반환된 Parcel을 받은 후 실행을 계속할 수 있습니다. 실제로 다른 프로세스는 현재 프로세스의 스레드처럼 보이지만, 그러나 현재 프로세스에서는 생성되지 않습니다.

바인더 메커니즘은 프로세스 간 재귀 호출도 지원합니다. 예를 들어 프로세스 A는 프로세스 B에 대해 자체 IBinder의 transact() 호출을 실행합니다. 바인더 및 프로세스 B는 Binder.onTransact()에서 transact()를 사용하여 프로세스 A에 대한 호출을 시작한 다음 프로세스 A를 호출합니다. 발행된 호출이 반환되기를 기다리는 동안 Binder.onTransact()를 사용하여 프로세스 B의 transact()에도 응답합니다. 간단히 말해서 Binder의 결과는 Cross-Process 호출과 Intra-Process 호출 간에 차이가 없다고 느끼는 것입니다.

원격 객체를 조작할 때 유효한지 확인해야 하는 경우가 많습니다. 사용할 수 있는 세 가지 방법이 있습니다.

  • 1 IBinder가 있는 프로세스가 존재하지 않으면 transact() 메서드는 RemoteException을 발생시킵니다.
  • 2 대상 프로세스가 존재하지 않는 경우 pingBinder()를 호출하면 false가 반환됩니다.
  • 3 linkToDeath() 메소드를 사용하여 IBinder.DeathRecipient를 IBinder에 등록할 수 있습니다. IBinder가 나타내는 프로세스가 종료될 때 호출됩니다.

PS: 중국어 번역은 다음에서 발췌되었습니다. Android 개발: IBinder란 무엇입니까?

알겠습니다. 이 목록을 읽으면 혼란스러울 수 있습니다. 다음은 간단한 요약입니다.

IBinder는 Android입니다. 프로세스 간 통신을 위한 인터페이스를 제공하지만 일반적으로 이 인터페이스를 직접 구현하지 않고 대신 Binder 클래스를 상속하여 프로세스 간 통신을 구현합니다. 안드로이드에서 IPC(Inter-Process Communication)를 구현하는 방법입니다!

2) 바인더 메커니즘에 대한 간략한 분석

2.png

Android의 바인더 메커니즘은 일련의 시스템 구성 요소로 구성됩니다.
클라이언트, 서버, 서비스 관리자 및 바인더 드라이버

대략적인 호출 프로세스는 다음과 같습니다. Service Manager는 더 복잡하므로 여기서는 자세히 다루지 않겠습니다!

프로세스 분석:

->

클라이언트가 프록시 인터페이스에서 메소드를 호출하면 프록시 인터페이스의 메소드는 클라이언트가 전달한 매개변수를 Parcel 객체로 패키징합니다. 그런 다음 프록시 인터페이스는 Parcel 개체를 커널의 바인더 드라이버로 보냅니다.
-> 그런 다음 서버는 바인더 드라이버의 요청 데이터를 읽습니다. 자체적으로 전송되면 Parcel 개체의 압축을 풉니다. PS: 프록시 인터페이스에 정의된 메서드는 서버에 일대일로 정의된 메서드에 해당합니다. 또한 전체 호출 프로세스는 동기식입니다. 즉, 서버가 처리하는 동안 클라이언트가 차단(잠금)됩니다! 여기에 언급된 프록시 인터페이스의 정의는 나중에 이야기할
AIDL(Android 인터페이스 설명 언어)입니다!


3) Android가 프로세스 간 통신을 달성하기 위해 바인더 메커니즘을 사용하는 이유는 무엇입니까?

  1. 신뢰성: 모바일 장치에서 클라이언트-서버 기반 통신은 일반적으로 인터넷과 장치 간의 내부 통신을 달성하는 데 사용됩니다. 현재 Linux는 전통적인 파이프, System V IPC, 즉 메시지 큐/공유 메모리/세마포어를 포함한 IPC를 지원하며, 소켓만 클라이언트-서버 통신 방식을 지원합니다. Android 시스템은 개발자에게 프로세스 간 통신, 미디어 재생, 센서 및 무선 전송을 위한 풍부한 기능 인터페이스를 제공합니다. 이러한 기능은 다른 서버에서 관리됩니다. 개발자는 이 서비스를 사용하기 위해 자체 애플리케이션의 클라이언트와 서버 간의 통신 설정에만 관심이 있습니다. 클라이언트-서버 통신을 구현하기 위해 하단에 일련의 프로토콜을 설정한다면 시스템의 복잡성이 증가할 것이라는 점은 의심할 여지가 없습니다. 제한된 자원을 가진 휴대폰에서 이러한 복잡한 환경을 구현하는 경우 안정성을 보장하기 어렵습니다.
  2. 전송 성능: 소켓은 주로 네트워크를 통한 프로세스 간 통신과 로컬 시스템의 프로세스 간 통신에 사용되지만 전송 효율성이 낮고 오버헤드가 높습니다. 메시지 큐와 파이프라인은 저장 및 전달 방식을 채택합니다. 즉, 먼저 데이터를 송신자 버퍼 영역에서 커널이 연 버퍼 영역으로 복사한 다음 커널 버퍼 영역에서 수신자 버퍼 영역으로 복사합니다. 이 프로세스에는 최소한 두 개의 복사본이 포함됩니다. 공유 메모리에는 복사가 필요하지 않지만 제어가 복잡합니다. 다양한 IPC 방식의 데이터 복사본 수를 비교해보세요. 공유 메모리: 0회. 바인더: 1회. 소켓/파이프라인/메시지 큐: 2회.
  3. 보안: Android는 개방형 플랫폼이므로 애플리케이션의 보안을 보장하는 것이 중요합니다. Android는 설치된 각 애플리케이션에 UID/PID를 할당하며, 프로세스의 UID를 사용하여 프로세스 ID를 식별할 수 있습니다. 전통적으로 사용자는 데이터 패킷에 UID/PID만 입력할 수 있었기 때문에 이는 신뢰할 수 없고 악성 프로그램에 의해 쉽게 악용되었습니다. 대신에 신뢰할 수 있는 UID를 추가하기 위해 커널이 필요합니다. 따라서 신뢰성, 전송 및 보안을 위해. Android는 프로세스 간 통신의 새로운 방법을 확립했습니다. ——다음에서 발췌: Android의 바인더 메커니즘에 대한 간략한 이해

물론 주니어 개발자로서 우리는 바인더 메커니즘이 우리에게 가져오는 가장 직접적인 이점에 대해 신경 쓰지 않습니다. 걱정할 필요가 없습니다. 하위 레이어를 어떻게 구현하나요? AIDL의 규칙을 따르고 인터페이스 파일을 맞춤설정한 다음 인터페이스에서 메서드를 호출하여 두 프로세스 간의 통신을 완료하면 됩니다!


2. AIDL 사용에 대한 자세한 설명


1) AIDL이란 무엇인가요?

안녕하세요. 앞서 IPC라는 용어에 대해 이야기했는데 전체 이름은 프로세스 간 통신입니다. Android 시스템에서는 각 애플리케이션이 자체 프로세스에서 실행되기 때문에 일반적으로 프로세스 간에 직접 데이터를 교환할 수 없습니다. 크로스 프로세스를 달성하기 위해 Android는 위에서 언급한 바인더 메커니즘을 제공하며 이 메커니즘에서 사용되는 인터페이스 언어는 AIDL(Android 인터페이스 정의 언어)입니다. 구문은 매우 간단하지만 이 인터페이스 언어는 그렇지 않습니다. 진짜 프로그래밍 언어는 두 프로세스 간의 통신 인터페이스를 정의합니다! 통신 프로토콜을 준수하는 Java 코드는 Android SDK에 의해 생성됩니다. aidl.exe 도구는 platform-tools 디렉터리에 생성되고 해당 인터페이스 파일은 :gen 디렉터리(일반적으로 Xxx.java! 인터페이스)에 생성됩니다. 인터페이스에는 IBinder 인터페이스와 사용자 정의 통신 인터페이스를 구현하는 Stub의 내부 클래스가 포함되어 있습니다. 이 클래스는 원격 서비스의 콜백 클래스로 사용됩니다. IBinder 인터페이스를 구현하므로 서비스 onBind() 메서드의 반환 값으로 사용할 수 있습니다!


2) AIDL은 두 프로세스 간의 간단한 통신을 구현합니다.

AIDL 인터페이스 파일 작성을 시작하기 전에 AIDL 작성에 대한 몇 가지 참고 사항을 이해해야 합니다.

AIDL 참고 사항:

  • 인터페이스 명사 AIDL 파일 이름과 동일해야 합니다
  • 인터페이스와 메소드 앞에 접근 권한 수정자를 추가하지 마세요: public, private, protected 등. 그리고 static final을 사용할 수 없습니다!
  • AIDL에서 기본적으로 지원하는 유형 Java 기본 유형, String , List, Map, CharSequence을 포함하며 기타 모든 유형은 사용자 정의 유형을 매개변수 또는 반환 값으로 사용하려면 import 문이 필요합니다. 사용자 정의 유형은 Parcelable 인터페이스를 구현해야 합니다. 자세한 내용은 나중에 복잡한 데이터 유형 전달을 참조하세요
  • AIDL에서 생성된 사용자 정의 유형 및 기타 인터페이스 유형은 클래스 및 정의에서도 aiml 설명 파일에서 명시적으로 가져와야 합니다. 동일한 패키지의 패키지.

또한,aidl을 작성하는 데 사용하는 컴파일러가 Eclipse인 경우 다음을 참고하세요. 새 파일을 직접 생성하지 마세요. 이 경우 파일을 열 수 없으므로 코드를 작성할 수 없습니다!
① 직접 새 txt 파일을 생성하고, 작성 후 .aidl 형식으로 저장한 후 해당 경로에 복사합니다.
②aidl은 인터페이스와 유사하므로 직접 새 인터페이스이므로 내용을 작성한 후 해당 디렉터리로 이동합니다. 해당 Java 파일이 있는 위치를 확인하고 수정하세요. File suffix name;

Android Studio를 사용하는 경우 Eclipse와 달리 Eclipse와 같은 AIDL 파일을 생성하면 다음과 같은 내용이 나옵니다. 해당 XXX.java 파일은 컴파일 및 생성되지 않습니다. AS 아래에 AIDL을 생성하려면 기본 디렉터리에 새aidl 폴더를 만든 다음 정의해야 합니다. aidl 패키지와 이름이 같은 패키지의 경우 마지막으로aidl 파일을 만든 다음 ctrl + f9를 눌러 다시 컴파일하면 끝입니다!

3.png

위 두 가지를 성공적으로 컴파일한 결과는 다음과 같습니다. 각각 해당 디렉터리에서 해당 AIDL 파일을 찾을 수 있습니다.

4.png5.png


1단계: AIDL 파일 생성 :

IPerson.aidl

package com.jay.aidl;

interface IPerson {
String queryPerson(int num);
}

IPerson.java를 열고 내부 코드를 살펴보겠습니다.

IPerson.java

/*
 * 이 파일은 자동 생성됩니다.  수정하지 마세요.
 * 원본 파일: C:\Code\ASCode\AIDLServer\app\src\main\aidl\com\jay\aidl\IPerson.aidl
 */
package com.jay.aidl;
public 인터페이스 IPerson 확장 android.os.IInterface
{
/** 로컬측 IPC 구현 스텁 클래스입니다. */
public 정적 추상 클래스 스텁 확장 android.os.Binder 구현 com.jay.aidl.IPerson
{
private 정적 최종 java.lang.String 설명자 = "com.jay.aidl.IPerson";
/** 인터페이스에 연결하여 스텁을 구성합니다. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * IBinder 개체를 com.jay.aidl.IPerson 인터페이스로 캐스팅하고,
* 필요한 경우 프록시를 생성합니다.
 */
public static com.jay.aidl.IPerson asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jay.aidl.IPerson))) {
return ((com.jay.aidl.IPerson)iin);
}
return new com.jay.aidl .IPerson.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android .os.Parcel 응답, int 플래그)는 android.os.RemoteException
{
switch(코드)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_queryPerson을 발생시킵니다.
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.queryPerson(_arg0);
reply.writeNoException();
reply.writeString(_result) ;
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private 정적 클래스 프록시는 com.jay.aidl.IPerson
{
private android.os.IBinder mRemote를 구현합니다.
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.lang.String queryPerson(int num) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken (설명자);
_data.writeInt(num);
mRemote.transact(Stub.TRANSACTION_queryPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
마지막으로 {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_queryPerson=(android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String queryPerson(int num)은 android.os.RemoteException을 발생시킵니다.
}

여기서 우리가 정의한 인터페이스의 **asInterface(IBinder)** 및 **queryPerson()** 메소드에만 관심이 있습니다!

이 메소드는 IBinder 유형 객체를 IPerson 유형으로 변환하고 IPerson을 생성합니다. 필요한 경우 유형을 입력하십시오. 프록시 객체가 결과를 반환합니다!

나머지는 읽을 필요 없이 그냥 건너뛰고 다음 단계로 진행하세요.

2단계: ** 서비스 클래스를 사용자 정의하고 다음 작업을 완료합니다.

1) 서비스 클래스를 상속하고 또한 PersonQueryBinder 클래스를 사용자 정의하여 IPerson.Stub 클래스를 상속합니다 그게 전부입니다 IPerson 인터페이스를 구현하고 IBinder 인터페이스

2) 사용자 정의 Stub 클래스를 인스턴스화하고 서비스의 onBind 메소드를 재정의하여 바인더 객체를 반환합니다!

AIDLService.java

package com.jay.aidlserver;

import android.app.Service ;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.jay.aidl.IPerson.Stub;

/**
 * 2015년 8월 18일 Jay가 0018년에 작성함.
 */
public 클래스 AIDLService는 서비스를 확장합니다. {

private IBinderinder = new PersonQueryBinder();
private String[] names = {"B神","艹神","九神","J神"," Xiang Shen "} r; Private String Query (int Num) {
if (num & gt; 0 && num & lt; 6) {
반환 이름 [num-1] Overrideb Public iBinder Onbind (Intent Intent) {
Return Null;}}

Private Final Class PersonQuery EXTENDS Stub {
@v Public String Query Person (int Num) Throws RemoteException {
Return Query (num)
}}}
}

3단계:에서 AndroidManifest.xml文件中注册Service

<service android:name=".AIDLService">
           <intent-filter>
              <action android:name="and roid.intent. action.AIDLService" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
        </service>

여기에서는 활동 인터페이스를 제공하지 않지만, 애플리케이션에서 제공하는 서비스를 다른 앱에서 호출할 수 있습니다!


2. 클라이언트가
서버에서 aiml 파일을 직접 복사한 후 MainActivity에서 직접 완성하는데, 이는 로컬 서비스를 바인딩하는 작업과 다소 유사합니다.
1) PersonConnection 클래스 사용자 정의
ServiceConnection 인터페이스 구현 2) PersonConnection 객체를 매개변수로 사용하여 바인딩 서비스를 호출하여 원격 서비스를 바인딩합니다.

bindService(service,conn,BIND_AUTO_CREATE); ps: 세 번째 매개변수는 서비스가 시작되지 않은 경우 자동 생성
3)은 로컬 서비스와 다릅니다.
원격 서비스에 바인딩된 ServiceConnection은 서비스의 onBind() 메서드에서 반환된 IBinder 객체를 직접 얻을 수 없습니다. 프록시만 반환할 수 있습니다. onBind()
메서드에서 반환된 object 다음 처리를 수행해야 합니다. iPerson = IPerson.Stub.asInterface(service);
그런 다음 초기화 및 버튼 이벤트 등을 완료합니다. 특정 코드

MainActivity.java

package com.jay.aidlclient;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget. TextView;

import com.jay.aidl.IPerson;

public 클래스 MainActivity 확장 AppCompatActivity 구현 View.OnClickListener{

    private EditText edit_num;
    private Button btn_query;
    private TextView txt_ 이름;
    private IPerson iPerson;
    private PersonConnection conn = new PersonConnection();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bin dViews();
        //绑定远程Service ㅋㅋㅋ tn_query.setOnClickListener(this) ;
    }

    private void bindViews() {
        edit_num = (EditText) findViewById(R.id.edit_num);
       btn_query = (버튼) findViewById(R.id.btn_query);
        txt_name = (TextView) findViewById(R .id.txt_name);
    }

    @Override
    public void onClick(View v) {
        String number = edit_num.getText().toString();
        int num = Integer.valueOf(number);
        시도해보세요 {
            txt_name.setText(iPerson.queryPerson(num));
        } catch (RemoteException e) {
           e.printStackTrace();
        }
        edit_num.setText("");
    }

    비공개 최종 클래스 PersonConnection은 ServiceConnection을 구현합니다.{
public void onServiceConnected(ComponentName 이름, IBinder 서비스) {
           iPerson = IPerson.Stub.asInterface(service);
        }
        public void onServiceDisconnected(ComponentName 이름) {
            iPerson = nu ll;
        }
    }
}

다음으로 AIDLServivce를 먼저 시작한 후 AIDLClient를 시작하고 쿼리 일련번호를 입력하면 해당 이름을 얻을 수 있습니다! 물론 AIDLClient를 직접 시작할 수도 있으며 동일한 효과를 얻을 수 있습니다.

렌더링은 다음과 같습니다.

6.gif


3) 복잡한 데이터를 전달하는 AIDL 서비스

위에서 예를 들어 int 유형 매개변수만 전달한 다음 서버는 String 유형 매개변수를 반환합니다. 기본적인 요구사항이지만 실제 개발에서는 복잡한 데이터 유형 전달을 고려해야 할 수도 있습니다! 다음으로 배워보자 복잡한 데이터 유형의 데이터를 서버에 전달하는 방법! 시작하기 전에 먼저 Parcelable 인터페이스를 이해해 봅시다!

——Parcelable 인터페이스 소개:

직렬화를 사용해 본 사람이라면 기본적으로 이 인터페이스 외에도 직렬화에 사용되는 또 다른 직렬화 가능 인터페이스가 있다고 생각합니다. Parcelable이 더 가볍고 빠르다는 것뿐입니다! 하지만 쓰기에는 좀 번거롭긴 하지만, 물론 as로 사용하면 사용이 가능합니다. 다음과 같은 직렬화를 완료하는 플러그인: Android Parcelable Code Generator물론 여기서도 이 인터페이스를 구현하는 방법을 알려드립니다~

먼저 구현해야 할 사항: writeToParcelreadFromPacel 메소드 write 메소드는 소포에 객체를 쓰는 반면, read 메소드는 소포에서 객체를 읽습니다. 속성을 쓰는 순서는 읽는 순서와 동일해야 합니다.

그런 다음 이 클래스에 CREATOR라는 static final 속성을 추가해야 합니다. 속성을 변경하려면 android.os.Parcelable.Creatorinterface

을 구현해야 합니다. 그런 다음 인터페이스에 두 가지 메서드를 작성해야 합니다.createFromParcel(Parcel 소스) 메서드: JavaBean 생성 기능을 구현합니다. source newArray(int size): T 유형과 길이 크기의 배열을 만듭니다. 간단한 return new T[size]만 있습니다(T는 Person 클래스입니다)

마지막으로, explainContents() : 이게 무슨 용도인지는 모르겠지만 그냥 0을 돌려주세요! 무시하세요

- 또한 , 비원시 유형 (StringCharSequence 제외)에는 모두 방향 표시기가 필요합니다. 방향 표시기에는 in, out, 및 inout이 포함됩니다. in은 클라이언트에 의해 설정됨을 의미하고, out은 서버에 의해 설정됨을 의미하며, inout은 값이 클라이언트와 서버 모두에 의해 설정됨을 의미합니다.


좋아, 코드를 작성해 보자(여기 AS의 사용자 정의 유형에 문제가 있는데 아직 해결되지 않았으므로 Eclipse를 사용하겠습니다~):

코드 예:

두 가지 객체 유형 사용자 정의 : Person With Salary, 원격 서비스 호출을 위한 파라미터로 Person 을, 반환값으로 Salary 를 사용합니다! 그런 다음 가장 먼저 해야 할 일은 Person 및 Salary 클래스를 생성하고 Parcelable 인터페이스도 구현해야 하는 것입니다

1.——서버 측

1단계: Parcelable 인터페이스를 구현해야 하기 때문에 Person.aidl 및 Salary.aidl 파일을 생성하므로 다음 명령문만 사용하세요.

Person.aidl:     parcelable Person; 
Salary.aidl:     parcelable Salary;
2단계: Person 생성 Class 및 Salary 클래스는 각각 Parcelable 인터페이스를 구현하고 해당 메소드를 다시 작성해야 합니다.


PS: 나중에 Person 객체를 기반으로 Map 컬렉션의 데이터를 얻으므로 Person.java에서 해시 코드와 같음을 다시 작성합니다. Salary 클래스에는 필요하지 않습니다!

Person.java:

package com.jay.example.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * 2015년 8월 18일 Jay가 0018년에 작성함.
 */
public 클래스 Person은 Parcelable을 구현합니다.{

비공개 정수 ID ; V 비공개 문자열 이름;

공개 사람() {}

공개 사람(정수 ID, 문자열 이름) {
this.id =
This.name = name; }

public void setid (integer id) {
this.id = id
}

public void setname (문자열 이름) {
this.name = name;
pub

pub LIC String Getname ( ) {
        return 이름;
                                                                                                       | ) {
//객체에 포함된 데이터를 Parcel
에 씁니다. dest.writeInt(id );
dest.writeString(name);
}

//CREATOR라는 정적 최종 속성을 제공해야 합니다. 이 속성은
//android.os.Parcelable.Creator<T>interface
public static final Parcelable.Creator<를 구현해야 합니다. ;Person> CREATOR = new Parcelable.Creator() {
                                             Public Person CreatefromParcel(소포 소스) {
새 사람 반환(Source.readint(), Source.readString())) {t 새 사람 반환 [크기];
                                           ~                                 //             )
{
final int prime = 31;
int result = 1;
r esult = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{ and >         ;
        기타 사람 = (사람) obj;
                                                                           아웃 아웃 아웃 아웃 [ 아웃 아웃 ( 아웃 밖으로 바로 당장 나가세요!         }
          else if (!name.equals(other.name))
                                                          반품                                                 반환  거짓;          <pre><p><strong>Salary.java</strong>~Photo葫芦画瓢</p>

<pre>
package com.jay.example.aidl; 

import android.os.Parcel;
import android.os.Parcelable;

/**
 * 2015년 8월 18일 Jay가 0018년에 작성함.
 */
public class Salary는 Parcelable을 구현하고 {

    private String type;
    private Integer salary;

    공공 급여( ) {}}

공개 급여 (문자열 유형, 정수 급여) {

this.type = type;
this.salary = 급여;
}}

공개 문자열 gettype () {
반환 유형;
}

공개 정수 getSalary() {
        급여 반환;
    }

    public void setType(String type) {
       this.type = type;
    }

    public void setSalary(Integer salary) {
        this.salary = salary;
    }

@Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(type);
        dest.writeInt(salary);
    }

    공개 정적 최종 Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() {
        //从Parcel中读取数据,返回Person对象
        @Override
        공개 급여 createFromParcel(Parcel 소스) {
            새 급여(source.readString(), source.return)를 반환합니다. readInt());
        }

@Override
        public Salary[] newArray(int size) {
           return new Salary[size]; "Job:" + type + " Salary: " + 급여;
}
}



3단계
: ISalary 만들기 .aidl 파일을 만들고 그 안에 급여 정보를 얻는 간단한 방법을 작성합니다.

package com.jay.example.aidl; import com.jay .example.aidl.Salary;

import com.jay.example.aidl. Person;
interface ISalary
{
//Person 객체를 수신 매개변수로 정의
//인터페이스에서 메소드를 정의할 때 새 매개변수의 전송 모드를 공식화해야 하며 여기에 전달되므로 다음이 있습니다. 그 앞에
Salary getMsg(in Person owner)
}

ps:여기에서 사용자 정의 데이터 유형을 사용하는 경우 이를 가져와야 한다는 점을 기억할 수 있습니다! ! ! 기억하다! ! !

4단계: 핵심 서비스 작성: ISalary 및 IBinder 인터페이스를 구현하기 위해 Stub을 상속하는 SalaryBinder 클래스를 정의하고 정보를 저장하기 위한 Map 컬렉션을 정의하세요. onBind 메소드를 재설정하고 SalaryBinder 클래스의 객체 인스턴스를 반환하세요!

AidlService.java

패키지 com.jay.example.aidl_complexservice;
import java.util.Map;
import com.jay.example.aidl.ISalary.Stub; aidl.Person;import com.jay.example.aidl.Salary;
import android.content.Intent;
import android.os.RemoteException; public class AidlService extends Service {

private SalaryBinder SalaryBinder;
private static Map<Person,Salary> ss = new HashMap<Person, Salary>()
//정적 코드 블록에서 초기화된 Map 컬렉션 물론 생성자 메서드에서 초기화를 완료할 수도 있습니다.
static
{
ss.put(new Person(1, "Jay"), new Salary("code farmer", 2000)), new Salary ("가수", 20000));
ss.put(new Person(3, "XM"), new Salary("학생", 20))
ss.put(new Person (4, "MrWang"), new Salary("Teacher", 2000))
}
   
     
  @Override  
    public void onCreate() {  
              super.onCreate();​
​ ​ ​ 급여Binder = new ​Sal aryBinder() ​
 } Er@ @Override
Public iBinder Onbind(의도 의도) {
Return Salary Binder는 스텁을 확장합니다
{
        @Override  
        public Salary getMsg(Person owner) throws RemoteException {  
            return ss.get(owner);  
        }  
    }  
      
    @Override  
    public void onDestroy() {  
        System.out.println("服务结束");  
        super.onDestroy();  
    }  
}

注册下Service:

<service android:name=".AidlService">  
    <인텐트 필터>    
        <action android:name="android.intent.action.AIDLService" />  
        <카테고리 android:name="android.intent.category.DEFAULT" />  
    </의도-필터>    
</service>

2——클라이언트 쓰기

1단계: 서버측 AIDL 파일을 복사합니다. 복사 후 디렉터리는 다음과 같습니다.

7.jpg

2단계: 간단한 레이아웃을 작성한 후 핵심 MainActvity 구현 이전 일반 데이터와 유사하게 ServiceConnection 개체를 정의하고 해당 메서드를 재정의합니다. 그런 다음 바인딩 서비스에서 버튼의 클릭 이벤트에 있는 Salary 객체를 가져와 표시합니다!

MainActivity.java

패키지 com.jay.example.aidl_complexclient;  
  
import com.jay.example.aidl.ISalary;  
import com.jay.example.aidl.Person;  
import com.jay.example.aidl.Salary;  
  
import android.app.Activity;  
android.app.Service 가져오기;  
android.content.ComponentName 가져오기;  
android.content.Intent 가져오기;  
android.content.ServiceConnection 가져오기;  
android.os.Bundle 가져오기;  
android.os.IBinder 가져오기;  
가져오기 android.os.RemoteException;  
android.view.View 가져오기;  
android.view.View.OnClickListener 가져오기;  
android.widget.Button 가져오기;  
android.widget.EditText 가져오기;  
android.widget.TextView 가져오기;  
  
  
public class MainActivity는 Activity 확장 {  
  
    private ISalary salaryService;  
    비공개 버튼 btnquery;  
    비공개 EditText 편집 이름;  
    비공개 TextView 텍스트 쇼;  
    private ServiceConnection conn = new ServiceConnection() {  
         
        @Override  
        public void onServiceDisconnected(ComponentName 이름) {  
            salaryService = null;  ㅋㅋㅋ要调用这个방법법!  
            salaryService = ISalary.Stub.asInterface(service);  
        }  
    };  
      
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
         
        btnquery = (버튼) findViewById(R.id.btnquery);  
        editname = (EditText) findViewById(R.id.editname);  
        textshow = (TextView) findViewById(R.id.textshow);  
          
        Intent it = new Intent();  
        it.setAction("com.jay.aidl.AIDL_SERVICE");  
        bindService(it, conn, Service.BIND_AUTO_CREATE);  ㅋㅋㅋ          시도해보세요  
              {  
                  문자열 이름 = editname.getText().toString();  
                  Salary Service. getMsg(신규 사람(1,이름));  
                  textshow.setText(이름 + salary.toString());  
             }catch(RemoteException e){e.printStackTrace();}  
          }  
        });  
         
    }  
    @Override  
    보호 무효 onDestroy() {  
        super.onDestroy();  
        this.unbindService(conn);  
    }  
      
}

실행 중인 스크린샷:

8.jpg

PS: 여기 코드는 Eclipse를 사용하기 전에 작성한 코드입니다. Android Studio에서 사용자 정의 유형에 문제가 있습니다. 아직 해결책을 찾지 못했습니다. 혹시 아시는 분 계시면 알려주세요! ! ! 매우 감사합니다! ! ! 발생하는 문제는 다음과 같습니다.

9.png

두 인스턴스의 코드 다운로드(Eclipse 기반):
1) AIDL을 사용하여 프로세스 간 간단한 통신 완료
2) 복잡한 데이터를 전송하는 AIDL 서비스 구현


3 .Direct Binder의 onTransact를 통한 완전한 크로스 프로세스 통신

위에서 언급했듯이 Android는 Binder의 onTrensact 메소드를 통해 통신을 완료할 수 있습니다. 일련번호 쿼리 이름의 예:

서버측 구현:

/**
 * 2015년 8월 18일 Jay가 0018년에 작성함.
 */
public class IPCService 서비스 확장{

    private static final String DESCRIPTOR = "IPCService";
    private final String[] names = {"B神","艹神","基神","J신","翔神"};
    private MyBinder mBinder = new MyBinder();


    private class MyBinder는 Binder를 확장합니다 {
       @Override
        protected boolean onTransact(int code, P arcel 데이터, 소포 회신, int 플래그) RemoteException을 발생시킵니다.        int num = data.readInt();
                   reply.writeNoException();
                  reply.writeString(names[num ]);
                   return true;
               }
            }
            return super.onTransact(코드, 데이터, 응답, 플래그);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

클라이언트 구현:

public 클래스 MainActivity는 AppCompatActivity를 확장하여 View.OnClickListener를 구현합니다.{

    private EditText edit_num;
    private Button btn_query;
    private TextView txt_result;
    private IBinder mBinder;
    private ServiceConnection PersonConnection  = new ServiceConnection()
    {
        @Override
        public void onServiceDisconnected(ComponentName 이름)
        {
            mIBinder = null;
        }

        @Override
        public void onServiceConnected(ComponentName 이름, IBinder 서비스)
        {
            mIBinder = service;
        }
    };

    @Override
    보호됨 무효 onCreate( Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();

       //绑定远程Service
        Int ent 서비스 = 새 Intent("android.intent.action.IPCService ");
        service.setPackage("com.jay.ipcserver");
        bindService(service, PersonConnection, BIND_AUTO_CREATE);
        btn_query.setOnClickListener(this);
    }

    private void bindViews() {
        edit_num = (EditText ) findViewById(R.id.edit_num);
        btn_query = (버튼) findViewById(R.id.btn_query);
       txt_result = (TextView) findViewById(R.id.txt_result);
    }

    @Override
    public void onClick (v 보기) {
int Num = Integer.parseint (Edit_num.gettext (). Tostring ())
if (mibinder == null) {
Toast.maketext (이것은 "연결되지 않은 서버 또는 서버가 비정상적으로 종료되었습니다.", toast .LENGTH_SHORT).show ();
    } else {
            android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply   =  android.os.Parcel.obtain()
        문자열 _결과 = null;
시도해 보세요{
          _data.writeInterfaceToken("IPCService"); 
                                                                                                                       (0x001, _data, _reply, 0);
               _reply.readException();                                                                       .readString ();                          e.printStackTrace();                                                    e.
               _data.recycle();

코드가 비교적 간단해서 길게 설명하진 않겠습니다~ 그냥 사용하시고 직접 수정해보세요! PS: 코드 참조: Android Aidl Binder 프레임워크에 대한 간략한 분석


4. Android 5.0 이후 서비스에서 주의할 사항:

오늘 서비스를 암묵적으로 시작했을 때 이런 문제가 발생했습니다.

10.png

그런 다음 프로그램이 시작되자마자 충돌이 발생했습니다. 원인이 Android 5.0이라는 것을 알아내는 데 오랜 시간이 걸렸습니다. 5.0 이후에는 다음과 같은 새로운 기능이 있는 것으로 나타났습니다. 서비스 의도는 명시적이어야 합니다! 서비스는 암시적으로 시작할 수 없으며 해결 방법은 매우 간단합니다! 예를 들어, StartService:

startService(new Intent(getApplicationContext(), "com.aaa.xxxserver")); 다음과 같이 프로그램을 작성하면 다음과 같이 직접 충돌이 발생합니다. startService(new) Intent(getApplicationContext(), LoadContactsService .class));

BindService인 경우: Intent service = new Intent("android.intent.action.AIDLService");위 내용을 바탕으로 패키지 이름 추가: service.setPackage("com.jay.ipcserver");그렇습니다~

공식 문서:http://developer.android.com/intl/zh-cn/guide/comComponents/intents-filters.html# 유형Documentation 설명 :

11.png


이 섹션 요약:

자, 서비스에 대한 마지막 섹션입니다. 이 섹션에서는 Binder의 기본 개념과 프로세스 간 통신 구현 방법을 설명합니다. 두 가지 방법: AIDL 및 Binder.onTransact()를 통한 프로세스 간 통신! 마지막으로 안드로이드 5.0 이후에 대해서도 설명했습니다. 서비스는 암시적으로 시작할 수 없습니다. 그렇군요, 감사합니다~