当前位置: 首页 > 图灵资讯 > 技术篇> Android学习笔记(五三):服务Service(下)- Remote Service

Android学习笔记(五三):服务Service(下)- Remote Service

来源:图灵教育
时间:2023-05-22 09:19:24

之前提到的Service属于Local Service,也就是说,Service和Client在同一过程中(即同一application内),Service的生命周期服从过程的生命周期。在实际应用中,有时希望Service作为后台服务,不仅用于同一过程中的activity,也用于其他过程。针对这种情况,需要使用bindservice,即remote。 Service的方式。

在Android中,不同的应用属于不同的过程(process),过程是安全策略的边界,一个过程不能访问其他过程的存储(例如使用ContentProvider)。在Remote Service将涉及进程间通信,通常称为IPCE(interprocess commnication),为了进行相互通信或数据传输,需要在过程A和过程B之间建立连接 。

Android学习笔记(五三):服务Service(下)- Remote Service_ide

Android提供AIDL(Android Interface Definition Language)工具有助于建立IPC之间的界面,大大简化了开发者的视图。右示意图仅用于帮助理解代码。通过以下步骤实现client和service之间的通信:

[1]定义AIDL接口 ,Eclipse自动为Service建立接口IService [2]Client连接到Service,连接到IService暴露给Client的Stub,以获得Stub对象;换句话说,Service通过界面中的Stub为Client提供服务,抽象IService在IService中.具体实现Stub。 【3】Client与Service连接后,Client可以像使用本地方法一样简单地直接调用IService.Stub中的方法。

下面的例子给出了client提供定时计数的Remotee Service,在Testremoteservice中获得服务的例子。

步骤1:Service向client提供的接口是通过AIDL文件定义的,ITestRemoteService.aidl文件如下

package com.wei.android.learning.part5;  interface ITestRemoteService {      int getCounter(); }

我们在SRC目录下添加I<ServiceClassName>.aidl文件,语法和java一样。Service在这个例子中非常简单,只提供计数器的值,所以我们在接口中定义了int getCounter( )。

AIDL文件很简单,Eclipse会根据文件自动生成相关的java 但是interface文件没有显示,如果直接使用命令行工具,将有助于生成java文件。

步骤2:Remote Service的编写,通过onbind(),在client连接时,传递stub对象。TestRemoteService.java文件如下:

/* Service提供Runnable实现的定时计数器,复习Android学习笔记(三一):线程:Message和Runnable中的例子3。灰掉这部分代码,以免干扰注意力。此外,我们还提供showinfo()来跟踪service的运行,这部分也是灰色的。*/ public class TestRemoteService extends Service{     private Handler serviceHandler = null;     private int counter = 0;      private TestCounterTask myTask = new TestCounterTask();            public void onCreate() {           super.onCreate();          showInfo("remote service onCreate()");     }       public void onDestroy() {          super.onDestroy();          serviceHandler.removeCallbacks(myTask); //停止计数器        serviceHandler = null;          showInfo("remote service onDestroy()");     }       public void onStart(Intent intent, int startId) {         // 开启计数器         super.onStart(intent, startId);         serviceHandler=new Handler();          serviceHandler.postDelayed(myTask, 1000);          showInfo("remote service onStart()");      }     //步骤2.1:为clientStub提供Stub,具体实现接口中暴露的Stub inner 具体实现class。      private ITestRemoteService.Stub stub= new ITestRemoteService.Stub() {        //步骤2.1:实现AIDL文件中界面定义的各种方法。        public int getCounter() throws RemoteException {  showInfo("getCounter()");             return counter;          }      };            //步骤2.2:当client连接时,它将触发onbind(),service将stub对象返回到client,因此client可以通过stub对象访问service,在这种情况下通过stub.getCounter()计时器的当前计数可以获得。在这个例子中,我们将相同的stub对象传递给所有client。     public IBinder onBind(Intent arg0) {          showInfo("onBind() " + stub); 我们特别跟踪了stub对象的地址,在client连接service中,您可以通过serviceconection将其传输到cliention        return stub;     }       /* 每10秒使用Runnable定时计数器加1次。 */     private class TestCounterTask implements Runnable{         public void run() {               ++ counter;              serviceHandler.postDelayed(myTask,10000);              showInfo("running " + counter);          }      }       /* showInfo( ) 帮助我们跟踪信息,更好地了解Service的运行情况 */    private void showInfo(String s){          System.out.println("[" +getClass().getSimpleName()+"@"""""" + Thread.currentThread().getName()+ "] " + s);     }  }

步骤3:Client与Service建立连接,获得Stub、ServiceTest4.java代码如下

Android学习笔记(五三):服务Service(下)- Remote Service_Android_02

public class Servicetest4 extends Activity{ private ITestRemoteService remoteService = null; //步骤3.1 界面变量的定义 private boolean isStarted = false; private CounterServiceConnection conn = null; //步骤3.1 定义连接变量,Serviceconection接口 protected void onCreate(Bundle savedInstanceState) { … … /5button分别触发startservice( ),stopService( ) , bindService( ), releaseService( )invokeservice( ),以下两行,一行显示Service获得的计数值,一行显示状态。 } private void startService(){ Intent i = new Intent(); i.setClassName("com.wei.android.learning", "com.wei.android.learning.part5.TestRemoteService"); ///我的包里还有层次,比如*.part1、*.part2,etc startService(i); ///和之前的local 像service一样,Service通过intent打开,oncreate()触发[if Service没有打开]->onStart() isStarted = true; updateServiceStatus(); } private void stopService(){ Intent i = new Intent(); i.setClassName("com.wei.android.learning","com.wei.android.learning.part5.TestRemoteService"); stopService(i); /////触发Service onDestroy()[if Service存在] isStarted = false; updateServiceStatus(); } //步骤3.3:bindService( )Service之间建立连接,实现ServiceConection接口,Context注意到参数.BIND_AUTO_CREATE,oncreate()触发[if Service不存在] –> onBind()。 private void bindService(){ if(conn == null){ conn = new CounterServiceConnection(); Intent i = new Intent(); i.setClassName("com.wei.android.learning","com.wei.android.learning.part5.TestRemoteService"); bindService(i, conn,Context.BIND_AUTO_CREATE); updateServiceStatus(); } }

Android学习笔记(五三):服务Service(下)- Remote Service_android_03

private void releaseService(){ if(conn !=null){ unbindService(conn); //断开连接,解除绑定 conn = null; updateServiceStatus(); } } private void invokeService(){ if(conn != null){ try{ Integer counter =///一旦client成功绑定到service,就可以直接使用stub中的方法。 TextView t = (TextView)findViewById(R.id.st4_notApplicable); t.setText("Counter value : " + Integer.toString(counter)); }catch(RemoteException e){ Log.e(getClass().getSimpleName(),e.toString()); } } } //步骤3.2 class Counterserviceconection实现Serviceconection接口,内两个触发onserviceconected()和onservicedisconected() private class CounterServiceConnection implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 根据我们的跟踪,从连接中获取stub对象,stub对象是service中的stub对象 remoteService = ITestRemoteService.Stub.asInterface(service); showInfo("onServiceConnected()" + remoteService); } @Override public void onServiceDisconnected(ComponentName name) { remoteService = null; updateServiceStatus(); showInfo("onServiceDisconnected"); } } private void updateServiceStatus() { TextView t = (TextView)findViewById( R.id.st4_serviceStatus); t.setText( "Service status: "+(conn == null ? "unbound" : "bound")+ ","+ (isStarted ? "started" : "not started"; )); } private void showInfo(String s){ System.out.println("[" +getClass().getSimpleName()+"@"""""" + Thread.currentThread().getName()+ "] " + s); } }