以前就知道其大概代码流程,但是一直没有敲代码去实现,今天将其实现了,android studio下编写也遇到了一些小细节的问题,特此记录一下。
既然是模拟Aidl通讯,那么当然要编写两个应用了,一个提供服务给另一个应用调用,那么开始吧。
一、服务提供方应用编写
①.为了更全面一些,我编写了一个自定义类Book在Aidl之中传递,不同进程间传递自定义对象必须实现Parcelable接口,Serializable是不行的哦。Book类如下:
package com.lbb.aidltest.bean;
import android.os.Parcel;
import android.os.Parcelable;
/**
* 作者:李少波
* 邮箱:lishaobo@seengene.com
* 日期:16/10/20
*/
public class Book implements Parcelable{
private String name;
private int id;
public Book(String name, int id) {
this.name = name;
this.id = id;
}
protected Book(Parcel in) {
name = in.readString();
id = in.readInt();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeInt(id);
}
}
②.编写.Aidl文件,因为此处用到了自定义类的对象的传递,所以需要编写两类aidl文件,分别是定义服务接口的和定义自定义类的aidl文件。
a.定义服务接口。右键-new-aidl-aidl file,文件名随便定义,可以是服务的类名前边加I。就像一个接口文件一样声明对外开放的方法即可,此处需要注意的是:如果用到了自定义类,则需要显示的import进来,此处需要显示import com.lbb.aidltest.bean.Book,否则会”Execution failed for task :app:compileDebugAidl”。我的接口定义文件如下:
// IAidlInterface.aidl
package com.lbb.aidltest;
import com.lbb.aidltest.bean.Book;
interface IAidlInterface {
Book getBookByIndex(int index);
}
b.定义需要在进程间传递的自定义类同名的.aidl文件,此处是Book.aidl。这一步不能右键创建aidl file了,因为输入Book会提示”Interface Name must be unique”,导致无法下一步。所以只能在aidl文件夹下创建和Book一样的包路径,并在路径下new-file,填入Book.aidl,手动输入文件内容如下(全程系统无任何提示输入。。汗):
package com.lbb.aidltest.bean;
parcelable Book;
③.万事俱备,创建自己的AidlService继承自Service,在onBind中返回一个.Stub自类的对象供其它app中使用,我的服务代码如下:
package com.lbb.aidltest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import com.lbb.aidltest.bean.Book;
/**
* 作者:李少波
* 邮箱:lishaobo@seengene.com
* 日期:16/10/20
*/
public class AidlService extends Service {
IAidlInterface.Stub mStub = new IAidlInterface.Stub() {
@Override
public Book getBookByIndex(int index) throws RemoteException {
Book book = null;
if(index ==0){
book = new Book("第一本书",0);
}
return book;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
}
至此服务供应方代码编写完毕,对了,还有一点很重要,记得在mainfest中声明服务的隐式intent-filter,因为其它app都是用隐式方式调用这个服务的:
<service android:name=".AidlService">
<intent-filter><action android:name="com.lbb.aidltest.AidlService"></action></intent-filter>
</service>
二、调用其它app中可以被调用的服务。此处我调用我上一个app中的服务,前提是安装了上一个app哦。。使用过程分两步:
①.带上包名拷贝上一个app中的bean和aidl文件(包括自定义类以及接口的都要),也就是使用端要和提供端的bean的包路径以及aidl的包路径是一样的,没有路径创造路径也要保证一样。
②.在Activity中愉快的引用其他app服务吧,这里是隐式调用的,如果直接隐式调用在android 5.0之后的环境下会报”只能显式启动Service”的错误,此处用了一个网上荡的方法,可以用隐式打开服务了,我是这样使用的:
public class MainActivity extends Activity {
private IAidlInterface aidlService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent intent = new Intent();
intent.setAction("com.lbb.aidltest.AidlService");
final Intent eintent = new Intent(createExplicitFromImplicitIntent(this,intent));
bindService(eintent,serviceConnection, Service.BIND_AUTO_CREATE);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
aidlService = IAidlInterface.Stub.asInterface(iBinder);
try {
Book book = aidlService.getBookByIndex(0);
Log.d(MainActivity.class.getSimpleName(),"book名字: "+book.getName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
三、小结
1.提供端接口aidl文件创建需要手动import 自定义类,自定义类的aidl需要全手工填写包名并parcelable Book;
2.使用端要拷贝提供端的bean和aidl文件,并且在android 5.0以上系统需要用以上的方式来使得隐式bindService变为显式bind。
3.aidl调用第一个应用服务,不需要启动第一个应用哦
demo下载链接:
http://download.csdn.net/detail/lishaobo211985/9659733