Android中的Service详解

来源:互联网 发布:网络的重要性的名言 编辑:程序博客网 时间:2024/05/19 16:36

转:http://blog.csdn.net/jiangwei0910410003/article/details/18978405

今天我们就来介绍一下Android中的四大组件中的服务Service,说到Service,

它分为本地服务和远程服务:区分这两种服务就是看客户端和服务端是否在同一个进程中,本地服务是在同一进程中的,远程服务是不在同一个进程中的。

开启服务也有两种方式,一种是startService(),他对应的结束服务的方法是stopService(),另一种是bindService(),结束服务的是unBindService(),这两种方式的区别就是:当客户端Client使用startService方法开启服务的时候,这个服务和Client之间就没有联系了,Service的运行和Client是相互独立的,想结束这个服务的话,就在服务本身中调用stopSelf()方法结束服务。而当客户端Client使用bindService方法开始服务的时候,这个服务和Client是一种关联的关系,他们之间使用Binder的代理对象进行交互,这个在后面会详细说到,要是结束服务的话,需要在Client中和服务断开,调用unBindService方法。

在这里我们只做bindService方式的研究,而startService方式比较独立和简单,这里就不做演示了。


首先来说一下本地服务:

本地服务很简单的,就是Client和这个服务在同一个进程中:

先来看一下代码吧:

下面这张图是项目的结构图:


为了方便数据的访问,这里定义一个数据的访问接口:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.nativeservice.demo;
  2. /**
  3. * 访问接口
  4. * @author weijiang204321
  5. */
  6. publicinterface IStudent {
  7. /**
  8. * 通过no访问name
  9. * @param no
  10. * @return
  11. */
  12. public String getNameByNumber(int no);
  13. }

下面再来看一下StudentService的代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.nativeservice.demo;
  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.Binder;
  5. import android.os.IBinder;
  6. /**
  7. * 定义的服务Service
  8. * @author weijiang204321
  9. */
  10. publicclass StudentService extends Service{
  11. //名称
  12. publicstatic String[] nameAry = {"张飞","李小龙","赵薇"};
  13. /**
  14. * 通过no获取name
  15. * @param no
  16. * @return
  17. */
  18. private String getNameByNo(int no){
  19. if(no>0 && no<4)
  20. return nameAry[no-1];
  21. returnnull;
  22. }
  23. @Override
  24. public IBinder onBind(Intent arg0) {
  25. returnnew StudentBinder();
  26. }
  27. /**
  28. * 自定义的Binder对象
  29. * @author weijiang204321
  30. *
  31. */
  32. privateclass StudentBinder extends Binder implements IStudent{
  33. @Override
  34. public String getNameByNumber(int no) {
  35. return getNameByNo(no);
  36. }
  37. }
  38. }


StudentService中就是定义一个访问name的方法,在onBind方法中返回Binder对象,这个就是Client和Service之间交互的关键对象

下面看一下Client代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.nativeservice.demo;
  2. import android.app.Activity;
  3. import android.content.ComponentName;
  4. import android.content.Intent;
  5. import android.content.ServiceConnection;
  6. import android.os.Bundle;
  7. import android.os.IBinder;
  8. import android.os.Looper;
  9. import android.widget.Toast;
  10. /**
  11. * 测试Service
  12. * @author weijiang204321
  13. *
  14. */
  15. publicclass MainActivity extends Activity {
  16. private IStudent student;
  17. @Override
  18. protectedvoid onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_main);
  21. //开启查询名称的服务
  22. Intent service = new Intent(this,StudentService.class);
  23. bindService(service,new StudentConnection(),BIND_AUTO_CREATE);
  24. //延迟2s在显示查询的内容,不然开启服务也是需要时间的,如果不延迟一段时间的话,student对象为null;
  25. new Thread(){
  26. @Override
  27. publicvoid run(){
  28. try {
  29. Thread.sleep(2*1000);
  30. Looper.prepare();
  31. Toast.makeText(getApplicationContext(), student.getNameByNumber(1), Toast.LENGTH_LONG).show();
  32. Looper.loop();
  33. }catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }.start();
  38. }
  39. /**
  40. * 自定义的服务连接connection
  41. * @author weijiang204321
  42. *
  43. */
  44. privateclass StudentConnection implements ServiceConnection{
  45. @Override
  46. publicvoid onServiceConnected(ComponentName name, IBinder service) {
  47. student = (IStudent)service;
  48. }
  49. @Override
  50. publicvoid onServiceDisconnected(ComponentName name) {
  51. }
  52. }
  53. }


在这里,用到了bindService方法,该方法的参数是:第一个参数是服务的intent,第二参数是一个ServiceConnection接口,第三个参数是启动服务的方式常量,这里最主要的就是第二个参数ServiceConnection接口,我们自己定义一个实现该接口的类。

StudentConnection,必须实现两个方法,这两个方法见名思议,一个是连接时调用的方法,一个是断开连接时的方法,在开始连接的方法onServiceConnected中传回来一个IBinder对象,这个时候需要将其转化一下,这个就是为什么要在开始的时候定义一个IStudent接口,在这里访问数据就方便了,同时在Client代码中要做个延迟的操作来访问数据,因为开启服务,连接这个过程是需要时间的,所以在这里就延迟了2s,这里只是为了能够正常显示数据,才这么做的,不然student对象是为null的,当然要根据自己的实际情况操作。最后还要在AndroidMainfest.xml中配置Service:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <serviceandroid:name=".StudentService"></service>
这里开启服务用的是显示意图,所以没有定义action过滤器了,运行结果很简单这里就不截图了


下面在来说一下远程服务:

在说到远程服务的时候,我们需要先了解一些预备的知识:

首先来了解一下AIDL机制:

AIDL的作用
由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。


AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。在Android中, 则采用AIDL(Android Interface Definition Language:接口描述语言)方式实现。
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。


选择AIDL的使用场合
官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。


了解了AIDL之后下面来看一下项目的结构:

我们这个远程服务是想在将服务端和客户端分别放到一个应用中,所以这里要建立两个Android项目一个是remoteService,一个是remoteClient

首先来看一下Service端的项目结构:


在这里我们需要定义一个aidl文件,具体步骤很简单的:

因为AIDL相当于是一个接口,所以它的定义和interface的定义很类似的,使用interface关键字,有一点不同的是,AIDL中不能有修饰符(public,protected,private),不然报错,这个你们可以自己尝试一下,然后将定义好的AIDL文件的后缀名.java改成.aidl,此时在gen文件夹下面就会多出一个与之对应的java文件,这个是编译器干的事情。这个AIDL接口中定义的一般都是Client和Service交互的接口。

下面来看一下StudentQuery.aidl文件:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package cn.itcast.aidl;
  2. //注意没有任何的访问权限修饰符
  3. interface StudentQuery {
  4. //通过number来访问学生的name
  5. String queryStudent(int number);
  6. }
代码结构和接口是大同小异的。

再来看一下服务端的代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package cn.itcast.remote.service;
  2. import cn.itcast.aidl.StudentQuery;
  3. import android.app.Service;
  4. import android.content.Intent;
  5. import android.os.IBinder;
  6. import android.os.RemoteException;
  7. /**
  8. * 远程服务端
  9. */
  10. publicclass StudentQueryService extends Service {
  11. //姓名名称
  12. private String[] names = {"张飞","李静","赵薇"};
  13. private IBinder binder = new StudentQueryBinder();
  14. @Override
  15. public IBinder onBind(Intent intent) {
  16. return binder;
  17. }
  18. /**
  19. * 服务中定义的访问方法
  20. * @param number
  21. * @return
  22. */
  23. private String query(int number){
  24. if(number > 0 && number < 4){
  25. return names[number - 1];
  26. }
  27. returnnull;
  28. }
  29. /**
  30. * 定义Binder,这里需要继承StudentQuery.Stub
  31. * StudentQuery是我们定义的AIDL
  32. * @author weijiang204321
  33. *
  34. */
  35. privatefinalclass StudentQueryBinder extends StudentQuery.Stub{
  36. public String queryStudent(int number) throws RemoteException {
  37. return query(number);
  38. }
  39. }
  40. }
这个服务端的代码和我们之前的本地服务代码差不多,不同的是我们定义的Binder类是继承了StudentQuery.Stub类,其中StudentQuery是我们定义的AIDL文件,编译器帮我们生成的StudentQuery.java(在gen文件夹中)这个类,下面来看一下这个类吧:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. /*
  2. * This file is auto-generated. DO NOT MODIFY.
  3. * Original file: C:\\Users\\weijiang204321\\Desktop\\传智播客Android视频教程_源代码\\remoteService\\src\\cn\\itcast\\aidl\\StudentQuery.aidl
  4. */
  5. package cn.itcast.aidl;
  6. //注意没有任何的访问权限修饰符
  7. publicinterface StudentQuery extends android.os.IInterface
  8. {
  9. /** Local-side IPC implementation stub class. */
  10. publicstaticabstractclass Stub extends android.os.Binder implements cn.itcast.aidl.StudentQuery
  11. {
  12. privatestaticfinal java.lang.String DESCRIPTOR = "cn.itcast.aidl.StudentQuery";
  13. /** Construct the stub at attach it to the interface. */
  14. public Stub()
  15. {
  16. this.attachInterface(this, DESCRIPTOR);
  17. }
  18. /**
  19. * Cast an IBinder object into an cn.itcast.aidl.StudentQuery interface,
  20. * generating a proxy if needed.
  21. */
  22. publicstatic cn.itcast.aidl.StudentQuery asInterface(android.os.IBinder obj)
  23. {
  24. if ((obj==null)) {
  25. returnnull;
  26. }
  27. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  28. if (((iin!=null)&&(iininstanceof cn.itcast.aidl.StudentQuery))) {
  29. return ((cn.itcast.aidl.StudentQuery)iin);
  30. }
  31. returnnew cn.itcast.aidl.StudentQuery.Stub.Proxy(obj);
  32. }
  33. @Overridepublic android.os.IBinder asBinder()
  34. {
  35. returnthis;
  36. }
  37. @Overridepublicboolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
  38. {
  39. switch (code)
  40. {
  41. case INTERFACE_TRANSACTION:
  42. {
  43. reply.writeString(DESCRIPTOR);
  44. returntrue;
  45. }
  46. case TRANSACTION_queryStudent:
  47. {
  48. data.enforceInterface(DESCRIPTOR);
  49. int _arg0;
  50. _arg0 = data.readInt();
  51. java.lang.String _result = this.queryStudent(_arg0);
  52. reply.writeNoException();
  53. reply.writeString(_result);
  54. returntrue;
  55. }
  56. }
  57. returnsuper.onTransact(code, data, reply, flags);
  58. }
  59. privatestaticclass Proxy implements cn.itcast.aidl.StudentQuery
  60. {
  61. private android.os.IBinder mRemote;
  62. Proxy(android.os.IBinder remote)
  63. {
  64. mRemote = remote;
  65. }
  66. @Overridepublic android.os.IBinder asBinder()
  67. {
  68. return mRemote;
  69. }
  70. public java.lang.String getInterfaceDescriptor()
  71. {
  72. return DESCRIPTOR;
  73. }
  74. //通过number来访问学生的name
  75. @Overridepublic java.lang.String queryStudent(int number) throws android.os.RemoteException
  76. {
  77. android.os.Parcel _data = android.os.Parcel.obtain();
  78. android.os.Parcel _reply = android.os.Parcel.obtain();
  79. java.lang.String _result;
  80. try {
  81. _data.writeInterfaceToken(DESCRIPTOR);
  82. _data.writeInt(number);
  83. mRemote.transact(Stub.TRANSACTION_queryStudent, _data, _reply, 0);
  84. _reply.readException();
  85. _result = _reply.readString();
  86. }
  87. finally {
  88. _reply.recycle();
  89. _data.recycle();
  90. }
  91. return _result;
  92. }
  93. }
  94. staticfinalint TRANSACTION_queryStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
  95. }
  96. //通过number来访问学生的name
  97. public java.lang.String queryStudent(int number) throws android.os.RemoteException;
  98. }
感觉好复杂,其实我们没必要看懂的,这个是Android内部实现的,我们在这里可以了解一下,看一下那个Stub抽象类,他实现了Binder接口,所以我们需要继承这个类就可以了,还有一个问题就是我们注意到,就是返回StudentQuery接口对象的问题:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. publicstatic cn.itcast.aidl.StudentQuery asInterface(android.os.IBinder obj)
  2. {
  3. if ((obj==null)) {
  4. returnnull;
  5. }
  6. //如果bindService绑定的是同一进程的service,返回的是服务端Stub对象本身,那么在客户端是直接操作Stub对象,并不进行进程通信了
  7. android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  8. if (((iin!=null)&&(iininstanceof cn.itcast.aidl.StudentQuery))) {
  9. return ((cn.itcast.aidl.StudentQuery)iin);
  10. }
  11. //bindService绑定的不是同一进程的service,返回的是代理对象,obj==android.os.BinderProxy对象,被包装成一个AIDLService.Stub.Proxy代理对象
  12. //不过AIDLService.Stub.Proxy进程间通信通过android.os.BinderProxy实现
  13. returnnew cn.itcast.aidl.StudentQuery.Stub.Proxy(obj);
  14. }

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. /*
  2. * Copyright (C) 2006 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package android.os;
  17. /**
  18. * Base class for Binder interfaces. When defining a new interface,
  19. * you must derive it from IInterface.
  20. */
  21. publicinterface IInterface
  22. {
  23. /**
  24. * Retrieve the Binder object associated with this interface.
  25. * You must use this instead of a plain cast, so that proxy objects
  26. * can return the correct result.
  27. */
  28. public IBinder asBinder();
  29. }
上面的是Interface接口,他只有一个方法asBinder()这个方法就是返回一个IBinder对象,而我们的AIDL接口需要实现这个接口的,所以说这个asBinder()方法的功能就是将AIDL接口转化成IBinder对象,这个是内部实现的,在asInterface()方法中可以看到:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. privatestaticclass Proxyimplements com.demo.aidl.StudentAIDL
  2. {
  3. private android.os.IBinder mRemote;
  4. Proxy(android.os.IBinder remote)
  5. {
  6. mRemote = remote;
  7. }
  8. @Overridepublic android.os.IBinder asBinder()
  9. {
  10. return mRemote;
  11. }
  12. public java.lang.String getInterfaceDescriptor()
  13. {
  14. return DESCRIPTOR;
  15. }
  16. @Overridepublic java.lang.String queryNameByNo(int no)throws android.os.RemoteException
  17. {
  18. android.os.Parcel _data = android.os.Parcel.obtain();
  19. android.os.Parcel _reply = android.os.Parcel.obtain();
  20. java.lang.String _result;
  21. try {
  22. _data.writeInterfaceToken(DESCRIPTOR);
  23. _data.writeInt(no);
  24. mRemote.transact(Stub.TRANSACTION_queryNameByNo, _data, _reply,0);
  25. _reply.readException();
  26. _result = _reply.readString();
  27. }
  28. finally {
  29. _reply.recycle();
  30. _data.recycle();
  31. }
  32. return _result;
  33. }
  34. }
这个是代理生成类,我们可以看到这个类中是返回的mRemote对象就是IBinder的一个引用,同时也返回了一个StudentQuery实现对象。

在StudentQuery.Stub中有一个asInterface方法,这个方法中我们可以看到,如果这个Service和Client是在同一个进程中,则在Client中的StudentConnection类中返回的是IBinder就是实际的对象,如果不是在同一个进程中的话,返回的是IBinder的代理对象。

其他的问题我们暂时不看了,也不去做深入的了解了,


再来看一下AndroidMainfest.xml文件中配置Service:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <service android:name=".StudentQueryService">
  2. <intent-filter >
  3. <action android:name="cn.itcast.student.query"/>
  4. </intent-filter>
  5. </service>
这里我们开启服务的话,需要用到隐式意图了,而不能直接用Service类了,因为是在不同的应用中,这样服务端的代码就差不多了,我们现在来看一下客户端的代码结构:


首先来看一下,客户端肯定要有aidl文件,所以我们将服务端的aidl的文件拷到Client中(包括AIDL所在的包也要拷过来,刷新一下,在gen文件夹中出现了StudentQuery.java类),下面来看一下Client的代码(MainActivity.java):

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package cn.itcast.remoteservice.client;
  2. import android.app.Activity;
  3. import android.content.ComponentName;
  4. import android.content.Intent;
  5. import android.content.ServiceConnection;
  6. import android.os.Bundle;
  7. import android.os.IBinder;
  8. import android.os.RemoteException;
  9. import android.util.Log;
  10. import android.view.View;
  11. import android.widget.EditText;
  12. import android.widget.TextView;
  13. import cn.itcast.aidl.StudentQuery;
  14. /**
  15. * 客户端的测试代码
  16. * @author weijiang204321
  17. *
  18. */
  19. publicclass MainActivity extends Activity {
  20. //定义控件
  21. private EditText numberText;
  22. private TextView resultView;
  23. private StudentQuery studentQuery;
  24. //定义一个连接
  25. private StudentConnection conn = new StudentConnection();
  26. @Override
  27. publicvoid onCreate(Bundle savedInstanceState) {
  28. super.onCreate(savedInstanceState);
  29. setContentView(R.layout.main);
  30. numberText = (EditText) this.findViewById(R.id.number);
  31. resultView = (TextView) this.findViewById(R.id.resultView);
  32. //这里开启一个Service使用隐式意图action的名称必须和remoteService中AndroidMainfest.xml中定义的服务的action的name一样
  33. Intent service = new Intent("cn.itcast.student.query");
  34. bindService(service, conn, BIND_AUTO_CREATE);
  35. }
  36. /**
  37. * 给按钮定义点击事件
  38. * @param v
  39. */
  40. publicvoid queryStudent(View v) {
  41. String number = numberText.getText().toString();
  42. int num = Integer.valueOf(number);
  43. try {
  44. resultView.setText(studentQuery.queryStudent(num));
  45. }catch (RemoteException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. @Override
  50. protectedvoid onDestroy() {
  51. unbindService(conn);
  52. super.onDestroy();
  53. }
  54. /**
  55. * 自定义StudentConnection实现了ServiceConnection
  56. * @author weijiang204321
  57. *
  58. */
  59. privatefinalclass StudentConnection implements ServiceConnection {
  60. publicvoid onServiceConnected(ComponentName name, IBinder service) {
  61. //这里就用到了Stub类中的asInterface方法,将IBinder对象转化成接口
  62. studentQuery = StudentQuery.Stub.asInterface(service);
  63. }
  64. publicvoid onServiceDisconnected(ComponentName name) {
  65. studentQuery = null;
  66. }
  67. }
  68. }
Client端的代码结构和我们之前的本地服务的代码结构差不多,有几处不同,第一处不同就是那个开启服务的方式,这里使用的是隐式的方式开启服务的,因为是跨应用访问的,还有一处不同的是返回的StudentQuery接口对象不同,本地服务的话是通过强制转化的,而远程服务这里是用asInterface方法进行转化的。

客户端和服务端的代码结构看完了,下面我们来运行一下,首先安装remoteService包,然后运行remoteClient包,在文本框中输入学号,查询到名称了,运行成功。在不同的应用中,一定是跨进程的,不行我们可以查看一下系统的进程:

这里我们使用adb命令查看:

http://blog.csdn.net/jiangwei0910410003/article/details/17114491

在这篇blog中我说到了怎么使用这条命令

我们可以看到有两个进程Proc #9和Proc #12

到这里我们就介绍了本地服务和远程服务的使用了,下面我们再来看一下额外的知识,就是怎么在一个应用中进行远程服务的访问,我们之前涉及到的是跨应用的访问,这里其实很简单,只需要改动一处就可以:下面是我改过的一个项目,在同一个应用中进行远程服务的访问,代码都是一样的,这里就不做介绍了;


这里需要修改的地方就是要在AndroidManifest.xml中修改一下Service的定义属性:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <service
  2. android:name="com.demo.remoteservice.StudentService"
  3. android:process=":remote">
  4. <intent-filter>
  5. <actionandroid:name="com.demo.remoteservice.studentservice"/>
  6. </intent-filter>
  7. </service>
这里我们添加了一个属性就是android:process=":remote",这个属性就是将该服务设置成远程的,就是和Activity不在同一个进程中,具体的Service属性说明请看我的另外的一篇blog:

http://blog.csdn.net/jiangwei0910410003/article/details/18794945

运行结果是一样的,为了证明服务是远程服务,我们在使用上面的命令打印一下当前系统中的进程信息:

系统中的Proc #9和Proc #10两个进程,这里就介绍了怎么在同一个应用中跨进程访问服务。


下面在来看一下AIDL:

我们上面说到的AIDL是说他怎么用,而且很是简单,我现在来具体看一下AIDL文件的定义:

Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),如果要传递自定义的类型该如何实现呢?

要传递自定义类型,首先要让自定义类型支持parcelable协议,实现步骤如下:

1>自定义类型必须实现Parcelable接口,并且实现Parcelable接口的publicvoid writeToParcel(Parcel dest, int flags)方法 。

2>自定义类型中必须含有一个名称为CREATOR的静态成员,该成员对象要求实现Parcelable.Creator接口及其方法。

3> 创建一个aidl文件声明你的自定义类型。

Parcelable接口的作用:实现了Parcelable接口的实例可以将自身的状态信息(状态信息通常指的是各成员变量的值)写入Parcel,也可以从Parcel中恢复其状态。Parcel用来完成数据的序列化传递。

下面来看一下例子:

1> 创建自定义类型,并实现Parcelable接口,使其支持parcelable协议。如:在cn.itcast.domain包下创建Person.java:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package cn.itcast.domain;
  2. import android.os.Parcel;
  3. import android.os.Parcelable;
  4. publicclass Person implements Parcelable
  5. privateInteger id;
  6. private Stringname;
  7. public Person(){}
  8. publicPerson(Integer id, String name) {
  9. this.id = id;
  10. this.name = name;
  11. }
  12. public IntegergetId() {
  13. return id;
  14. }
  15. public voidsetId(Integer id) {
  16. this.id = id;
  17. }
  18. public StringgetName() {
  19. return name;
  20. }
  21. public voidsetName(String name) {
  22. this.name = name;
  23. }
  24. @Override
  25. public intdescribeContents() {
  26. return0;
  27. }
  28. @Override
  29. public voidwriteToParcel(Parcel dest, int flags) {//把javanbean中的数据写到Parcel
  30. dest.writeInt(this.id);
  31. dest.writeString(this.name);
  32. }
  33. //添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口
  34. publicstaticfinal Parcelable.Creator<Person> CREATOR = newParcelable.Creator<Person>(){
  35. @Override
  36. public PersoncreateFromParcel(Parcel source) {//从Parcel中读取数据,返回person对象
  37. return newPerson(source.readInt(), source.readString());
  38. }
  39. @Override
  40. public Person[]newArray(int size) {
  41. return newPerson[size];
  42. }
  43. };
  44. }

2> 在自定义类型所在包下创建一个aidl文件对自定义类型进行声明,文件的名称与自定义类型同名。

3> 在接口aidl文件中使用自定义类型,需要使用import显式导入,本例在cn.itcast.aidl包下创建IPersonService.aidl文件,内容如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package cn.itcast.aidl;
  2. import cn.itcast.domain.Person;
  3. interface IPersonService {
  4. void save(inPerson person);
  5. }

4> 在实现aidl文件生成的接口(本例是IPersonService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. publicclass ServiceBinder extends IPersonService.Stub {
  2. @Override
  3. public voidsave(Person person) throws RemoteException {
  4. Log.i("PersonService",person.getId()+"="+ person.getName());
  5. }
  6. }

5> 创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. publicclass PersonService extends Service {
  2. privateServiceBinder serviceBinder = new ServiceBinder();
  3. @Override
  4. public IBinderonBind(Intent intent) {
  5. return serviceBinder;
  6. }
  7. public class ServiceBinder extends IPersonService.Stub {
  8. @Override
  9. publicvoid save(Person person) throwsRemoteException {
  10. Log.i("PersonService",person.getId()+"="+ person.getName());
  11. }
  12. }
  13. }

其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <serviceandroid:name=".PersonService">
  2. <intent-filter>
  3. <actionandroid:nameactionandroid:name="cn.itcast.process.aidl.PersonService "/>
  4. </intent-filter>
  5. </service>

6> 把应用中的aidl文件和所在package一起拷贝到客户端应用的src目录下,eclipse会自动在客户端应用的gen目录中为aidl文件同步生成IPersonService.java接口文件,接下来再把自定义类型文件和类型声明aidl文件及所在package一起拷贝到客户端应用的src目录下。

最后就可以在客户端应用中实现与远程服务的通信,代码如下:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class ClientActivity extends Activity {
  2. private IPersonService personService;
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.main);
  7. this.bindService(newIntent("cn.itcast.process.aidl.PersonService"),this.serviceConnection, BIND_AUTO_CREATE);//绑定到服务
  8. }
  9. @Override
  10. protected voidonDestroy() {
  11. super.onDestroy();
  12. this.unbindService(serviceConnection);//解除服务
  13. }
  14. privateServiceConnectionserviceConnection =new ServiceConnection() {
  15. @Override
  16. public void onServiceConnected(ComponentName name, IBinder service) {
  17. personService =IPersonService.Stub.asInterface(service);
  18. try {
  19. personService.save(new Person(56,"liming"));
  20. } catch(RemoteException e) {
  21. Log.e("ClientActivity",e.toString());
  22. }
  23. }
  24. @Override
  25. public void onServiceDisconnected(ComponentName name) {
  26. personService =null;
  27. }
  28. };
  29. }
这样就可以访问我们自己定义的类型了。


下面来总结一下定义一个远程服务的步骤:

假设A应用需要与B应用进行通信,调用B应用中的download(String path)方法,B应用以Service方式向A应用提供服务。需要下面四个步骤:
1> 在B应用中创建*.aidl文件,aidl文件的定义和接口的定义很相类,如:在cn.itcast.aidl包下创建IDownloadService.aidl文件,内容如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package cn.itcast.aidl;
  2. interfaceIDownloadService {
  3. void download(String path);
  4. }

当完成aidl文件创建后,eclipse会自动在项目的gen目录中同步生成IDownloadService.java接口文件。接口文件中生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其它辅助方法。值得关注的是asInterface(IBinder iBinder),它返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端在onServiceConnected(ComponentName name, IBinder service)方法引用该对象时不能直接强转成接口类型的实例,而应该使用asInterface(IBinder iBinder)进行类型转换。


编写Aidl文件时,需要注意下面几点:
1.接口名和aidl文件名相同。
2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static。
3.Aidl默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence),使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口。
4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
5.在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
6.Java原始类型默认的标记为in,不能为其它标记。


2> 在B应用中实现aidl文件生成的接口(本例是IDownloadService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class ServiceBinder extends IDownloadService.Stub {
  2. @Override
  3. publicvoid download(String path) throws RemoteException {
  4. Log.i("DownloadService",path);
  5. }
  6. }

3> 在B应用中创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class DownloadService extendsService {
  2. private ServiceBinder serviceBinder = new ServiceBinder();
  3. @Override
  4. public IBinder onBind(Intent intent) {
  5. return serviceBinder;
  6. }
  7. publicclass ServiceBinder extends IDownloadService.Stub {
  8. @Override
  9. publicvoid download(String path) throws RemoteException {
  10. Log.i("DownloadService",path);
  11. }
  12. }
  13. }

其他应用可以通过隐式意图访问服务,意图的动作可以自定义,AndroidManifest.xml配置代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <serviceandroid:name=".DownloadService">
  2. <intent-filter>
  3. <action android:name="cn.itcast.process.aidl.DownloadService"/>
  4. </intent-filter>
  5. </service>

4> 把B应用中aidl文件所在package连同aidl文件一起拷贝到客户端A应用,eclipse会自动在A应用的gen目录中为aidl文件同步生成IDownloadService.java接口文件,接下来就可以在A应用中实现与B应用通信,代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. publicclass ClientActivity extendsActivity {
  2. private IDownloadService downloadService;
  3. @Override
  4. publicvoid onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.main);
  7. this.bindService(newIntent("cn.itcast.process.aidl.DownloadService"),this.serviceConnection,BIND_AUTO_CREATE);//绑定到服务
  8. }
  9. @Override
  10. protectedvoid onDestroy() {
  11. super.onDestroy();
  12. this.unbindService(serviceConnection);//解除服务
  13. }
  14. private ServiceConnection serviceConnection = new ServiceConnection() {
  15. @Override
  16. publicvoid onServiceConnected(ComponentName name, IBinder service){
  17. downloadService = IDownloadService.Stub.asInterface(service);
  18. try {
  19. downloadService.download("http://www.itcast.cn");
  20. }catch (RemoteException e) {
  21. Log.e("ClientActivity", e.toString());
  22. }
  23. }
  24. @Override
  25. publicvoid onServiceDisconnected(ComponentName name) {
  26. downloadService = null;
  27. }
  28. };
  29. }

最后看一下Service的生命周期以及和生命周期相关的方法:

与采用Context.startService()方法启动服务有关的生命周期方法

onCreate()->onStart()->onDestroy()

onCreate()该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。

onStart()只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。

onDestroy()该方法在服务被终止时调用。

l 与采用Context.bindService()方法启动服务有关的生命周期方法

onCreate()->onBind()->onUnbind()->onDestroy()

onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。

onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。

如果先采用startService()方法启动服务,然后调用bindService()方法绑定到服务,再调用unbindService()方法解除绑定,最后调用bindService()方法再次绑定到服务,触发的生命周期方法如下:

onCreate()->onStart()->onBind()->onUnbind()[重载后的方法需返回true]->onRebind()


总结:

Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。服务的开发比较简单,如下:
第一步:继承Service类

public class SMSService extends Service { }


第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置:
<service android:name=".SMSService" />


服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。

下面两张图是Service的生命周期:


图一



图二


关于服务的介绍就到这里了,具体详情可以参考以下两篇blog:

http://blog.csdn.net/zhaoweixing1989/article/details/7892635

http://blog.csdn.net/jiangwei0910410003/article/details/16983173



service与thread的区别:

转:http://blog.csdn.net/jiangwei0910410003/article/details/17008687

很多时候,你可能会问,为什么要用 Service,而不用 Thread 呢,因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

3、Service的生命周期

onCreate  onStart  onDestroy  onBind

1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

特别注意:

1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);

2、你应当注意使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;

3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;

4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。

5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。

6、Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上,service和调用者之间的通讯都是同步的(不论是远程service还是本地service),它跟线程一点关系都没有!


7、Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用,也就是你下次启动的时候,无法控制之前创建的线程,而service则可以。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。


8、你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,这些是 Thread 做不到的。


0 0
原创粉丝点击