Android的每一个应用都是运行在单独的一个进程里。进程间的通信主要有以下四种:
1> Activity:可以结合Intent使用,启动其他进程的活动;
2> 广播:广播为跨进程间的通信;
3> ContentProvide:内容提供者,以Cursor对象访问其他进程数据或者为其他进程提供数据;
3> Service:主要通过AIDl实现进程间通信;
AIDL全程为安卓接口定义语言。
下面通过在Androidstudio上实现Client向服务端提交数据的实例对AIDL进行学习。
Client:
1> 我们都知道,每一个进程都有自己的独立内存。如果需要在进程间传递对象,则需将对象序列化。
#java语言
创建类,直接实现Serializable接口
#Android独有,实现Parcelable接口
创建Persion类,实现Parcelable接口
package com.example.aidltest;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by 阿正 on 2017/4/10 0010.
*/
public class Persion implements Parcelable {
public String name;
public int age;
public Persion() {
}
protected Persion(Parcel in) {
name = in.readString();
age = in.readInt();
}
public Persion(String name, int age) {
this.name = name;
this.age = age;
}
public static final Creator<Persion> CREATOR = new Creator<Persion>() {
@Override
public Persion createFromParcel(Parcel in) {
return new Persion(in);
}
@Override
public Persion[] newArray(int size) {
return new Persion[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
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 String toString() {
return "Persion{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2> 创建AIDL
对应文件位置:
Persion.aidl
// Persion.aidl
package com.example.aidltest;
// Declare any non-default types here with import statements
parcelable Persion;
PersionManager.aidl
// IPersionManager.aidl
package com.example.aidltest;
// Declare any non-default types here with import statements
// 这里引入的是Persion.aidl
import com.example.aidltest.Persion;
interface IPersionManager {
// 定义方法,所有的返回值前不需要修饰关键字;如果非默认类型,需要在参数前添加 in/out/inout
// in 客户端向服务端提供数据;out 客户端向服务端获取数据
List<Persion> getPersion();
void addPersion(in Persion ipersion);
}
到这里,创建结构类并序列化,创建AIDL已经完成。需要注意的是在创建Persion.aidl的时候,名字尽量和Persion.java一致。比如我之前创建IPersion.aidl时,clean项目的时候就报错。
客户端:客户端主要实现启动/停止service
package com.example.aidltest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button mAdd;
private Button mConnect;
private EditText mTextName;
private EditText mTextAge;
//是否连接服务端
private boolean isconnect = false;
private IPersionManager mPersionManager;
private List<Persion> mPersions;
public MainActivity() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextName = (EditText) findViewById(R.id.text_name) ;
mTextAge = (EditText) findViewById(R.id.text_age);
mAdd = (Button) findViewById(R.id.add);
mConnect = (Button)findViewById(R.id.connect);
mAdd.setOnClickListener(this);
mConnect.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.add:
// 调用服务端实现的方法,对数据进行改动
addBook();
break;
case R.id.connect:
// 启动service并绑定
Intent intent = new Intent();
intent.setAction("azheng");
intent.setPackage("com.example.aidltestservice");
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
break;
}
}
private void addBook(){
if (!isconnect){
Log.d("azheng" ,"clent: Not connected,please connect to service first!");
return;
}
if (mPersionManager == null){return;}
Persion persion = new Persion();
String name = mTextName.getText().toString();
persion.setName(name);
int age = Integer.parseInt(mTextAge.getText().toString());
persion.setAge(age);
try {
mPersionManager.addPersion(persion);
} catch (RemoteException e) {
e.printStackTrace();
}
}
// 创建ServiceConnection,与服务端连接
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("azheng" ,"clent: connected");
// 获得服务端的IPersionManager
mPersionManager = IPersionManager.Stub.asInterface(service);
isconnect = true;
try {
Log.d("azheng" ,"clent: before add:" + mPersionManager.getPersion().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("azheng" ,"clent: connected");
isconnect = false;
}
};
@Override
protected void onStop() {
super.onStop();
if (isconnect){
unbindService(serviceConnection);
}
}
}
服务端:
服务端的Persion.java Persion.aidl IPersionManager.aidl三个文件的包名,内容需要与client一致。因此最好的办法是直接从client复制过来。
Persion.java
// 这里的包名需要与服务端一致
package com.example.aidltest;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by 阿正 on 2017/4/10 0010.
*/
public class Persion implements Parcelable {
public String name;
public int age;
public Persion() {
}
protected Persion(Parcel in) {
name = in.readString();
age = in.readInt();
}
public Persion(String name, int age) {
this.name = name;
this.age = age;
}
public static final Creator<Persion> CREATOR = new Creator<Persion>() {
@Override
public Persion createFromParcel(Parcel in) {
return new Persion(in);
}
@Override
public Persion[] newArray(int size) {
return new Persion[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
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 String toString() {
return "Persion{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
具体的服务类AIDLService
package com.example.aidltestservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.example.aidltest.IPersionManager;
import com.example.aidltest.Persion;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 阿正 on 2017/4/10 0010.
*/
public class AIDLService extends Service {
private List<Persion> mPersions = new ArrayList<>();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return persionManager;
}
// 实现IPersionManager。aidl
private final IPersionManager.Stub persionManager = new IPersionManager.Stub(){
@Override
public List<Persion> getPersion() throws RemoteException {
synchronized (this){
if (mPersions != null){
return mPersions;
}
}
return null;
}
@Override
public void addPersion(Persion ipersion) throws RemoteException {
synchronized (this){
if (ipersion == null){
Log.d("azheng","service: persion from clent is null!");
}else {
mPersions.add(ipersion);
}
Log.d("azheng","service: result" + mPersions.toString());
}
}
};
@Override
public void onCreate() {
super.onCreate();
Log.d("azheng" ,"service: onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("azheng" ,"service: onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("azheng" ,"service: onDestroy()");
}
}
ok,到这里就完成了。
补充一点:上面提到客户端和服务端的{Persion.java Persion.aidl IPersionManager.aidl}需要一致,为了方便复制,这里提供另一种方法。
1> 将Persion.java 文件拷贝到aidl文件夹中
2> 上面的操作会导致Persion.java文件编译器找不到,因为AndroidStudio是通过sourceSets来配置不同文件的访问路径的。
修改 build.gradle 文件:在 android{} 中间加上下面的内容:
sourceSets {
main {
java.srcDirs = java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}