IPC机制之远程服务

2.Android:远程服务Service(含AIDL & IPC讲解)--转载

1. Android:(本地、可通信的、前台、远程)Service使用全面介绍 

=====

 2.Android:远程服务Service(含AIDL & IPC讲解)--转载

前言

  • Service作为Android四大组件之一,应用非常广泛
  • 本文将介绍Service其中一种常见用法:远程Service

如果你对Service还未了解,建议先阅读我写的另外一篇文章:
Android四大组件:Service史上最全面解析


目录

 
目录

1. 远程服务与本地服务的区别

  • 远程服务与本地服务最大的区别是:远程Service与调用者不在同一个进程里(即远程Service是运行在另外一个进程);而本地服务则是与调用者运行在同一个进程里
  • 二者区别的详细区别如下图:
 
按运行地点分类

2. 使用场景

多个应用程序共享同一个后台服务(远程服务)

即一个远程Service与多个应用程序的组件(四大组件)进行跨进程通信

 
使用场景

3. 具体使用

  • 为了让远程Service与多个应用程序的组件(四大组件)进行跨进程通信(IPC),需要使用AIDL
  1. IPC:Inter-Process Communication,即跨进程通信
  2. AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
  • 在多进程通信中,存在两个进程角色(以最简单的为例):服务器端和客户端

  • 以下是两个进程角色的具体使用步骤:
    服务器端(Service)
    步骤1:新建定义AIDL文件,并声明该服务需要向客户端提供的接口
    步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()、blabla)
    步骤3:在AndroidMainfest.xml中注册服务 & 声明为远程服务

    客户端(Client)
    步骤1:拷贝服务端的AIDL文件到目录下
    步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
    步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service

接下来,我将用一个具体实例来介绍远程Service的使用


4. 具体实例

  • 实例描述:客户端远程调用服务器端的远程Service
  • 具体使用:

4.1 服务器端(Service)

新建一个服务器端的工程:Service - server

先下Demo再看,效果会更好:Github_RemoteService_Server

步骤1. 新建一个AIDL文件

 
新建AIDL文件

步骤2. 在新建AIDL文件里定义Service需要与Activity进行通信的内容(方法),并进行编译(Make Project)

// 在新建的AIDL_Service1.aidl里声明需要与Activity进行通信的方法
package scut.carson_ho.demo_service;

interface AIDL_Service1 {
    void AIDL_Service();
}
//AIDL中支持以下的数据类型
//1. 基本数据类型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型;
//4. AIDL自动生成的接口(需要导入-import)
//5. 实现android.os.Parcelable 接口的类(需要导入-import)

 
编译

步骤3:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()、blabla)
MyService.java

public class MyService extends Service {
    
    // 实例化AIDL的Stub类(Binder的子类)
    AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {

        //重写接口里定义的方法
        @Override
        public void AIDL_Service() throws RemoteException {
            System.out.println("客户端通过AIDL与远程后台成功通信");
        }
    };

//重写与Service生命周期的相关方法
    @Override
    public void onCreate() {
        super.onCreate();

        System.out.println("执行了onCreat()");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("执行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        System.out.println("执行了onBind()");
    //在onBind()返回继承自Binder的Stub类型的Binder,非常重要
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("执行了onUnbind()");
        return super.onUnbind(intent);
    }

}

步骤4:在AndroidMainfest.xml中注册服务 & 声明为远程服务

<service
            android:name=".MyService"
            android:process=":remote"  //将本地服务设置成远程服务
            android:exported="true"      //设置可被其他进程调用
            >
            //该Service可以响应带有scut.carson_ho.service_server.AIDL_Service1这个action的Intent。
            //此处Intent的action必须写成“服务器端包名.aidl文件名”
            <intent-filter>
                <action android:name="scut.carson_ho.service_server.AIDL_Service1"/>
            </intent-filter>

        </service>

至此,服务器端(远程Service)已经完成了。

4.2 客户端(Client)

新建一个客户端的工程:Service - Client

先下Demo再看,效果会更好:Github_RemoteService_Client

步骤1:将服务端的AIDL文件所在的包复制到客户端目录下(Project/app/src/main),并进行编译

注:记得要原封不动地复制!!什么都不要改!

 
复制后的目录

步骤2:在主布局文件定义“绑定服务”的按钮
MainActivity.xml

<?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="scut.carson_ho.service_client.MainActivity">

    <Button
        android:layout_centerInParent="true"
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="绑定服务"
        />
</RelativeLayout>

步骤3:在MainActivity.java里

  • 使用Stub.asInterface接口获取服务器的Binder;
  • 通过Intent指定服务端的服务名称和所在包,进行Service绑定;
  • 根据需要调用服务提供的接口方法。

MainActivity.java

public class MainActivity extends AppCompatActivity {

        private Button bindService;

        //定义aidl接口变量
        private AIDL_Service1 mAIDL_Service;

        //创建ServiceConnection的匿名类
        private ServiceConnection connection = new ServiceConnection() {

            //重写onServiceConnected()方法和onServiceDisconnected()方法
            //在Activity与Service建立关联和解除关联的时候调用
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }

            //在Activity与Service建立关联时调用
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {

                //使用AIDLService1.Stub.asInterface()方法获取服务器端返回的IBinder对象
                //将IBinder对象传换成了mAIDL_Service接口对象
                mAIDL_Service = AIDL_Service1.Stub.asInterface(service);

                try {

                    //通过该对象调用在MyAIDLService.aidl文件中定义的接口方法,从而实现跨进程通信
                    mAIDL_Service.AIDL_Service();

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            bindService = (Button) findViewById(R.id.bind_service);

            //设置绑定服务的按钮
            bindService.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    
                    //通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
                    //参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
                    Intent intent = new Intent("scut.carson_ho.service_server.AIDL_Service1");
                    
                    //Android5.0后无法只通过隐式Intent绑定远程Service
                    //需要通过setPackage()方法指定包名
                    intent.setPackage("scut.carson_ho.service_server");
                    
                    //绑定服务,传入intent和ServiceConnection对象
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);

                }
            });
        }

    }

4.3 测试结果

 
点击绑定服务按钮

从上面测试结果可以看出:

  • 打印的语句分别运行在不同进程(看语句前面的包名);
  • 客户端调用了服务端Service的方法

客户端和服务端进行了跨进程通信

4.4 Demo地址


5. 总结

  • 本文对Android组件Service中的远程Service进行了全面介绍
  • 如果你还想了解关于Service的其他知识,请浏览以下文章:

Android四大组件:Service史上最全面解析
Android:Service生命周期最全面解析
Android:(本地、可通信的、前台、远程)Service使用全面介绍
Android:远程服务Service(含AIDL & IPC讲解)
Android多线程全面解析:IntentService用法&源码

1. Android:(本地、可通信的、前台、远程)Service使用全面介绍 

前言

  • Service作为Android四大组件之一,应用非常广泛
  • 本文将介绍Service最基础的知识:Service的生命周期

如果你对Service还未了解,建议先阅读我写的文章:
Android四大组件:Service史上最全面解析


目录

 
目录

1. Service分类

1.1 Service的类型

 
分类

1.2 特点

 
Service类型的详细介绍

2.具体使用解析

2.1 本地Service

这是最普通、最常用的后台服务Service。

2.1.1 使用步骤

  • 步骤1:新建子类继承Service类

需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法

  • 步骤2:构建用于启动Service的Intent对象
  • 步骤3:调用startService()启动Service、调用stopService()停止服务
  • 步骤4:在AndroidManifest.xml里注册Service

2.1.2 实例Demo

接下来我将用一个实例Demo进行本地Service说明

建议先下载Demo再进行阅读:(carson.ho的Github地址)Demo_for_Service

  • 步骤1:新建子类继承Service类

需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()

MyService.java

public class MyService extends Service {


//启动Service之后,就可以在onCreate()或onStartCommand()方法里去执行一些具体的逻辑
//由于这里作Demo用,所以只打印一些语句
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("执行了onCreat()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("执行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

  • 步骤2:在主布局文件设置两个Button分别用于启动和停止Service
    activity_main.xml
<?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="scut.carson_ho.demo_service.MainActivity">

    <Button
        android:layout_centerInParent="true"
        android:id="@+id/startService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@+id/startService"
        android:id="@+id/stopService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止服务" />
</RelativeLayout>

  • 步骤3:构建Intent对象,并调用startService()启动Service、stopService停止服务

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button startService;
    private Button stopService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService = (Button) findViewById(R.id.startService);
        stopService = (Button) findViewById(R.id.stopService);

        startService.setOnClickListener(this);
        startService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {

            //点击启动Service Button
            case R.id.startService:
                //构建启动服务的Intent对象
                Intent startIntent = new Intent(this, MyService.class);
                //调用startService()方法-传入Intent对象,以此启动服务
                startService(startIntent);
                
            //点击停止Service Button
            case R.id.stopService:
                //构建停止服务的Intent对象
                Intent stopIntent = new Intent(this, MyService.class);
                //调用stopService()方法-传入Intent对象,以此停止服务
                stopService(stopIntent);
                
        }
    }
}
  • 步骤4:在AndroidManifest.xml里注册Service
    AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="scut.carson_ho.demo_service">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

        </activity>

        //注册Service服务
        <service android:name=".MyService">
        </service>

    </application>

</manifest>

Androidmanifest里Service的常见属性说明

属性说明备注
android:name Service的类名  
android:label Service的名字 若不设置,默认为Service类名
android:icon Service的图标  
android:permission 申明此Service的权限 有提供了该权限的应用才能控制或连接此服务
android:process 表示该服务是否在另一个进程中运行(远程服务) 不设置默认为本地服务;remote则设置成远程服务
android:enabled 系统默认启动 true:Service 将会默认被系统启动;不设置则默认为false
android:exported 该服务是否能够被其他应用程序所控制或连接 不设置默认此项为 false

2.1.3 测试结果

 
测试结果.png

2.1.4 Demo地址

Carson.ho的Github地址:Demo_for_Service

2.2 可通信的服务Service

  • 上面介绍的Service是最基础的,但只能单机使用,即无法与Activity通信
  • 接下来将在上面的基础用法上,增设“与Activity通信”的功能,即使用绑定Service服务(Binder类、bindService()、onBind()、unbindService()、onUnbind())

2.2.1 实例Demo

接下来我将用一个实例Demo进行可通信的服务Service说明

建议先下载Demo再进行阅读:(carson.ho的Github地址)Demo_for_Service

  • 步骤1:在新建子类继承Service类,并新建一个子类继承自Binder类、写入与Activity关联需要的方法、创建实例
public class MyService extends Service {

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("执行了onCreat()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("执行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("执行了onDestory()");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        System.out.println("执行了onBind()");
        //返回实例
        return mBinder;
    }


    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("执行了onUnbind()");
        return super.onUnbind(intent);
    }

    //新建一个子类继承自Binder类
    class MyBinder extends Binder {

        public void service_connect_Activity() {
            System.out.println("Service关联了Activity,并在Activity执行了Service的方法");

        }
    }
}
  • 步骤2:在主布局文件再设置两个Button分别用于绑定和解绑Service
<?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="scut.carson_ho.demo_service.MainActivity">

    <Button
        android:layout_centerInParent="true"
        android:id="@+id/startService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@+id/startService"
        android:id="@+id/stopService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@id/stopService"
        android:id="@+id/bindService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务" />

    <Button
        android:layout_centerInParent="true"
        android:layout_below="@id/bindService"
        android:id="@+id/unbindService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务"
        />
</RelativeLayout>

  • 步骤3:在Activity通过调用MyBinder类中的public方法来实现Activity与Service的联系

即实现了Activity指挥Service干什么Service就去干什么的功能

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button startService;
    private Button stopService;
    private Button bindService;
    private Button unbindService;

    private MyService.MyBinder myBinder;


    //创建ServiceConnection的匿名类
    private ServiceConnection connection = new ServiceConnection() {

        //重写onServiceConnected()方法和onServiceDisconnected()方法
        //在Activity与Service建立关联和解除关联的时候调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        //在Activity与Service解除关联的时候调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //实例化Service的内部类myBinder
            //通过向下转型得到了MyBinder的实例
            myBinder = (MyService.MyBinder) service;
            //在Activity调用Service类的方法
            myBinder.service_connect_Activity();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        startService = (Button) findViewById(R.id.startService);
        stopService = (Button) findViewById(R.id.stopService);

        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);

        bindService = (Button) findViewById(R.id.bindService);
        unbindService = (Button) findViewById(R.id.unbindService);

        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {

            //点击启动Service
            case R.id.startService:
                //构建启动服务的Intent对象
                Intent startIntent = new Intent(this, MyService.class);
                //调用startService()方法-传入Intent对象,以此启动服务
                startService(startIntent);
                break;

            //点击停止Service
            case R.id.stopService:
                //构建停止服务的Intent对象
                Intent stopIntent = new Intent(this, MyService.class);
                //调用stopService()方法-传入Intent对象,以此停止服务
                stopService(stopIntent);
                break;

            //点击绑定Service
            case R.id.bindService:
                //构建绑定服务的Intent对象
                Intent bindIntent = new Intent(this, MyService.class);
                //调用bindService()方法,以此停止服务

                bindService(bindIntent,connection,BIND_AUTO_CREATE);
                //参数说明
                //第一个参数:Intent对象
                //第二个参数:上面创建的Serviceconnection实例
                //第三个参数:标志位
                //这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service
                //这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行
                break;

            //点击解绑Service
            case R.id.unbindService:
                //调用unbindService()解绑服务
                //参数是上面创建的Serviceconnection实例
                unbindService(connection);
                break;

                default:
                    break;

        }
    }
}

2.2.2 测试结果

 
测试结果11.png

2.2.3 Demo

carson.ho的Github地址:Demo_for_Service

2.3 前台Service

前台Service和后台Service(普通)最大的区别就在于:

  • 前台Service在下拉通知栏有显示通知(如下图),但后台Service没有;
 
TT9$TN8IK1SAPDT%~0IRLS2.png
  • 前台Service优先级较高,不会由于系统内存不足而被回收;后台Service优先级较低,当系统出现内存不足情况时,很有可能会被回收

2.3.1 具体使用

用法很简单,只需要在原有的Service类对onCreate()方法进行稍微修改即可,如下图:

@Override
    public void onCreate() {
        super.onCreate();
        System.out.println("执行了onCreat()");

        //添加下列代码将后台Service变成前台Service
        //构建"点击通知后打开MainActivity"的Intent对象
        Intent notificationIntent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);

        //新建Builer对象
        Notification.Builder builer = new Notification.Builder(this);
        builer.setContentTitle("前台服务通知的标题");//设置通知的标题
        builer.setContentText("前台服务通知的内容");//设置通知的内容
        builer.setSmallIcon(R.mipmap.ic_launcher);//设置通知的图标
        builer.setContentIntent(pendingIntent);//设置点击通知后的操作

        Notification notification = builer.getNotification();//将Builder对象转变成普通的notification
        startForeground(1, notification);//让Service变成前台Service,并在系统的状态栏显示出来

    }

2.3.2 测试结果

运行后,当点击Start Service或Bind Service按钮,Service就会以前台Service的模式启动(通知栏上有通知),如下图

 
 
点击启动服务

2.4 远程Service

具体请看我写的另外一篇文章:Android:远程服务Service(含AIDL & IPC讲解)

3. 使用场景

  • 通过上述描述,你应该对Service类型及其使用非常了解;
  • 那么,我们该什么时候用哪种类型的Service呢?
  • 各种Service的使用场景请看下图:

     
    使用场景

4. 总结

  • 本文对Service的使用进行了全面解析(本地、可通信、前台和远程Service)
  • 如果你还想了解关于Service的其他知识,请浏览以下文章:

Android四大组件:Service史上最全面解析
Android:Service生命周期最全面解析
Android:(本地、可通信的、前台、远程)Service使用全面介绍
Android:远程服务Service(含AIDL & IPC讲解)
Android多线程全面解析:IntentService用法&源码

原文地址:https://www.cnblogs.com/awkflf11/p/12669258.html