码迷,mamicode.com
首页 > 移动开发 > 详细

android4.4组件分析--service组件

时间:2014-10-13 23:24:27      阅读:433      评论:0      收藏:0      [点我收藏+]

标签: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
将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到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的启动流程如下:



android4.4组件分析--service组件

标签:android4.4组件分析   service组件   

原文地址:http://blog.csdn.net/xiashaohua/article/details/40051941

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!