安卓之Service详解(三)【安卓IPC之AIDL】

Android BoundService 详解

1.一般实现步骤讲解

在客户端(Activity中)要完成

1.客户端通过BindService()方法来绑定一个服务对象(业务对象)
  如绑定成功会回调ServiceConnection接口方法onServiceConnected()
2.OnServiceConnection()方法的其中一个参数是在Service中OnBind()返回的Binder的实例。
3.通过在OnServiceConnection()方法中接受Binder的实例来调用Binder中返回Service实例的方法,获得Service的实现。
4.通过Service的实例就可以调用Service的的共有方法。

在服务端一般要实现:

1.服务端通过创建一个*.aidl文件来定义一个可以被客户端调用的业务接口
       一个AIDL文件的规范:
       1>不能有修饰符,类似接口的写法
       2>支持数据类型,String\CharSequence\List(存放字符串)\Map\自定义类型
       自定义类型:
       (要实现Parcelable接口,定义一个AIDL文件声明该类型,在其他AIDL中使用该类型需要import包)
2.服务端需要提供一个业务接口的实现类,通常继承 Stub类
3.通过Service的onBind()方法返回被绑定的业务对象

2.例子讲解

先来张图片



接下来我们按照源码一步步分析

1>先上布局文件,activity_main.xml,三个Button,三个点击事件,很简单,不做讲解

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.allan.android_async_http.MainActivity">   
<Button
        android:onClick="boundService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="boundService"/>
    <Button
        android:onClick="unboundService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="unboundService"/>
    <Button
        android:onClick="useIPC"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="useIPC"/>

</LinearLayout>

2>接下来是客户端MainActivity.java的代码

public class MainActivity extends AppCompatActivity {
    private ICat cat;
    private boolean mBound = false;  //是否绑定


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }
 //绑定方法  this Activity as a Client
    public void boundService(View view) {
        Intent intent = new Intent(this,MyBoundService.class);
        //三个参数  1.Intent  2.ServiceConnection接口 3.Flag
        bindService(intent,connection, Context.BIND_AUTO_CREATE);
        //异步绑定,绑定成功后回调ServiceConnection
    }

    //解绑方法
    public void unboundService(View view) {
        if(mBound) {
            unbindService(connection);
            Toast.makeText(this, "解除绑定", Toast.LENGTH_SHORT).show();
        }
    }

    public void useIPC(View view) {
        if(cat == null) {
            return;
        }else {
            try {
                cat.setName("小花");
                Toast.makeText(this, ""+ cat.desc(), Toast.LENGTH_SHORT).show();
                Toast.makeText(this, "" + cat.getPeson().toString(), Toast.LENGTH_SHORT).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    /*
     *    ServiceConnection是一个接口
     */
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //绑定成功后回调的方法
            cat = ICat.Stub.asInterface(service);
            mBound = !mBound;
            Toast.makeText(MainActivity.this, "绑定成功", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //服务异常终止时调用
            mBound = !mBound;
        }
    };


}

 
 
然我们分析一下MainActivity的代码,首先看boundService()方法,里面最重要的bindService()方法,一共接受三个参数,一个是
Intent,第二个是ServiceConnection接口,自己在下面实现一个即可,第三个是一个Flag,用默认这个就行 
算了,讲一次,就说深一点吧,继续,讲讲bindService()这个方法:
【以下为补充,不感兴趣跳过即可】
1>bindService()是Context的一个方法,它是抽象的。函数原型的代码如下:
public abstract boolean bindService(Intent service, ServiceConnection conn,  
        int flags);  
2>而最终实现bindService()方法的是ContextImpl这个类,代码如下:
    @Override  
        public boolean bindService(Intent service, ServiceConnection conn,  
                int flags) {  
            IServiceConnection sd;  
            if (mPackageInfo != null) {  
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),  
                        mMainThread.getHandler(), flags);  
            } else {  
                throw new RuntimeException("Not supported in system context");  
            }  
            try {  
                int res = ActivityManagerNative.getDefault().bindService(  
                    mMainThread.getApplicationThread(), getActivityToken(),  
                    service, service.resolveTypeIfNeeded(getContentResolver()),  
                    sd, flags);  
                if (res < 0) {  
                    throw new SecurityException(  
                            "Not allowed to bind to service " + service);  
                }  
                return res != 0;  
            } catch (RemoteException e) {  
                return false;  
            }  
        }  
这里先不讨论是如何实现bindService的,先来说一下他的三个参数
    public abstract boolean bindService(Intent service, ServiceConnection conn,  
             int flags);  

从函数原型可知,bindService()有3个参数,官方文档解释如下:
service Identifies the service to connect to. The Intent may specify either an explicit component name, or a logical description (action, category, etc) to match an IntentFilter published by a service.
conn Receives information as the service is started and stopped. This must be a valid ServiceConnection object; it must not be null.
flags Operation options for the binding. May be 0, BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY.

这里简要解释一下:
第一个service它的意思是:标识服务连接。目的可以指定一个明确的组件名称,说白了就说指定你的那个service
第二个conn意思是:这个参数这必须是一个有效的ServiceConnection对象,主要用于接收信息,启动和停止服务。
第三个是一个flags:相当于一个操作服务绑定的控制位,一般使用Context.BIND_AUTO_CREATE,意思就说自动创建绑定
好了,回归正题:
。。。。。。。。。。。。。。。。

3>接下来新建一个AIDL文件,名字为ICat.aidl,用于描述一只猫咪的属性方法和他的主人
// ICat.aidl
package com.example.allan.android_async_http;
import com.example.allan.android_async_http.Person;
// Declare any non-default types here with import statements
 //支持String,List ,charSqeunce

interface ICat {
    /**
     * 提供给客户端绑定的业务方法
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */

    Person getPeson();     //自定义数据类型,获得Cat的主人
    void setName(String name);
    String desc();

    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
AIDL文件就像接口一样,在里面定义个两基本数据类型的个方法,和一个自定义类型的方法
这里需要注意的是如何自定义类型(以本例子讲解)
步骤:
1)在ICat.aidl文件中,我自定义了一个Person类型的方法,getPerson();定义以后会发现,没法使用,是因为还没有声明这个类型(如3>步)
 
 
 Person getPeson();     //自定义数据类型,获得Cat的主人
2)接下来,新建一个Person.aidl文件,代码很简单,就是指定一下package,定义这个类型(如4>步)
3)然后就是在ICat.aidl文件中,导入Person.aidl包
4)最后新建这个Person.java具体的类(如6>步),实现Parcelable接口
这样就完成了自定义类型

4>然后接下来就是Person.aidl自定义数据类型
package com.example.allan.android_async_http;
parcelable Person;
这里面只有一个类型的声明,很简单

5>接下来写一个Cat.aidl的具体实现类CatIpml.java[服务类]
package com.example.allan.android_async_http;

import android.os.RemoteException;

/**
 * Created by allan on 17-5-30.
 * QQ Num: 1750398075
 * CSDN Blog:  http://blog.csdn.net/allan_bst
 * Personal Website: http://mobiledream.top
 * Personal Sina E-mail: [email protected]
 */

//业务接口的具体实现类
public class CatImpl extends ICat.Stub {
    private String name;

    @Override
    public Person getPeson() throws RemoteException {
        Person p = new Person();
        p.name = "小明";
        p.works = "程序员";
        p.age = "21";
        return p;
    }

    @Override
    public void setName(String name) throws RemoteException {
        this.name = name;
    }

    @Override
    public String desc() throws RemoteException {
        return "hello my name is "+name+",I am a cat!";
    }

    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

    }
}


这个实现类实现了Cat中的抽象方法,相当于一个Service服务类( 服务端需要提供一个业务接口的实现类,通常继承 Stub类
6>然后是Person.java自定义数据类型,实现了Parcelable接口,实现里面的方法,照写就好,看不懂下面有详解 
 
package com.example.allan.android_async_http;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by allan on 17-5-30.
 * QQ Num: 1750398075
 * CSDN Blog:  http://blog.csdn.net/allan_bst
 * Personal Website: http://mobiledream.top
 * Personal Sina E-mail: [email protected]
 */

public class Person implements Parcelable {

    String name;
    String age;
    String works;


    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", works='" + works + '\'' +
                '}';
    }



    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeString(age);
        dest.writeString(works);
    }

    public static final Parcelable.Creator<Person> CREATOR
            = new Parcelable.ClassLoaderCreator<Person>(){

        @Override
        public Person createFromParcel(Parcel source) {
            Person p = new Person();
            p.name = source.readString();
            p.age = source.readString();
            p.works = source.readString();
            return p;
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }

        @Override
        public Person createFromParcel(Parcel source, ClassLoader loader) {
            return null;
        }
    };
}
既然讲到序列化接口,那我们就来说一下安卓的序列化接口Pracelable,我们都知道java中有 Serializable接口,但是
Pracelable的效率要比它高得多,我们先来说一下,为什么需要序列化接口:

1)永久性保存对象,保存对象的字节序列到本地文件中;

2)通过序列化对象在网络中传递对象;

3)通过序列化在进程间传递对象。

 
 
补充:实现序列化的方法

Android中实现序列化有两个选择:一是实现Serializable接口(是JavaSE本身就支持的),

一是实现Parcelable接口(是Android特有功能,效率比实现Serializable接口高效,可用于Intent数据传递,

也可以用于进程间通信(IPC))。实现Serializable接口非常简单,声明一下就可以了,

而实现Parcelable接口稍微复杂一些,但效率更高,推荐用这种方法提高性能。

我们来看一下Parcelable接口:

public interface Parcelable 
{
    //内容描述接口,基本不用管
    public int describeContents();
    //写入接口函数,打包
    public void writeToParcel(Parcel dest, int flags);
    //读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入
    //为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例
    public interface Creator<T> 
    {
           public T createFromParcel(Parcel source);
           public T[] newArray(int size);
    }
}
实现Parcelable步骤

1)implements Parcelable

2)重写writeToParcel方法,将你的对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从 Parcel容器获取数据

3)重写describeContents方法,内容接口描述,默认返回0就可以

4)实例化静态内部对象CREATOR实现接口Parcelable.Creator


好了,就补充到这儿了,如果有什么疑惑的,可以给我留言



猜你喜欢

转载自blog.csdn.net/Allan_Bst/article/details/72811628
今日推荐