目录
startServic
bindService
AIDL和Messenger区别
IntentService
AIDL
startServicService的创建 1) 创建一个类继承Service,当然子类也可以例如IntentService.重写方法:
onCreate(): 在每个service的生命周期中这个方法会且仅会调用一次,并且它的调用在onStartCommand()以及onBind()之前,我们可以在这个方法中进行一些一次性的初始化工作。
onStartCommand(): 其他组件通过startService()启动service,就会调用这个方法,service的主要操作就在这里.
onBind(): 其他组件通过bindService()进行绑定Service,就会调用这个方法.这个方法返回一个IBinder,这意味重写的时候必须返回一个IBinder对象,这个IBinder是用来支撑其他组件和Service通信.假如不希望被绑定,返回一个Null就可以了.
onDestory(): 这是service一生中调用的最后一个方法,当这个方法被调用之后,service就会被销毁。所以我们应当在这个方法里面进行一些资源的清理,比如注册的一些监听器什么的。
2)注册
一般我们用名字在Mainfest文件进行声明就好了. 属性介绍:
android:enabled : 如果为true,则这个service可以被系统实例化,如果为false,则不行。默认为true
android:exported : 如果为true,则其他应用的组件也可以调用这个service并且可以与它进行互动,如果为false,则只有与service同一个应用或者相同user ID的应用可以开启或绑定此service。它的默认值取决于service是否有intent filters。如果一个filter都没有,就意味着只有指定了service的准确的类名才能调用,也就是说这个service只能应用内部使用——其他的应用不知道它的类名。这种情况下exported的默认值就为false。反之,只要有了一个filter,就意味着service是考虑到外界使用的情况的,这时exported的默认值就为true
android:icon : 一个象征着这个service的icon
android:isolatedProcess : 如果设置为true,这个service将运行在一个从系统中其他部分分离出来的特殊进程中,我们只能通过Service API来与它进行交流。默认为false。
android:label : 显示给用户的这个service的名字。如果不设置,将会默认使用的label属性。
android:name : 这个service的路径名,例如“com.lypeer.demo.MyService”。这个属性是唯一一个必须填的属性。
android:permission : 其他组件必须具有所填的权限才能启动这个service。
android:process : service运行的进程的name。默认启动的service是运行在主进程中的。
启动Service startServic
bindService首先我们来说明一下,就是说这个bindService是一种比startService复杂的方式,不过这个BindService的交互比startService更加复杂.
客户端配置 调用bindService()方法.
public boolean bindService(Intent service, ServiceConnection conn, int flags) { return mBase.bindService(service, conn, flags); } 参数分析:
参数一:intent,指定启动哪一个service以及传递一些数据过去
参数二:ServiceConnection 通信的关键类 重写两个回调方法: onServiceConnected(): 目的获取返回的IBinder接口,类似一个代理类的作用,然后通过这个代理类去获取Service,然后调用其内部的方法.
onServiceDisconnected() Android系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。当客户端取消绑定时,系统“绝对不会”调用该方法。
参数三:int值,它是一个指示绑定选项的标志,通常应该是 BIND_AUTO_CREATE,以便创建尚未激活的服务。 其他可能的值为 BIND_DEBUG_UNBIND 和 BIND_NOT_FOREGROUND,或 0(表示无)。
服务端配置 根据上面绑定的Service需求,需要重写OnBind(),返回一个IBinder接口对象,用于交互.
获取IBinder接口 IBinder接口的,一个在整个Android系统中都非常重要的东西,是为高性能而设计的轻量级远程调用机制的核心部分.它不仅仅是内部调用,也可以远程调用. 我们只要记住一个重点:Binder类对象,客户端可以通过这个类似代理类的方式调用服务端的共有方法.
1)继承Binder类(同一个进程间的) 1. service类中创建一个Binder实例(重点在于公共方法) 1) 包含客户端可调用的公共方法 2) 返回当前Service实例,其中有公共方法 3) 由当前service承载的其他类的实例,其中包含客户端可调用的公共方法 方式一:
OnBind()方法返回这个Binder实例. 客户端通过onServiceDisconnected(),接受这个对象,使用这个方法.
2)使用Messenger Messenger核心是Message以及Handler进行线程间通信. 步骤:
服务端实现一个Handler,接受客户端的调用的回调. 服务端创建Messenger对象通过Messenger得到IBinder对象,返回给客户端. 客户端使用IBinder将Messenger实例化,然后将Message对象发给服务. 服务端Handler接受Message对象,然后实现对应方法. 这种是利用了Handler和Message的机制.
//服务端 public class MessengerServiceDemo extends Service {
static final int MSG_SAY_HELLO = 1;
class ServiceHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: //当收到客户端的message时,显示hello Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } }
final Messenger mMessenger = new Messenger(new ServiceHandler());
@Nullable @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); //返回给客户端一个IBinder实例 return mMessenger.getBinder(); } } 这里最主要的方法是我们需要在OnBind上面下功夫,要知道Messenger是可以关联Handler,并且可以返回IBinder的.
当然,这里因为是跨进程通讯的,所以要让别的应用知道怎么找到我们Service.就是在注册文件上做文章.
客户端:
public class ActivityMessenger extends Activity {
static final int MSG_SAY_HELLO = 1;
Messenger mService = null; boolean mBound;
private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { //接收onBind()传回来的IBinder,并用它构造Messenger mService = new Messenger(service); mBound = true; }
public void onServiceDisconnected(ComponentName className) { mService = null; mBound = false; } };
//调用此方法时会发送信息给服务端 public void sayHello(View v) { if (!mBound) return; //发送一条信息给服务端 Message msg = Message.obtain(null, MSG_SAY_HELLO, 0, 0); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
@Override protected void onStart() { super.onStart(); //绑定服务端的服务,此处的action是service在Manifests文件里面声明的 Intent intent = new Intent(); intent.setAction("com.lypeer.messenger"); //不要忘记了包名,不写会报错 intent.setPackage("com.lypeer.ipcserver"); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); }
@Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } } 细节: 1) 首先我们需要注意在OnCreate()上面,对Intent做文章,要设置包名,毕竟是跨进程. 2) onServiceConnected()方法获取服务端返回的IBinder,构造Messenger,然后通过Messenger发送Message进行交互. 3)Messenger是一个信使.
AIDL和Messenger区别这么说,Messenger的内部实现还是AIDL. Messnger好处:它会把所有的请求排入队列,因此你几乎可以不用担心多线程可能会带来的问题。但是只能实现串行的信息交互. AIDL:可以令项目中存在大量的并发交互.
IntentService默认情况下service将工作于应用的主线程,而这将会降低所有正在运行的Activity的性能。而IntentService就不同了. 不需要担心多线程问题,会自动请求停止服务,但是不绑定,会有一个工作队列. 特色:
创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent
创建工作队列,用于将一个 Intent 逐一传递给 onHandleIntent() 实现,这样的话就永远不必担心多线程问题了
在处理完所有启动请求后停止服务,从此再也不用担心我忘记调用 stop() 了
提供 onBind() 的默认实现(返回 null)
提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现
因此我们只需要实现onHandleIntent()方法来完成具体的功能逻辑就可以了。 例子:
public class IntentServiceDemo extends IntentService {
public IntentServiceDemo(String name) { super(name); //构造方法 }
@Override protected void onHandleIntent(Intent intent) { //在这里根据intent进行操作 } }
接受Intent,会根据Intent进行操作,但是主要要是重写其他方法,就不要删除超类实现,而且不能处理多个请求.
AIDL这个语言的目的是为了实现进程间通信. 在一个进程访问另外一个进程的数据,还有调用特定的方法.
特点:
文件类型: .aidl文件
数据类型: AIDL默认支持一些数据类型.非默认数据使用前必须导包.使用对象也需要导包. 默认: Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。 String 类型。 CharSequence类型。 List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。 Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
定向Tag:除非是基本类型和String ,CharSequence.这些是默认的in in:客户端流向服务端 out:服务端流向客户端 inout:双向流动 大约思路: 客户端传递对象信息给客户端,服务端改动,客户端就会改动.
AIDL类型: 1) 定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。 2) 定义方法接口,以供系统使用来完成跨进程通信的.定义接口,里面都是方法.
注意AIDL我们在乎的是定义并非实现. AIDL的实现: 1) 实现Parcelable接口,目的是为了我们传送的数据能够在内存之间流通.过程可以说是序列化和反序列化. 客户端–>序列化对象–>服务端—>反序列化对象
如何快速生成一个可序列化类?目的是为了我们的操作对象能够传. 注意:假如是默认就不需要这样了.
编译器生成: 1. Bean类(成员变量,set,get); 2. 实现Parcelable接口,主动解决错误.(alt+enter) 3. 注意,因为默认是in定向tag,所以只有写出的这么一个方法,假如需要其他就添加.
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(price); }
/** * 参数是一个Parcel,用它来存储与传输数据 * @param dest */ public void readFromParcel(Parcel dest) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 name = dest.readString(); price = dest.readInt(); } 2)书写AIDL Book.aild文件引入后,就能让其他类使用这么一个对象.
AS: AIDL:直接生成,写好接口. 但是AS的文件路径需要我们处理一下,因为AS上面有分成Aidl包.这时候aidl文件就不能跟自己的.java文件对应了.(因为我们要求文件的报名要一样.) 因为我们的工具是自动到java下找java文件,我们要将java文件放到aidl包的的方式.
修改 build.gradle 文件:在 android{} 中间加上下面的内容:
sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/aidl'] } } 但是目录很难看.
3)移植文件 我们必须保证客户端服务端都有对应的.aidl和.java文件. 直接复制aidl包就可以了.
4)编写服务端代码 文件移植之后,假如我们clean,编译器就会根据AIDL文件生成.java文件.在服务端实现具体的方法,然后客户端调用方法接口.
AIDL文件的结构: 1. 初始化 2. 重写Stub类中的方法,这其中有AIDL的方法接口的具体逻辑,可以说是引入AIDL文件生成的对象. 3. OnBind(),返回Stub代理类
5)编写客户端代码. 1. 初始化AIDL生成的java类对象 2. 通过Intent进行bindService绑定. 3. Conn就利用Stub作为对象类.调用其中的方法.
AIDL本质工作: 1) AIDL目的是为了让编译器帮我们生成一个.java文件 2) 生成的.java文件会有一个类似Stub的代理类,服务端重写了之前AIDL中定义的方法,就是这个Stub中的方法,并且通过OnBind()返回. 3) 客户端捕获这个对象然后调用.
我们的分析从应用开始: 1. 使用的是.java文件,但是它是一个接口类.实际我们调用的是它的一个实现类.获取的方法是通过BookManager.Stub.asInterface(BinderProxy 对象)方法获取. 2. BinderProxy是实现了BookManager接口的一个类,也就是我们上文使用的这么一个类.它是经过搜查得到的.也是我们最终需要进行交互的一个类.
而Proxy的方法工作流程: 1,生成 _data 和 _reply 数据流,并向 _data 中存入客户端的数据。 2,通过 transact() 方法将它们传递给服务端,并请求服务端调用指定方法。 3,接收 _reply 数据流,并从中取出服务端传回来的数据。
客户端–>存入_data–>transact()–>传给服务器–>reply()–>服务器返回的数据.
服务端分析 .java文件中有个方法onTransact(),负责接收传递过来的信息. 工作流程: 1,获取客户端传过来的数据,根据方法 ID 执行相应操作。 2,将传过来的数据取出来,调用本地写好的对应方法。 3,将需要回传的数据写入 reply 流,传回客户端。
总结: 客户端代理服务端 其中我们的过程就是一个通过代理进行交互的过程.