zoukankan      html  css  js  c++  java
  • Android 四大组件之再论service

    service常见的有2种方式,本地service以及remote service。

    这2种的生命周期,同activity的通信方式等,都不相同。

    关于这2种service如何使用,这里不做介绍,只是介绍一些被遗漏的地方

    1.远程Service(AIDL方式)

    package com.joyfulmath.samples.basecontrol;
    
    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.os.RemoteException;
    
    import com.joyfulmath.samples.R;
    import com.joyfulmath.samples.TraceLog;
    
    import org.androidannotations.annotations.Click;
    import org.androidannotations.annotations.EActivity;
    
    /**
     * Created by Administrator on 2016/10/11 0011.
     * service connect activity samples
     */
    @EActivity(R.layout.activity_connect_service)
    public class ServiceConActivity extends Activity {
        private ISamplesAidlInterface binder;
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                TraceLog.i();
                binder = ISamplesAidlInterface.Stub.asInterface(service);
                if(binder!=null)
                {
                    try {
                        binder.registerCallBack(mCallBack);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                TraceLog.i();
                binder = null;
            }
        };
    
        private ICallBack.Stub mCallBack = new ICallBack.Stub() {
            @Override
            public void onServiceStateChanged(int s) throws RemoteException {
                TraceLog.i(String.valueOf(s));
            }
        };
    
        public void bindSamplesService()
        {
            TraceLog.i();
            Intent intent = new Intent(this,ServiceSamples.class);
    //        intent.setAction("com.joyfulmath.service.samples");
            bindService(intent,connection,BIND_AUTO_CREATE);
        }
    
        public void unBindSamplesService()
        {
            TraceLog.i();
            if(binder!=null)
            {
                try {
                    binder.unRegisterCallBack(mCallBack);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            unbindService(connection);
        }
    
        @Click(R.id.btn_connect)
        void connectClick()
        {
            TraceLog.i();
            bindSamplesService();
        }
    
        @Click(R.id.btn_unconnect)
        void unConnectClick()
        {
            TraceLog.i();
            unBindSamplesService();
        }
    
        @Click(R.id.btn_do)
        void doAction()
        {
            if(binder!=null)
            {
                try {
                    int r = binder.doBackground("action");
                    TraceLog.i(String.valueOf(r));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            TraceLog.i();
            unBindSamplesService();
        }
    }
    ServiceConActivity
    package com.joyfulmath.samples.basecontrol;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteCallbackList;
    import android.os.RemoteException;
    import android.support.annotation.Nullable;
    
    import com.joyfulmath.samples.TraceLog;
    
    /**
     * Created by Administrator on 2016/10/11 0011.
     */
    public class ServiceSamples extends Service {
    
        private SamplesBinder samplesBinder = null;
        private RemoteCallbackList<ICallBack> mCallbacks = new RemoteCallbackList<>();
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            TraceLog.i();
            return samplesBinder;
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            TraceLog.i();
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            TraceLog.i();
            return super.onUnbind(intent);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            TraceLog.i();
            mCallbacks.kill();
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            samplesBinder = new SamplesBinder();
            TraceLog.i();
        }
    
        public class SamplesBinder extends ISamplesAidlInterface.Stub{
    
            @Override
            public int doBackground(String action) throws RemoteException {
                TraceLog.i();
                return -1;
            }
    
            @Override
            public void findPerson(PersonCall p) throws RemoteException {
                notifyFindPerson();
            }
    
            @Override
            public void registerCallBack(ICallBack cb) throws RemoteException {
                mCallbacks.register(cb);
            }
    
            @Override
            public void unRegisterCallBack(ICallBack cb) throws RemoteException {
                mCallbacks.unregister(cb);
            }
        }
    
        private void notifyFindPerson() throws RemoteException {
            try{
                synchronized (this){
                    int n = mCallbacks.beginBroadcast();
                    for(int i=0;i<n;i++){
                        mCallbacks.getBroadcastItem(i).onServiceStateChanged(0x11);
                    }
                    mCallbacks.finishBroadcast();
                }
            }catch (RemoteException e)
            {
                TraceLog.i(e.getMessage());
            }
    
        }
    }
    ServiceSamples

    这是简单的service & activity交互的代码。

    在看关键的AIDL代码:

    // ISamplesAidlInterface.aidl
    package com.joyfulmath.samples.basecontrol;
    import com.joyfulmath.samples.basecontrol.PersonCall;
    import com.joyfulmath.samples.basecontrol.ICallBack;
    // Declare any non-default types here with import statements
    
    interface ISamplesAidlInterface {
    
        int doBackground(in String action);
        void findPerson(in PersonCall p);
        void registerCallBack(ICallBack cb);
        void unRegisterCallBack(ICallBack cb);
    }
    // PersonCall.aidl
    package com.joyfulmath.samples.basecontrol;
    parcelable PersonCall;
    // ICallBack.aidl
    package com.joyfulmath.samples.basecontrol;
    
    // Declare any non-default types here with import statements
    
    interface ICallBack {
        void onServiceStateChanged(int s);
    }

    这里有3个问题,我们从头往下看,就能明白。

    1)为什么在其他APK调用该service的时候,aidl的文件包必须一致

    2)为什么要自定义PersonCall.aidl

    3) ICallBack是什么玩意。

    4)多个APK连接同一个service,该service会产生多个实例吗。怎么保证不冲突呢?

    其实1) & 2)的问题是一样的,都是基于java的classloader原理。

    同一个类,必须在同一个包内,并且由同一个classloader加载,才能表示他们是同一个类。

    所以AIDL在拷贝的时候,必须保证是同一个包名(AIDL在打包的时候会生成java文件。)

    并且自定义的参数class,必须有AIDL定义,才能让其他APK可以理解该类。当然为了传输,需要继承自pracacle

    3)关于service回调的工作,是由RemoteCallbackList 专门用来回调通知client端。

    首先在client端定义的listener,远端是没有实体对象的,所以在作为参数传入到远端的时候,会复制一份,并且与binder绑定。

    下面来看看真正的干货,第4个问题:

    我们分成几个小问题来解答。

    I,如果service和activity不在同一个app,那么activity可以通过startservice or bindservice的方式启动该service吗?如果不行,怎么启动该service。

    经测试,可以通过bindservice的方式启动。

    II,如果2个client同时对同一个service做bind操作,会有什么结果?

    binderservice都会返回成功操作,并且前一个client,没有收到disconnect的通知。

    此时service的操作,会返回对后面一个client传递的参数的操作,也就是只有一份service实例,会同时binder2个client,but只会处理后面一个client的行为。

    所以此时,service应该阻止由其他client端输入的请求,并且可以提供接口给到client,由他决定是否关闭这个binder。

    下面是bindservice的flag参数说明:

    常量名含义
    BIND_ABOVE_CLIENT 8 如果当绑定服务期间遇到OOM需要杀死进程,客户进程会先于服务进程被杀死。
    BIND_ADJUST_WITH_ACTIVITY 128 允许客户进程提升被绑定服务进程的优先级
    BIND_ALLOW_OOM_MANAGEMENT 16 如果绑定服务期间遇到OOM需要杀死进程,被绑定的服务进程会被OOM列入猎杀对象中。
    BIND_AUTO_CREATE 1 若绑定服务时服务未启动,则会自动启动服务。 注意,这种情况下服务的onStartCommand仍然未被调用(它只会在显式调用startService时才会被调用)。
    BIND_DEBUG_UNBIND 2 使用此标志绑定服务之后的unBindService方法会无效。 这种方法会引起内存泄露,只能在调试时使用。
    BIND_IMPORTANT 64 被绑定的服务进程优先级会被提到FOREGROUND级别
    BIND_NOT_FOREGROUND 4 被绑定的服务进程优先级不允许被提到FOREGROUND级别
    BIND_WAIVE_PRIORITY 32 被绑定的服务进程不会被OOM列入猎杀对象中。

    可以看到,他们是可以组合使用的。

    如果在第三方APP 使用service

    第一步:在java同级目录下,创建aidl文件夹

    第二步:把AIDL文件copy该目录下,注意保持包名一致。

    第三步:把自定义的class,copy到java目录下,包名一致。

    第四步:启动service需要用显示的定义(android5.0开始):

        public void bindSamplesService()
        {
            TraceLog.i();
    //        ComponentName name = new ComponentName("com.joyfulmath.samples","ServiceSamples");
            Intent intent = new Intent();
    //        intent.setComponent(name);
            intent.setAction("com.joyfulmath.service.samples");
            intent.setPackage("com.joyfulmath.samples");
            intent.putExtra("cookie","third");
            bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY);
        }

    android 5.1上,亲测,该方式可行,使用componentName不行,需进一步研究。

    2.startservice

    startservice可以跨进程调用,也就是调用其他app的service。

        public void bindSamplesService()
        {
            TraceLog.i();
            Intent intent = new Intent();
            intent.setAction("com.joyfulmath.service.samples");
            intent.setPackage("com.joyfulmath.samples");
            intent.putExtra("cookie","third");
    //        bindService(intent,connection,BIND_AUTO_CREATE|BIND_ADJUST_WITH_ACTIVITY);
            startService(intent);
        }

    关于startservice,你所不知道的内容如下:

    public abstract ComponentName startService(Intent service);

    该方法还会返回一个ComponentName ,这个name就是表示package+name,因为classname会重复。

        * @return If the service is being started or is already running, the
         * {@link ComponentName} of the actual service that was started is
         * returned; else if the service does not exist null is returned.

    注释说的很清楚。

        * <p>This function will throw {@link SecurityException} if you do not
         * have permission to start the given service.

    没有权限,就会报安全异常。

    跨进程启动service的流程:

    如果考虑到进程,那么我们就应该暂时撇开四大组件的概念。

    从操作系统,进程线程的本质来考虑问题。

    Activity是生存在一个ActivityThread。它就是一个app(一般对应一个进程)的主线程。

    那么service在哪里,也在主线程中。可以通过tracelong来认证这个结论。

    所以说,service虽然是有独立生命周期的一大组件,但是它默认还是在主线程中。所以也会ANR。

    既然要跨进程,必然也需要binder机制,可能我们看不到而已。

    大致流程如下:

    从主进程调用到AMS进程(SystemServer进程),创建新的进程。这个过程需要用到binder通信。

    从新进程回调AMS,获取新进程的一些信息。关键是这些信息是从源进程传递过来+manifest注册的。

    从AMS回到新进程,直到新进程启动(同时包括service启动)

    这三步都是跨进程启动service的过程,都需要binder机制来通信。

    具体详细流程,后续会继续分析。

    3.process lifecycle

    关于service对应的lifecycle已经在activity那篇里说明了。

    Android 四大组件之Activity(续2)

    4.binder机制

    关于这块之前以及有相关博文,接下来打算再详细分析下。binder机制是android最重要的基石。

     server 会在通过servermanger注册它,然后提供远程调用的句柄,通过binder机制

    client获取servermanger不需要通过binder,应为servermanger是默认的句柄为0,可以直接获取到。

    所以说,servermanager是在等待client端发送请求,然后它去寻找以及注册的server,得到它的远程对象,进行通信。

  • 相关阅读:
    Linux服务器之间进行文件目录映射/挂载 nfs rpcbind
    计算机英语Computer English
    收藏的网站
    java 通过sftp服务器上传下载删除文件
    收藏的博客
    MySQL练习题(简单查询)
    MySQL练习题
    Comparable接口和Comparator接口的使用和区别
    排序算法:快速排序
    排序算法:插入排序
  • 原文地址:https://www.cnblogs.com/deman/p/5951236.html
Copyright © 2011-2022 走看看