zoukankan      html  css  js  c++  java
  • android4.4组件分析--service组件

    6       Service

    6.1            service介绍

    6.1.1.            基本介绍

    Service是Android四大组件之中的一个(其余的是activity、BroadcastReceiver、Content Provider)。

    Service(服务)是一个没实用户界面的在后台执行执行耗时操作的应用组件。其它应用组件可以启动Service。而且当用户切换到另外的应用场景,Service将持续在后台执行。另外。一个组件可以绑定到一个service并与之交互(IPC机制),比如,一个service可能会处理网络操作,播放音乐。操作文件I/O或者与内容提供者(content provider)交互。全部这些活动都是在后台进行。不同应用程序也可以通过service来实现进程间通信(IPC)。

    service不能自己启动执行,它须要通过某一个Activity或者其它Context对象来启动。假设在Service的onCreate或者onStart做一些非常耗时间的事情。最好在 Service里启动一个线程来完毕,由于Service是跑在主线程中,会影响到UI操作或者堵塞主线程中的其它事情。

    官方对Service的定义:

     API Guides:Services

      http://developer.android.com/guide/components/services.html

       API Guides:Bound Services  http://developer.android.com/guide/components/bound-services.html

    6.1.2.    service的生命周期

    Service的生命周期图:



    service的生命周期,从它被创建開始。到它被销毁为止。依据启动方式不同,

    能够有两条不同的路径:

    通过startService()启动

    被开启的service通过其它组件调用 startService()被创建。这样的service能够无限地执行下去。必须调用stopSelf()方法或者其它组件调用stopService()方法来停止它。当service被停止时。系统会销毁它。

     通过bindService()启动

        被绑定的service是当其它组件(一个客户)调用bindService()来创建的。

      客户能够通过一个IBinder接口和service进行通信。客户能够通 unbindService()方法来关闭这样的连接。

      一个service能够同一时候和多个客户绑定。当多个客户都解除绑定之后。系统会销毁service。 这两条路径并非全然分开的。即是说,你能够和一个已经调用了 startService()而被开启的service进行绑定。

    service总体的生命时间是从onCreate()被调用開始,到onDestroy()方法返回为止。和activity一样,service在onCreate()中进行它的初始化工作。在onDestroy()中释放残留的资源。

    service活动的生命时间(active lifetime)是从onStartCommand() 或onBind()被调用開始,

    它们各自处理由startService()或 bindService()方法传过来的Intent对象。

    假设service是被开启的,那么它的活动生命周期和整个生命周期一同结束。

    假设service是被绑定的。它们它的活动生命周期是在onUnbind()方法返回后结束。

    6.1.3.    service的启动方式

    有了 Service 类我们怎样启动他呢,通常有两种方法:

    • Context.startService()

    • Context.bindService() 

    Ps:跨进程通信:AIDL。这也能够启动服务。

    使用context.startService() 启动Service

    其生命周期为context.startService() ->onCreate()- >onStart()->Service running-->(假设调用context.stopService() )->onDestroy() ->Service shut down

    假设Service还没有执行,则android先调用onCreate()然后调用onStart()
    假设Service已经执行。则仅仅调用onStart(),所以一个ServiceonStart方法可能会反复调用多次。

     

    调用stopService的时候直接onDestroy
    假设是调用者自己直接退出而没有调用stopService的话。Service会一直在后台执行。
    Service的调用者再启动起来后能够通过stopService关闭Service

    所以调用startService的生命周期为:onCreate --> onStart(可多次调用) --> onDestroy

    使用bindService()启动Service
    context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop

    onBind
    将返回给client一个IBind接口实例,IBind同意client回调服务的方法,比方得到Service执行的状态或其它操作。这个时候把调用者(Context。比如Activity)会和Service绑定在一起。Context退出了,Srevice就会调用onUnbind->onDestroy对应退出。

     

     

    所以调用bindService的生命周期为:onCreate --> onBind(仅仅一次,不可多次绑定) --> onUnbind --> onDestory。一但销毁activity它就结束,假设按home把它放到后台。那他就不退出。

     

    PSService每一次的开启关闭过程中,仅仅有onStart可被多次调用(通过多次startService调用),其它onCreateonBindonUnbindonDestory在一个生命周期中仅仅能被调用一次。

    6.1.4.    其它方面

    提高 Service 优先级 

    Android 系统对于内存管理有自己的一套方法。为了保障系统有序稳定的执行,

    系统内部会自己主动分配,控制程序的内存使用。当系统觉得当前的资源很有限的时候,为了保 证一些优先级高的程序能执行,就会杀掉一些他觉得不重要的程序或者服务来释放内存。

    这样就能保证真正对用户实用的程序仍然再执行。

    假设你的 Service 碰上了这样的情况,多半会先被杀掉。

    但假设你添加 Service 的优先级就能让他多留一会,我们能够用 setForeground(true) 来设置 Service 的优先级。

    为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前执行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在执行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉。仅仅是提高了他的优先级。

    设置 Service 訪问空间 

    在应用声明service的manifest文件中面,声明service的时候。设置其exported属性,能够控制service被安全訪问。

    6.2            代码分析

    6.1.1.    startService

        从前面的介绍我们知道,启动一个service能够使用startService()来实现,我们现来分析startService的代码实现。

    6.1.1.1.      阶段一:应用调用startService()

    启动service比較常见的是通过activity来启动,在activity里面调用startService

    是怎样实现的呢?

    首先,我们看以下的关系图,



    Context

    抽象类。定义了一些全局常量和应用环境抽象方法。

      ContextWrapper

    类。继承了Context,实现了父类方法,方法全都以类构建的时候传入的Context实例为基础实现,通过使用子类的实例,子类就能够不用重写很多函数。

        ContextThemeWrapper

    类。继承了ContextWrapper,添加了theme相关的方法。

    也是使用传入的Context实例为基础实现。

          Activity

    类。继承了ContextThemeWrapper,作为应用窗体界面的父类,维护相关显示和交互功能。

        Application

    类,继承了ContextWrapper,维护应用信息。

        Service

    抽象类,继承了ContextWrapper,

     

     

      ContextImpl

    类。继承了Context,实现了父类方法,是Context类的真正实现之处。

    它为activity、service、application提供了context实例。

        能够看到,Activity继承自Context,ContextImpl是Context类的真正实现之处,所以在Activity里面调用startService。实际是在ContextImpl里运行,

        public ComponentName startService(Intent service) {

            warnIfCallingFromSystemProcess();

            return startServiceCommon(service, mUser);

        }

    startServiceCommon的代码例如以下。

        private ComponentName startServiceCommon(Intent service, UserHandle user) {

            try {

                validateServiceIntent(service);

                service.prepareToLeaveProcess();

                ComponentName cn = ActivityManagerNative.getDefault().startService(

                    mMainThread.getApplicationThread(), service,

                    service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());

    这里通过ActivityManagerNative获取到IActivityManager的实例。实际就是AMS的远程接口。使用Binder方式。调用AMS的startService方法,

         public ComponentName startService(IApplicationThread caller, Intent service,

                String resolvedType, int userId) throws RemoteException

        {

            Parcel data = Parcel.obtain();

            Parcel reply = Parcel.obtain();

            data.writeInterfaceToken(IActivityManager.descriptor);

            data.writeStrongBinder(caller != null ? caller.asBinder() : null);

            service.writeToParcel(data, 0);

            data.writeString(resolvedType);

            data.writeInt(userId);

            mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);

            reply.readException();

            ComponentName res = ComponentName.readFromParcel(reply);

            data.recycle();

            reply.recycle();

            return res;

        }

             參数caller是一个ApplicationThread实例。它是在ActivityThread里通过ApplicationThread mAppThread = new ApplicationThread();创建的,ApplicationThread继承自ApplicationThreadNative。间接继承自Binder,在Android应用程序中。每个进程都用一个ActivityThread实例来表示,而在ActivityThread类中,成员变量mAppThread,它是一个ApplicationThread实例。实现了IApplicationThread接口,它的作用是用来辅助ActivityThread类来运行一些操作,这几者之间的相互关系见专门的章节。

    參数service是一个Intent实例,它里面指定了要启动的服务的名称,就是我们在创建intent的时候使用的名称。

            參数resolvedType是一个字符串。String类型,它表示service这个IntentMIME类型,它是在解析Intent时用到的。

    通过service.resolveTypeIfNeeded()获取到。      

    6.1.1.2.      阶段二:AMS运行startService

       在AMS里面。startService的实现例如以下,

         public ComponentName startService(IApplicationThread caller, Intent service,

                String resolvedType, int userId) {

    enforceNotIsolatedCaller("startService");

            synchronized(this) {

                final int callingPid = Binder.getCallingPid();

                final int callingUid = Binder.getCallingUid();

                final long origId = Binder.clearCallingIdentity();

                ComponentName res = mServices.startServiceLocked(caller, service,

                        resolvedType, callingPid, callingUid, userId);

                Binder.restoreCallingIdentity(origId);

                return res;

            }

        }

    通过binder传递,调用到该方法,并解析出相关參数。当中,service是应用发起的intent,mServices是AMS里面定义的一个ActiveServices实例引用,其方法startServiceLocked经过參数推断和转化后,继续调用startServiceInnerLocked。进一步到bringUpServiceLocked,

    当中在startService里,enforceNotIsolatedCaller("startService");用来推断应用是否被隔离。假设是。则抛出异常,推断方法是通过UID获取到APPID(APPID=UID%100000)。再推断APPID是否在隔离沙箱定义的UID范围之内(99000-99999),是的话就觉得是被隔离的应用,不同意启动服务。

    通常APPID值是在10000到19999之间的。

    ActiveServices类是AMS的辅助类,完毕。其成员变量mAm相应的就是一个AMS的实例,ActiveServices的startServiceLocked主要代码例如以下,

         ComponentName startServiceLocked(IApplicationThread caller,

                Intent service, String resolvedType,

                int callingPid, int callingUid, int userId) {

    //获取进程的执行状态

                final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);

    callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;

    //新建一个ServiceLookupResult容器 。并创建ServiceRecord

            ServiceLookupResult res =

                retrieveServiceLocked(service, resolvedType,

                        callingPid, callingUid, userId, true, callerFg);

    // record为空会返回

            if (res.record == null) {

                return new ComponentName("!", res.permission != null

                        ? res.permission : "private to package");

            }

            ServiceRecord r = res.record;

    //填充ServiceRecord的初始状态

            r.lastActivity = SystemClock.uptimeMillis();

            r.startRequested = true;

            r.delayedStop = false;

            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),

                    service, neededGrants));

            if (!callerFg && r.app == null && mAm.mStartedUsers.get(r.userId) != null) {

    }

    //继续进一步调用

            return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);

    mAm.getRecordForAppLocked通过IApplicationThread获取到其相应的进程信息,当前执行进程的信息都存放在ProcessRecord里面。通过进程信息,推断当前进程是前台进程还是后台进程,设置callerFg标识,供后面启动service处理。

        retrieveServiceLocked 会根据userid新建一个ServiceMap,里面存放service的相关信息,再根据service的訪问权限和manifest里设置的exported属性,创建不同的ServiceLookupResult并返回,ServiceLookupResult的ServiceRecord类型成员record是ServiceRecord类型,存放的就是将要启动的service的状态信息,创建这个实例后,后面就開始初始状态。

    假设record为null,会返回。

    兴许的代码对ServiceRecord进行初始填充。

    startServiceInnerLocked处理逻辑比較简单,主要是调用bringUpServiceLocked,再依据callerFg标识。处理服务的后台属性,主要代码例如以下,

         ComponentName startServiceInnerLocked(ServiceMap smap, Intent service,

                ServiceRecord r, boolean callerFg, boolean addToStarting) {

            String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);

                smap.ensureNotStartingBackground(r);

    }

    而bringUpServiceLocked的代码例如以下,

        private final String bringUpServiceLocked(ServiceRecord r,

                int intentFlags, boolean execInFg, boolean whileRestarting) {

            if (r.app != null && r.app.thread != null) {

                sendServiceArgsLocked(r, execInFg, false);

                return null;

            }

     …

            // Service is now being launched, its package can't be stopped.

            try {

                AppGlobals.getPackageManager().setPackageStoppedState(

                        r.packageName, false, r.userId);

                app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);

                if (app != null && app.thread != null) {

                    try {

                        app.addPackage(r.appInfo.packageName, mAm.mProcessStats);

                        realStartServiceLocked(r, app, execInFg);

                        return null;

    }}

            if (app == null) {

                if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,

                        "service", r.name, false, isolated, false)) == null) {

    }}

    能够看出,ServiceRecord类型的变量r在创建过程中并没有初始化app成员变量,所以不会运行sendServiceArgsLocked并返回。而是继续向下运行,当设置好包状态后,会通过AMS获取到启动应用的app实例,进而运行realStartServiceLocked,

    realStartServiceLocked代码例如以下。

        private final void realStartServiceLocked(ServiceRecord r,

                ProcessRecord app, boolean execInFg) throws RemoteException {

            app.services.add(r);

            bumpServiceExecutingLocked(r, execInFg, "create");

            mAm.updateLruProcessLocked(app, true, false);

                app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);

                app.thread.scheduleCreateService(r, r.serviceInfo,

                        mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),

                        app.repProcState);

                r.postNotification();

                created = true;

            requestServiceBindingsLocked(r, execInFg);

            sendServiceArgsLocked(r, execInFg, true);

    }

    在realStartServiceLocked里面,有两个关键的流程,

    一个是app.thread.scheduleCreateService的运行。它发送一个创建服务的CREATE_SERVICE消息出去,当消息被运行的时候,服务类被创建。service的Oncreate()方法被运行,后面我们会具体分析。

    还有一个是sendServiceArgsLocked。它会调用 scheduleServiceArgs,后者也会发出一个消息。当消息被运行时。service的Onstart()方法被运行。

    6.1.1.3.      第三阶段:Service创建和Oncreate()运行

    scheduleCreateService 的功能是发送一个创建服务的CREATE_SERVICE消息出去,当这个消息被处理时,会进行服务类的创建。

    在ActivityThread.java的handleMessage里,会处理CREATE_SERVICE消息。处理的方法为handleCreateService,它会创建一个service实例。在service实例创建的时候,new一个ContextImpl。并调用init进行初始化,

        private void handleCreateService(CreateServiceData data) {

                java.lang.ClassLoader cl = packageInfo.getClassLoader();

                service = (Service) cl.loadClass(data.info.name).newInstance();

                ContextImpl context = new ContextImpl();

                context.init(packageInfo, null, this);

                Application app = packageInfo.makeApplication(false, mInstrumentation);

                context.setOuterContext(service);

                service.attach(context, this, data.info.name, data.token, app,

                        ActivityManagerNative.getDefault());

                service.onCreate();

                mServices.put(data.token, service);

                try {

                    ActivityManagerNative.getDefault().serviceDoneExecuting(

                            data.token, 0, 0, 0);

    ContextImpl实例的内部的创建步骤例如以下:

    ContextImpl有例如以下一些重要的变量。其类型和函数说明例如以下,

    LoadedApk mPackageInfo   服务相应的包信息。

    ActivityThread mMainThread  启动服务的应用相应的ActivityThread,

    Context mOuterContext 服务相应的ContextImpl所关联的Context。就是要创建的服务实例。

    UserHandle mUser 应用进程UID相应的UserHandle,

       它另一个SYSTEM_SERVICE_MAP的hashMap。通过registerService。用来存放系统服务的接口实例,这个数组的初始化过程在static语句块里面。

        private static void registerService(String serviceName, ServiceFetcher fetcher) {

            if (!(fetcher instanceof StaticServiceFetcher)) {

                fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;

            }

            SYSTEM_SERVICE_MAP.put(serviceName, fetcher);

        }

        static {

            registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {

                    public Object getService(ContextImpl ctx) {

                        return AccessibilityManager.getInstance(ctx);

                    }});

            registerService(CAPTIONING_SERVICE, new ServiceFetcher() {

                    public Object getService(ContextImpl ctx) {

                        return new CaptioningManager(ctx);

                    }});

            registerService(ACTIVITY_SERVICE, new ServiceFetcher() {

                    public Object createService(ContextImpl ctx) {

                        return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());

                    }});

     对于ContextImpl实例的创建。除了以下的初始化函数外,依据类的创建过程,也包括上面静态语句块的运行。

        ContextImpl() {

            mOuterContext = this;

        }

    ContextImpl.init的目的就是完毕上面提到过的基本的成员变量的初始化。让使用这个类的方法能使用相关类提供的服务。

        final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread,

                Resources container, String basePackageName, UserHandle user) {

            mPackageInfo = packageInfo;

            mResources = mPackageInfo.getResources(mainThread);

            mResourcesManager = ResourcesManager.getInstance();

            mMainThread = mainThread;

            mActivityToken = activityToken;

            mContentResolver = new ApplicationContentResolver(this, mainThread, user);

            mUser = user;

    }

    mMainThread就是创建服务的应用相应的ActivityThread,

    packageInfo 就是要创建的服务类的包信息。

    mUser 相应于Process.myUserHandle()。

    对于service。还要将mOuterContext设置为service的实例。

     context.setOuterContext(service);

        final void setOuterContext(Context context) {

            mOuterContext = context;

        }

    创建服务的时候,还须要获取到一个Application实例。对于同一个包,公用一个应用,在第一次启动应用时,须要创建一个应用。应用相同也要新建一个ContextImpl实例。并与之关联起来,

        public Application makeApplication(boolean forceDefaultAppClass,

                Instrumentation instrumentation) {

            if (mApplication != null) {

                return mApplication;

            }

                java.lang.ClassLoader cl = getClassLoader();

                ContextImpl appContext = new ContextImpl();

                appContext.init(this, null, mActivityThread);

                app = mActivityThread.mInstrumentation.newApplication(

                        cl, appClass, appContext);

                appContext.setOuterContext(app);

            mActivityThread.mAllApplications.add(app);

            mApplication = app;

            if (instrumentation != null) {

                try {

                    instrumentation.callApplicationOnCreate(app);

    …   

    service.attach的目的就是在创建service实例后,把service里一些关键的成员变量初始化。和关联类相互关联起来,如context、mThread、mApplication、mActivityManager,

        public final void attach(

                Context context,

                ActivityThread thread, String className, IBinder token,

                Application application, Object activityManager) {

            attachBaseContext(context);

            mThread = thread;           // NOTE:  unused - remove?

            mClassName = className;

            mToken = token;

            mApplication = application;

            mActivityManager = (IActivityManager)activityManager;

            mStartCompatibility = getApplicationInfo().targetSdkVersion

                    < Build.VERSION_CODES.ECLAIR;

        }

    之后,便開始运行service的onCreate()方法了,service类本身是一个抽象类,其onCreate类就须要子类自行实现了。

    6.1.1.4.      第四阶段:Onstart()方法被运行

    如前所述,在sendServiceArgsLocked会调用r.app.thread.scheduleServiceArgs(),

    后者会发送一个SERVICE_ARGS消息。这个消息会被ActivityThread.java的handleMessage方法处理,详细处理方法则为handleServiceArgs,

         private void handleServiceArgs(ServiceArgsData data) {

            Service s = mServices.get(data.token);

                    if (data.args != null) {

                        data.args.setExtrasClassLoader(s.getClassLoader());

                    }

                    int res;

                    if (!data.taskRemoved) {

                        res = s.onStartCommand(data.args, data.flags, data.startId);

                    } else {

                        s.onTaskRemoved(data.args);

                        res = Service.START_TASK_REMOVED_COMPLETE;

                    }

                    QueuedWork.waitToFinish();

                    try {

                        ActivityManagerNative.getDefault().serviceDoneExecuting(

                                data.token, 1, data.startId, res);

                    } catch (RemoteException e) {

                        // nothing to do.

                    }

                    ensureJitEnabled();

    }

    当中onStartCommand会调用onStart()方法。相同,因为service是抽象类。所以会运行到用户自己定义的类。

    至此,service的启动过程分析完毕。上面仅仅是分析了其启动的主流程,而实际上,因为各种不同的状态。其启动过程比这复杂非常多。

    6.1.1.5.      总结StartService启动流程

    综上所述。Service的启动流程例如以下:



  • 相关阅读:
    一文解读RESTful (转)
    一文解读Redis (转)
    一文解读JSON (转)
    一文解读单点登录 (转)
    一文解读雪碧图 (转)
    一文解读骨架屏 (转)
    一文解读MPA/SPA(转)
    一文解读HTTP2 (转)
    一文解读HTTP (转)
    HTML5中Video标签无法播放mp4的解决办法
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5118351.html
Copyright © 2011-2022 走看看