调用远程服务里的方法service,进程间通信adil的学习
1当一个进程需要调用另外一个进程的方法时候,进程可以通过aidl文件以接口的方式将方法抛出。比如android没有对外提供挂电话的方法,若用户想要调用这个方法就必须与电话管理这个应用程序通信,调用挂电话的方法。
2、下面我就举例一个demo调用远程服务里的方法。为了验证service能否单独启动,这个demo启动了2个远程服务,一个有activity的一个只有service的。并且他们抛出的接口名字相同,正好学习一下同名的引用,发现一个java文件里只能import 1个同同名的类,若想调用另外一个同名类则只有引用他的全路径名:包名+类名。
(1)、含activity远程服务和接口:(注意:清单文件里需要配置service)
//服务 package com.example.androidservice; import com.example.androidservice.Iservicemethod.Stub; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class IService extends Service { @Override public IBinder onBind(Intent intent) { System.out.println("onbind"); return new Mybindle(); } @Override public void onCreate() { System.out.println("oncreate"); super.onCreate(); } private class Mybindle extends Stub
//一旦修改接口的扩展名为aidl,就会在gen文件下与接口所在包名同名,里面自动生成一个接口名的java文件,通过查看该接口,
发现里面自动实现了一个内部类Stub,它实现Ibundle和抛出的接
口public static abstract class Stub extends android.os.Binder implements com.example.demoonlyservice.Iservicemethod,
因此我们实现返回的Ibundle的时候写的类可以直接继承Stub即可,后面调用Stub里的方法可以返回接口,即将返回去的bundle service实例化接口,通过接口来调用服务里的方法。
{ @Override public void sayhello() throws RemoteException { // TODO Auto-generated method stub // sayhello(); // (1)接口抛出的方法名字不能和服务的名字相同,否则覆写的时候不能调用,我先开始直接调用发现和覆写名相同出现 // 无限循环
// System.out.println("hello service!!!"); // (2)一旦在接口定义了该方法,生成了aidl文件后,就不能修改覆写方法里的内容,内里实现的方法是调用service里 // 的方法,因此可以修改service里的方法来修改接口抛出的方法。 sayhello2(); } } public void sayhello2() { System.out.println("hello service!!!"); System.out.println("hello service2!!!"); } } //抛出的接口 package com.example.androidservice; interface Iservicemethod { void sayhello(); }
第二个只含service的服务和接口:(注意:清单文件里也需要配置service)
package com.example.demoonlyservice; import com.example.demoonlyservice.Iservicemethod.Stub; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class IService extends Service { @Override public IBinder onBind(Intent intent) { System.out.println("onbind"); return new Mybindle(); } @Override public void onCreate() { System.out.println("oncreate"); super.onCreate(); } private class Mybindle extends Stub { @Override public void sayhello() throws RemoteException { // TODO Auto-generated method stub // sayhello(); // System.out.println("hello service!!!"); sayhello2(); } } public void sayhello2() { System.out.println("hello service!!!"); } } //接口 package com.example.demoonlyservice; interface Iservicemethod { void sayhello(); }
3在这个应用里通过点击2个按钮调用上面的2个服务,通过返回的Ibundle实现了接口方法来调用服务的方法sayhello
package com.example.getothermehod; import com.example.androidservice.Iservicemethod; //import com.example.demoonlyservice.Iservicemethod; import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBar; import android.support.v4.app.Fragment; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.os.Build; public class MainActivity extends ActionBarActivity { private Iservicemethod is; private com.example.demoonlyservice.Iservicemethod is2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Intent intent=new Intent("onlyservice"); // bindService(intent, new Myconnect2(), BIND_AUTO_CREATE); } public void startonlyservice(View v) throws InterruptedException { Intent intent=new Intent("onlyservice"); bindService(intent, new Myconnect2(), BIND_AUTO_CREATE); // try { // is2.sayhello(); // } catch (RemoteException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // Thread.sleep(2000); System.out.println(is2==null); } public void startservice(View v) { Intent intent=new Intent("providerservice"); bindService(intent, new Myconnect(), BIND_AUTO_CREATE); // try { // is.sayhello(); // } catch (RemoteException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } class Myconnect implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("fuck"); is=Iservicemethod.Stub.asInterface(service); try { is.sayhello(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } } class Myconnect2 implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("fuck"); is2=com.example.demoonlyservice.Iservicemethod.Stub.asInterface(service); try { is2.sayhello(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } } }
下面总结一下完成的步骤和注意的问题:
1 、首先创建一个远程服务,demo里创建了2个,并且服务的名字相同,在清单文件里注册的action不同而已。服务里有个方法需要被其他的应用程序访问,即sayhello。
2、定义一个接口,接口里有需要被调用的方法,名字不要与服务里需要的方法同名(具体说明见第一个服务里mybundle类实现的代码解释部分),去掉接口和方法前面的访问权限,即public,private等,并到相应文件目录下将接口扩展名修改为aidl,保存刷新项目,会发现在gen文件下的和接口同包名的目录下产生一个接口同名的java文件,点击进去发现里面的是我们写的接口,它里面有个Stub的子类,它继承了Bunle类并实现了我们的抱出去的接口里的方法,因此在服务里的onbind()返回去的Ibindl,实现该类的时候只需要继承Stub即可,在调用的应用程序里利用stub的asinstance方法实例将返回的Ibindle实例化成抛出的接口,利用这借口就可以调用服务里的方法了。
自动生成的Iservicemethod 接口
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\毕业设计\\adt-bundle-windows-x86_64-20140321\\workspace\\demoOnlyservice\\src\\com\\example\\demoonlyservice\\Iservicemethod.aidl */ package com.example.demoonlyservice; public interface Iservicemethod extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.demoonlyservice.Iservicemethod { private static final java.lang.String DESCRIPTOR = "com.example.demoonlyservice.Iservicemethod"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.demoonlyservice.Iservicemethod interface, * generating a proxy if needed. */ public static com.example.demoonlyservice.Iservicemethod asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.example.demoonlyservice.Iservicemethod))) { return ((com.example.demoonlyservice.Iservicemethod)iin); } return new com.example.demoonlyservice.Iservicemethod.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_sayhello: { data.enforceInterface(DESCRIPTOR); this.sayhello(); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.demoonlyservice.Iservicemethod { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void sayhello() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_sayhello, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_sayhello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void sayhello() throws android.os.RemoteException; }
3、写一个类mybindle继承stub,在该类里调用服务里的方法来覆写接口里的方法,在onbindle里调用返回给开启服务的程序。
4 、在其他的应用程序里创建一个包名,注意包名必须与调用接口aidl所在的原应用程序里的包名相同,将源程序里的接口的aidl文件拷贝到包下,同源服务一样,应用程序下会自动生成aidl文件对应的包名和接口。再调用bindservice开启服务。
5、实现一个类myconnection,即开启服务的第二个参数,它需要实现ServiceConnection接口,实现接口方法,有方法onServiceConnected(ComponentName name, IBinder service),它是在连接服务成功后返回的Ibindle的时候执行,里面第二个参数就是返回的Ibinlde,它继承了Stub,通过stub里方法实例化service为接口IService = IService.Stub.asInterface(service),利用接口就可以调用服务里的方法。
注意:在运行的程序的时候发现,点击button响应事件startservice(View v)和startonlyservice(View v)时,在绑定服务了后,用接口调用远程服务方法时候,接口为空,就是说还没初始化。通过打印结果的顺序发现:点击事件执行完了才会去执行的回调事件,即onServiceConnected(ComponentName name, IBinder service)。我将绑定服务的代码移到oncreate方法在程序启动就绑定的服务,在点击事件里再通过接口调用服务里的方法就没错了。
打印的顺序: