Android AIDL:跨进程调用Service (AIDL Service)

来源:互联网 发布:高清矩阵 编辑:程序博客网 时间:2024/04/29 14:15

一、概述

开发环境:Android Studio 1.4.1
Android Developer Guides : Bound Services 和 AIDL .

1. 相关知识点

Android系统中的进程之间不能共享内存,需要提供一些机制在不同进程之间进行数据通信,这个机制就是AIDL。
1. AIDL: Android Interface definition language
Android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。
2. IPC:interprocess communication (内部进程通信)
3. 如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

2. 选择AIDL的使用场合

官方文档特别提醒我们何时使用AIDL是必要的:

  • 只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。
  • 如果不需要进行不同应用程序间的并发通信(IPC),you should create your interface by implementing a Binder;或者你想进行IPC,但不需要处理多线程的,则implement your interface using a Messenger。
  • 无论如何,在使用AIDL前,必须要理解如何绑定service——bindService。

二、跨应用启动Service

从 Android 5.0 以后只能通信显式 Intent 来启动服务。
在这里我们新建一个Project:StartServiceFromAnotherAPP

  • 再创建一个Service:APPService
    • 重写 onCreate(),onDestroy()方法如下
    @Override    public void onCreate() {        super.onCreate();        Log.i("TAG","AppService onCreate");    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i("TAG", "AppService onDestroy");    }
public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        startService(new Intent(this, AppService.class));    }    @Override    protected void onDestroy() {        super.onDestroy();        stopService(new Intent(this, AppService.class));    }}

启动应用和退出后可以看到日志信息:
这里写图片描述
至此可以发现在同一个应用中启动和停止Service是非常简单的。

那么如何在另一个应用中启动Service呢?

  • 下面我们在该Project中新建一个Module:AnotherApp
  • 在布局文件中加入两个Button:“启动服务”和“停止服务”

这里写图片描述
修改 MainActivity.java 代码 :

public class MainActivity extends AppCompatActivity implements OnClickListener {    private Intent mServiceIntent;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        findViewById(R.id.btn_start_Service).setOnClickListener(this);        findViewById(R.id.btn_stop_Service).setOnClickListener(this);        mServiceIntent = new Intent();        mServiceIntent.setComponent(            new ComponentName("com.zhuanghongji.startservicefromanotherapp",                "com.zhuanghongji.startservicefromanotherapp.AppService"));    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_start_Service:                startService(mServiceIntent);                break;            case R.id.btn_stop_Service:                stopService(mServiceIntent);                break;        }    }}

分别点击两个按钮同样可以看到输出的日志:
这里写图片描述

我们也可以在Service中重写 onStartCommand() 来接收其他应用传递过来的数据。

    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        return super.onStartCommand(intent, flags, startId);        // 做你想做的事    }

三、跨应用绑定Service

1. 创建AIDL文件

右键app包名新建.aidl文件:

  • 自动生成的代码如图中右边所示(IAppServiceRemoteBinder.aidl)。
  • 同时也自动新建了个aidl文件夹,.aidl文件就是在这个文件夹下(图中左边)。

这里写图片描述

Build->Make Project 发现在…debug目录下自动生成了对应的java接口文件
这里写图片描述

该文件代码如下:

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: F:\\AndroidG\\StartServiceFromAnotherApp\\app\\src\\main\\aidl\\com\\zhuanghongji\\startservicefromanotherapp\\IAppServiceRemoteBinder.aidl */package com.zhuanghongji.startservicefromanotherapp;// Declare any non-default types here with import statementspublic interface IAppServiceRemoteBinder extends android.os.IInterface {    /**     * Local-side IPC implementation stub class.     */    public static abstract class Stub extends android.os.Binder implements com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder {        private static final java.lang.String DESCRIPTOR = "com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder interface,         * generating a proxy if needed.         */        public static com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder))) {                return ((com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder) iin);            }            return new com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder.Stub.Proxy(obj);        }        @Override        public android.os.IBinder asBinder() {            return this;        }        @Override        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {            switch (code) {                case INTERFACE_TRANSACTION: {                    reply.writeString(DESCRIPTOR);                    return true;                }                case TRANSACTION_basicTypes: {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    long _arg1;                    _arg1 = data.readLong();                    boolean _arg2;                    _arg2 = (0 != data.readInt());                    float _arg3;                    _arg3 = data.readFloat();                    double _arg4;                    _arg4 = data.readDouble();                    java.lang.String _arg5;                    _arg5 = data.readString();                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);                    reply.writeNoException();                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        private static class Proxy implements com.zhuanghongji.startservicefromanotherapp.IAppServiceRemoteBinder {            private android.os.IBinder mRemote;            Proxy(android.os.IBinder remote) {                mRemote = remote;            }            @Override            public android.os.IBinder asBinder() {                return mRemote;            }            public java.lang.String getInterfaceDescriptor() {                return DESCRIPTOR;            }            /**             * Demonstrates some basic types that you can use as parameters             * and return values in AIDL.             */            @Override            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(anInt);                    _data.writeLong(aLong);                    _data.writeInt(((aBoolean) ? (1) : (0)));                    _data.writeFloat(aFloat);                    _data.writeDouble(aDouble);                    _data.writeString(aString);                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }        }        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    }    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;}/*Stub对象是在被调用端进程,也就是服务端进程 */

在Service中重写onBind()方法:

    @Override    public IBinder onBind(Intent intent) {        return new IAppServiceRemoteBinder.Stub() {            @Override            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {                // ....            }        };    }

这时重写AnotherApp中的MainActivity:

public class MainActivity extends AppCompatActivity implements OnClickListener, ServiceConnection {    private Intent mServiceIntent;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        findViewById(R.id.btn_start_Service).setOnClickListener(this);        findViewById(R.id.btn_stop_Service).setOnClickListener(this);        findViewById(R.id.btn_bind_Service).setOnClickListener(this);        findViewById(R.id.btn_unbind_Service).setOnClickListener(this);        mServiceIntent = new Intent();        mServiceIntent.setComponent(new ComponentName("com.zhuanghongji.startservicefromanotherapp",                "com.zhuanghongji.startservicefromanotherapp.AppService"));    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_start_Service:                startService(mServiceIntent);                break;            case R.id.btn_stop_Service:                stopService(mServiceIntent);                break;            case R.id.btn_bind_Service:                bindService(mServiceIntent, this, Context.BIND_AUTO_CREATE);                break;            case R.id.btn_unbind_Service:                unbindService(this);                break;        }    }    @Override    public void onServiceConnected(ComponentName name, IBinder service) {        Log.i("TAG", "Bind Service : " + service);    }    @Override    public void onServiceDisconnected(ComponentName name) {    }}

重新安装后,点击“绑定外部服务”
这里写图片描述

会看到如下日志:
这里写图片描述

四、跨应用绑定Service并通信

  1. 在app 中的IAppServiceRemoteBinder增加接口方法:void setData(String data);
  2. 复制该文件的包名com.zhuanghongji.startservicefromanotherapp
  3. 在AnotherApp中新建一个AIDL Folder,再在里面新建一个包(包名是刚才复制的包名,否则会编译错误)。
  4. IAppServiceRemoteBinder.aidl文件整个复制到刚才新建的包下。
// IAppServiceRemoteBinder.aidlpackage com.zhuanghongji.startservicefromanotherapp;// Declare any non-default types here with import statementsinterface IAppServiceRemoteBinder {    /**     * 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);}

Build -> ReBuild Project 后在AppService中的onBind(Intent intent)中实现void setData(String data);

package com.zhuanghongji.startservicefromanotherapp;public class AppService extends Service {    private String mData = "默认数据";    private boolean isRunning = false;    public AppService() {    }    @Override    public void onCreate() {        super.onCreate();        Log.i("TAG", "AppService onCreate");        new Thread(new Runnable() {            @Override            public void run() {                isRunning = true;                while (isRunning) {                    Log.i("TAG", mData);                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }).start();    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i("TAG", "AppService onDestroy");        isRunning = false;    }    @Override    public IBinder onBind(Intent intent) {        return new IAppServiceRemoteBinder.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 {                mData = data;            }        };    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        return super.onStartCommand(intent, flags, startId);        // 做你想做的事    }}

此时务必要在App的MainActivity中注释掉 startServicestopService

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);//        startService(new Intent(this, AppService.class));    }    @Override    protected void onDestroy() {        super.onDestroy();//        stopService(new Intent(this, AppService.class));    }}

分别运行app和anotherApp后点击“绑定外部服务”后会发现日志每隔一秒打印出“默认数据”。
点击“同步按钮”后变成打印“这是另一个应用中的数据”。
至此就实现了跨进程间的通信。

下面给出anotherApp的主布局及其xml代码:
这里写图片描述

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Hello World!"/>    <Button        android:id="@+id/btn_start_Service"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="启动服务"/>    <Button        android:id="@+id/btn_stop_Service"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="停止服务"/>    <Button        android:id="@+id/btn_bind_Service"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="绑定外部服务"/>    <Button        android:id="@+id/btn_unbind_Service"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="解除绑定外部服务"/>    <EditText        android:id="@+id/edit_text"        android:text="这是另一个应用中的数据"        android:layout_width="match_parent"        android:layout_height="wrap_content"/>    <Button        android:id="@+id/btn_sync_data"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="同步按钮"/></LinearLayout>
0 0