使用AIDL实现IPC通信之——简单调用远程服务的方法

来源:互联网 发布:linux 只显示文件大小 编辑:程序博客网 时间:2024/05/16 15:28

为什么使用AIDL

AIDL和Messenger都可以实现跨进程通信,Messenger底层也是基于AIDL的。使用AIDL而不使用Messenger的情况是:允许从不同应用的客户端访问你的Service且你的Service需要处理多线程的情况,因为Messenger中的消息默认是串行执行的。还有一点就是:使用Messenger主要是为了传递消息,很多时候需要跨进程调用服务端的方法,这种情况Messenger就无法做到了,可以使用AIDL实现跨进程的方法的调用。


AIDL主要分为客户端和服务端实现:


1、服务端实现:

1、创建aidl文件:在Android Studio当中创建一个文件夹aidl,然后再找个文件夹中创建一个包,在这个包里面创建一个aidl文件,如下所示。在接口中定义了三个方法。当重新Make Project,会在Android Studio的工程目录:build/generated/source/aidl/debug/aidl包名 目录下面生成对应的.java文件。这个.java文件就是对Binder的封装,使得我们不需要直接对Binder进行操作。

// IRemoteService.aidlpackage com.easyliu.demo.aidlremotedemo;// Declare any non-default types here with import statementsinterface IRemoteService {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    int getPid();    int add(int a,int b);    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);}

2、创建一个Service,在Service当中使用上面的aidl接口创建一个IBinder对象,然后在Service的onBind()方法中返回这个IBinder对象即可。如下所示:

package com.easyliu.demo.aidlremotedemo;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.Process;import android.os.RemoteException;import android.util.Log;public class RemoteService extends Service {    private static final String TAG = RemoteService.class.getSimpleName();    @Override    public void onCreate() {        super.onCreate();    }    @Override    public IBinder onBind(Intent intent) {        Log.i(TAG, "onBind");        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        Log.i(TAG, "onUnbind");        return super.onUnbind(intent);    }    @Override    public void onDestroy() {        Log.i(TAG, "onDestroy");        super.onDestroy();    }    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {        @Override        public int getPid() throws RemoteException {            return Process.myPid();        }        @Override        public int add(int a, int b) throws RemoteException {            return a + b;        }        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {        }    };}

3、然后记得在Manifest文件里面进行注册,不过Android Studio会自动生成,这一点很方便。在这里指定了一个action,这样别的进程就可以通过Intent来查找此Service。注意,当给Service加上Intent-filter之后,其android:exported默认就为true了,这样别的APP就可以访问了。同时,设置了android:process属性,让Service运行在不同的进程。这跟运行在两个APP当中效果是一样的,所以可以直接放在一个APP当中进行测试。

 <service            android:name=".RemoteService"            android:process=":remote"            android:enabled="true">            <intent-filter>                <action android:name="com.easyliu.demo.aidlremotedemo.RemoteService" />            </intent-filter>        </service>

2、客户端实现:

客户端的代码比较简单,在布局文件当中创建两个文本输入框、一个按钮和一个文本显示控件。点击按钮调用远程服务端的方法执行加法操作,然后在文本控件中显示。

布局文件:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout 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: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="com.easyliu.demo.aidlremotedemo.MainActivity">    <EditText        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:id="@+id/et_mainActivity_inputA"        android:layout_alignParentTop="true"        android:layout_alignParentLeft="true"        android:layout_alignParentStart="true"        android:hint="input a number a"        android:inputType="number" />    <EditText        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:id="@+id/et_mainActivity_inputB"        android:layout_below="@+id/et_mainActivity_inputA"        android:layout_alignParentLeft="true"        android:layout_alignParentStart="true"        android:hint="input a number b"        android:inputType="number" />    <Button        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="Add"        android:id="@+id/btn_mainActivity_add"        android:layout_below="@+id/et_mainActivity_inputB"        android:layout_alignParentLeft="true"        android:layout_alignParentStart="true" />    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="1"        android:textSize="20sp"        android:layout_marginTop="20dp"        android:id="@+id/tv_sum"        android:layout_below="@+id/btn_mainActivity_add"        android:layout_centerHorizontal="true" /></RelativeLayout>

主Activity:

package com.easyliu.demo.aidlremotedemo;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity {    private AdditionServiceConnection mServiceConnection;    private EditText et_mainActivity_inputA;    private EditText et_mainActivity_inputB;    private Button btn_mainActivity_add;    private TextView tv_sum;    private boolean mIsBound;    private IRemoteService mService;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initViews();    }    @Override    protected void onStart() {        super.onStart();        doBindService();    }    /**     * init Views     */    private void initViews() {        et_mainActivity_inputA = (EditText) findViewById(R.id.et_mainActivity_inputA);        et_mainActivity_inputB = (EditText) findViewById(R.id.et_mainActivity_inputB);        tv_sum = (TextView) findViewById(R.id.tv_sum);        btn_mainActivity_add = (Button) findViewById(R.id.btn_mainActivity_add);        btn_mainActivity_add.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                try {                    if (mService != null) {                        int sum = mService.add(Integer.parseInt(et_mainActivity_inputA.getText().toString()),                                Integer.parseInt(et_mainActivity_inputB.getText().toString()));                        tv_sum.setText(String.valueOf(sum));                    } else {                        tv_sum.setText("请先绑定服务!");                    }                } catch (RemoteException e) {                    e.printStackTrace();                }            }        });    }    /**     * bind service     */    private void doBindService() {        mServiceConnection = new AdditionServiceConnection();        Intent intent = new Intent(RemoteService.class.getName());        intent.setPackage("com.easyliu.demo.aidlremotedemo");        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);    }    /**     * unbind service     */    private void doUnbindService() {        if (mIsBound) {            unbindService(mServiceConnection);            mServiceConnection = null;            mIsBound = false;        }    }    /**     * ServiceConection     */    class AdditionServiceConnection implements ServiceConnection {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mService = IRemoteService.Stub.asInterface((IBinder) service);            mIsBound = true;            try {                //设置死亡代理                service.linkToDeath(mDeathRecipient, 0);            } catch (RemoteException e) {                e.printStackTrace();            }            tv_sum.setText("Servie Conected!");        }        @Override        public void onServiceDisconnected(ComponentName name) {            mService = null;            mIsBound = false;            tv_sum.setText("Servie DisConected!");        }    }    /**     * 监听Binder是否死亡     */    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {        @Override        public void binderDied() {            if (mService == null) {                return;            }            mService.asBinder().unlinkToDeath(mDeathRecipient, 0);            mService = null;            //重新绑定            doBindService();        }    };    @Override    protected void onStop() {        super.onStop();        doUnbindService();    }    @Override    protected void onDestroy() {        super.onDestroy();    }}

在主Activity当中,在onStart方法当中bindService,然后在onStop方法当中unBindService。当然你可以在onCreate方法当中bindService,然后在onDestory方法unBindService,看具体需求。也可以手动进行绑定和解绑。

同时给返回的IBinder对象设置了一个死亡代理,当远端Service由于某种原因死亡的时候,就会调用此回调方法,我们就可以在此方法当中进行一些操作,比如,重新bindService等。

当然,也可以在onServiceDisconnected里面重新连接Service,只是这个方法运行在主线程,可以访问主UI,而DeathRecipient接口的binderDied回调方法中不能访问主UI。


3、验证AIDL功能

当执行打开主界面->回到home->再进入主界面,打印的log如下所示,说明远程绑定成功。当回到桌面的时候,会调用Activity的onStop方法,在里面会解除绑定。由于这里只有一个客户端绑定到Service,所以当绑定解除的时候就销毁了。关于绑定服务的详细简介,请参考:Bound Service

<span style="font-size:12px;">06-18 21:59:11.313 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onBind06-18 21:59:36.842 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onUnbind06-18 21:59:36.842 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onDestroy06-18 21:59:51.743 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onBind</span><strong style="font-size: 24px;"></strong>

下面来看一下执行效果

启动Activity,输入两个数字1和7,点击ADD按钮,得到数字8,说明成功调用远程服务端的方法。


然后当按home键退回桌面再打开,界面如下所示,说明当返回Activity的时候调用了onStart方法,重新bindService。


0 0
原创粉丝点击