最近在考虑项目重构的时候,考虑将项目拆分红两个APK,一个用于数据服务,一个用于UI展现。
数据服务APK向本身编写APK提供数据,同时也能够向第三方提供数据。考虑使用这样的方式代替向第三方提供jar形式的sdk包。
若是拆分红多个APK,不得不考虑 进程间通讯(IPC)的问题。Android提供了一种IPC的实现,就是AIDL.java
在学习AIDL时编写示例造成本文。放在Github的demo项目中。能够在下面的地址下载到源代码
github: https://github.com/vir56k/demo/tree/master/aidlDemoandroid
AIDL (Android Interface Definition Language, Android接口定义语言)
在不一样的进程(应用)之间进行数据交换,就要约定 之间的通讯接口。git
从面向对象的角度来看,接口设计要考虑状态和行为。通常来讲,接口定义的内容分为:
1.方法操做(描述行为)
2.参数(描述状态,数据的类型,数据的载体/实体)github
AIDL是一种IDL,它有特有的语法描述。咱们须要编写一个AIDL文件做为约定。它的语法很是相似java语法。
它支持基础数据类型,好比 int,String,float等。
它支持实体类,必须是实现了Parcelable接口,支持序列化。多线程
AIDL经过服务绑定的方式来使用。你须要定义一个service,传递一个 IBinder对象。这个 IBinder对象具备咱们须要的方法。
拿到这个对象后执行具体方法。app
AIDL分为 服务端和客户端
服务端即服务提供着,提供可操做的方法和数据。
客户端即调用者,使用方法和数据。async
何时适合使用AIDL:
官方文档建议只有你容许客户端从不一样的应用程序为了进程间的通讯而去访问你的service,以及想在你的service处理多线程。ide
1.定义一个AIDL文件
2.实现描述的接口,编写service
3.若是有实体类,须要提供实体类(jar包形式)学习
1.拿到AIDL文件
2.绑定服务,得到接口持有对象。ui
Android提供的特殊的文件夹来放置AIDL文件,位于 src/mian/aidl 文件夹下。
因为java类/接口是有 package(命名空间)的。咱们须要定义命名空间,通常和文件位置一致。
在这里,咱们在 src/mian/aidl 文件夹下,建立package,名称为:com.example.myserver。
对应文件夹路径为src/mian/aidl/com/example/myserver,咱们在这个文件下创建咱们的aidl文件,内容以下:
IRemoteService.aidl
package com.example.myserver; import com.example.myserver.Entity; import com.example.myserver.IMyCallback; // Declare any non-default types here with import statements interface IRemoteService { void doSomeThing(int anInt,String aString); void addEntity(in Entity entity); List<Entity> getEntity(); }
Entity.aidl,这个是实体类 ,它还须要对应一个java class文件
// Entity.aidl package com.example.myserver; parcelable Entity;
在src/java文件夹写下 MyService class,集成服务Service类.在mainifest文件中注册这个服务类。
若是你的aidl描述文件编写无误的话,android studio 会自动帮你生成一些辅助类,你能够在下面的目录找到:
build/generated/source/debug
在这个文件夹下回自动生成有 IRemoteService类,和它的子类 IRemoteService.Stub类及其余。感兴趣的同窗能够读读。
IRemoteService.Stub是一个根文件,它是一个抽象类。下面代码演示了,一个 IRemoteService.Stub 的匿名类的实现。
在这个服务类的 public IBinder onBind(Intent intent) 方法中,咱们return 一个 IRemoteService.Stub 的匿名类实现。
在客户绑定到这个服务的时候,将能够得到到这个实现的一个实例,调用它的方法。
代码以下
package com.example.myserver; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.support.annotation.Nullable; import android.util.Log; import java.util.ArrayList; import java.util.List; /** * Created by zhangyunfei on 16/10/12. */ public class MyService extends Service { public static final String TAG = "MyService"; @Nullable @Override public IBinder onBind(Intent intent) { Log.e(TAG, String.format("on bind,intent = %s", intent.toString())); return binder; } private final IRemoteService.Stub binder = new IRemoteService.Stub() { public static final String TAG = "IRemoteService.Stub"; private List<Entity> data = new ArrayList<Entity>(); @Override public void doSomeThing(int anInt, String aString) throws RemoteException { Log.d(TAG, String.format("收到:%s, %s", anInt, aString)); } @Override public void addEntity(Entity entity) throws RemoteException { Log.d(TAG, String.format("收到:entity = %s", entity)); data.add(entity); } @Override public List<Entity> getEntity() throws RemoteException { return data; } }; }
咱们上面提到,接口的参数能够是实体类。咱们在前面定义了一个entity.aidl,它里面写了一句
parcelable Entity;
这么一句话指明它须要关联到一个具体的实体类。咱们须要在src/java文件夹编写这么一个类的实现,必须实现parcelable接口。
注意咱们要先创建package,这个 package要和aidl接口声明里的一致。
android studio为咱们方便的提供自动生成parcelable实现的快捷键,在mac下是 command+空格。实现后的代码以下:
package com.example.myserver; import android.os.Parcel; import android.os.Parcelable; /** * Created by zhangyunfei on 16/10/12. */ public class Entity implements Parcelable { int age; String name; public Entity() { } public Entity(int age, String name) { this.age = age; this.name = name; } protected Entity(Parcel in) { age = in.readInt(); name = in.readString(); } public static final Creator<Entity> CREATOR = new Creator<Entity>() { @Override public Entity createFromParcel(Parcel in) { return new Entity(in); } @Override public Entity[] newArray(int size) { return new Entity[size]; } }; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(age); dest.writeString(name); } @Override public String toString() { return String.format("age=%s, name=%s", age, name); } }
再开始以前,咱们能够新建一个app来作演示.步骤以下:
咱们先拿到AIDL描述文件才用使用,将AIDL文件放到aidl文件夹下。android studio 自动生成根文件类。
得到实体类Entity.class 放入到项目中。
在它的 MainActivity 下绑定服务
Intent intent = new Intent(); intent.setAction("com.example.REMOTE.myserver"); intent.setPackage("com.example.myserver"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
指定服务的名称,bindService方法中须要传入一个 ServiceConnection对象。
咱们写一个ServiceConnection的匿名类,在它的onServiceConnected方法中,得到 aidl定义的接口持有类。
iRemoteService = IRemoteService.Stub.asInterface(service);
还记得刚刚编写服务类返回的 binder吗,在这里得到的就是那个binder示例。咱们能够经过对这个示例进行 转型 后的对象来调用 接口定义的方法。
经过 iRemoteService.addEntity(entity) 方法,咱们能够操做具体的实体,传入实体类做为参数。
if (!mBound) { alert("未链接到远程服务"); return; } try { Entity entity = new Entity(1, "zhang"); if (iRemoteService != null) iRemoteService.addEntity(entity); } catch (RemoteException e) { e.printStackTrace(); }
完整代码以下:
package com.example.zhangyunfei.myapplication; 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.Toast; import com.example.myserver.Entity; import com.example.myserver.IMyCallback; import com.example.myserver.IRemoteService; import java.util.List; public class MainActivity extends AppCompatActivity { private boolean mBound = false; private IRemoteService iRemoteService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btnAdd).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mBound) { alert("未链接到远程服务"); return; } try { Entity entity = new Entity(1, "zhang"); if (iRemoteService != null) iRemoteService.addEntity(entity); } catch (RemoteException e) { e.printStackTrace(); } } }); findViewById(R.id.btnList).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mBound) { alert("未链接到远程服务"); return; } if (iRemoteService != null) { try { List<Entity> entityList = iRemoteService.getEntity(); StringBuilder sb = new StringBuilder("当前数量:" + entityList.size() + "\r\n"); for (int i = 0; i < entityList.size(); i++) { sb.append(i + ": "); sb.append(entityList.get(i) == null ? "" : entityList.get(i).toString()); sb.append("\n"); } alert(sb.toString()); } catch (RemoteException e) { e.printStackTrace(); } } } }); findViewById(R.id.btnCallback).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mBound) { alert("未链接到远程服务"); return; } try { if (iRemoteService != null) { final String para = "canshu"; iRemoteService.asyncCallSomeone(para, new IMyCallback.Stub() { @Override public void onSuccess(String aString) throws RemoteException { alert(String.format("发送: %s, 回调: %s", para, aString)); } }); } } catch (RemoteException e) { e.printStackTrace(); } } }); } private void alert(String str) { Toast.makeText(this, str, 0).show(); } @Override protected void onStart() { super.onStart(); if (!mBound) { attemptToBindService(); } } @Override protected void onStop() { super.onStop(); if (mBound) { unbindService(mServiceConnection); mBound = false; } } /** * 尝试与服务端创建链接 */ private void attemptToBindService() { Intent intent = new Intent(); intent.setAction("com.example.REMOTE.myserver"); intent.setPackage("com.example.myserver"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(getLocalClassName(), "service connected"); iRemoteService = IRemoteService.Stub.asInterface(service); mBound = true; if (iRemoteService != null) { try { iRemoteService.doSomeThing(0, "anything string"); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { Log.e(getLocalClassName(), "service disconnected"); mBound = false; } }; }
在AIDL中,有时候须要实现回调,传入一个回调callbak,或者listener类。如何实现呢?
IMyCallback类具备一个 onSuccess回调方法
IMyCallback.aidl,这个文件里描述一个回调接口
// IMyCallback.aidl package com.example.myserver; // Declare any non-default types here with import statements interface IMyCallback { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void onSuccess(String aString); }
IRemoteService.aidl
package com.example.myserver; import com.example.myserver.Entity; import com.example.myserver.IMyCallback; // Declare any non-default types here with import statements interface IRemoteService { void asyncCallSomeone( String para, IMyCallback callback); }
发起回调有点相似广播的方式,示例:
@Override public void asyncCallSomeone(String para, IMyCallback callback) throws RemoteException { RemoteCallbackList<IMyCallback> remoteCallbackList = new RemoteCallbackList<>(); remoteCallbackList.register(callback); final int len = remoteCallbackList.beginBroadcast(); for (int i = 0; i < len; i++) { remoteCallbackList.getBroadcastItem(i).onSuccess(para + "_callbck"); } remoteCallbackList.finishBroadcast(); }
咱们须要一个 RemoteCallbackList 集合类,把 要回调的类的示例callback示例放到这集合内。调用这个集合类RemoteCallbackList的下面两个方法:
beginBroadcast 开始广播,finishBroadcast 结束广播,配合使用。
客户端在得到接口操做对象后,传入回调类,示例:
try { if (iRemoteService != null) { final String para = "canshu"; iRemoteService.asyncCallSomeone(para, new IMyCallback.Stub() { @Override public void onSuccess(String aString) throws RemoteException { alert(String.format("发送: %s, 回调: %s", para, aString)); } }); } } catch (RemoteException e) { e.printStackTrace(); }
谷歌官方文档