天天看点

Android AIDL跨进程通信

在Android系统中,一般的进程间的通信,数据传递,我们经常会使用,也很简单。那么,如果一个应用想访问另一个应用中的数据,或者交互我们又该如何做呢?

不用担心,Android系统也已经给我们提供了解决的方法,那就是 AIDL(Android Interface Definition Language) Android接口定义语言。

内容:

  • 跨应用启动Service
  • 与Service跨应用通信(一)
  • 与Service跨应用通信(二)

跨应用启动Service

1. 同一个进程启动Service

MainActivity :

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService(new Intent(this,MyService.class));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(new Intent(this,MyService.class));
    }
           

MyService :

@Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        Log.e("MyService","Started Service");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService","Destroy Service");
    }
           

运行效果:

Android AIDL跨进程通信

可以看到我们的Log在启动和退出MainActivity的时候 分别启动了Service 和停止了Service。这很简单,接下来我们看跨进程启动。

2. 跨进程启动Service

  • 重新创建一个module (anotherApp)
  • 在anotherApp中启动上面创建的app中的service

我们先看第一个,创建module 之后

Android AIDL跨进程通信

修改AnotherApp中的MainActivity :

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                break;

            case R.id.btn_stopAppService:
                break;
        }
    } 
           

那么问题来了,我们如何启动App中的服务呢?很明显,我们不可能像在同一个进程中,直接获取到指定的类的定义

其实,我们可以使用Intent。在配置文件中直接给service配置intent-filter。注意:(Android5.0之后的版本是不可以这样的,从5.0开始(包括5.0),必须使用显示Intent启动。)

我们可以在AnotherApp代码中这样写:

private Intent Serviceintent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Serviceintent = new Intent();
        Serviceintent.setComponent(new   ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                break;
        }
    }
           

我们new 了一个Intent,并且给他设置了一个setComponent,其中第一个参数传递的是要启动的那个app的包名,第二个为要启动的类名。

运行AnotherApp的结果:

Android AIDL跨进程通信

可以看到在AnotherApp中开启和停止 App中的Service依然是成功的。

注意,划红线的地方要调整到你要启动的应用包名,否则看不到对应的日志。

与Service跨应用通信(一)

上面是启动service,现在我们说下绑定service。

我们知道一个程序B想绑定A中的service,我们是拿不到A中定义的IBind对象的,那么这们该如何去做呢?不要着急,我们的问题Android系统以及给我们提供了方法,AIDL应运而生!

  • 在App中创建ADIL文件
  • 重写App中的Service中的onBind方法
  • 修改AnotherApp中的主函数逻辑

创建aidl文件后,系统会自动生成相关的类。

Android AIDL跨进程通信
interface AppServiceBinder {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
           

那么此时,我们就可以直接在MyService中的onBind方法中直接返回 并且实现aidl的方法:(注意,在使用之前,请先Build——Rebuild Project)

public class MyService extends Service {
    public MyService() {

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new AppServiceBinder.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }
        };
    }

    @Override
    public void onCreate() {
        super.onCreate();

        Log.e("MyService","Started Service");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService","Destroy Service");
    }
}
           

最后,我们去修改AnotherApp中的逻辑:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private Intent Serviceintent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Serviceintent = new Intent();
        Serviceintent.setComponent(new ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
        findViewById(R.id.btn_bindAppService).setOnClickListener(this);
        findViewById(R.id.btn_unbindAppService).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                stopService(Serviceintent);
                break;

            case R.id.btn_bindAppService:
                bindService(Serviceintent,this, Context.BIND_AUTO_CREATE);
                break;

            case R.id.btn_unbindAppService:
                unbindService(this);
                break;
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.e("anotherApp","Bind Service");
        Log.e("anotherApp", String.valueOf(iBinder));
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }
}
           

我们运行:

运行的时候需要注意几个问题:

  • App要重新启动
  • 启动AnotherApp

App的日志:

我们点击AnotherApp中的启动,停止,绑定,接触绑定,都会调用到App中的MyService服务

Android AIDL跨进程通信

这时候,我们调换一下包名,看看AnotherApp的日志:

Android AIDL跨进程通信

可以看到,此时AnotherApp已经知道了绑定App中的服务成功了,并且也获取到了IBind。

到此,我们绑定了服务,那么我们如何去和他进行通信呢?因为不通信,我们绑定服务是没有意义的。好,别着急,请看下面:

与Service跨应用通信(二)

目的:

修改App中的数据。

步骤:

  • 在aidl文件中添加修改数据方法
  • 在App中的MyService重写新增的方法
  • 在App中的MyService的OnCreate方法中启动一个循环数据线程
  • 在AnotherApp中操作进行数据同步到App中

AIDL中添加方法 setData() 并重写MyService中的OnBind方法:

interface AppServiceBinder {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    void setData(String data);
}
           
@Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new AppServiceBinder.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public void setData(String data) throws RemoteException {
                MyService.this.data = data;
            }
        };
    }
           

在App中的MyService的OnCreate方法中启动一个循环数据线程,每个1s输出一遍数据,整个MyService代码:

public class MyService extends Service {
    public MyService() {

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new AppServiceBinder.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public void setData(String data) throws RemoteException {
                MyService.this.data = data;
            }
        };
    }

    @Override
    public void onCreate() {
        super.onCreate();

        new Thread(){
            @Override
            public void run() {
                super.run();

                running = true;

                while (running) {

                    Log.e("MyService",data);

                    try {
                        sleep();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService","Destroy Service");

        running = false;
    }

    private String data = "AIDL学习";
    private boolean running = false;
}
           

接着我们去修改AnotherApp:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private Intent Serviceintent;

    private EditText et;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Serviceintent = new Intent();
        Serviceintent.setComponent(new ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
        findViewById(R.id.btn_bindAppService).setOnClickListener(this);
        findViewById(R.id.btn_unbindAppService).setOnClickListener(this);
        findViewById(R.id.btn_Sync).setOnClickListener(this);

        et = (EditText) findViewById(R.id.et);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                stopService(Serviceintent);
                break;

            case R.id.btn_bindAppService:
                bindService(Serviceintent,this, Context.BIND_AUTO_CREATE);
                break;

            case R.id.btn_unbindAppService:
                unbindService(this);
                break;

            case R.id.btn_Sync:

                break;
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.e("anotherApp","Bind Service");
        Log.e("anotherApp", String.valueOf(iBinder));
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }
}
           

到这里,问题来了,我们同步的时候该如何去使用呢。我们需要有一个IBinder,然后我们如何去通过IBinder很方便的去执行一个远程的函数呢?

我们需要给AnotherApp也创建一个AIDL文件。并且一定要保证同样的包名:

如何在AnotherApp中创建包名:

Android AIDL跨进程通信

然后在这个文件中创建一个包,注意,包名要和App的AIDL包名一样,然后将那个AIDL文件拷贝过来。

此时,AnotherApp有了一个同样的AIDL文件,意味着可以在里面生成同样的AIDL相关定义。(重新Rebuild)

这样,我们修改AnotherApp的MainActivity:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private Intent Serviceintent;

    private EditText et;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Serviceintent = new Intent();
        Serviceintent.setComponent(new ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
        findViewById(R.id.btn_bindAppService).setOnClickListener(this);
        findViewById(R.id.btn_unbindAppService).setOnClickListener(this);
        findViewById(R.id.btn_Sync).setOnClickListener(this);

        et = (EditText) findViewById(R.id.et);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                stopService(Serviceintent);
                break;

            case R.id.btn_bindAppService:
                bindService(Serviceintent,this, Context.BIND_AUTO_CREATE);
                break;

            case R.id.btn_unbindAppService:
                unbindService(this);
                break;

            case R.id.btn_Sync:
                if (binder != null){
                    try {
                        binder.setData(et.getText().toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

        Log.e("anotherApp","Bind Service");
        Log.e("anotherApp", String.valueOf(iBinder));

        binder = AppServiceBinder.Stub.asInterface(iBinder);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }

    private AppServiceBinder binder = null;
}
           

注意::!!

在onServiceConnected,不可以直接这样写binder = (AppServiceBinder) iBinder;,因为binder 和iBnder是在两个应用程序中,在内存中也是所占用两个不同的地址。

注意:运行前把App中的启动和停止服务代码注释掉

我们观察下结果:

Android AIDL跨进程通信

在App中,每个1S就会输出一条数据,然后我们点击同步数据,就会发现,数据已经改变了

Android AIDL跨进程通信

到此,我们已经成功的在另一个应用中通过远程方法将数据传递给了当前应用中的服务!

源码有需要的私信我