博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android进程间通信:使用AIDL
阅读量:5949 次
发布时间:2019-06-19

本文共 12514 字,大约阅读时间需要 41 分钟。

android 的binder其实是基于 openbinder实现的,openbinder的地址:http://www.angryredplanet.com/~hackbod/openbinder/docs/html/

http://blog.csdn.net/saintswordsman/article/details/5130947

 

欢迎阅读本文,你能关注本文,你知道你需要进程间通信、需要AIDL(以及Binder),那么可以默认你对这些概念已经有了一些了解,你(大致) 知道它们是什么,它们有什么用,所以为了节约大家的眼力和时间,在此我不复制粘贴网上泛滥的博客或者翻译冗长的android文档。

      关于AIDL的介绍在文档:docs/guide/developing/tools/aidl.html

      关于IBinder的介绍在文档:docs/reference/android/os/IBinder.html

      以及Binder:docs/reference/android/os/Binder.html

      在后文中,我将以我自己的理解向你介绍相关的概念。以我目前粗浅的经验,应用程序使用AIDL的地方,几乎都和Service有关,所以你也需要知道一些关于Service的知识。日后得闲我也会继续写一些关于Service的贴。

      本文将以一个例子来和你分享使用AIDL的基础技能,这个例子里有:

1、一个类mAIDLActivity,继承Activity。里面有三个按钮,text分别为StartService,StopService,CallbackTest。

2、一个类mAIDLService,继承Service。为了充分展示ADIL的功能,它做以下工作:当用户点击CallbackTest按钮时,从mAIDLActivity调用mAIDLService中的Stub 对象的一个方法invokCallBack(),而这个方法又会调用mAIDLActivity中Stub 对象的一个方法performAction(),这个方法在屏幕上显示一个toast。没什么意义,只是展示一下AIDL如何使用。

3、两个AIDL文件:forService.aidl和forActivity.aidl。对应名字,在Service和Activity中分别有对象需要用到它们定义的接口。

4、相关XML文件,略过。关于manifest中Service的语法,见docs/guide/topics/manifest /service-element.html。你也可以简单地在<application></application>中加入

     <service android:name=".mAIDLService" android:process=":remote"> </service>

开发环境为Eclipse。

拣重要的先说,来看看aidl文件的内容:

文件:forActivity.aidl

package com.styleflying.AIDL;  interface forActivity {      void performAction();  }

文件:forService.aidl

package com.styleflying.AIDL;  import com.styleflying.AIDL.forActivity;  interface forService {      void registerTestCall(forActivity cb);      void invokCallBack();  }

这两个文件和Java文件放置的地方一样,看包名。

在Eclipse中它们将被自动编译为forActivity.java和forService.java,它们存放在gen目录下。为了方便手头无法演练的读者,代码不贴,不用细看。

 

再看mAIDLActivity.java:

package com.styleflying.AIDL;  import android.app.Activity;  import android.content.ComponentName;  import android.content.Context;  import android.content.Intent;  import android.content.ServiceConnection;  import android.os.Bundle;  import android.os.IBinder;  import android.os.RemoteException;  import android.util.Log;  import android.view.View;  import android.view.View.OnClickListener;  import android.widget.Button;  import android.widget.Toast;  public class mAIDLActivity extends Activity {      private static final String TAG = "AIDLActivity";      private Button btnOk;      private Button btnCancel;      private Button btnCallBack;      private void Log(String str) {          Log.d(TAG, "------ " + str + "------");      }      private forActivity mCallback = new forActivity.Stub() {          public void performAction() throws RemoteException          {              Toast.makeText(mAIDLActivity.this, "this toast is called from service", 1).show();          }      };      forService mService;      private ServiceConnection mConnection = new ServiceConnection() {          public void onServiceConnected(ComponentName className,                  IBinder service) {              mService = forService.Stub.asInterface(service);              try {                  mService.registerTestCall(mCallback);}              catch (RemoteException e) {              }          }          public void onServiceDisconnected(ComponentName className) {              Log("disconnect service");              mService = null;          }      };      @Override          public void onCreate(Bundle icicle) {              super.onCreate(icicle);              setContentView(R.layout.main);              btnOk = (Button)findViewById(R.id.btn_ok);              btnCancel = (Button)findViewById(R.id.btn_cancel);              btnCallBack = (Button)findViewById(R.id.btn_callback);              btnOk.setOnClickListener(new OnClickListener() {                      public void onClick(View v) {                      Bundle args = new Bundle();                      Intent intent = new Intent(mAIDLActivity.this, mAIDLService.class);                      intent.putExtras(args);                      bindService(intent, mConnection, Context.BIND_AUTO_CREATE);                      startService(intent);                      }                      });              btnCancel.setOnClickListener(new OnClickListener() {                      public void onClick(View v) {                      unbindService(mConnection);                      //stopService(intent);                      }                      });              btnCallBack.setOnClickListener(new OnClickListener() {                      @Override                      public void onClick(View v)                      {                      try                      {                      mService.invokCallBack();                      } catch (RemoteException e)                      {                      // TODO Auto-generated catch block                      e.printStackTrace();                      }                      }                      });          }  }

很短,相信大家很容易看明白。注意mConnection,它的onServiceConnected()中有一句mService = forService.Stub.asInterface(service);给mService赋值了,这个mService是一个 forService,而service是onServiceConnected()传进来的参数,onServiceConnected()会在连接 Service的时候被系统调用,这个service参数的值来自哪里呢?

看mAIDLService.java:

package com.styleflying.AIDL;  import android.app.Service;  import android.content.Intent;  import android.os.IBinder;  import android.os.RemoteCallbackList;  import android.os.RemoteException;  import android.util.Log;  public class mAIDLService extends Service {      private static final String TAG = "AIDLService";        private forActivity callback;      private void Log(String str) {          Log.d(TAG, "------ " + str + "------");      }      @Override          public void onCreate() {              Log("service create");          }      @Override          public void onStart(Intent intent, int startId) {              Log("service start id=" + startId);          }      @Override          public IBinder onBind(Intent t) {              Log("service on bind");              return mBinder;          }      @Override          public void onDestroy() {              Log("service on destroy");              super.onDestroy();          }      @Override          public boolean onUnbind(Intent intent) {              Log("service on unbind");              return super.onUnbind(intent);          }      public void onRebind(Intent intent) {          Log("service on rebind");          super.onRebind(intent);      }      private final forService.Stub mBinder = new forService.Stub() {          @Override              public void invokCallBack() throws RemoteException              {                  callback.performAction();              }          @Override              public void registerTestCall(forActivity cb) throws RemoteException              {                  callback = cb;              }      };  }

注意onBind(),它的返回类型为IBinder,返回了一个mBinder,看看mBinder的定义:

    private final forService.Stub mBinder = new forService.Stub() {

        @Override

        public void invokCallBack() throws RemoteException
        {
            callback.performAction();
         }

        @Override

        public void registerTestCall(forActivity cb) throws RemoteException
        {
            callback = cb;

        }

       };

它是实现了我们在AIDL中定义的方法,这个mBinder最终返回给了mAIDLActivity中的mService,于是在 mAIDLActivity中可以使用mBinder中的方法了。在mAIDLActivity中也有一个类似mBinder的对象,看看定义:   

        private forActivity mCallback = new forActivity.Stub()

    {

        public void performAction() throws RemoteException
        {
            Toast.makeText(mAIDLActivity.this, "this toast is called from service", 1).show();
        }
      };

我们要在界面上显示一个toast,就是在这里实现的。这个对象,在mConnection的onServiceConnected()被调用时, 通过调用mService(也就是远程的mAIDLService中的mBinder)的registerTestCall(),传递给了 mAIDLService,于是在mAIDLService中可以调用performAction()了。

很啰嗦,只为了能把这个细节说清楚。请大家认真看,我尽量避免错别字、混乱的大小写和逻辑不清的语法,相信你会看明白。是不是很简单?再啰嗦一下,做一个大致总结,我们使用AIDL是要做什么呢:

让Acticity(或者说一个进程/一个类?)和Service(或者说远端进程/远端类/对象?)获取对方的一个Stub对象,这个对象在定义 时实现了我们在AIDL中定义的方法,于是这些远程对象中的方法可以在本地使用了。如果这种使用(通信)是单向的,比如只是Activity需要通知 Service做什么,那么只要Service中有一个Stub对象,并且传给Acticity就够了。

至于如何获得远程的Stub,参看上面的代码,看mConnection、registerTestCall、onRebind,它们展示了一种方法。

另外,有时候我们可能在一个类中有多个Stub对象,它们都要给远程交互的类的实例,这个时候可以考虑使用RemoteCallbackList<>(docs/reference/android/os/RemoteCallbackList.html)。

 

=============================

Service的生命周期 Service的生命周期方法比Activity少一些,只有onCreate, onStart, onDestroy 
我们有两种方式启动一个Service,他们对Service生命周期的影响是不一样的。

1 通过startService 
    Service会经历 onCreate -> onStart 
   stopService的时候直接onDestroy 

   如果是调用者(TestServiceHolder)自己直接退出而没有调用stopService的 
   话,Service会一直在后台运行。 
   下次TestServiceHolder再起来可以stopService。

  2 通过bindService    
    Service只会运行onCreate, 这个时候 TestServiceHolder 和TestService绑定在一起 

   TestServiceHolder 退出了,Srevice就会调用onUnbind->onDestroyed 
   所谓绑定在一起就共存亡了。 

那有同学问了,要是这几个方法交织在一起的话,会出现什么情况呢? 
一 个原则是Service的onCreate的方法只会被调用一次,就是你无论多少次的startService又 bindService,Service只被创建一次。如果先是bind了,那么start的时候就直接运行Service的onStart方法,如果先 是start,那么bind的时候就直接运行onBind方法。如果你先bind上了,就stop不掉了,对啊,就是stopService不好使了,只 能先UnbindService, 再StopService,所以是先start还是先bind行为是有区别的。 

服 务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可 以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务 仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特 点。

如 果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用 onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服 务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方 法结束服务,服务结束时会调用onDestroy()方法。

如 果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用 onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,接着调用onDestroy() 方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说 onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方 法,调用该方法也会导致系统调用服务的onUnbind()-->onDestroy()方法.

 

你可以绑定一个已经通过startService()方法启动的服务。例如:一 个后台播放音乐服务可以通过startService(intend)对象来播放音乐。可能用户在播放过程中要执行一些操作比如获取歌曲的一些信息,此时 activity可以通过调用bindServices()方法与Service建立连接。这种情况下,unbindService() 及 stopServices()方法实际上不会停止服务,直到最后一次的绑定被关闭。

 

================================================================

 

Android之进程间传递自定义类型参数

【0】AIDL默认支持的类型包话java基本类型(int、long、boolean等)和(String、List、Map、CharSequence)(好像数组也可以,只要实现了Parcelable接口),如果要传递自定义的类型该如何实现呢?

要传递自定义类型,首先要让自定义类型支持parcelable协议,实现步骤如下:

1>自定义类型必须实现Parcelable接口,并且实现Parcelable接口的public void writeToParcel(Parcel dest, int flags)方法 。

2>自定义类型中必须含有一个名称为CREATOR的静态成员,该成员对象要求实现Parcelable.Creator接口及其方法。

3>创建一个aidl文件声明你的自定义类型。

Parcelable接口的作用:实现了Parcelable接口的实例可以将自身的状态信息(状态信息通常指的是各成员变量的值)写入Parcel,也可以从Parcel中恢复其状态。 Parcel用来完成数据的序列化传递。 

【1】进程间传递自定义类型的实现过程 

1> 创建自定义类型,并实现Parcelable接口,使其支持parcelable协议。如:在com.hoo.domin创建Person.java:

  1. public class Person implements Parcelable  
  2.   private Integer id;  
  3.   private String name;  
  4.    
  5.   public Person(){}  
  6.   public Person(Integer id, String name) {  
  7.   this.id = id;  
  8.   this.name = name;  
  9.   }  
  10.   public Integer getId() {  
  11.   return id;  
  12.   }  
  13.   public void setId(Integer id) {  
  14.   this.id = id;  
  15.   }  
  16.   public String getName() {  
  17.   return name;  
  18.   }  
  19.   public void setName(String name) {  
  20.   this.name = name;  
  21.   }   
  22.   @Override  
  23.   public int describeContents() {  
  24.   return 0;  
  25.   }  
  26.   @Override  
  27.   public void writeToParcel(Parcel dest, int flags) {
    //把javanbean中的数据写到Parcel   
  28.   dest.writeInt(this.id);  
  29.   dest.writeString(this.name);  
  30.   }  
  31.   //添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口   
  32.   public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){  
  33.   @Override  
  34.   public Person createFromParcel(Parcel source) {
    //从Parcel中读取数据,返回person对象   
  35.   return new Person(source.readInt(), source.readString());  
  36.   }  
  37.   @Override  
  38.   public Person[] newArray(int size) {  
  39.   return new Person[size];  
  40.   }  
  41.   };  
  42. }  

2> 在自定义类型所在包下创建一个aidl文件对自定义类型进行声明,文件的名称与自定义类型同名。

package com.hoo.domin

parcelable Person; 

3> 在接口aidl文件中使用自定义类型,需要使用import显式导入,本例在com.hoo.aidl包下创建IPersonService.aidl文件,内容如下:

  1. package com.hoo.aidl;  
  2. import cn.itcast.domain.Person;  
  3. interface IPersonService {  
  4.       void save(in Person person);  
  5. }   

4> 在实现aidl文件生成的接口(本例是IPersonService),但并非直接实现接口,而是通过继承接口的Stub来实现(Stub抽象类内部实现了aidl接口),并且实现接口方法的代码。内容如下:

  1. public class ServiceBinder extends IPersonService.Stub {  
  2.        @Override  
  3.        public void save(Person person) throws RemoteException {  
  4.   Log.i("PersonService", person.getId()+"="+ person.getName());  
  5.        }   
  6. }  

5> 创建一个Service(服务),在服务的onBind(Intent intent)方法中返回实现了aidl接口的对象(本例是ServiceBinder)。内容如下:

  1. public class PersonService extends Service {  
  2.   private ServiceBinder serviceBinder = new ServiceBinder();  
  3.   @Override  
  4.   public IBinder onBind(Intent intent) {  
  5.   return serviceBinder;  
  6.   }  
  7. public class ServiceBinder extends IPersonService.Stub {  
  8.        @Override  
  9.        public void save(Person person) throws RemoteException {  
  10.   Log.i("PersonService", person.getId()+"="+ person.getName());  
  11.        }  
  12. }  
  13. }  

其他应用可以通过隐式意图访问服务,意图的动作可以自定义,Manifest.xml配置代码如下:

  1. <service android:name=".PersonService" >  
  2.   <intent-filter>  
  3.   <action android:name="com.hoo.process.aidl.PersonService " />  
  4.   </intent-filter>  
  5. </service> 

 

你可能感兴趣的文章
基于泛型实现的ibatis通用分页查询
查看>>
gopacket 使用
查看>>
AlertDialog对话框
查看>>
我的友情链接
查看>>
linux安全---cacti+ntop监控
查看>>
鸟哥的linux私房菜-shell简单学习-1
查看>>
nagios配置监控的一些思路和工作流程
查看>>
通讯组基本管理任务三
查看>>
Centos下基于Hadoop安装Spark(分布式)
查看>>
3D地图的定时高亮和点击事件(基于echarts)
查看>>
mysql开启binlog
查看>>
设置Eclipse编码方式
查看>>
分布式系统唯一ID生成方案汇总【转】
查看>>
并查集hdu1232
查看>>
开发安全的 API 所需要核对的清单
查看>>
Mycat源码中的单例模式
查看>>
WPF Dispatcher介绍
查看>>
Mysql 监视工具
查看>>
【290】Python 函数
查看>>
godaddy域名转发(域名跳转)设置教程
查看>>