Android Service 远程服务

远程服务的创建和调用需要使用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对象中,穿越进程边界后可再转换为初始格式,关于自定义数据类型的传递,在下一篇文章中归纳总结。

时间: 2024-10-09 21:18:43

Android Service 远程服务的相关文章

Android:远程服务Service(含AIDL &amp; IPC讲解)

前言 Service作为Android四大组件之一,应用非常广泛 本文将介绍Service其中一种常见用法:远程Service 如果你对Service还未了解,建议先阅读我写的另外一篇文章: Android四大组件:Service史上最全面解析 目录 1. 远程服务与本地服务的区别 远程服务与本地服务最大的区别是:远程Service与调用者不在同一个进程里(即远程Service是运行在另外一个进程):而本地服务则是与调用者运行在同一个进程里 二者区别的详细区别如下图: 2. 使用场景 多个应用程

android Service学习笔记

服务简介 Service在继承关系上是Activity的大爷,也就是说Activity多继承了一个类,封装了界面相关,等等其他操作. 服务没有onPause.onStop.onResume.onRestart方法,因为service没有界面,长期运行在后台. startService方式启动服务 Start方式启动服务声明周期: 服务被创建时依次调用onCreate.onStartCommand: 同一个服务只能被创建一次(onCreate),再次创建就只会执行onStartCommand: 一

Android Service AIDL 远程调用服务 简单音乐播放实例的实现

Android Service是分为两种: 本地服务(Local Service): 同一个apk内被调用 远程服务(Remote Service):被另一个apk调用 远程服务需要借助AIDL来完成. AIDL 是什么 AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码.如果在一个进程中(例如Activi

android Service Activity三种交互方式(付源码)(转)

android Service Activity三种交互方式(付源码) Android应用服务器OSBeanthread android Service Binder交互通信实例 最下边有源代码: android SDK提供了Service,用于类似*nix守护进程或者windows的服务. Service有两种类型: 本地服务(Local Service):用于应用程序内部 远程服务(Remote Sercie):用于android系统内部的应用程序之间 前者用于实现应用程序自己的一些耗时任务

Android Service的两种启动方式

参考链接 1. 概念 开始,先稍稍讲一点android中Service的概念和用途吧~ Service分为本地服务(LocalService)和远程服务(RemoteService): 1.本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC, 也不需要AIDL.相应bindService会方便很多.主进程被Kill后,服务便会终止. 2.远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符

Android Service完全解析,关于服务你所需知道的一切(下) (转载)

转自:http://blog.csdn.net/guolin_blog/article/details/9797169 转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在 上一篇文章中,我们学习了Android Service相关的许多重要内容,包括Service的基本用法.Service和Activity进行通信.Service的销毁方式. Service与Thread的关系.以及如何创建前台Service.以上

Android Service完全解析,关于服务你所需知道的一切(上)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了.Service作为Android四大组件之一,在每一个应用程序中都扮演着非常重要的角色.它主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务.必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持

Android Service完全解析,关于服务你所需知道的一切(下)

转载请注册出处:http://blog.csdn.net/guolin_blog/article/details/9797169 在上一篇文章中,我们学习了Android Service相关的许多重要内容,包括Service的基本用法.Service和Activity进行通信.Service的销毁方式.Service与Thread的关系.以及如何创建前台Service.以上所提到的这些知识点,基本上涵盖了大部分日常开发工作当中可能使用到的Service技术.不过关于Service其实还有一个更加

Android Service演义

摘要: 本文基于Android 5.1代码,介绍了Android Service的运作机理.按理说,网上此类文章已经很多了,本不需我再赘述.但每个人理解技术的方式多少会有所不同,我多写一篇自己理解的service,也未尝不可吧. (本文以Android 5.1为准) 侯亮 1.概述 在Android平台上,那种持续性工作一般都是由service来执行的.不少初学者总是搞不清service和线程.进程之间的关系,这当然会影响到他们开展具体的开发工作. 其实,简单说起来,service和线程.进程是