前言
最近在研究aidl传递对象的时候,自己在网上搜了下,发现了不少问题。由于网上的资料有的对应的Android版本比较老,有的可能不适应现在的Android Studio的版本,一直有这样那样的问题。这里把我遇到的问题以及解决办法说明下。
步骤
服务端
- 首先我们先把新建一个aidl接口类
IMyAidlInterface
,在项目的app目录上右键new一个AIDL File,as会自动在main目录下创建一个aidl文件夹。 - 接着再新建一个AIDL File,
Person.aidl
。然后可以先rebuild
下工程。 - 接着在java的目录下,也就是与
MainActivity
同目录下新建一个类Person.java
并实现Parcelable
接口。 - 最后就是新建
Service
,命名为MyService
。
这里说一下,网上有些文章说是在aidl的目录下建Person.java,然后最后在gradle中把aidl目录配置为java源码目录。这个我试了,但是一直编译不过,找不到Person类。所以我就直接在默认的地方新建Person类,就没有问题了。
目录结构
代码
IMyAidlInterface.aidl
package com.limingjian.aidltest2;
import com.limingjian.aidltest2.Person;
interface IMyAidlInterface {
int add(int a,int b);
String inPerson(in Person p);
String outPerson(out Person p);
String inOutPerson(inout Person p);
}
这里注意要把我们的Person
类的包手动导下。
Person.aidl
package com.limingjian.aidltest2;
parcelable Person;
这里的parcelable,开头是小写的p
Person.java
package com.limingjian.aidltest2;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private int age;
public Person() {
}
protected Person(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public void readFromParcel(Parcel dest) {
name = dest.readString();
age = dest.readInt();
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
MyService.java
package com.limingjian.aidltest2;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Log_LYL", "Onstart");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.e("MyService", "onBind");
return stub;
}
IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
@Override
public String inPerson(Person p) throws RemoteException {
String old = "name:" + p.getName() + " age:" + p.getAge();
Log.d("Log_LYL:inPerson_", old);
p.setName("李四");
p.setAge(13);
return "name:" + p.getName() + " age:" + p.getAge();
}
@Override
public String outPerson(Person p) throws RemoteException {
String old = "name:" + p.getName() + " age:" + p.getAge();
Log.d("Log_LYL:outPerson_", old);
p.setName("周六");
p.setAge(20);
return "name:" + p.getName() + " age:" + p.getAge();
}
@Override
public String inOutPerson(Person p) throws RemoteException {
String old = "name:" + p.getName() + " age:" + p.getAge();
Log.d("Log_LYL:inOutPerson_", old);
p.setName("弓七");
p.setAge(57);
return "name:" + p.getName() + " age:" + p.getAge();
}
};
}
最后记得在AndroidManifest.xml文件里配置Service
<service android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.limingjian.aidl"/>
</intent-filter>
</service>
到这里,服务端就写好了。接下来是客户端。
客户端
我们新建个工程用来写客户端。
- 先把服务端的
aidl
目录直接拷贝到客户端的main
目录下,也就是与java
目录同级。 - 然后新建一个包,这里的包名要与服务端的
Person.java
所在的包名相同。然后把服务端的Person.java
拷贝到这个包里。
到这里aidl方面的工作就完成了。
目录结构
代码
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="客户端"
android:textColor="@android:color/holo_red_dark"
android:textSize="15sp" />
<EditText
android:id="@+id/et_num1"
android:layout_width="200dp"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_num2"
android:layout_width="200dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bt_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加运算" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="结果:" />
<TextView
android:id="@+id/tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2" />
</LinearLayout>
</LinearLayout>
MainActivty.java
package com.limingjian.clienttest;
import android.content.ComponentName;
import android.content.Context;
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.limingjian.aidltest2.IMyAidlInterface;
import com.limingjian.aidltest2.Person;
public class MainActivity extends AppCompatActivity {
private EditText et_num1, et_num2;
private TextView tv;
private IMyAidlInterface mService;
private Button bt_add;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_num1 = (EditText) findViewById(R.id.et_num1);
et_num2 = (EditText) findViewById(R.id.et_num2);
bt_add = (Button) findViewById(R.id.bt_add);
tv = (TextView) findViewById(R.id.tv);
Intent intent = new Intent();
intent.setAction("com.limingjian.aidl");
//兼容Android 5.0以上
intent.setPackage("com.limingjian.aidltest2");
bindService(intent, mServiceC, Context.BIND_AUTO_CREATE);
bt_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
add();
}
});
}
public void add() {
int a = Integer.valueOf(et_num1.getText().toString());
int b = Integer.valueOf(et_num2.getText().toString());
try {
int res = mService.add(a, b);
tv.setText(res+"");
Person p1 = new Person();
p1.setName("刘大");
p1.setAge(3);
Person p2 = new Person();
p2.setName("赵二");
p2.setAge(3);
Person p3 = new Person();
p3.setName("张三");
p3.setAge(3);
tv.setText(res + "\n" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3)+"\n" + p1.toString() + "\n" + p2.toString() + "\n" + p3.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
ServiceConnection mServiceC = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceC);
}
}
从Android 5.0开始已经不能隐式启动Service
了,所以要加上intent.setPackage("com.limingjian.aidltest2")
,这里的包名就是服务端的MyService
所在的包的名字。
最后我们把服务端和客户端都部署到手机上。运行结果图如下