zoukankan      html  css  js  c++  java
  • android进程间通信:使用AIDL

    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. }  

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

    1. <service android:name=".PersonService" >  
    2.   <intent-filter>  
    3.   <action android:name="com.hoo.process.aidl.PersonService " />  
    4.   </intent-filter>  
    5. </service> 
  • 相关阅读:
    redis主从模式
    深入理解BigDecimal
    double使用BigDecimal进行计算出现精确度问题
    代理IP爬取和验证(快代理&西刺代理)
    Jsoup-简单爬取知乎推荐页面(附:get_agent())
    Jsoup-基础练习
    取数据超过内存限制的问题-解决方案(sample,takeSample,filter)
    说出你的故事:你为什么学爬虫
    hadoop第一次面到hr(品友互动)
    MapReduce本地运行模式wordcount实例(附:MapReduce原理简析)
  • 原文地址:https://www.cnblogs.com/welhzh/p/4683465.html
Copyright © 2011-2022 走看看