《老罗Android第二季》使用Service(2)及Binder、AIDL

来源:互联网 发布:台州市公务员网络学堂 编辑:程序博客网 时间:2024/04/28 08:27

所谓的service有Local Service和Remote Service区分:

LocalService:就是client和Service在同一个进程当中。

RemoteService:就是client 和Service在不同的进程中。

我们通常的用法是自己建一个.java文件中通过继承Service来实现一个子Service。然后在通过其他的Activity来连接实现的那个Service就有点像网络编程中的链接服务器一样。但是这里的Service不是独立的一个服务器端,它可以说是手机app的一个实现模块。所以不是像传统的网络编程一样,首先启动服务器,然后在从client去访问。android中的Service要通过访问端通过传递Intent去开启Service

1. Service服务之绑定Service
 1. 创建一个Service类
  bindService() 方法的意思是,把这个 Service 和调用 Service 的客户类绑起来,如果调用这个客户类被销毁,Service 也会被销毁。用这个方法的一个好处是,bindService() 方法执行后 Service 会回调上边提到的 onBind() 方发,你可以从这里返回一个实现了 IBind 接口的类,在客户端操作这个类就能和这个服务通信了,比如得到 Service 运行的状态或其他操作。如果 Service 还没有运行,使用这个方法启动 Service 就会 onCreate() 方法而不会调用 onStart()。
public class MyService extends Service {private final Binder binder = new LocalBinder();private final Random random = new Random();// 产生一个随机数public IBinder onBind(Intent intent) { return binder;}// 返回MyService的实例给客户端调用service中的方法public class LocalBinder extends Binder {public MyService getService() { return MyService.this;}}// 给Client端调用的方法public int getRandomNum() { return random.nextInt(30);}}
2. 在MainActivity.java中实现
public class MainActivity extends Activity {  ......private boolean mBind = false;// 默认是不绑定Serviceprotected void onCreate(Bundle savedInstanceState) {......button.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {Intent intent = new Intent(MainActivity.this, MyService.class);bindService(intent, connection, Context.BIND_AUTO_CREATE);}});// 调用Service的方法button2.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {//处于绑定的状态        if (mBind) {int result = myService.getRandomNum();textView.setText("-->>"+result);}}});}protected void onStart() {super.onStart();// Intent intent = new Intent(MainActivity.this, MyService.class);// bindService(intent, connection, Context.BIND_AUTO_CREATE);}protected void onStop() {super.onStop();if (mBind) {unbindService(connection);mBind = false;}}private ServiceConnection connection = new ServiceConnection() {public void onServiceDisconnected(ComponentName name) {mBind = false;}public void onServiceConnected(ComponentName name, IBinder service) {LocalBinder localBinder = (LocalBinder) service;myService = localBinder.getService();mBind = true;}};}
绑定函数原型:public boolean bindService (Intent intent, ServiceConnection conn, int flags)
参数说明:intent:用于指定哪个Activity和哪个Service绑定。
conn是一个ServiceConnection实例,ServiceConnection是一个public interface,用于监视app service状态的Interface,必须去实现ServiceConnection的两个抽象函数:
onServiceConnected(ComponentName name, IBinder service)和onServiceDisconnected(ComponentName name)这两个函数就是决定连接和断开Service。
flags:绑定操作选项
返回值:绑定成功返回true,失败false。

public abstract IBinder onBind (Intent intent) 

intent:该参数由bindService()函数传递过来,用于绑定指定的Service。
   可以创建一个即是运行的又是绑定的service,也就是通过startService()来让这个Service运行,然后client通过bindService()来绑定到Service。那么当service启动后,系统不会在所有客户端解除绑定unBindService()后销毁service,而是,你必须通过调用stopSelf()stopService()显式的停止这个service。
      多个client可以同时连接到一个service,当地一个Client绑定到Service的时候Service的onBinder()方法才接受到IBinder,之后Service将把同一个IBinder传递给其他的client,之后其他Client的连接service时service就不再会调用自身的onBind()方法了。当最后一个client取消绑定到Service时,系统就会销毁这个Service

2. Service服务之Service和Activity数据交互
把数据从Activity传到Service,再从Service中取回来显示:
MyService.java:
public class MyService extends Service {private final Random random = new Random();private LocalBinder binder = new LocalBinder();public void onCreate() {super.onCreate();}public IBinder onBind(Intent intent) {return binder;}//service服务中的方法public int getRandom() {return random.nextInt(100);}public class LocalBinder extends Binder {public MyService getService() {return MyService.this;}protected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {//表示从activity中获取数值Log.i("MyService", "--MyService-readInt->>"+data.readInt());Log.i("MyService", "--MyService-readString->>"+data.readString());reply.writeString("rose");reply.writeInt(getRandom());return super.onTransact(code, data, reply, flags);}}}
MainActivity.java:
public class MainActivity extends Activity {private Button button;// 绑定serviceprivate Button button2;// 调用Service的方法private TextView textView;private boolean flag = false;// 是否绑定服务private LocalBinder localBinder;// 服务的对象private MyService myService;private Button button3;// 获得service的回传值protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = (Button) this.findViewById(R.id.button1);button2 = (Button) this.findViewById(R.id.button2);button3 = (Button) this.findViewById(R.id.button3);textView = (TextView) this.findViewById(R.id.textView1);button.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {// 绑定service的服务Intent intent = new Intent(MainActivity.this, MyService.class);bindService(intent, connection, Context.BIND_AUTO_CREATE);}});button2.setOnClickListener(new View.OnClickListener() {                                                                                                                          public void onClick(View v) {if (flag) {int random = myService.getRandom();textView.setText("-->>" + random);}}});button3.setOnClickListener(new View.OnClickListener() {// 往service中传递值的对象Parcel data = Parcel.obtain();data.writeInt(23);data.writeString("jack");Parcel reply = Parcel.obtain();//try {localBinder.transact(IBinder.LAST_CALL_TRANSACTION, data,reply, 0);} catch (RemoteException e) {e.printStackTrace();}Log.i("MainActivity", "--MainActivity-readString->>"+reply.readString());Log.i("MainActivity", "--MainActivity-readInt->>"+reply.readInt());}});}protected void onStop() {super.onStop();if (flag) {unbindService(connection);flag = false;}}// 链接activity和service之间的一个桥梁public ServiceConnection connection = new ServiceConnection() {public void onServiceDisconnected(ComponentName name) {flag = false;}public void onServiceConnected(ComponentName name, IBinder service) {localBinder = (LocalBinder) service;myService = localBinder.getService();flag = true;}};}

二、扩展知识

进程间通信

   每一个进程都有自己独立的内存,一个进程的1000地址也许是另一个进程的100000地址,所以如何在进程间通信就变得复杂了,对于基本类型的数据比如整数直接传递过去就行了,但是要是字符串的话就要先在目的进程空间中分配一块能容纳该字符串的内存,然后再复制到该内存中。再如果要传递一个类的实例,也是先为该对象分配内存,然后复制一份再传递可以吗?我认为不可以,我至少可以找到一个理由:类中成员除了属性还有方法,即使属性能完整传过去,但还有方法呢?方法是独立于类对象存在的,所以到另一个进程中再引用同一个方法就要出错了,还是因为独立地址空间的原因。Android中通常要在Activity之间传递数据,而根据Android的设计,即使同一个Activity都不一定运行在同一个进程中,所以就要进程间传递数据了,那么到底如何在进程之间传递类对象呢?简单来说可以这样做:在进程A中把类中的非默认值的属性和类的唯一标志打成包(这就叫序列化),把这个包传递到进程B,进程B接收到包后,根据类的唯一标志把类创建出来,然后把传过来的属性更新到类对象中,这样进程A和进程B中就包含了两个完全一样的类对象。

IBinder

Android的远程调用就是通过IBinder来实现的,远程调用的基本接口,还可以用于进程内调用,该接口定义了与远程对象交互的协议,不可以直接实现这个接口,要派生实现。

主要API:

transact():向远端的IBinder对象发送发出调用。onTransact():使远程的对象能够响应另一端发送过来的调用请求。这两个API都是同步执行的。transact()方法要一直阻塞到onTransact()方法调用完成后才返回。

transact()发送的数据类型是Pracel类型的数据,是一种一般的缓冲区,除了有数据外还有一些描述它内容的元数据。元数据用于管理IBinder对象的引用,这样就能在缓冲区从一个进程移动到另一个进程时保存这些引用。这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中,如果另一个进程把同一个IBinder的引用回发到原来的进程,那么这个原来的进程就能接收到发出的那个IBinder的引用。这种机制使IBinder和Binder像唯一标志符那样在进程间管理。



3. Service服务之AIDL进程间通信

Remote Service

通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service。在android平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。
AIDL(Android接口定义语言)执行把对象分解为操作系统能够理解并能跨进程封送的基本体以执行IPC的所有的工作
过程:
1,创建xx.aidl接口文件,然后在gen目录下生成一个对应XX.java实现类这是AIDL工具自己生成的文件不可编辑。在该文件中
定义的接口继承自IInterface来实现,在实现的内部有一个内部类Stub。类中实现了定义的方法。
2,实现你在xx.aidl文件中定义的方法,实现类继承自AIDL工具自动生成的实现文件中定义的Stub内部类。
public class xxImpl extends XX.Stub {
实现定义的方法
}
要实现进程间通信就要实现两个apk,一个做Service,一个Client.
Service端:
1.在DataService.aidl 中定义接口

package com.example.service;interface DataService{   double getData(String arg);}
在Eclipse插件的帮助下,编译器会自动在gen目录中生成对应的DataService.java文件.
DataService接口中的抽象内部类Stub继承android.os.Binder类并实现DataService接口,比较重要的方法是asInterface(IBinder)方法,该方法会将IBinder类型的对象转换成DataService类型,必要的时候生成一个代理对象返回结果。
2. 实现接口并expose the interface
当建立应用程序时,SDK工具会在/gen目录下生成一个DataService.java接口,这个接口中有一个名为Stub的子类,它抽象的实现了上面定义的那个接口,并包含上面文件的方法。
public static abstract class Stub extends android.os.Binder implements com.example.service.DataService

public class MyService extends Service {public IBinder onBind(Intent intent) {return binder;}// 定义提供给Client端调用的方法Binder binder = new DataService.Stub() {public double getData(String arg) throws RemoteException {if (arg.equals("a")) {return 1;} else if (arg.equals("b")) {return 2;}return 0;}};}
我们实现了DataService.Stub这个抽象类的getData方法,然后再onBind(Intent)方法中返回我们的stub实例,这样一来调用方获取的DataService.Stub就是我们的这个实例,greet方法也会按照我们的期望那样执行。
3. 定义一个MainActivity
在这里面不需要做什么。
4.配置Manifest,xml
在</application>上一行添加:

 <service android:name=".MyService">       <intent-filter >         <action android:name="com.example.service.DataService"></action>       </intent-filter> </service>
Client端:
1. 客户端也要实现service端相同的DataService.aidl ,所以直接拷贝就行。
2. MainActivity.java
public class MainActivity extends Activity {private Button button;private Button button2;private DataService dataService;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = (Button) this.findViewById(R.id.button1);button2 = (Button) this.findViewById(R.id.button2);button.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {Intent intent = new Intent(DataService.class.getName());                //或者是<action name="">: Intent intent=new Intent("com.example.service.DataService");bindService(intent, connection, BIND_AUTO_CREATE);}});button2.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {try {double result = dataService.getData("a");System.out.println("---->>" + result);} catch (Exception e) {e.printStackTrace();}}});}private ServiceConnection connection = new ServiceConnection() {public void onServiceDisconnected(ComponentName name) {}public void onServiceConnected(ComponentName name, IBinder service) {dataService = DataService.Stub.asInterface(service);}};public boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main, menu);return true;}}
Messenger实现远程进程通信:
使用方法:
  • service实现一个接收从客户端的每个调用引起的回调的Handler

  • Handler被用来创建一个Messenger对象(它是Handler的一个引用)

  • Messenger创建一个从serviceonBind()返回给客户端的IBinder

  • 客户端使用IBinder来实例化这个Messenger(它引用到serviceHandler),客户端用它来service发送Message

  • service在它的Handler中接收每个消息—具体的,是在handleMessage()方法中.

     

这此方式下,service中没有能让客户端调用的方法,客户端传送的是service在它的Handler中接收的"消息"(Message对象)

应用组件(客户端)可以调用bindService()绑定到一个serviceAndroid系统之后调用serviceonBind()方法,它返回一个用来与service交互的IBinder

绑定是异步的.bindService()会立即返回,它不会返回IBinder给客户端.要接收IBinder,客户端必须创建一个ServiceConnection的实例并传给bindService()ServiceConnection包含一个回调方法,系统调用这个方法来传递要返回的IBinder

注:只有activities,services,contentproviders可以绑定到一个service—你不能从一个broadcastreceiver绑定到service





0 0
原创粉丝点击