1、AIDL
2、BroadCast
3、Activity
AIDL(Android Interface Defition Language),对外暴露自身接口(该接口只支持方法,不支持常量),用到了Service。上篇关于广播的文章,为了增加难度也使用了Service。
AIDL:
客户端bindService(...)开启服务,unBindService()关闭服务。如果系统自动调用onDestroy()方法,就会自动调用unBindService(),根本原因是Context不存在了。注意:是自动,假如人为调用finish(),会报错,此时需要重写onDestroy(),手动添加unBindService()方法。
服务端实现Service的onBind(Intnt intent)方法,返回一个Binder对象。这个Binder对象是谁?在新建AILD文件的时候,SDK会自动生成一个Stub内部类,它的父类正是Binder,返回Stub即可。
返回Binder对象做什么呢?客户端通过这个Binder对象,得到服务端暴露的接口,进而通过接口方法,获取服务端数据。一段伪代码解释具体过程:
private IMyAidlInterface iMyAidlInterface;//对外暴露的接口
private boolean flag;//跨进程是否成功
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);//通过Binder对象获得
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
flag = bindService(intent,serviceConncetion,Context.BIND_AUTO_CREATE);
if(flag){
iMyAidlInterface.getData();//通过接口方法,跨进程获取数据
} else {
//跨进程访问失败
}
startService(...)开启服务,stopService()或者stopSelf()关闭服务。startService执行N次,onStart()执行N次,但是只有一个Service对象,所以关闭服务调用一次即可。
实现Service的onBind(Intnt intent)方法,返回一个null。实现onStartCommand(Intent intent,int flags,int startId)具体操作在这里实现,现在官方不建议用onStart()方法了,建议用inStartCommand()提高兼容性。
AIDL效果图:A访问B,获取B的数据。GitHub下载A,GitHub下载B
注意:AIDL文件支持哪些数据类型?
基本数据类型、String和CharSequence、List(只支持ArryList)、Map(只支持HashMap)、Pacelable(本文介绍的)、AIDL(接口本身)。
干货AIDL:
准备两个程序:A程序,B程序。
目的:A跨进程访问B,得到数据。(专业一点:B对外暴露接口,A通过接口访问B)
1.创建B程序。
先看一下AIDL文件的结构:
新建AIDL文件IMyAidlInterface.aidl。
package com.wgl.share.share;
import com.wgl.share.share.MyData;//需要手写导入
interface IMyAidlInterface {
MyData getData();
}
新建类MyData实现Parcelable。
public class MyData implements Parcelable{
private String data;
public MyData(String data){
this.data = data;
}
private MyData(Parcel in) {
data = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public MyData createFromParcel(Parcel in) {
return new MyData(in);
}
@Override
public MyData[] newArray(int size) {
return new MyData[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data);
}
@Override
public String toString() {
return "MyData{" +
"data='" + data + '\'' +
'}';
}
}
新建AIDL文件MyData.aidl(作用,找到自定义类MyData)。只需要两行
package com.wgl.share.share;
parcelable MyData;
新建service类MyService,继承Service
public class MyService extends Service {
private IMyAidlInterface.Stub iMyAidlInterface = new IMyAidlInterface.Stub() {
@Override
public MyData getData() throws RemoteException {
return new MyData("给你的数据!!!");
}
};
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return iMyAidlInterface;
}
}
在app的build.gradle文件里的
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
如果还是报错,就sync或者clean一下。
运行A程序
2.创建A程序。
A程序的aidl文件复制过来,别放错位置。
在app的build.gradle文件里的android{},default之后加入:
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
然后在MainActivity里面:
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface iMyAidlInterface;
private boolean flag;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//跨进程连接B程序
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//捆绑Service
Intent intent = new Intent();
intent.setAction("com.wgl.aidl");
intent.setPakage("com.wgl.b");
flag = mainActivity.bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);
}
}
最后,在MainActivity里加入一个点击事件,获取B程序数
try {
//flag = true绑定服务成功
if(flag){
MyData a = iMyAidlInterface.getData();
ToastUtil.getInstance().toastInCenter(mainActivity,a.toString());
}
} catch (RemoteException e) {
e.printStackTrace();}
接下来,给AIDL添加权限验证:
在B程序中,MyService重写onBind方法
@Override
public IBinder onBind(Intent intent) {
String permission = intent.getStringExtra("permission");
Log.e("MyService:permission",permission);
int check = checkCallingOrSelfPermission(permission);
Log.e("MyService:check",check+"");
if(check == PackageManager.PERMISSION_DENIED) {
return null;
}
return iMyAidlInterface;
}
同时,在B中Manifests文件里,添加:
另一方面,A程序中MainActivity里onCreate方法和doClick方法修改如下:
intent.putExtra("permission","com.wgl.b.permission.ACCESS_B_SERVICE");
if(null != iMyAidlInterface) {
MyData data = iMyAidlInterface.getData();
Toast.makeText(MainActivity.this,data.toString(),Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this,"权限验证失败!",Toast.LENGTH_SHORT).show();
}
其实还有另一种方法,重写Binder的onTransact方法,返回false就是失败,反之成功,不在讲述了。
多提意见和问题。