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

为什么使用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.aidl
package com.easyliu.demo.aidlremotedemo;

// Declare any non-default types here with import statements

interface 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: onBind
06-18 21:59:36.842 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onUnbind
06-18 21:59:36.842 2471-2471/com.easyliu.demo.aidlremotedemo:remote I/RemoteService: onDestroy
06-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。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值