上一篇我们说到了Android下服务Service的学习。接下来学习Service下稍微复杂的内容 AIDL 。
什么是AIDL? 是Android Interface definition language的缩写。简单理解为Android下进程之间的访问,和java中的RPC一样。采用的是代理方式来完成的。
AIDL有啥作用呢?服务Service只能给自己的Android程序来提供,如果想要给其他的Android程序提供某些功能,那就需要用AIDL了。
下面我们就来写一个简单的例子来完成他的初步学习把,以下是步骤:
Eclipse 和 Android Studio方式!
//AIDL的实现--服务端
1.创建aidl文件。文件内容 类似java里的接口的定义。该文件(服务)的作用是为了给其他的进程提供服务功能。
语法中方法不能有修饰符,支持基本的数据类型,也支持java类型但需要让该类实现Perale
文件成功创建后会在gen文件夹下自动生成IMyService的java文件。
package com.example.aidltest.aidl;
interface IMyService{
String getValue();
}
2.编写服务类(继承Service)。重写onBind方法 并返回IBinder对象。因为自动生成的文件中包含Stub 已经实现了
IBinder接口。所以只需要编写继承Stub类的子类即可
3.重写aidl中的接口方法实现要提供的服务功能。
package com.example.aidltest.service;
import com.example.aidltest.aidl.IMyService;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class CsuService extends Service{
private IMyService.Stub stub = new MyStub() ;
@Override
public IBinder onBind(Intent intent) {
return stub;
}
public class MyStub extends IMyService.Stub{
@Override
public String getValue() throws RemoteException {
return "小样你能收到我发给你的消息不! ";
}
}
}
4.在清单里注册服务,且提供action名 用来给外部进程访问
<service android:name="com.example.aidltest.service.CsuService">
<intent-filter>
<action android:name="com.example.aidltest.service.aidl"/><!-- 访问标记 -->
</intent-filter>
</service>
5.开启该服务。等待客户来访问。
package com.example.aidltest;
import android.os.Bundle;
import android.os.IBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
public class MainActivity extends Activity {
//服务提供类
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent service = new Intent("com.example.aidltest.service.aidl");//隐式启动
bindService(service, conn, Context.BIND_AUTO_CREATE);//不是必须
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
};
}
-----------------------------------
客户端进程调用RPC
1. 复制aidl文件中自动生成的java类(包含包名)到src中
2. 绑定服务。获得远程服务类对象
Intent intent = new Intent("服务清单里的action名");
如: Intent service = new Intent("com.example.aidltest.service.aidl") ;
bindService(service, conn, Context.BIND_AUTO_CREATE);
在onServiceConnected方法中获得远程服务类对象:IMyService.
IMyService serv = null ;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serv = IMyService.Stub.asInterface(service);
}
package com.example.aidlclient;
import com.example.aidltest.aidl.IMyService;
import android.app.Activity;
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.view.View;
import android.widget.Toast;
public class MainActivity extends Activity {//测试客户端
IMyService serv = null ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent service = new Intent("com.example.aidltest.service.aidl") ;//访问服务端的标识
bindService(service, conn, Context.BIND_AUTO_CREATE);
}
public void load(View v){
//按钮事件:读取AIDL数据
try {
Toast.makeText(this, serv.getValue() , Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(this, "有错误哦!" , Toast.LENGTH_SHORT).show();
}
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
serv = IMyService.Stub.asInterface(service);//获得远程服务对象
}
};
}
3. 使用远程服务对象。
========================================
PS:如果要访问复杂的远程数据呢?
AIDL只支持有限的数据类型 :
Java的简单类型(不要导包)
String 和 CharSequence (不要导包)
List 和 Map。里面的内容也必须是AIDL支持的类型。(不要导包)
AIDL自动生成的接口(要导包)
其他Java类需实现了android.os.Parcelable接口(要导包)。
如:
在aidl文件包下建立类:
import android.os.Parcel;
import android.os.Parcelable;
public class Student implements Parcelable{
private String name ;
private int 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;
}
public Student(){
}
public Student(Parcel in){
//给属性赋值:
readFromParcel(in);
}
public void readFromParcel(Parcel in){
name = in.readString();
age = in.readInt();
}
//必须提供。且名字固定
public static final Parcelable.Creator<Student> CREATOR
= new Creator<Student>() {
@Override
public Student[] newArray(int size) {
return new Student[size];
}
@Override
public Student createFromParcel(Parcel source) {
return new Student(source);
}
};
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
//需要序列化的值写入对象Parcel
dest.writeString(name);
dest.writeInt(age);
}
}
再编写Student的aidl文件 Student.aidl(注意,类和aidl文件要放在一起)
package com.example.aidltest.aidl;
parcelable Student ;
如果服务类中要使用这些类,则需要导包:
package com.example.aidltest.aidl;
import com.example.aidltest.aidl.Student ;
interface IMyService{
String getValue();
Student getStudent();
Map getAll(in String name , in Student student);
}
其中如果参数为非简单类型 , 需要使用in、out、inout修饰 。in表示这个值被客户端设置,out被服务端设置
Ok, 基本就能在客户端访问了。记得客户端也要把Student.java类和对应的aidl文件一起拷贝过去。
大家去试试吧 。有问题请留言了。
-------------------------------------------------------------------- 华丽分割线 -------------------------------------------------------------------
以上是基于Eclipse的,现在说说Android Studio里面的操作:
1.建议先编写好Parcelable的实现类 。比如 vo.Person 等;
2.右键选择vo类所在的包名,在New菜单下 选择“New AIDL" 文件,此时输入要提供的远程服务接口名:IWebService .在里面输入你的功能方法,比如:String getName();
暂时不要包含非基本类型的数据。
3. 继续右键在vo类所在包名上,继续的创建AIDL文件,此时输入Parcelable的类名,会提示 “interface name must unique ”, 重名嘛,所以随便改个名字即可,等
创建了aidl文件后在 refactor --> rename 重命名即可。把里面的内容替换成:parcelable Person ; 此时再make project 编译项目,这样 vo类就生成了!
4. 回到2,继续在IWebService接口中添加方法, 比如: Person getPerson() ; 记得把Person的包导入 !其他的类似了。
5. 编写一个Service 服务,并开启,类似Eclipse。这样服务端的代码就Ok了。
下面编写 客户端:
需要注意的是 客户和服务端的包名问题,这点很重要,否则会找不到类,以及会引发一个安全异常!
1. 把服务端的vo类连同包一起拷贝到工程里,包名路径与服务端一致!
2.把服务端所有的.aidl 文件,复制到工程了,注意包的名字与服务端一致!(建议手动方式创建,好像复制不能自动生成java文件。)
剩下的 就跟Eclipse的写法一样了 就不再啰嗦。
完毕!