美文网首页组件AndroidMaterial Design
Android:远程服务Service(含AIDL &

Android:远程服务Service(含AIDL &

作者: Carson带你学安卓 | 来源:发表于2016-11-14 09:52 被阅读7107次

    前言

    • 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用法&源码


    请点赞!因为你们的赞同/鼓励是我写作的最大动力!

    相关文章阅读
    Android开发:最全面、最易懂的Android屏幕适配解决方案
    Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)
    Android开发:顶部Tab导航栏实现(TabLayout+ViewPager+Fragment)
    Android开发:底部Tab菜单栏实现(FragmentTabHost+ViewPager)
    Android开发:JSON简介及最全面解析方法!
    Android开发:XML简介及DOM、SAX、PULL解析对比


    欢迎关注Carson_Ho的简书!

    不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度

    相关文章

      网友评论

      • 0100dbcd1448:必须写成“服务器端包名.aidl文件名”这里不正确,intent-filter的action可以随意写成任何内容都可以
      • 2f3430ff932c:辛苦了,专门注册支持一下楼主,整理这些基础的知识对大家帮助挺大的。
      • 安静听歌01:删除android:process=":remote" //将本地服务设置成远程服务
        android:exported="true" //设置可被其他进程调用
        可以调用
      • 小学留了三年:谢谢博主的慷慨
      • 青蛙要fly://3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型;

        List只支持ArrayList , Map只支持HashMap。
        小学留了三年:你是真的皮
      • 灰咲无但八汪:很棒~刚接触aidl
        清晰明了的文章
      • 妙法莲花1234:一个aidl文件怎么绑定多个远程Service呢,需要多个aidl文件吗,也就是说一个aidl只能绑定一个remote service吗?
        妙法莲花1234:可以的,bind时指定service名字和包名即可,service名字在清单文件的指定
      • 天生爱走神:没成功,哪里有问题?
        灰咲无但八汪:服务端也是要运行的。不然怎么请求服务,看看是不是这个问题
      • Smartown:LocalService特点1,运行在线程中,是不是敲错了?不是主进程吗?
      • 千里之行死于足下:按照你的方法做,为什么没有成功.客户端好像已经绑定了,但是远程服务的生命周期方法没有执行,aidl方法也没有执行,怎么回事?
        zzl93:@cherish126 不知道啊。。当时就练手试下,没深入研究。。不过既然已经是两个不同的进程了,那么为啥还要再开启一个远程service呢,所以我就删了。
        02dd9939be3b:@zzl93 想问一下为什么加上这句就成功不了呢?
        zzl93:我也遇到了,我把这个去掉就好了 android:process=":remote" //将本地服务设置成远程服务

      本文标题:Android:远程服务Service(含AIDL &

      本文链接:https://www.haomeiwen.com/subject/oaxzuttx.html