English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android IPC 프로세스 간 통신 자세히 설명하기_최신 AndroidStudio의 AIDL 작업

서론

먼저 Android의 스레드 간의 통신을 정리했습니다. 《Thread, Handler와 HandlerThread의 관계는 무엇인가요?》 이들은 모두 동일한 프로세스 내에서이지만, 프로세스 간의 통신이나 다른 애플리케이션 간의 통신은 어떻게 구현할 수 있을까요? 이때 AIDL(Android Interface Definition Language, Android 인터페이스 정의 언어)를 사용해야 합니다.

사용 방법 (Android Studio)

현재 AIDL의 튜토리얼은 대부분 Eclipse를 기반으로 하고 있지만, Android Studio에서 AIDL을 사용하는 것은 조금 다릅니다. 어떻게 사용하는지 보겠습니다. 먼저 서버 서비스로 사용할 프로젝트를 새로 만듭니다:

만들고 나면 어떤 폴더에서든 우클릭하여 New을 선택합니다.-->AIDL-->AIDL File, 파일 이름을 편집하면 src/main 디렉토리 아래에 새로운 aidl 폴더를 생성합니다. 패키지 디렉토리 구조는 다음과 같습니다:

main
aidl
com.example.tee.testapplication.aidl
java
com.example.tee.testapplication
res
AndroidManifest.xml

자동으로 생성된 aidl 파일은 다음과 같습니다:

// AidlInterface.aidl
package com.example.tee.testapplication.aidl;
// Declare any non-default types here with import statements
interface AidlInterface {
 /**
 * 사용할 수 있는 파라미터로 사용할 수 있는 일부 기본 타입을 보여줍니다
 * AIDL에서 반환 값을 사용할 수 있는 일부 기본 타입을 보여줍니다.
 */
 void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
 double aDouble, String aString);
}

aidl 파일의 코드 형식은 Java와 매우 유사하며, Java의 기본 타입 및 List, Map 등을 지원합니다. 커스터마이zed 클래스는 나중에 다룹니다. 일단 가장 간단한 것을 해보겠습니다. IMyAidlInterface.aidl 파일을 새로 생성하고 다음과 같이 수정합니다:

package com.example.tee.testapplication.aidl;
interface IMyAidlInterface {
 String getValue();
}

인터페이스에서 getValue 메서드를 정의하고 문자열을 반환하면, 이제 프로젝트를 컴파일하여 app을 찾을 수 있습니다./build/generated/source/aidl/debug 디렉토리에서 우리 애플리케이션 패키지 이름 아래에 우리가 정의한 aidl 파일 이름과 같은 Interface 클래스가 생성되었습니다. 이는 실제로 aidl 파일이 최종적으로 인터페이스로 변환되어 구현된다는 것을 의미하며, 이 파일은 우리가 유지보수할 필요가 없으며, 컴파일 후 자동으로 생성됩니다.

그런 다음 Service를 상속받는 새로운 클래스를 생성합니다:

public class MAIDLService extends Service{
 public class MAIDLServiceImpl extends IMyAidlInterface.Stub{
 @Override
 public String getValue() throws RemoteException {
 "get value"를 반환합니다;
 }
 }
 @Nullable
 @Override
 public IBinder onBind(Intent intent) {
 new MAIDLServiceImpl();를 반환합니다.
 }
}

MAIDLService 클래스에서 IMyAidlInterface.Stub를 상속받는 내부 클래스를 정의하고, aidl에서也就是接口에서 정의한 getValue 메서드를 재정의하여 문자열 get value를 반환합니다.

이제 여기에 도달하여, 호출 후 문자열을 반환하는 역할을 하는 서비스를 새로 생성했습니다. 마지막으로 AndroidManifest 파일에서 선언합니다:

<service
 android:name=".MAIDLService"
 android:process=":remote"//이 줄을 추가하면 클라이언트 호출이 새 프로세스를 생성합니다
 android:exported="true"//기본적으로 true로 설정되어 있으며 제거할 수 있습니다. 원격 호출이 가능한지 선언
 >
 <intent-filter>
 <category android:name="android.intent.category.DEFAULT"> />
 <action android:name="com.example.tee.testapplication.aidl.IMyAidlInterface"> />
 </intent-filter>
</service>

android:process=":remote" 이 줄의 역할은 호출할 때 새 프로세스를 생성할지 여부를 선언하는 것입니다. 그다음 클라이언트 코드를 작성하고, 새 프로젝트를 생성하여, 얼마 전에 생성한 aidl 파일을 이 프로젝트에 복사합니다. 주의할 것은, 여전히 aidl 폴더에 위치해야 합니다. 그런 다음 MainActivity에서 다음과 같은 코드를 작성합니다:

public class MainActivity extends AppCompatActivity {
 private TextView mValueTV;
 private IMyAidlInterface mAidlInterface = null;
 private ServiceConnection mServiceConnection = new ServiceConnection() {
 @Override
 public void onServiceConnected(ComponentName name, IBinder service) {
 mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
 }
 @Override
 public void onServiceDisconnected(ComponentName name) {
 }
 };
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 Intent intent = new Intent("com.example.tee.testapplication.aidl.IMyAidlInterface");
 bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
 mValueTV = (TextView) findViewById(R.id.tv_test_value);
 mValueTV.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 try {
  mValueTV.setText(mAidlInterface.getValue());
 } catch (RemoteException e) {
  e.printStackTrace();
 }
 }
 });
 }
 @Override
 protected void onDestroy() {
 if(mAidlInterface != null){
 unbindService(mServiceConnection);
 }
 super.onDestroy();
 }
}

이곳에서 새로운 Intent에 전달하는 문자열은 manifest에서 정의된 action 태그입니다. onDestroy를 호출할 때 서비스를 해제绑定해야 합니다.

결과는 TextView를 클릭할 때 서버에서 반환한 get value 문자열이 표시된다는 것입니다

정의된 객체

그리고 우리는 기본 타입 String을 사용했지만, 우리가 정의한 클래스를 사용할 때 위의 방법은 적용되지 않습니다. 우리가 정의한 클래스를 사용하려면 수동으로 임포트해야 하며, 서버로 사용된 프로젝트를 수정해야 합니다.

먼저 시작할 때 생성된 aidl 패키지 아래에 (모든 aidl 관련 파일을 이 패키지 아래에 넣어야 합니다) Student.java를 새로 만듭니다

public class Student implements Parcelable{
 public String name;
 public int age;
 protected Student(Parcel in) {
 readFromParcel(in);
 }
 public Student() {
 }
 public static final Creator<Student> CREATOR = new Creator<Student>() {
 @Override
 public Student createFromParcel(Parcel in) {
 return new Student(in);
 }
 @Override
 public Student[] newArray(int size) {
 return new Student[size];
 }
 };
 @Override
 public int describeContents() {
 return 0;
 }
 @Override
 public void writeToParcel(Parcel dest, int flags) {
 dest.writeInt(age);
 dest.writeString(name);
 }
 public void readFromParcel(Parcel in){
 age = in.readInt();
 name = in.readString();
 }
 @Override
 public String toString() {
 return String.format(Locale.ENGLISH, "STUDENT[%s:%d]", name, age);
 }
}

Parcelable 시리얼라이즈 인터페이스를 구현해야 합니다. AndroidStudio는 자동으로 static inner class CREATOR와 describeContents 메서드를 생성합니다. 이 부분은 수정하지 않고 자동으로 생성된 것을 사용하세요. 그런 다음 writeToParcel 메서드를 다시 작성하고 custom readFromParcel 메서드를 작성합니다. 이 두 메서드의 속성 순서는 반드시 일치해야 합니다. 하나는 입력, 다른 하나는 출력입니다. Student(Parcel in) 생성자에서 readFromParcel(in) 메서드를 호출합니다.

다음으로 Student.aidl 파일을 새로 만듭니다. (aidl 패키지 내에 있습니다):

// Student.aidl
package com.example.tee.testapplication.aidl;
// Declare any non-default types here with import statements
parcelable Student;

이곳에서 Student 앞의 키워드 parcelable의 첫 글자가 소문자인 점을 주의하세요. 그런 다음 IMyAidlInterface.aidl 파일을 다음과 같이 수정하세요:

// IMyAidlInterface.aidl
package com.example.tee.testapplication.aidl;
// Declare any non-default types here with import statements
import com.example.tee.testapplication.aidl.Student;
interface IMyAidlInterface {
 Student getStudent();
 void setStudent(in Student student);
 String getValue();
}

두 가지 메서드를 정의했습니다. 하나는 Student를 설정하는 것, 다른 하나는 Student를 가져오는 것입니다. setStudent 메서드에서 인자의 타입 앞에 in 키워드가 있으며, aidl에서 인자는 입력(in)과 출력(out)으로 나뉩니다.

현재 MAIDLService.java에서 새로 추가된 두 메서드를 재정의합니다:

private Student mStudent;
public class MAIDLServiceImpl extends IMyAidlInterface.Stub{
 @Override
 public Student getStudent() throws RemoteException {
 return mStudent;
 }
 @Override
 public void setStudent(Student student) throws RemoteException {
 mStudent = student;
 }
 @Override
 public String getValue() throws RemoteException {
 return "get value : " + Thread.currentThread().getName(); + Thread.currentThread().getId();
 }
}

서버쪽 코드 수정이 완료되면, 클라이언트 프로젝트로 이동하여, 양쪽을 일치시키기 위해 양쪽에 있는 aidl 패키지 아래의 파일을 복사하여 덮어씌우고, MainActivity.java에서 다음과 같이 수정합니다:

mValueTV = (TextView) findViewById(R.id.tv_test_value);
mStudentTV = (TextView) findViewById(R.id.tv_test_student);
mValueTV.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 try {
 mValueTV.setText(mAidlInterface.getValue());
 } catch (RemoteException e) {
 e.printStackTrace();
 }
 }
});
mStudentTV.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 try {
 Student student = new Student();
 student.age = 10;
 student.name = "Tom";
 mAidlInterface.setStudent(student);
 mStudentTV.setText(mAidlInterface.getStudent().toString());
 } catch (RemoteException e) {
 e.printStackTrace();
 }
 }
});

현재 프로젝트를 컴파일하면, 프로젝트가 오류를 보고, 클래스 Student를 찾을 수 없다고 표시합니다. 따라서 app 디렉토리의 build.gradle 파일에 다음과 같은 코드를 추가해야 합니다:

android {
 sourceSets {
 main {
 manifest.srcFile 'src/main/AndroidManifest.xml'
 java.srcDirs = ['src/main/java/main/aidl
 resources.srcDirs = ['src/main/java/main/aidl
 aidl.srcDirs = ['src/main/aidl
 res.srcDirs = ['src/main/res
 assets.srcDirs = ['src/main/assets
 }
 }
}

그래서 파일 디렉토리를 지정하는 것만으로도 재编译할 때 문제가 없게 됩니다

결론

Android의 IPC는 사용하기 매우 간단합니다. AIDL 파일의 문법도 우리가 일상적으로 인터페이스를 사용할 때와 유사하지만, 기본 타입만 지원하며, AIDL 파일을 참조할 수 있으며, 사용자 정의 클래스를 사용할 때는 조금 복잡해집니다.

이것이 Android IPC 프로세스 통신 자료 정리입니다. 이후 추가 자료를 계속 추가하겠습니다. 많은 도움을 주셔서 감사합니다!

성명서: 본 내용은 인터넷에서 가져왔으며, 원저자에게 속합니다. 내용은 인터넷 사용자가 자발적으로 기여하고 업로드한 것이며, 본 사이트는 소유권을 가지지 않으며, 인공 편집을 하지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 침해 콘텐츠가 발견되면, notice#w 이메일로 메일을 보내 주시기 바랍니다.3codebox.com에 (보내는 이메일에서 #을 @으로 변경하십시오) 신고하여, 관련 증거를 제공하시면, 해당 콘텐츠가 침해되었다는 사실이 확인되면, 본 사이트는 즉시 해당 침해 콘텐츠를 제거할 것입니다.

추천해드립니다