《老罗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服务之绑定Service1. 创建一个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 abstract IBinder onBind (Intent intent)
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
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;}}
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.
- 《老罗Android第二季》使用Service(2)及Binder、AIDL
- android service binder 和AIDL
- Android中的Service:Binder,Messenger,AIDL(2)
- Android中的Service:Binder,Messenger,AIDL(2)
- Android中的Service:Binder,Messenger,AIDL(2)
- Android中的Service:Binder,Messenger,AIDL(2)
- Android中Binder机制理解及AIDL使用基本步骤
- android 多进程 Binder AIDL Service
- Android中的Service:Binder,Messenger,AIDL
- Android中的Service:Binder,Messenger,AIDL
- 《老罗Android第二季》使用Services(1)及进程优先级
- AIDL使用及Binder连接池
- 《老罗Android第二季》ViewPager分页及JSON
- 37.Android Service 及 AIDL
- Binder框架 -- android AIDL 的使用
- Android源码(9) --- Binder(3) AIDL使用
- Android-AIDL,service,Binder,Bundles,Messenger,transact()与onTransact()函数
- android: 通过AIDL使用SERVICE
- configure.in Makefile.am解析
- 交通运输行业--改革与新兴
- Struts2返回JSON数据的具体应用范例
- python升级带来的yum异常:File "/usr/bin/yum", line 30
- 用事实告诉你企业竞争优势的企业资源判断标准
- 《老罗Android第二季》使用Service(2)及Binder、AIDL
- Lucene整理--索引的建立
- 图解aclocal、autoconf、automake、autoheader、configure
- 【hoj】1604 cable master
- HDOJ 2062 Subset sequence
- 程序员的自我修养——操作系统篇
- Android下的代码混淆
- cocos2d-x绑lua的开发环境
- 进程通信消息队列(IBM)