Android学习笔记(2) - Service组件之一

本文详细探讨了Android Service组件的工作原理,从Service的声明、启动到客户端与服务端的交互,特别是通过AIDL进行跨进程通信的过程。文章指出Service并非独立进程或线程,而是运行在应用程序的主线程中,需要手动创建线程处理耗时任务。通过Context.startService()和Context.bindService()启动Service,分别用于后台执行任务和提供长期连接。Service的生命周期管理和回调方法在不同场景中的应用也进行了说明。
摘要由CSDN通过智能技术生成

在AIDL之一的学习中看到,用户在声明AIDL之后,server端只需要实现service接口,在client端通过ServiceConnection等帮助类就可以直接实现和server端的通信功能,底层实现完全可以不用关注;其次,通过学习AIDL编译的中间代码,我们发现实际上client端是拿到server端的一个IBinder实例,通过调用这个IBinder的tranact方法向server端发送命令和数据, 此时server端的onTransact会被调用,继而调用server端的用户实现后将结果返回给client端。

本次继续往下分析代码,先回到server端,其中只有一个AdditionService类(如下)继承Service,两个主要的方法:

  1. 实例化一个Stub对象,这个Stub类类在1中介绍过:它需要继承自Binder,同时还会实现AIDL中实现用户逻辑
  2. 在onBind中返回这个新建的Binder
public class AdditionService extends Service {

    private ArrayList users;

    public AdditionService() {

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        users = new ArrayList<Userd>();
        return iBinder;
    }

    private  IBinder iBinder = new  IAddService.Stub() {
        @Override
        public int add(int v1, int v2) throws RemoteException {
            return v1 + v2;
        }

        @Override
        public List<Userd> addUser(Userd user) {
            users.add(user);
            return users;
        }
    };
}

从android文档中得到Service的解释:

Service 是Android四大组件之一,它用来实现那些没有UI交互且需要长时间运行的应用或者是为其它应用提供特定功能的程序。每个Service类都需要在AndroidManifest.xml中声明。可以通过Context.startService()和Context.bindService()来启动。

Service类和其它应用实体一样,运行在主线程中。所以,如果你想在Service中完成一些耗费CPU的操作(比如播放MP3)或者阻塞的操作(如网络),需要自己创建线程来完成。IntentService类是一个标准的实现这类功能的service类。

Service类不是:

  1. 不是一个单独的进程。一个Service实例并不意味着它跑在自己的进程之中。除非专门的指定,否则Service和应用APP运行在同一个进程当中。
  2. 不是一个线程。Service并不意味着它就不在主线程运行。

Service本身概念很简单,提供两种功能:

  1. 提供这样一种功能,应用通知系统它想在后台做一些事情(在没有用户和应用交互的时候)。使用Context.startService()来告诉系统为这个service的工作做调度,直到系统自己退出或者被显式的停止。
  2. 提供这样一种功能,一个应用为其它应用提供某些功能。使用Context.bindService(),允许Service被另外的应用长时间的连接,以便获得它的服务。

不论是哪种方式,当一个service组件被创建时,系统所需要做的只是在主线程中实例化这个主件并调用合适的回调,对于其它的工作,比如创建第二实际工作的线程等,则完全由Service自己去实现。

可以知道的是Service本身十分简单,用户自己决定与它交互的方式是简单或者复杂;比如把它当做是本地的Java实体而直接调用它的方法(就像在Local Service例子中看到的那样),或者通过AIDL提供完全的远程接口。

文档中的给出了local和远程的例子,其中local的例子和我的例子类似;只不过在已经知道是同一个进程的情况下可以直接通过强制转换来实现。找到Service代码 frameworks/base/core/java/android/app/service.java,它继承ContextWrapper和ComponentCallbacks2

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 

其中ContextWrapper类是Context类的一个封装,它代表着Android系统在应用环境下提供的全局功能和信息,比如可以通过它访问资源,类和调用应用级的操作,像启动activity,broadcasting并接受intents,ComponentCallback2是回调类。但我们看到这个类是一个abstract函数,需要子类去实现,server端无从下手,所以再回到client端,在那里我们调用了bindService()方法来绑定服务(本地或者远程),由于我们是在Activity类中直接调用的这个方法,也就是说这个方法属于Activity的一个方法;查看Activity的代码frameworks/base/core/java/android/app/Activity.java它并没有bindService这个方法,只能从其父类分析,我们发现Activity类继承ContextThemeWrapper,ContextThemeWrapper又继承ContextWrapper。前面看到Service继承ContextWrapper,这里Activity还是继承ContextWrapper,事实上我们会发现很多类比如Application...等都继承自ContextWrapper;前面也知道ContextWrapper继承自Context类,这是Android全局上下文的实例,那这么说假如一个APP中既有Service又有Activity,则会有两个Context实例?结论是否定的,查看ContextWrapper类可以知道,Wrapper类虽然继承自父类Context(是一个虚类),但是它内部维护的mBase 这个Context实例是唯一的一个Context。

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
...

所以说,在Activity中调用的bindService实际上调用的是父类ContextWrapper中的bindService,但是查看ContextWrapper类时发现frameworks/base/core/java/android/content/ContextWrapper.java,其中的bindService直接调用了mBase.bindService,这个mBase从哪里来呢,我们后面学习Activity和Context的时候再来补上这里。暂且按照网上资料认为它的实现是ContextImpl,另外一个印证也可以从ContextImpl的注释中获得,该类位于frameworks/base/core/java/android/app/ContextImpl.java。

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context {

如下是ContextImpl中的bindService实现。

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
    }

...
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(this);
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

有两个地方需要注意,

1. getServiceDispatcher和ServiceConnection相关,我们知道client端需要在ServiceConnection中返回绑定成功的bind;通过sd参数传到了ActivityManager中,就能将结果返回

2. 流程转到ActivityManager的bindService,ActivityManager.java中最终又转到远程ActivityManagerService,如下(这里看到的Stub和前面学习AIDL是一样的,也是跨进程通信),代码位于frameworks/base/core/java/android/app/ActivityManager.java.

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

于是我们找到frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中ActivityManagerService的代码

    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
            int userId) throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");

        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        synchronized(this) {
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }

接下来转到mServices.bindServiceLocked(),而这个mService的构造是mServices = new ActiveServices(this);,代码位于frameworks/base/services/core/java/com/android/server/am/ActiveService.java,这个函数十分复杂且包含很多基础类如Handler等,后面再学习。从代码中也可以看到主要的调用过程是启动Service(包括新建进程等,bringUpServiceLocked),然后将结果通知到client端,比如connection直接调用了connected等。

对于前面传入的sd是如何将结果返回给client端的,可以查看ContextImpl.java中的实现

sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);

将conn接口实例传入,并返回sd,其中sd是一个IServiceConnection类型,这个类型是ServiceDispatcher的一个内部类,而且是一个Binder接口,也就是说它能够跨进程传输。代码位于frameworks/base/core/java/android/app/LoadedApk.java中。

    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
                sd = map.get(c);
            }
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
                if (map == null) {
                    map = new ArrayMap<>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            return sd.getIServiceConnection();
        }
    }

代码将sd.getIServiceConnection返回并传到ActivityMangerService类中,即mIServiceConnection;以前面ActivityManagerService调用connected方法为例,对应到Client进程(也就是LoadedApk.java中)的调用为

        private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service, boolean dead)
                    throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service, dead);
                }
            }
        }


...

        public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }

在doConnected中调用的onServiceConnected,将结果返回到APP中的回调函数当中

            // If there is a new viable service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            } else {

参考:https://developer.android.com/reference/android/app/Service

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值