美文网首页
4.2.3 Service 结丹

4.2.3 Service 结丹

作者: 努力学习的安同学 | 来源:发表于2018-07-15 22:50 被阅读0次

标注:本文为个人学习使用,仅做自己学习参考使用,请勿转载和转发
2018-07-11: 初稿,感觉自己的Android的水平还是差了很多很多啊,参考博主coder-pig
2018-07-13: 脑壳痛,就让自己有理由休息了

0. 引言

  • Android中跨进程通信AIDL的一些概念
  • 本节对应的官方文档:Binder

1. Bander机制

1.1 IBander和Bander
  • IBinder是远程对象的基本接口,是为了高性能而设计的轻量级远程调用机制的核心部分。但他不仅用于远程调用,也用于进程内调用。该接口定义了与远程对象间交互的协议。但不要直接实现这个接口,而是继承(extends)Binder
  • IBinder主要的API是transact(),与之对应的API是Binder.onTransact()。通过前者,你能 想远程IBinder对象发送发出调用,后者使你的远程对象能够响应接收到的调用。IBinder的API都是 Syncronous(同步)执行的,比如transact()直到对方的Binder.onTransact()方法调用玩 后才返回。 调用发生在进程内时无疑是这样的,而在进程间时,在IPC的帮助下,也是同样的效果。
  • 通过transact()发送的数据是Parcel,Parcel是一种一般的缓冲区,除了有数据外还带有 一些描述它内容的元数据。元数据用于管理IBinder对象的引用,这样就能在缓冲区从一个进程移动 到另一个进程时保存这些引用。这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中, 如果另一个进程把同一个IBinder的引用回发到原来的进程,那么这个原来的进程就能接收到发出的 那个IBinder的引用。这种机制使IBinder和Binder像唯一标志符那样在进程间管理。
  • 系统为每个进程维护一个存放交互线程的线程池。这些交互线程用于派送所有从另外进程发来的IPC 调用。例如:当一个IPC从进程A发到进程B,A中那个发出调用的线程(这个应该不在线程池中)就阻塞 在transact()中了。进程B中的交互线程池中的一个线程接收了这个调用,它调用 Binder.onTransact(),完成后用一个Parcel来做为结果返回。然后进程A中的那个等待的线程在 收到返回的Parcel后得以继续执行。实际上,另一个进程看起来就像是当前进程的一个线程, 但不是当前进程创建的。
  • Binder机制还支持进程间的递归调用。例如,进程A执行自己的IBinder的transact()调用进程B 的Binder,而进程B在其Binder.onTransact()中又用transact()向进程A发起调用,那么进程A 在等待它发出的调用返回的同时,还会用Binder.onTransact()响应进程B的transact()。 总之Binder造成的结果就是让我们感觉到跨进程的调用与进程内的调用没什么区别。
  • 当操作远程对象时,你经常需要查看它们是否有效,有三种方法可以使用:
    1. transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常。
    2. 如果目标进程不存在,那么调用pingBinder()时返回false。
    3. 可以用linkToDeath()方法向IBinder注册一个IBinder.DeathRecipient, 在IBinder代表的进程退出时被调用。

嗯,总体上来说

  • IBinder是Android给我们提供的一个进程间通信的一个接口,而我们一般是不直接实现这个接口的, 而是通过继承Binder类来实现进程间通信!是Android中实现IPC(进程间通信)的一种方式!
1.2 Binder机制
  • Android中的Binder机制由一系列组件构成:Client、Server、ServiceManager、Binder驱动程序
  • 调用流程大致如下


流程解析

  1. Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打包成Parcel对象;
  2. 然后代理接口把该Parcel对象发送给内核中的Binder driver;;
  3. 然后Server会读取Binder Driver中的请求数据,假如是发送给自己的,解包Parcel对象, 处理并将结果返回;
  4. PS:代理接口中的定义的方法和Server中定义的方法是一一对应的, 另外,整个调用过程是一个同步的,即Server在处理时,Client会被Block(锁)住! 而这里说的代理接口的定义就是等下要说的AIDL(Android接口描述语言)!
1.3 为何Android会使用Binder机制来实现进程间的通信
  1. 可靠性:在移动设备上,通常采用基于Client-Server的通信方式来实现互联网与设备间的内部通信。目前linux支持IPC包括传统的管道,System V IPC,即消息队列/共享内存/信号量,以及socket中只有socket支持Client-Server的通信方式。Android系统为开发者提供了丰富进程间通信的功能接口,媒体播放,传感器,无线传输。这些功能都由不同的server来管理。开发都只关心将自己应用程序的client与server的通信建立起来便可以使用这个服务。毫无疑问,如若在底层架设一套协议来实现Client-Server通信,增加了系统的复杂性。在资源有限的手机 上来实现这种复杂的环境,可靠性难以保证。
  2. 传输性能:socket主要用于跨网络的进程间通信和本机上进程间的通信,但传输效率低,开销大。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的一块缓存区中,然后从内核缓存区拷贝到接收方缓存区,其过程至少有两次拷贝。虽然共享内存无需拷贝,但控制复杂。比较各种IPC方式的数据拷贝次数。共享内存:0次。Binder:1次。Socket/管道/消息队列:2次。
  3. 安全性:Android是一个开放式的平台,所以确保应用程序安全是很重要的。Android对每一个安装应用都分配了UID/PID,其中进程的UID是可用来鉴别进程身份。传统的只能由用户在数据包里填写UID/PID,这样不可靠,容易被恶意程序利用。而我们要求由内核来添加可靠的UID。 所以,出于可靠性、传输性、安全性。android建立了一套新的进程间通信方式。 ——摘自:Android中的Binder机制的简要理解

Binder的好处

  • 我们无需关心底层如何实现,只需按照AIDL的规则,自定义一个接口文件, 然后调用调用接口中的方法,就可以完成两个进程间的通信了!

2. AIDL使用解析

2.1 什么是AIDL
  • IPC这个名词,他的全名叫做:跨进程通信(interprocess communication)
  • Android系统中,个个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的, 而为了实现跨进程,Android给我们提供了上面说的Binder机制
  • 这个机制使用的接口语言就是: AIDL(Android Interface Definition Language),他的语法很简单,而这种接口语言并非真正的编程语言,只是定义两个进程间的通信接口而已。
  • 而生成符合通信协议的Java代码则是由Android SDK的 platform-tools目录下的aidl.exe工具生成,生成对应的接口文件在:gen目录下,一般是:Xxx.java的接口!
  • 而在该接口中包含一个Stub的内部类,该类中实现了在该类中实现了IBinder接口与自定义的通信接口, 这个类将会作为远程Service的回调类——实现了IBinder接口,所以可作为Service的onBind( )方法的返回值。
2.2 AIDL实现两个进程间通信

编写AIDL的一些注意事项:

  1. 接口名词需要与aidl文件名相同
  2. 接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final!
  3. AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence,除此之外的其他类型都 需要import声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口, 详情请看后面的传递复杂数据类型
  4. 自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义 的包在同一个包中。自定义类型应该可以转化为Json串传输。

AS中可以直接创建AIDL文件

  • 然后就直接创建了AIDL的文件夹和AIDL文件了,建立之前给文件取一个名,然后重新编译下就好了。

一、服务端

  1. 创建AIDL文件
    IPerson.aidl
package com.jay.aidl;

interface IPerson {
    String queryPerson(int num);
}

IPerson.java

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\Code\\ASCode\\AIDLServer\\app\\src\\main\\aidl\\com\\jay\\aidl\\IPerson.aidl
 */
package com.jay.aidl;
public interface IPerson extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jay.aidl.IPerson
{
private static final java.lang.String DESCRIPTOR = "com.jay.aidl.IPerson";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.jay.aidl.IPerson interface,
 * generating a proxy if needed.
 */
public static com.jay.aidl.IPerson asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jay.aidl.IPerson))) {
return ((com.jay.aidl.IPerson)iin);
}
return new com.jay.aidl.IPerson.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_queryPerson:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.queryPerson(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jay.aidl.IPerson
{
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 java.lang.String queryPerson(int num) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num);
mRemote.transact(Stub.TRANSACTION_queryPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_queryPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String queryPerson(int num) throws android.os.RemoteException;
}
  • 上面的文件很复杂,我们只关注的是asInterface(IBinder)和我们定义的接口中的queryPerson()方法!
  • 这个方法会把IBander类型的对象转换成IPerson类型的,必要时生成一个代理对象返回结果
  1. 自定义我们的Service类,完成以下操作
    1)继承Service类,同时也自定义了一个PersonQueryBinder类来继承IPerson.Stub类,就是实现了IPerson接口和IBander接口
    2)实例化自定义的Stub类,并重写Service的onBind方法,返回一个binder对象!

AIDLService.java

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.jay.aidl.IPerson.Stub;

/**
 * Created by Jay on 2015/8/18 0018.
 */
public class AIDLService extends Service {

    private IBinder binder = new PersonQueryBinder();
    private String[] names = {"B神","艹神","基神","J神","翔神"};

    private String query(int num)
    {
        if(num > 0 && num < 6){
            return names[num - 1];
        }
        return null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    private final class PersonQueryBinder extends Stub{
        @Override
        public String queryPerson(int num) throws RemoteException {
            return query(num);
        }
    }
}
  1. 在AndroidMainfest.xml中注册Service
<service android:name=".AIDLService">
       <intent-filter>
              <action android:name="android.intent.action.AIDLService" />
              <category android:name="android.intent.category.DEFAULT" />
       </intent-filter>
</service>

二、客户端

  • 直接把服务端的那个aidl文件复制过来,然后我们直接在MainActivity中完成,和绑定本地Service的操作
  • 有点类似,流程如下:
    1)自定义PersonConnection类实现ServiceConnection接口
    2)以PersonConnection对象作为参数,调用bindService绑定远程Service
    bindService(service,conn,BIND_AUTO_CREATE);
    ps:第三个参数是设置如果服务没有启动的话,自动创建
    3)和本地Service不同,绑定远程Service的ServiceConnection并不能直接获取Service的onBind( )方法
  • 返回的IBinder对象,只能返回onBind( )方法所返回的代理对象,需要做如下处理:
    iPerson = IPerson.Stub.asInterface(service);
    再接着完成初始化,以及按钮事件等就可以了
    具体代码如下:
    MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private EditText edit_num;
    private Button btn_query;
    private TextView txt_name;
    private IPerson iPerson;
    private PersonConnection conn = new PersonConnection();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
        //绑定远程Service, android6.0之后采用显示通信
        Intent serviceIntent = new Intent();
        serviceIntent.setComponent(new ComponentName("com.project.ankie.aidlservicedemo", "com.project.ankie.aidlservicedemo.AIDLService"));

        bindService(service, conn, BIND_AUTO_CREATE);
        btn_query.setOnClickListener(this);
    }

    private void bindViews() {
        edit_num = (EditText) findViewById(R.id.edit_num);
        btn_query = (Button) findViewById(R.id.btn_query);
        txt_name = (TextView) findViewById(R.id.txt_name);
    }

    @Override
    public void onClick(View v) {
        String number = edit_num.getText().toString();
        int num = Integer.valueOf(number);
        try {
            txt_name.setText(iPerson.queryPerson(num));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        edit_num.setText("");
    }

    private final class PersonConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder service) {
            iPerson = IPerson.Stub.asInterface(service);
        }
        public void onServiceDisconnected(ComponentName name) {
            iPerson = null;
        }
    }
}

接下来先启动AIDLServivce,然后再启动AIDLClient,输入查询序号,即可获得对应姓名! 当然也可以直接启动AIDLClient,也会获得同样效果:
效果图如下:

2.4 传递复杂数据的AIDL通信
  • 上面的例子我们传递的只是要给int类型的参数,然后服务端返回一个String类型的参数,看似满足 我们的基本需求,不过实际开发中,我们可能需要考虑传递复杂数据类型的情况!下面我们来学习下 如何向服务端传递复杂数据类型的数据!开始之前我们先来了解Parcelable接口!

Parcelable接口简介:

  • Parcelable主要是用于序列化,除了这个还有一个Serializable,也是用于序列化的,其中Parcelable更加轻量级,速度更快,就是写起来比较麻烦,当然还有as的插件来完成序列化,比如Gson
  1. 首先,需要实现writeToParcel和readFromParcel方法写入方法将对象写入到包裹parcel中,而读取方法则从包裹中读取对象,请注意,写入属性顺序与读取顺序相同
  2. 接着,需要在该类中添加一个名为CREATOR的static final属性,改属性需要实现:android.os.Parcelable.Creator接口
  3. 接着,需要从写接口中的两个方法,createFromParcel(Parcel source)方法,实现source创建出JavaBean实例的功能 newArray(int size),创建一个类型为T,长度为size的数组,只有一个简单的return new T[size],这里的T是Person类
  4. 最后,discribeContents():嗯,这个直接返回0即可
  5. 另外,除了String和CharSequence以外,其余俊宇要一个方向指示符。方向指示符包括in、out和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。

代码示例

  • 自定义了两种对象:Person与Salary,Person作为调用远程的Serviec的参数,Salary作为返回值!那么首先就要做的是创建Person与Salary类,同时实现Parcelable接口
    1. 服务端
  1. 创建Person.aidl和Salary.aidl的文件,因为他们需要实现Parcelable接口,所以就下面一条语句:
Person.aidl:     parcelable Person; 
Salary.aidl:     parcelable Salary; 
  1. 分别建立Person类与Salary类,需实现Parcelable接口,重写对应的方法!
    因为我们后面是根据Person对象来获取Map集合中的数据,所以Person.java中我们重写了hashcode和equals 的方法;而Salary类则不需要!
public class Person implements Parcelable{

    private Integer id;
    private String name;

    public Person() {}

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }


    //实现Parcelable必须实现的方法,不知道拿来干嘛的,直接返回0就行了
    @Override
    public int describeContents() {
        return 0;
    }


    //写入数据到Parcel中的方法
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //把对象所包含的数据写入到parcel中
        dest.writeInt(id);
        dest.writeString(name);
    }

    //必须提供一个名为CREATOR的static final属性 该属性需要实现
    //android.os.Parcelable.Creator<T>接口
    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
        //从Parcel中读取数据,返回Person对象
        @Override
        public Person createFromParcel(Parcel source) {
            return new Person(source.readInt(),source.readString());
        }
        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    //因为我们集合取出元素的时候是根据Person对象来取得,所以比较麻烦,
    //需要我们重写hashCode()和equals()方法
    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (name == null)
        {
            if (other.name != null)
                return false;
        }
        else if (!name.equals(other.name))
            return false;
        return true;
    }
}

Salary类

public class Salary implements Parcelable {

    private String type;
    private Integer salary;

    public Salary() {
    }

    public Salary(String type, Integer salary) {
        this.type = type;
        this.salary = salary;
    }

    public String getType() {
        return type;
    }

    public Integer getSalary() {
        return salary;
    }

    public void setType(String type) {
        this.type = type;
    }

    public void setSalary(Integer salary) {
        this.salary = salary;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(type);
        dest.writeInt(salary);
    }

    public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() {
        //从Parcel中读取数据,返回Person对象
        @Override
        public Salary createFromParcel(Parcel source) {
            return new Salary(source.readString(), source.readInt());
        }

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

    public String toString() {
        return "工作:" + type + "    薪水: " + salary;
    }
}

3、创建一个ISalary.aidl的文件,在里面写一个简单的获取工资信息的方法:

package com.jay.example.aidl;
  
import com.jay.example.aidl.Salary;  
import com.jay.example.aidl.Person;  
interface ISalary  
{  
    //定义一个Person对象作为传入参数  
    //接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in  
    Salary getMsg(in Person owner);  
}  

ps:这里可以记得如果使用的是自定义的数据类型的话,需要import哦!!!切记!!!
Step 4:核心Service的编写: 定义一个SalaryBinder类继承Stub,从而实现ISalary和IBinder接口;定义一个存储信息的Map集合! 重新onBind方法,返回SalaryBinder类的对象实例!
AidlService.java

public class AidlService extends Service {  
  
    private SalaryBinder salaryBinder;  
    private static Map<Person,Salary> ss = new HashMap<Person, Salary>();  
    //初始化Map集合,这里在静态代码块中进行初始化,当然你可也以在构造方法中完成初始化  
    static  
    {  
        ss.put(new Person(1, "Jay"), new Salary("码农", 2000));  
        ss.put(new Person(2, "GEM"), new Salary("歌手", 20000));  
        ss.put(new Person(3, "XM"), new Salary("学生", 20));  
        ss.put(new Person(4, "MrWang"), new Salary("老师", 2000));  
    }  
      
      
    @Override  
    public void onCreate() {  
        super.onCreate();  
        salaryBinder = new SalaryBinder();  
    }  
      
    @Override  
    public IBinder onBind(Intent intent) {  
        return salaryBinder;  
    }  
  
      
    //同样是继承Stub,即同时实现ISalary接口和IBinder接口  
    public class SalaryBinder extends Stub  
    {  
        @Override  
        public Salary getMsg(Person owner) throws RemoteException {  
            return ss.get(owner);  
        }  
    }  
      
    @Override  
    public void onDestroy() {  
        System.out.println("服务结束!");  
        super.onDestroy();  
    }  
}  

注册下Service:

<service android:name=".AidlService">  
    <intent-filter>    
        <action android:name="android.intent.action.AIDLService" />  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>    
</service>

2、客户端编写

Step 1:把服务端的AIDL文件拷贝下,拷贝后目录如下:


Step 2:编写简单的布局,再接着就是核心MainActvitiy的实现了 定义一个ServciceConnection对象,重写对应方法,和前面的普通数据的类似 再接着在bindService,然后再Button的点击事件中获取Salary对象并显示出来!
MainActivity.java
public class MainActivity extends Activity {  
  
    private ISalary salaryService;  
    private Button btnquery;  
    private EditText editname;  
    private TextView textshow;  
    private ServiceConnection conn = new ServiceConnection() {  
          
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            salaryService = null;  
        }  
          
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            //返回的是代理对象,要调用这个方法哦!  
            salaryService = ISalary.Stub.asInterface(service);  
        }  
    };  
      
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
          
        btnquery = (Button) findViewById(R.id.btnquery);  
        editname = (EditText) findViewById(R.id.editname);  
        textshow = (TextView) findViewById(R.id.textshow);  
          
        Intent it = new Intent();  
        it.setAction("com.jay.aidl.AIDL_SERVICE");  
        bindService(it, conn, Service.BIND_AUTO_CREATE);  
          
        btnquery.setOnClickListener(new OnClickListener() {           
            @Override  
            public void onClick(View v) {  
                try  
                {  
                    String name = editname.getText().toString();  
                    Salary salary = salaryService.getMsg(new Person(1,name));  
                    textshow.setText(name + salary.toString());  
                }catch(RemoteException e){e.printStackTrace();}  
            }  
        });  
          
    }  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        this.unbindService(conn);  
    }  
} 

运行截图:

2.5 注意事项
  1. 经过测试,上面的例子可以写在一个Demo或者两个Demo里面,无论写在一个或者两个里面,其中AIDL的部分必须复制


    我是写在了两个demo中,测试了很久都没成功,最开始client的IPerson的AIDL的接口是存放在com.project.ankie.aidlclientdemo包下,而不是通过service端复制过来的,所以会出现错误,经查是因为不是在同一个包下所引发的问题!
  2. 参考了一下别的工程,主要是在主工程下的其他的library中添加的服务,然后通过和服务绑定,然后调用AIDL接口进行通信,此举主要是这个AIDL只有一个就可以了啊,因为可以将intent的连接绑定bindService和AIDL接口写在同一个library的包下,就不会产生上面我遇到的错误了,但是两个app之间的通信要么采用上面的复制连包和AIDL,要么采用其他的方式。

  3. Android在6.0之后必须用显示了,所以不能Intent然后再setAction了,需要直接指定包了,由于上面的例子是不同的app,所以采用的是setComponent方法

Intent serviceIntent = new Intent();
// CompontentName 的方法前面是服务的 包名、后面是class的名
serviceIntent.setComponent(new ComponentName("com.project.ankie.aidlservicedemo", "com.project.ankie.aidlservicedemo.AIDLService"));
final boolean b = bindService(serviceIntent, conn, BIND_AUTO_CREATE);
// 可以通过返回值来判断是否绑定成功,也可以在Service的onBind方法那边打log查看
Log.e(TAG, "onCreate: " +  " b: " + b );

3. 总结

  1. 总体思想就是Activity与Service之间的通信,通信依托AIDL的形式进行;
  2. 进行通信的时候,在启动Service的方式就应该是通过Activity绑定服务的形式了,而不是StartService的形式了,因为Service一旦启动之后,可以由其他的Activity来绑定;
  3. 绑定Service的主要方法为bindService(intent,conn, BIND_AUTO_CREATE)方法,其中intent现在只能是显示绑定了,conn是一个new ServiceConnection的回调,最后一个参数是自动绑定,直接写就完了;
  4. 在回调里由返回的IBand的类型的Service,然后通过ISalary.Stub.asInterface(service); 方法,将service转化为AIDL的接口的实例对象,这样就可以调用AIDL中的接口了
  5. ADIL模式只是支持现有的基本的数据模型,若复杂的数据模型需要序列化;
  6. 对于复杂类型模型的传输,也可以通过将复杂模型转化为Json串的类型,然后传输,在接收端将该类型转化为相同过得模型,即可完成序列化和反序列化的形式。
  7. 上文的例子没有验证,也只是对Service的不同情况的分析。还是为具体到Service的实现机制还待研究。

参考文献

  1. Android开发:什么是IBinder

相关文章

网友评论

      本文标题:4.2.3 Service 结丹

      本文链接:https://www.haomeiwen.com/subject/pcukpftx.html