本文为原创文章,欢迎转载!转载时请注明出处:http://blog.csdn.net/windskier

前面2篇文章介绍了acitivity的管理,其中保存task的管理,activity生命周期中各个阶段的操作等问题,这篇文章我们来详细的研究一下android系统中application Service的管理过程。

Service是android中一个非常重要的组件,作为一个从事android开发的人,service是必须掌握的一个组件,这篇文章不是从如何使用Service角度来分析的,而是从AMS如何管理Service的角度来分析AMS的源码,以期理解AMS针对Service的操作的源码实现。

我们知道,Service的启动方式有两种,一种是通过startService()方法来启动,这种方法的启动作为client方,只是作为一个启动者存在的,它只能通过onStartCommand()方法去要求Service做某些操作,而通过bind方式的Service则不同,而通过bindService()方法启动并bind Service的话,client可以通过Service提供的业务接口,通过IPC的方式来访问Service的相关业务操作。

而两种方式的生命周期也不尽相同,当client通过startService()方法来启动service,client通过调用stopService()或者service本身调用stopSelf()来结束当前的Service,假如多个client启动的这个Service,那么只要一个client stop它,那么就将会被stop掉。

而client通过bindService()方式启动的Service,那么client将通过unbindService()来结束与service的连接,这种方式的生命周期后面会介绍到。

更多的关于service的应用请参考android的官方文档。

关于通过startService()方式启动serivce的源码,这里就不再分析了,下面着重分析一下通过bindService()方法来启动并bind service的过程,着启动其实已经包含了startService()方式启动serivce的源码。



1. ServiceDispatcher

1.1 创建ServiceDispatcher

ServiceDispatcher从它的名字中可以看出它类似于一个Dispatcher的作用,这个类的作用就是当client bind某个service成功之后,负责向client分配service IBinder的;以及当client unbind service时,负责通知client unbind service的状态。

@ContextImpl.java

    @Override    public boolean bindService(Intent service, ServiceConnection conn,            int flags) {        IServiceConnection sd;        if (mPackageInfo != null) {            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),                    mMainThread.getHandler(), flags);        } else {            throw new RuntimeException("Not supported in system context");        }        try {            int res = ActivityManagerNative.getDefault().bindService(                mMainThread.getApplicationThread(), getActivityToken(),                service, service.resolveTypeIfNeeded(getContentResolver()),                sd, flags);            if (res < 0) {                throw new SecurityException(                        "Not allowed to bind to service " + service);            }            return res != 0;        } catch (RemoteException e) {            return false;        }    }

mPackageInfo对象是一个LoadedApk类型,LoadedApk类型存储着当前package的内容。

@ContextImpl.java

    /*package*/ LoadedApk mPackageInfo;
@LoadedApk.java
    public final IServiceConnection getServiceDispatcher(ServiceConnection c,            Context context, Handler handler, int flags) {        synchronized (mServices) {            LoadedApk.ServiceDispatcher sd = null;            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);            if (map != null) {                sd = map.get(c);            }            if (sd == null) {                sd = new ServiceDispatcher(c, context, handler, flags);                if (map == null) {                    map = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();                    mServices.put(context, map);                }                map.put(c, sd);            } else {                sd.validate(context, handler);            }            return sd.getIServiceConnection();        }    }
从上面代码可以看出,对于ServiceDispatcher的管理,是以package为载体的,也就是说对于某个package,定义在其中的component如果请求去bind一个service,那么

LoadedApk将为这个component分配一个ServiceDispatcher。因此,ServiceDispatcher是client方的一个概念。component每请求bind一个不同的service,LoadedApk将会为其分配一个ServiceDispatcher,如下图所示。

1.2InnerConnection

从上面bindService()的代码中可以看出,我们在bind 过程中传递给AMS的并不是ServiceConnection,而是一个IServiceConnection interface,它对应的服务实体类型是InnerConnection,InnerConnection是ServiceDispatcher的一个内部静态类。

疑问1:为什么不直接把ServiceConnection传递给AMS作为回调,而是传递另外一个类型InnerConnection呢?

首要一点是因为,ServiceConnection是一个Interface(java),我们传递的之时implementsServiceConnection之后的一个内部类对象,根据android IBinder的实现机制,在IPC调用过程中,传递的参数必须是Parcel对象,这就要求传递的对象是Parcel类型的,或者这个类型实现了写入Parcel对象的方法,即writeToParcel()。即使ServiceConnection实现了writeToParcel(),它也不可能将其回调函数传递给AMS,因为writeToParcel()主要是对成员变量的打包过程,Parcel并没有实现对方法的打包,因此系统不可能将ServiceConnection内部类传递给AMS,因此需要提供给AMS另外一种方法来回调ServiceConnection中的方法,这个InnerConnection就起到了这个作用。

疑问2:既然决定用InnerConnection来提供给AMS远程调用ServiceConnection的方法,那么为什么还需要ServiceDispatcher?

其实,在我看来ServiceDispatcher完全可以被InnerConnection代替,从功能来看是可以实现的,但是在IPC通信编程中,我们应该尽量的减少IBinder参数的负荷,这么做无可厚非。

2. Client Intent

在android中有这么一种情况,如果某个application包括了"android.uid.system"的sharedUserId,这其中包括system service。这个application需要bind一个service,并且这个service只允许"android.uid.system"的application来bind。这么做是处于安全方面考虑的,某些Service不想处system uid之外的应用来请求服务。

在上述描述的情景中,如果一个第三方的appliction,它不可能有"android.uid.system"的sharedUserId,想要去请求上述的service时,就不能够直接bind 这个servcie,而是只能通过上述的system uid的application来请求。那么如何通过system uid的application来请求service呢?AMS为这种情形的需求实现了一套解决方案。下面我们以InputMethodManagerService为例来说明一下。

先简单介绍一下输入法的逻辑,由于系统中可以存在多个输入法,InputMethodManagerService会去bind系统设置的或者用户选择的输入法service,这些输入法service只能被system uid的application来请求。IMS在bind输入法service时,会提供一个PendingIntent给AMS,由于这个PendingIntent是供Client来访问输入法Service,我们称这个PendingIntent为ClientIntent,相关代码如下所示:

startInputInnerLocked()@InputMethodManagerService.java

        mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);        mCurIntent.setComponent(info.getComponent());        mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,                com.android.internal.R.string.input_method_binding_label);        mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(                mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));        if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {            mLastBindTime = SystemClock.uptimeMillis();            mHaveConnection = true;            mCurId = info.getId();            mCurToken = new Binder();            try {                if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);                mIWindowManager.addWindowToken(mCurToken,                        WindowManager.LayoutParams.TYPE_INPUT_METHOD);            } catch (RemoteException e) {            }            return new InputBindResult(null, mCurId, mCurSeq);        } else {            mCurIntent = null;            Slog.w(TAG, "Failure connecting to input method service: "                    + mCurIntent);        }


由上图所示,输入法service只能被system uid的application请求,系统服务InputMethodManagerService bind了它,并且向AMS提供了一个client Intent,第三方的application如果需要对输入法service进行请求,只能通过IMS提供的这个client Intent指示的activity来进行操作,这个activity是LanguageSettings。这样就达到了避免输入法service不被第三方应用随意操作引起的安全问题。

AMS中对client Intent的管理如下代码所示:

bindService()@ActivityManagerService.java

            if (callerApp.info.uid == Process.SYSTEM_UID) {                // Hacky kind of thing -- allow system stuff to tell us                // what they are, so we can report this elsewhere for                // others to know why certain services are running.                try {                    clientIntent = (PendingIntent)service.getParcelableExtra(                            Intent.EXTRA_CLIENT_INTENT);                } catch (RuntimeException e) {                }                if (clientIntent != null) {                    clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);                    if (clientLabel != 0) {                        // There are no useful extras in the intent, trash them.                        // System code calling with this stuff just needs to know                        // this will happen.                        service = service.cloneFilter();                    }                }            }

3. Service相关数据结构

下面简单的介绍一下AMS进行Service管理时涉及到的数据结构,通过这些数据结构我们就能够很好的了解到AMS是如何对Service进行管理的,这部分没有什么很复杂的难以理解的内容,有一个直观的认识即可,能够方便阅读代码即可。

每个Service均可能有不同的应用进程来bind,同时bind该Service的Intent也可能有多个,也就是说可能存在多个进程来bind该service,并且多个不同进程可能使用同一个Intent来bind serice。下图示意了这种存在的可能。

AMS为每个bind 该service的Intent分配了一个IntentBindRecord类型对象,并存储在ServiceRecord.bindings成员变量中;

AMS为每个bind该service的进程分配一个AppBindRecord类型对象,因为不同的应用程序可能使用同一个Intent来bind service,因此AMS将这个AppBindRecord对象存储在IntentBindRecord.apps中,多进程使用同一个Intent的典型案例就是上节所说的Client Intent;

AMS为每次bind的连接分配一个ConnectionRecord类型对象,每个应用进程中可能有多个Component来使用相同的Intent来bind该Service,那么处于同一个进程的ConnectionRecord对象被存储在AppBindRecord.connections中;

而ActivityRecord以及ProcessRecord中的connections则包含了所有的bind不同的Service的ConnectionRecord对象。它与AppBindRecord.connections范围不同。

总而言之,与Service相关的几种数据结构关系如下:


4. 设置Forground属性

在系统空间吃紧的情况下,系统需要杀死一些进程并收回其内存供其他进程使用,当然service所在的进程与不能幸免,但是如果当前的service非常重要,不允许被系统回收,那么就需要在service启动时设置其Forground属性,很简单就通过Service的一个方法startForeground()来设置。系统默认Service是background的。

如果Service不想保持forground属性,那么可以调用Service的stopForeground()方法来实现,使其回到background属性。

在设置Service 为forground属性时,可以要求系统显示一个notification在status bar,user可以通过notification来执行设定的操作。

如果进程中只要有一个Service处在forground属性状态,那么这个进程被回收的oom_adj值就为PERCEPTIBLE_APP_ADJ,从而达到避免被系统回收的目的。关于AMS的oom机制以后专门分析。

computeOomAdjLocked()@ActivityManagerService.java

         else if (app.foregroundServices) {            // The user is aware of this app, so make it visible.            adj = PERCEPTIBLE_APP_ADJ;            schedGroup = Process.THREAD_GROUP_DEFAULT;            app.adjType = "foreground-service";        } 

5. unbind Service

bind Service过程是一个从无到有的集创建和bind的过程,而unbind过程则是集unbind和销毁Service的过程,但是unbind何时才会包含销毁service的过程,这个规则还是有必要研究一下。

5.1 unbind Service

对应client来说,unbind Service过程将会unbind掉所有通过同一IServiceConnection bind的client; 对于service来说,只有当某一个Intent记录的通过该Intent bind service的进程个数为0时,service的进程才会去执行unbind的操作,如下面代码所描述。 removeConnectionLocked()@ActivityManagerService.java
        if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0                && b.intent.hasBound) {            try {                bumpServiceExecutingLocked(s, "unbind");                updateOomAdjLocked(s.app);                b.intent.hasBound = false;                // Assume the client doesn't want to know about a rebind;                // we will deal with that later if it asks for one.                b.intent.doRebind = false;                s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());            } catch (Exception e) {                Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);                serviceDoneExecutingLocked(s, true);            }        }

5.2 destroy Service

unbind一个Service过程中,何时需要destroy这个Service,首先还是需要看bind该Service时设置的flag,如果设置了flag BIND_AUTO_CREATE,那么在unbind之后会去检查是否应该destroy这个Service,如下面代码所示:

removeConnectionLocked()@ActivityManagerService.java

        if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {            bringDownServiceLocked(s, false);        }
如果没有设置flagBIND_AUTO_CREATE,则AMS根本就不考虑destoy service的操作。下面来分析一下如果设置了BIND_AUTO_CREATE,那么AMS满足哪些条件就会去destroy 这个Service。

● unbind过程中,remove掉当前这个ConnectionRecord之后,发现目前Service中尚有其他的ConnectionRecord仍旧设置了BIND_AUTO_CREATE,那么这个Service将不会被destroy;

只有当Service中不再有任何的ConnectionRecord设置BIND_AUTO_CREATE之后,才去destroy这个Service。因此,可以看出BIND_AUTO_CREATE对Service的销毁过程起到了决定性的作用。

bringDownServiceLocked()@ActivityManagerService.java

            if (!force) {                // XXX should probably keep a count of the number of auto-create                // connections directly in the service.                Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();                while (it.hasNext()) {                    ArrayList<ConnectionRecord> cr = it.next();                    for (int i=0; i<cr.size(); i++) {                        if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {                            return;                        }                    }                }            }

● 如果Service中没有了任何ConnectionRecord设置了BIND_AUTO_CREATE,那么将会销毁这个Service,在销毁之前,还有通过IServiceConnection.connected()通知所有的Client。

bringDownServiceLocked()@ActivityManagerService.java

            Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();            while (it.hasNext()) {                ArrayList<ConnectionRecord> c = it.next();                for (int i=0; i<c.size(); i++) {                    try {                        c.get(i).conn.connected(r.name, null);                    } catch (Exception e) {                        Slog.w(TAG, "Failure disconnecting service " + r.name +                              " to connection " + c.get(i).conn.asBinder() +                              " (in " + c.get(i).binding.client.processName + ")", e);                    }                }            }

并且unbind掉所有的client.

bringDownServiceLocked()@ActivityManagerService.java

        if (r.bindings.size() > 0 && r.app != null && r.app.thread != null) {            Iterator<IntentBindRecord> it = r.bindings.values().iterator();            while (it.hasNext()) {                IntentBindRecord ibr = it.next();                if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr                        + ": hasBound=" + ibr.hasBound);                if (r.app != null && r.app.thread != null && ibr.hasBound) {                    try {                        bumpServiceExecutingLocked(r, "bring down unbind");                        updateOomAdjLocked(r.app);                        ibr.hasBound = false;                        r.app.thread.scheduleUnbindService(r,                                ibr.intent.getIntent());                    } catch (Exception e) {                        Slog.w(TAG, "Exception when unbinding service "                                + r.shortName, e);                        serviceDoneExecutingLocked(r, true);                    }                }            }        }
总而言之,unbind的过程需要注意4点:

1. unbind过程会unbind掉所有使用同一个IServiceConnection bind 该Service的Client;

2. unbind过程是针对Intent进行的,因此只有bind Service的IntentBindRecord中记录的进程个数为0时,才会去执行unbind过程。也就是说假如有多个进程通过同一个Intent来bind Service,那么只有当基于这个Intent的进程均被remove出IntentBindRecord之后采取真正的去unbind。

3. unbind的ConnectionRecord如果设置了BIND_AUTO_CREATE,那么AMS就会尝试去销毁这个Service,如果Service中剩余的所有ConnectionRecord均未设置BIND_AUTO_CREATE,则会去销毁这个Service,换句话说,如果当Service中有多个ConnectionRecord设置了BIND_AUTO_CREATE,那么只有当最后一个设置了BIND_AUTO_CREATE ConnectionRecord在被unbind时,才会去销毁这个Service,即使当前还有其他的ConnectionRecord bind着这个Service。

4. 如果当前的Service是通过startService()来启动的,那么任何的unbind过程均不会去销毁.

更多相关文章

  1. Android之ActivityManager与Proxy模式的运用
  2. Android应用程序启动过程——Launcher源码分析
  3. Android中AIDL实现进程通信(附源码下载)
  4. android学习笔记之十二进程通信(IPC)
  5. [5] Zygote
  6. android 开机启动桌面Activity(Launcher )的过程(一)
  7. (转)Android(安卓)知识梳理
  8. Android系统启动之Zygote
  9. Android内存泄漏检测工具大全

随机推荐

  1. Android(安卓)如何在xmL 里面动态设置pad
  2. Android入门第十六篇之Style与Theme
  3. Android(安卓)- 主要的UI元素
  4. xamarin Android(安卓)activity生命周期
  5. Android数字签名
  6. Android中巧妙的位运算
  7. Android(安卓)人脸识别+人脸匹配(OpenCV+
  8. Google I/O Android(安卓)相关演讲视频汇
  9. [Android] 使用Matrix矩阵类对图像进行缩
  10. Android多核与单核的性能持平的问题