zoukankan      html  css  js  c++  java
  • Android中的Service与进程间通信(IPC)详解

    Service

    什么是Service

    在后台长期运行的没有界面的组件。其他组件可以启动Service让他在后台运行,或者绑定Service与它进行交互,甚至实现进程间通信(IPC)。例如,可以让服务在后台处理网络交互,播放音乐,文件I/O,或者与ContentProvider交互。

    创建一个Service

    1. 新建一个类,继承Service,重写相关方法,如onBind,onUnBind,onCreate,onDestorey。
    2. 在AndroidManifest.xml中配置Service和相关权限
    <manifest ... >
      ...
      <application ... >
          <service android:name=".MyService" />
          ...
      </application>
    </manifest>

    开启服务:

    Intent service = new Intent(this,Service.class);
    startService(service);

    停止服务:

    Intent service = new Intent(this,Service.class);
    stopService(service);

    绑定服务:

    private boolean mIsBound = false;
    private ServiceConnection mConnection = new ServiceConnection() {
           // 服务连接成功回调
           @Override
           public void onServiceConnected(ComponentName name, IBinder service) {
               MyService.MyBinder binder = (MyService.MyBinder) service;
    
           }
           // 服务失去连接回调
           @Override
           public void onServiceDisconnected(ComponentName name) {
    
           }
       };
    
    @Event(value = R.id.btn_bind_service)
    private void onBindServiceClick(View view) {
        bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);// 绑定时如果没有创建Service则自动创建Service。
        mIsBound = true;
    }

    解绑服务:

    @Event(value = R.id.btn_unbind_service)
    private void onUnbindServiceClick(View view) {
        if (!mIsBound) {
            ToastUtil.show("未绑定服务");
            return;
        }
        try {
            unbindService(mConnection);//注意:ServiceConnection要传绑定时的ServiceConnection对象,否则会报错。
        } catch (Exception e) {
            ToastUtil.show("解除綁定服务失败");
            e.printStackTrace();
        }
        mIsBound = false;
    }

    Service的生命周期

    这里写图片描述

    public class MyService extends Service {
    
        // 服务创建
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        // 每次startService都会调用;通过bindService方式启动服务,该方法不会被调用
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return super.onStartCommand(intent, flags, startId);
        }
    
        // 服务销毁
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    
        // bindService时调用,返回一个IBinder对象,用于与Service交互,IBinder就是Service的代理
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        // unbindService时调用
        @Override
        public boolean onUnbind(Intent intent) {
            return super.onUnbind(intent);
        }
    }

    startService:onCreate——>onStartCommand 
    stopService:onDestory

    注意:服务只会被创建一次,如果服务已经创建,并且没有销毁,多次调用startService方法,只会执行onStartCommand方法。

    bindService:onCreate——>onBind 
    unbindService:onUnbind——>onDestory

    注意:

    • 如果多次bindService,onBind方法只会在第一次绑定时被调用;同样,多次startService,onCreate方法也只会在第一次创建时被调用;
    • 服务只能被解绑一次,服务需要先绑定才能解除绑定,多次解绑会报错。
    • 通过bindService方式启动的Service,在调用unbindService时就会自动销毁。
    • 服务只会停止一次,多次调用stopService()的方法无效,但不报错。
    • 每次调用startService()开启服务都会执行onStartCommand()方法。

    如何调用Service里的方法

    由于系统框架在创建服务的时候会创建与之对应的上下文,直接new出来的服务对象是没有上下文的,所以直接new服务对象调用其方法会报异常。

    与Service之间交互都是通过其代理人(IBinder)来间接调用Service里的方法的。

    这里写图片描述

    这样设计主要出于安全考虑,有限的暴露出一些方法,而不是直接返回服务对象,因为服务对象里可能有一些成员变量和方法不允许外界直接访问,需要保护起来。

    一般IBinder(代理人)也应该设计成私有的,因为是IBinder中的一些数据也需要保护起来,只需要暴露出一些指定的方法,那么外界如何引用IBinder对象呢?通过接口引用代理人,在接口定义供外界调用的方法,让IBinder类实现该接口。

    bindService与startService

    bindService与startService的区别:

    • 绑定服务:可以间接调用服务里面的方法;如果绑定的Activity被销毁了,服务也会跟着销毁。
    • 开启服务:不可以调用服务里面的方法;如果开启服务的Activity销毁,服务还可以长期的在后台运行。

    既要保证服务长期在后台运行,又想去调用服务里面的方法。

    步骤: 
    1. startService(),保证服务在后台长期的运行; 
    2. bindService(),获取中间人(IBinder对象),间接的调用服务里面的方法;

    这时,解绑服务并不会导致服务销毁,服务可长期在后台运行。

    注意:如果服务已经被绑定,直接调用stopService()是停不掉的,必须先解除绑定服务再调stopService(),服务才会被销毁。

    示例代码

    @ContentView(value = R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
    
        private boolean mIsBound = false;
        private ServiceConnection mConnection = new ServiceConnection() {
            // 服务连接成功回调
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                LogUtil.d(name + " onServiceConnected");
                PayService.PayBinder binder = (PayService.PayBinder) service;
                binder.pay(100);
            }
            // 服务失去连接回调
            @Override
            public void onServiceDisconnected(ComponentName name) {
                LogUtil.d(name + " onServiceDisconnected");
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            x.view().inject(this);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            //当Activity被销毁时解绑服务,因为如果已经绑定服务不显式解绑会报异常。
            onUnbindServiceClick(null);
        }
    
        private Intent getServiceIntent() {
            return new Intent(this, PayService.class);
        }
    
        // 启动服务
        @Event(value = R.id.btn_start_service)
        private void onStartServiceClick(View view) {
            startService(getServiceIntent());
        }
    
        // 绑定服务
        @Event(value = R.id.btn_bind_service)
        private void onBindServiceClick(View view) {
            bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);// 绑定时如果没有创建服务则自动创建Service。
            mIsBound = true;
        }
    
        // 解绑服务
        @Event(value = R.id.btn_unbind_service)
        private void onUnbindServiceClick(View view) {
            if (!mIsBound) {
                ToastUtil.show("未绑定服务");
                return;
            }
            try {
                unbindService(mConnection);//注意:ServiceConnection要传绑定时的ServiceConnection对象,否则会报错。
            } catch (Exception e) {
                e.printStackTrace();
            }
            mIsBound = false;
        }
    
        // 停止服务
        @Event(value = R.id.btn_stop_service)
        private void onStopServiceClick(View view) {
            stopService(getServiceIntent());
        }
    
    }

    参考文档: 
    https://developer.android.com/reference/android/app/Service.html 
    https://developer.android.com/guide/components/services.html

    使用AIDL实现进程间通信

    AIDL(Android Interface Definition Language)用于进程间通信接口的定义,是一种进程间通讯的规范 。

    Service端: 
    1.New一个aidl文件在src目录下 

    这里写图片描述

    2.在aidl文件中定义Service中对外开放的接口

    // IPayService.aidl
    package linchaolong.android.aidldemo.service;
    
    // Declare any non-default types here with import statements
    
    /**
     * AIDL Demo
     *
     * Created by linchaolong on 2016/4/22.
     */
    interface IPayService {
    
        void pay(int price);
    
        void startTimer();
    
        void stopTimer();
    
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         *
         * 翻译:
         *
         * 展示一些可以在AIDL中用作参数和返回值的基本类型。
         *
         */
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);
    }

    aidl语言中没有权限修饰符,因为进程间通信接口权限肯定是public的。

    3.aidl编写完成后,make一下工程,在build目录下就会生成该aidl文件对应的java文件,比如我这是IPayService.java。 
    这里写图片描述

    4.在IPayService中有一个Stub静态类,继承了Binder和实现了IPayService接口,定义一个Binder类继承IPayService.Stub并实现相关接口。

       @Override
        public IBinder onBind(Intent intent) {
            if (mBinder == null) {
                mBinder = new PayBinder(); 
            }
            return mBinder; // 其他应用绑定服务时返回binder对象
        }
    
        // Binder
        public class PayBinder extends IPayService.Stub {
    
            public void pay(int price) {
                PayService.this.pay(price);
            }
    
            public void startTimer() {
                PayService.this.startTimer();
            }
    
            public void stopTimer() throws RemoteException {
                PayService.this.stopTimer();
            }
    
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
                // Does nothing
            }
        }

    5.在AndroidManifest.xml配置Service

    <service
        android:name=".service.PayService"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="linchaolong.android.aidldemo.service.PayService" />
        </intent-filter>
    </service>

    到这里Service端就完成了,其他应用需要调用该Service只需要把aidl文件拷贝到自己工程的src目录下(make一下),并绑定服务即可得到IBinder对象,通过IBinder对象可以实现与Service的交互。

    调用示例:

    在onServiceConnected回调里,调用YourServiceInterface.Stub.asInterface(service)把IBinder对象转换为YourServiceInterface类型。

        private IPayService iPayService;
    
        private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // 获取远程接口实例
                iPayService = IPayService.Stub.asInterface(service);
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Log.e(TAG, "Service has unexpectedly disconnected");
                iPayService = null;
            }
        };

    绑定远程服务并调用IPC方法

    /**
         * 判断是否已经绑定远程服务
         *
         * @return
         */
        private boolean isBinded() {
            return mIsBound && iPayService != null;
        }
    
        private Intent getServiceIntent() {
            return new Intent("linchaolong.android.aidldemo.service.PayService");
        }
    
        // 绑定远程服务
        @Event(value = R.id.btn_bind_remote_service)
        private void bindRemoteService(View view) {
            if (isBinded()) {
                showToast("已绑定远程服务");
                return;
            }
            bindService(getServiceIntent(), mConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
        }
    
        // 调用远程服务方法
        @Event(value = R.id.btn_call_service_pay)
        private void callServicePay(View view) {
            if (!isBinded()) {
                showToast("未绑定远程服务");
                return;
            }
            try {
                // 通过IBinder对象调用远程服务中方法
                iPayService.pay(100);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

    Demo地址:https://coding.net/u/linchaolong/p/AIDLDemo/git

    参考文档: 
    http://developer.android.com/guide/components/aidl.html

  • 相关阅读:
    java实现九九乘法表
    for循环的阶乘
    Struts2 表单提交与execute()方法的结合使用
    Struts2 第三个程序 namespacce的用法
    java 字符串的比较compareTo
    java中的位预算
    java调用C++ DLL库方法
    Socket编程模式理解与对比
    c 高级函数的简单用法
    TCP粘包分析与处理
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/9685620.html
Copyright © 2011-2022 走看看