【跨进程】跨进程通信---AIDL

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下载AGitHub下载B

注意:AIDL文件支持哪些数据类型?

基本数据类型、String和CharSequence、List(只支持ArryList)、Map(只支持HashMap)、Pacelable(本文介绍的)、AIDL(接口本身)。

【跨进程】跨进程通信---AIDL_第1张图片

干货AIDL:

准备两个程序:A程序,B程序。

目的:A跨进程访问B,得到数据。(专业一点:B对外暴露接口,A通过接口访问B)

1.创建B程序。

先看一下AIDL文件的结构:

【跨进程】跨进程通信---AIDL_第2张图片【跨进程】跨进程通信---AIDL_第3张图片


新建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;
    }
}

Manifest文件中,注册service:



            
                
            
        


在app的build.gradle文件里的中间,defaultConfig之后加入(为了找到MyData类)

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就是失败,反之成功,不在讲述了。

多提意见和问题。


你可能感兴趣的:(Android跨进程通信)