Android Service
所谓的service有Local Service和Remote Service区分:
LocalService:就是client和Service在同一个进程当中。
RemoteService:就是client 和Service在不同的进程中。
我们通常的用法是自己建一个.java文件中通过继承Service来实现一个子Service。然后在通过其他的Activity来连接实现的那个Service就有点像网络编程中的链接服务器一样。但是这里的Service不是独立的一个服务器端,它可以说是手机app的一个实现模块。所以不是像传统的网络编程一样,首先启动服务器,然后在从client去访问。android中的Service要通过访问端通过传递消息(Intent)去开启Service。通常有两种方法去启动一个Service。
一)context.startService()启动方式
public abstract ComponentName startService(Intent service)
通过函数的原型我们可以看出是一个Component去调用的,参数就是一个Intent,在该Intent中去指定一些规则和消息来启动符合条件的Service。
The Intent can either contain the complete class name of a specific service implementation to start, or an abstract definition through the action and other fields of the kind of service to start(by gg doc).
此时我们明确了client端发出去Intent去启动Service的过程,但是Service端是怎么来响应的?然后怎么启动你实现的子Service的呢?
Every call to this method(startService(Intent intent)) will result in a corresponding call to the target service's
onStartCommand(Intent, int, int)(by gg doc)就是each component调用startService(Intent intent)来启动Service的时候,service端作出的响应就是call onStartCommand(Intent,int,int)函数
public int onStartCommand (Intent intent, int flags, int startId)
当调用该函数的时候intent对象就是startService(Intent intent)红传递过来的intent对象。flags:start request的额外请求,可以是:0,
START_FLAG_REDELIVERY
,START_FLAG_RETRY
.
startId:an unique integer 来展示这次请求
目前gg doc给出了四种返回值类型。
START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
START_NOT_STICKY:返回该返回值时,如果在执行完onStartCommand后,Service被一场kill掉后,系统不会重启该Service。
START_REDELIVER_INTENT:但返回这个返回值的时候,从startService传过来的intent对象将保存它的数据,当Service被异常kill掉的后,Service会重启,然后再将Intent传入。
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启
这个函数不能人为的去直接调用,而是由Android system在Service的main thread中有系统去调用。
所以一般情况下继承Service的子类都要重写Service类的:OnCreate(),onStartCommand(),onBinder(),onStart(),onDestory(),onRebind(),onUnbind()方法,然后在编写其他自己要是想的功能实现函数。
所以通过context.startService(Intent intent)来启动Service时将是一个:
client端 Service端
startService()---------------->onCreate()--->onStart().当stopService的时候Service端就会调用onDestroy()方法来Destroy
stopService()----------------->onDestory()--->Service stop
如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。下次调用者再起来可以stopService。
二)context.bindService()启动方式
public boolean bindService (Intent service, ServiceConnection conn, int flags)
public abstract IBinder onBind (Intent intent)
进程间通信
每一个进程都有自己独立的内存,一个进程的1000地址也许是另一个进程的100000地址,所以如何在进程间通信就变得复杂了,对于基本类型的数据比如整数直接传递过去就行了,但是要是字符串的话就要先目的进程空间中分配一快能容纳该字符串内存,然后在复制到内存中。再如果要传递一个类的实例,也是先为类分配内存,然后复制一份再传递可以吗?我认为不可以,我至少可以找到一个理由:类中成员除了属性还有方法,即使属性能完整传过去,但还有方法呢?方法是独立于类对象存在的,所以到另一个进程中再引用同一个方法就要出错了,还是因为独立地址空间的原因。Android中通常要在Activity之间专递数据,而根据Android的设计,即使同一个Activity都不一定运行在同一个进程中,所以就要进程间传递数据了,那么到底如何在进程之间传递类对象呢?简单来说可以这样做:在进程A中把类中的非默认值的属性和类的唯一标志打成包(这就叫序列化),把这个包传递到进程B,进程B接收到包后,跟据类的唯一标志把类创建出来,然后把传来的属性更新到类对象中,这样进程A和进程B中就包含了两个完全一样的类对象。
活动对象
Binder对象
FileDescriptor对象
无类型容器类
一类final方法,用于读写标准的java容器类。这些方法们是:writeArray(Object[]), readArray(ClassLoader), writeList(List), readList(List, ClassLoader), readArrayList(ClassLoader), writeMap(Map), readMap(Map, ClassLoader), writeSparseArray(SparseArray), readSparseArray(ClassLoader)。
IBinder
Android的远程调用就是通过IBinder来实现的,远程调用的基本接口,还可以用于进程内调用,该接口定义了与远程对象交互的协议,不可以直接实现这个接口,要派生实现。
主要API:
transact():向远端的IBinder对象发送发出调用。onTransact():是远程的对象能够响应另一端发送过来的调用请求。这两个API都是同步执行的。transact()方法要一直阻塞到onTransact()方法调用完成后才返回。
transact()发送的数据类型是Pracel类型的数据,是一种一般的缓冲区,除了有数据外还有一些描述他内容的元数据。元数据用于管理IBinder对象的引用,这样就能在缓冲区从一个进程移动到另一个进程时保存这些引用。这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中,如果另一个进程把同一个IBinder的引用回发到原来的进程,那么这个原来的进程就能接收到发出的那个IBinder的引用。这种机制使IBinder和Binder像唯一标志符那样在进程间管理。
系统为每个进程维护一个存放交互线程的线程池。这些交互线程用于派送所有从另外进程发来的IPC调用。例如:当一个IPC从进程A发到进程B,A中那个发出调用的线程(这个应该不在线程池中)就阻塞在transact()中了。进程B中的交互线程池中的一个线程接收了这个调用,它调用Binder.onTransact(),完成后用一个Parcel来做为结果返回。然后进程A中的那个等待的线程在收到返回的Parcel后得以继续执行。实际上,另一个进程看起来就像是当前进程的一个线程,但不是当前进程创建的。
Binder机制还支持进程间的递归调用。例如,进程A执行自己的IBinder的transact()调用进程B的Binder,而进程B在其Binder.onTransact()中又用transact()向进程A发起调用,那么进程A在等待它发出的调用返回的同时,还会用Binder.onTransact()响应进程B的transact()。总之Binder造成的结果就是让我们感觉到跨进程的调用与进程内的调用没什么区别。
当操作远程对象时,你经常需要查看它们是否有效,有三种方法可以使用:
1 transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常。
2 如果目标进程不存在,那么调用pingBinder()时返回false。
3 可以用linkToDeath()方法向IBinder注册一个IBinder.DeathRecipient,在IBinder代表的进程退出时被调用。
用法:
要实现IBinder来支持远程调用,应从Binder类派生一个类。Binder实现了IBinder接口。但是一般不需要直接实现此类,而是跟据你的需要由开发包中的工具生成,这个工具叫aidi。你通过aidi语言定义远程对象的方法,然后用aidi工具生成Binder的派生类,然后就可使用之。当然,你也可以直接从Binder类派生以实现自定义的RPC调用,或只是实例化一个原始的Binder对象直接作为进程间共享的令牌来使用。
Remote Service
-
service实现一个接收从客户端的每个调用引起的回调的Handler.
-
Handler被用来创建一个Messenger对象(它是Handler的一个引用).
-
Messenger创建一个从service的onBind()返回给客户端的IBinder.
-
客户端使用IBinder来实例化这个Messenger(它引用到service的Handler),客户端用它来向service发送Message.
-
service在它的Handler中接收每个消息—具体的,是在handleMessage()方法中.
这此方式下,service中没有能让客户端调用的方法,客户端传送的是service在它的Handler中接收的"消息"(Message对象).
应用组件(客户端)可以调用bindService()绑定到一个service.Android系统之后调用service的onBind()方法,它返回一个用来与service交互的IBinder.
绑定是异步的.bindService()会立即返回,它不会返回IBinder给客户端.要接收IBinder,客户端必须创建一个ServiceConnection的实例并传给bindService().ServiceConnection包含一个回调方法,系统调用这个方法来传递要返回的IBinder.
注:只有activities,services,和contentproviders可以绑定到一个service—你不能从一个broadcastreceiver绑定到service.