Android的每一个应用都是运行在单独的一个进程里。进程间的通信主要有以下四种:
1> Activity:可以结合Intent使用,启动其他进程的活动;
2> 广播:广播为跨进程间的通信;
3> ContentProvide:内容提供者,以Cursor对象访问其他进程数据或者为其他进程提供数据;
3> Service:主要通过AIDl实现进程间通信;
AIDL全程为安卓接口定义语言。
具体参考: http://blog.csdn.net/luoyanglizi/article/details/51980630
下面通过在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‘] } }