Android Service 远程服务

标签: android
4234人阅读 评论(3) 收藏 举报
分类:

远程服务的创建和调用需要使用AIDL语言,步骤如下:

  1. 使用AIDL语言定义远程服务的接口

  2. 通过继承Service类实现接口中定义的方法和属性

  3. 绑定和使用远程服务

以下为一个简单Demo ,RemoteMathCallerDemo界面如下:
这里写图片描述

绑定远程服务后,调用RemoteMathServiceDemo中的MathService服务进行加法运算。

1.使用AIDL语言定义远程服务的接口

以Android Studio为例,首先需要建立对应目录及aidl文件,如下:
这里写图片描述

(比如直接在java目录下的包上右键新建aidl文件 IDE会自动生成aidl目录及该目录下的包和文件这样的小技巧我可不会随便告诉别人)
IMathService.aidl文件内容如下:

// IMathService.aidl
package com.example.remotemathservicedemo;

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

interface IMathService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     long Add(long a,long b);
}

然后在build目录下会自动生成与该aidl文件对应的java接口文件,(若没有生成则重新make project) 如下:
这里写图片描述

在看IMathService.java内容之前呢,不知道你有没有注意到,我前两张截图都截到了上面的一个Module:remotemathcallerdemo,这个就是调用端,目前我们编辑的remotemathservicedemo是服务端。

下面为IMathService.java的完整代码,加上了我自己的理解和注释:

/*
 * 这个文件是自动生成的。不要修改
 */
package com.example.remotemathservicedemo;
/*   在这里声明任何非默认类型
     所有使用AIDL建立的接口都必须继承 android.os.IInterface 基类接口
     这个基类接口中定义了 asBinder()方法 用来获取Binder对象
 */

public interface IMathService extends android.os.IInterface {
    /**
     * 本地IPC实现stub类
     */
    public static abstract class Stub extends android.os.Binder implements com.example.remotemathservicedemo.IMathService {

        private static final java.lang.String DESCRIPTOR = "com.example.remotemathservicedemo.IMathService";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        //asInterface(IBinder) 是Stub内部的远程服务接口,调用者可以通过该方法获得远程服务的实例
        public static com.example.remotemathservicedemo.IMathService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //判断android.os.IInterface实例是否为本地服务 若是返回android.os.IInterface
            //若不是本地服务 构造Proxy对象并返回之
            if (((iin != null) && (iin instanceof com.example.remotemathservicedemo.IMathService))) {
                return ((com.example.remotemathservicedemo.IMathService) iin);
            }
            return new com.example.remotemathservicedemo.IMathService.Stub.Proxy(obj);
        }

        //实现了android.os.IInterface接口定义的asBinder()方法
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /*
        Parcel是Android系统应用程序间传递数据的容器,能够在两个进程中完成打包和拆包的工作
             但Parcel不同于通用意义上的序列化
        Parcel的设计目的是用于高性能IPC传输 不能将其保存在持久存储设备上
         */
        //接收Parcel对象,并从中逐一读取每个参数,然后调用Service内部制定的方法,将结果写进另一个Parcel对象,
        // 准备将这个Parcel对象返回给远程的调用者
        @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_Add: {
                    data.enforceInterface(DESCRIPTOR);
                    long _arg0;
                    _arg0 = data.readLong();
                    long _arg1;
                    _arg1 = data.readLong();
                    long _result = this.Add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeLong(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        //用来实现远程服务调用
        private static class Proxy implements com.example.remotemathservicedemo.IMathService {
            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;
            }

            //以一定顺序将所有参数写入Parcel对象,以供Stub内部的onTransact()方法获取参数
            @Override
            public long Add(long a, long b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                long _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeLong(a);
                    _data.writeLong(b);
                    mRemote.transact(Stub.TRANSACTION_Add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readLong();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_Add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

public long Add(long a, long b) throws android.os.RemoteException;
}

IMathService.aidl是对远程服务接口的定义,自动生成的IMathService.java内部实现了远程服务数据传递的相关方法,下一步介绍如何实现远程服务,这需要建立一个Service类,并在该类中通过onBind()方法返回IBinder对象,这样调用者使用获取的IBinder对象就可以访问远程服务。

下面是MathService.java的完整代码:

package com.example.remotemathservicedemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.widget.Toast;

/**
 * Created by yinghao on 2016/5/7.
 */
public class MathService extends Service {
    /*
           1. 建立 IMathService.Stub的实例mBinder并实现AIDL文件定义的远程服务接口
           2. 在onBind()方法中将mBinder返回给远程调用者
     */

    private final IMathService.Stub mBinder = new IMathService.Stub(){
        @Override
        public long Add(long a, long b) throws RemoteException {
            return a + b;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(MathService.this, "远程绑定:MathService", Toast.LENGTH_SHORT).show();
        return mBinder;
    }

    //Return true if you would like to have the service's onRebind method later called when new clients bind to it.
    @Override
    public boolean onUnbind(Intent intent) {
        Toast.makeText(MathService.this, "取消远程绑定", Toast.LENGTH_SHORT).show();
        return false;
    }
}

最后一步,注册service:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.remotemathservicedemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service android:name=".MathService"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.remote.MathService" />
            </intent-filter>
        </service>
    </application>

</manifest>

到这里我们的服务端Module : remotemathservicedemo 就完成了。

这完成了文章开头所列举的三个步骤的前两步,最后一步,让我们来看看怎么绑定和使用远程服务吧。

首先,我们需要引入与服务端相同的aidl文件并确保自动生成对应的IMathService.java接口文件

那么为什么要这样做呢? 这就需要我们了解aidl文件和对应接口文件的用处到底是什么,为了时数据能穿越进程边界,所有数据都必须是“打包”,而自动生成的IMathService.java内部实现了远程服务数据传递的相关方法,那么服务端就有了将数据打包、拆包的能力。而调用端也需要发出数据和接收数据,也需要有将数据打包、拆包的能力,所以它也需要IMathService.java这个类。

然后对远程服务的绑定与调用,其实与本地服务的绑定区别不大,不同之处主要包括两处:

  1. 使用IMathService生命远程服务实例

  2. 通过IMathService.Stub的asInterface()方法获取服务实例

下面为remotemathcallerdemo Module中MainActivity.java的完整代码:

package com.example.remotemathcallerdemo;

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.TextView;

import com.example.remotemathservicedemo.IMathService;

import org.w3c.dom.Text;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Button bind;
    private Button unbind;
    private Button add;

    private boolean isBound = false;
    private IMathService mathService;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mathService = IMathService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mathService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
        bind = (Button) findViewById(R.id.bind);
        unbind = (Button) findViewById(R.id.unbind);
        add = (Button) findViewById(R.id.add);

        bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isBound) {
                    final Intent serviceIntent = new Intent();
                    serviceIntent.setAction("com.example.remote.MathService");
                    bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
                    isBound = true;
                }
            }
        });

        unbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isBound) {
                    unbindService(mConnection);
                    isBound = false;
                    mathService = null;
                }
            }
        });

        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mathService == null) {
                    textView.setText("未绑定远程服务");
                    return;
                }
                long a = Math.round(Math.random() * 100);
                long b = Math.round(Math.random() * 100);
                long result = 0;
                try {
                    result = mathService.Add(a, b);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                String msg = String.valueOf(a) + "+" + String.valueOf(b) + "=" + String.valueOf(result);
                textView.setText(msg);
            }
        });
    }
}

在此例中传递的数据类型为基本数据类型,打包过程是自动完成的,但对于自定义的数据类型,用户则需要实现Parcelable接口,使自定义的数据类型能够转换为系统级原语保存在Parcel对象中,穿越进程边界后可再转换为初始格式,关于自定义数据类型的传递,在下一篇文章中归纳总结。

2
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    旨在匠心写作,精益求精

     个人博客 


       github   


     简        书 


    个人资料
    • 访问:193550次
    • 积分:2023
    • 等级:
    • 排名:千里之外
    • 原创:34篇
    • 转载:0篇
    • 译文:0篇
    • 评论:222条
    文章分类