出差深圳一个月,终于回来了,一个月里干了不少,这些天里会慢慢总结一点东西出来,今天说的是关于Service的一点事:通信。通信的做法比较固定,基本上按照模板来写就可以实现。
1、Service与Activity通信
Activity通过startService()方法启动Service之后,Service将独立于Activity运行(虽然仍然是同一个进程),Activity无法指导Service如何运行。当Activity需要根据一些条件决定Service如何运行的时候,就需要有另外的方法了:将Service申明为远程Service。
(1)、声明
将一个服务声明为远程服务只需在manifest文件的声明中加一句process=":remote"。如下所示:
<service android:name="com.iflytek.service.PowerService" android:process=":remote" > </service>
(2)Service端
private MyBinder mBinder= new MyBinder(); public IBinder onBind(Intent intent){ return mBinder; } public MyBinder extends Binder{ public void doSomething(){ Log.d("Timothy", "I am doing something!") } }
onBind()方法在创建Service的时候就已经默认创建了,这里只是实现了这个方法。mBinder就像是一座桥梁,连接了Service与Activity,将Service中的接口方法暴露给Activity,让Activity可以通过mBinder去调用这些接口。
(3)Activity端
private MyService.MyBinder myBinder; private ServiceConnection connection = new ServiceConnection(){ @Override public void onServiceDisconnected(ComponentName name){ } @Override public void onServiceConnected(ComponentName name, IBinder service){ myBinder = (MyService.MyBinder)service; myBinder.doSomething(); } } Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE);
首先是声明一个MyBInder类,让Activity可以使用这座桥梁。然后就是声明一个ServiceConnection对象,绑定的时候如何调用Service的方法。最后就是绑定了。
需要说明一点:bindService()函数的第三个参数说明,当绑定服务的时候,将自动调用Service的onCreate()方法。
当需要多次调用doSomething()方法的时候,如果直接bindService是会报错的。这时候可以在bind之前加上下面这样的一段:
try{ unbindService(connection); }catch(Exception e){ e.printTrace(); }
这样就不会报错了。
2、AIDL
我在开发中遇到的问题是:应用层app需要在一定的条件下调用系统的休眠和关机,而休眠和关机的接口只有系统级应用才能调用,这样就必须在系统层为应用提供能够远程调用的服务了。
上面说的远程服务,实际上依然是在同一个project中,但是我面对的问题很明显,是完全独立的两个应用,怎么样才能在project1的类中引用到project2中的类呢?
Google为此提供了一个叫做AIDL的东西,也就是Android Interface Describe Language。用这个东东作为调用的桥梁。关于AIDL的理论这里不多说,在此只介绍其用法。
(1)Service端
a、首先是修改manifest文件:
<service android:name="com.iflytek.service.PowerService" android:process=":remote" > <intent-filter> <action android:name="com.iflytek.vbox.power" /> </intent-filter> </service>
这东西看上去很像Broadcast Receiver的声明,猜测其内部实现也应该和广播差不多。
b、新建一个package,在package中新建AIDL文件,声明好Activity与Service通信的方法:
package com.***.aidl; interface MyService{ void goToSleep(); void shutDown(); }
注意这里不能用public、private修饰。编写好保存之后,将在gen目录下自动生成一个Java文件,这个文件不需要维护。
c、修改PowerService文件,实现上面声明的接口。
public class PowerService extends Service { MyService.Stub mBinder; PowerManager pm; String filePath; Handler handler; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return mBinder; } @Override public void onCreate() { super.onCreate(); mBinder = new Stub() { @Override public void goToSleep() throws RemoteException { Tools.writeLog("PowerService.goToSleep() is called"); pm = (PowerManager) getSystemService(Context.POWER_SERVICE); SleepThread sleepThread = new SleepThread(pm); sleepThread.start(); } @Override public void shutDown() throws RemoteException { ShutDownThread shutDownThread = new ShutDownThread(); shutDownThread.start(); } }; } }
这里就是用mBinder实现了接口。接口中我启动了子线程去做真正的执行工作,这也是比较常见的用法。这里写法比较固定,照抄就好。
(2)Activity端
a、将Service中的AIDL连package一起复制过来,记得,是要连package一起。
b、开始抄吧
private MyService myService; private static ServiceConnection sleepConnection; private static ServiceConnection shutdownConnection; sleepConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName arg0, IBinder arg1) { Log.d("Timothy", "sleepConnection connected"); myService = MyService.Stub.asInterface(arg1); try { myService.goToSleep(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName arg0) { } }; shutdownConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName arg0, IBinder arg1) { Log.d("Timothy", "shutdownConnection"); myService = MyService.Stub.asInterface(arg1); try { myService.shutDown(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName arg0) { } }; Intent sleepIntent = new Intent("com.***.***.power"); bindService(sleepIntent, sleepConnection, BIND_AUTO_CREATE); Intent updateIntent = new Intent("com.***.***.power"); bindService(updateIntent, updateConnection, BIND_AUTO_CREATE);
可以看到,这里和上面的远程服务差的不多,也是在bind的时候调用Service的方法。
仔细看看上面的写法,你就会发现AIDL的精妙之处就在于:1、将远程接口声明在本地,这样就能像本地类一样调用远程方法,符合Java的语法规则。2、使用类似于广播的机制启动远程的服务,并调用其方法。
Service与Activity通信与AIDL,布布扣,bubuko.com