梳理Android的IPC进程间通讯(最新AndroidStudio的AIDL操做)

前言

前面梳理了Android的线程间的通讯《Thread、Handler和HandlerThread关系何在?》,这些都是在同一个进程中,那进程间的通讯,或者说不一样的应用间的通讯该如何实现呢?这个时候就要用到AIDL(Android Interface Definition LanguageAndroid接口定义语言 )。java

使用方法(AndroidStudio)

我发现如今AIDL的教程基本上仍是eclipse的,可是在AndroidStudio里面使用AIDL仍是有一些不一样的,来看看怎么用,首先新建一个工程当作server服务端:android

建立好后在任意文件夹右键New-->AIDL-->AIDL File,编辑文件名后会自动在src/main目录下面新建aidl文件夹,包的目录结构以下:app

  • maineclipse

    • aidlide

      • com.example.tee.testapplication.aidl
    • javagradle

      • com.example.tee.testapplication
    • res
    • AndroidManifest.xml

自动生成的aidl文件以下:ui

// AidlInterface.aidl
package com.example.tee.testapplication.aidl;

// Declare any non-default types here with import statements

interface AidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

咱们能够看到aidl文件的代码格式跟java很像,支持java的基础类型以及List、Map等,若是是自定义类的话须要手动导入,咱们后面再说,先来最简单的,新建一个 IMyAidlInterface.aidl文件,修改以下:.net

package com.example.tee.testapplication.aidl;

interface IMyAidlInterface {
     String getValue();
}

在接口中定义一个getValue方法,返回一个字符串,如今能够编译一下工程,找到app/build/generated/source/aidl/debug目录,在咱们应用包名下会发现生成了一个Interface类,名字跟咱们定义的aidl的文件名字同样,这说明其实aidl文件在最后仍是会转换成接口来实现,并且这个文件不须要咱们维护,在编译后自动生成。线程

而后新建一个类继承Service:debug

public class MAIDLService extends Service{
    public class MAIDLServiceImpl extends IMyAidlInterface.Stub{
        @Override
        public String getValue() throws RemoteException {
            return "get value";
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return 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会自动生成静态内部类CREATORdescribeContents方法,这些部分咱们都不须要修改,用自动生成的就好。而后重写writeToParcel方法,自定义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', 'src/main/aidl']
            resources.srcDirs = ['src/main/java', 'src/main/aidl']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }
}

也就是指定一下文件目录,如今再编译就没有问题了

总结

Android的IPC使用起来仍是挺简单的,AIDL文件的语法也跟咱们平时使用接口的时候很类似,可是它只支持基础类型,只能引用AIDL文件,须要使用自定义类的时候要稍微麻烦一点。

相关文章
相关标签/搜索