Android基础新手教程——4.2.3 Service精通

Android基础新手教程——4.2.3 Service精通

标签(空格分隔): Android基础新手教程


本节引言:

本节,我们继续来研究Service(服务)组件,本节将会学习下Android中的AIDL跨进程通信的一些

概念,并不深入到源代码层次。临时知道是什么。会用就可以。開始本节内容~

本节相应官方文档:Binder


1.Binder机制初涉


1)IBinder和Binder是什么鬼?

我们来看看官方文档怎么说:

中文翻译:

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代表的进程退出时被调用。

PS:中文翻译摘自 : Android开发:什么是IBinder

好吧,预计你看完上这一串东西可能云里雾里的,这里简单的小结下:

IBinder是Android给我们提供的一个进程间通信的一个接口,而我们通常是不直接实现这个接口的,

而是通过继承Binder类来实现进程间通信!是Android中实现IPC(进程间通信)的一种方式!


2)Binder机制浅析

Android中的Binder机制由一系列系统组件构成:

Client、Server、Service Manager和Binder驱动程序

大概调用流程例如以下。另外Service Manager比較复杂,这里并不具体研究!

流程解析:

-> Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的參数打包成Parcel对象;

-> 然后代理接口把该Parcel对象发送给内核中的Binder driver;

-> 然后Server会读取Binder Driver中的请求数据,假如是发送给自己的。解包Parcel对象,

处理并将结果返回。

PS:代理接口中的定义的方法和Server中定义的方法是一一相应的,

另外,整个调用过程是一个同步的,即Server在处理时。Client会被Block(锁)住!

而这里说的代理接口的定义就是等下要说的AIDL(Android接口描写叙述语言)!


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。这样不可靠,easy被恶意程序利用。而我们要求由内核来加入可靠的UID。

    所以。出于可靠性、传输性、安全性。

    android建立了一套新的进程间通信方式。

    ——摘自:Android中的Binder机制的简要理解

当然。作为一个0基础的开发人员我们并不关心上述这些,Binder机制给我们带来的最直接的优点就是:

我们无需关心底层怎样实现。仅仅需依照AIDL的规则。自己定义一个接口文件,

然后调用调用接口中的方法,就能够完毕两个进程间的通信了。


2.AIDL使用具体解释


1)AIDL是什么?

嘿嘿。前面我们讲到IPC这个名词。他的全名叫做:跨进程通信(interprocess communication)

由于在Android系统中,个个应用程序都执行在自己的进程中,进程之间通常是无法直接进行数据交换的,

而为了实现跨进程,Android给我们提供了上面说的Binder机制,而这个机制使用的接口语言就是:

AIDL(Android Interface Definition Language)。他的语法非常easy。而这样的接口语言并不是真正的编程

语言,仅仅是定义两个进程间的通信接口而已!而生成符合通信协议的Java代码则是由Android SDK的

platform-tools文件夹下的aidl.exe工具生成,生成相应的接口文件在:gen文件夹下。通常是:Xxx.java的接口!

而在该接口中包含一个Stub的内部类,该类中实现了在该类中实现了IBinder接口与自己定义的通信接口,

这个类将会作为远程Service的回调类——实现了IBinder接口,所以可作为Service的onBind( )方法的返回值。


2)AIDL实现两个进程间的简单通信

在開始编写AIDL接口文件前。我们须要了解下编写AIDL的一些注意事项:

AIDL注意事项:

  • 接口名词须要与aidl文件名称相同
  • 接口和方法前面不要加訪问权限修饰符:public ,private,protected等。也不能用static final!
  • AIDL默认支持的类型包含Java基本类型StringListMapCharSequence,除此之外的其它类型都

    须要import声明,对于使用自己定义类型作为參数或者返回值,自己定义类型须要实现Parcelable接口,

    详情请看后面的传递复杂数据类型

  • 自己定义类型和AIDL生成的其它接口类型在aidl描写叙述文件里,应该显式import。即便在该类和定义

    的包在同一个包中。

另外,假设编写aidl你用的编译器是:Eclipse的话要注意:

不要直接new file然后建立哦!这样的话是打不开文件,从而不能编写代码哦。

①直接新建一个txt文件,编写好后保存为.aidl格式,然后复制到相应路径下

②由于aidl和接口相似,所以直接new interface,编写好内容后,来到相应java文件所在文件夹下改动文件后缀名;

假如你使用的是Android Studio的话,不同于Eclipse,假设你依照Eclipse那样创建一个AIDL文件。会发现

并没有编译生成相应的XXX.java文件,AS下创建AIDL须要在main文件夹下新建一个aidl文件夹,然后定义一个

和aidl包名相同的包,最后创建一个aidl文件,接着按ctrl + f9又一次编译,就能够了!

上面两者成功编译的结果例如以下。你能够分别在相应文件夹下找到相应的AIDL文件



1.服务端:

Step 1:创建AIDL文件:

IPerson.aidl

package com.jay.aidl;

interface IPerson {
    String queryPerson(int num);
}

我们打开IPerson.java看看里面的代码:

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()方法!

该方法会把IBinder类型的对象转换成IPerson类型的,必要时生成一个代理对象返回结果!

其它的我们能够不看。直接跳过,进行下一步~



Step 2:自己定义我们的Service类,完毕下述操作:

1)继承Service类,同一时候也自己定义了一个PersonQueryBinder类用来继承IPerson.Stub类

就是实现了IPerson接口和IBinder接口

2)实例化自己定义的Stub类,并重写**Service的**onBind方法,返回一个binder对象!

AIDLService.java

package com.jay.aidlserver;

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 null;
    }

    private final class PersonQueryBinder extends Stub{
        @Override
        public String queryPerson(int num) throws RemoteException {
            return query(num);
        }
    }
}

Step 3:在AndroidManifest.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>

这里我们并没有提供Activity界面。可是改应用提供的Service能够供其它app来调用!



2.client

直接把服务端的那个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);

再接着完毕初始化,以及button事件等就能够了

具体代码例如以下:

MainActivity.java

package com.jay.aidlclient;

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.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.jay.aidl.IPerson;

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
        Intent service = new Intent("android.intent.action.AIDLService");
        service.setPackage("com.jay.aidlserver");

        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,也会获得相同效果:

效果图例如以下:


3)传递复杂数据的AIDL Service

上面的样例我们传递的仅仅是要给int类型的參数,然后服务端返回一个String类型的參数。看似满足

我们的基本需求,只是实际开发中。我们可能须要考虑传递复杂数据类型的情况!以下我们来学习下

怎样向服务端传递复杂数据类型的数据!開始之前我们先来了解Parcelable接口

——Parcelable接口简单介绍:

相信用过序列化的基本上都知道这个接口了。除了他还有另外一个Serializable,相同是用于序列化的,

仅仅是Parcelable更加轻量级,速度更快。可是写起来就有点麻烦了,当然假设你用的as的话能够用

的插件来完毕序列化,比方:Android Parcelable Code Generator

当然。这里我们还是手把手教大家来实现这个接口~

首先须要实现:writeToParcelreadFromPacel方法

写入方法将对象写入到包裹(parcel)中,而读取方法则从包裹中读取对象,

请注意,写入属性顺序需与读取顺序相同

接着须要在:该类中加入一个名为CREATORstatic final属性

改属性须要实现:android.os.Parcelable.Creator接口

再接着须要从写接口中的两个方法:

createFromParcel(Parcel source)方法:实现从source创建出JavaBean实例的功能

newArray(int size):创建一个类型为T,长度为size的数组,仅仅有一个简单的return new T[size]; (这里的T是Person类)

最后,describeContents():这个我也不知道是拿来干嘛的,直接返回0就可以。不用理他

——另外非原始类型中,除了StringCharSequence以外。其余均须要一个方向指示符

方向指示符包含

inout和inout。in表示由client设置,out表示由服务端设置。inout表示client和服务端都设置了该值。



好的。接着来写代码试试(AS这里自己定义类型有点问题,临时还没解决,就用回Eclipse~):

代码演示样例:

自己定义两种对象类型:Person与Salary,Person作为调用远程的Service的參数,Salary作为返回值!

那么首先要做的就是创建Person与Salary类,同一时候须要实现Parcelable接口

1.——服务端

Step 1:创建Person.aidl和Salary.aidl的文件,由于他们须要实现Parcelable接口,所以就以下一条语句:

Person.aidl:     parcelable Person;
Salary.aidl:     parcelable Salary;  

Step 2:分别建立Person类与Salary类,需实现Parcelable接口,重写相应的方法!

PS:由于我们后面是依据Person对象来获取Map集合中的数据,所以Person.java中我们重写了hashcode和equals

的方法;而Salary类则不须要!

Person.java:

package com.jay.example.aidl; 

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

/**
 * Created by Jay on 2015/8/18 0018.
 */
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.java~照葫芦画瓢

package com.jay.example.aidl; 

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

/**
 * Created by Jay on 2015/8/18 0018.
 */
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;
    }
}

Step 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

package com.jay.example.aidl_complexservice;  

import java.util.HashMap;
import java.util.Map;
import com.jay.example.aidl.ISalary.Stub;
import com.jay.example.aidl.Person;
import com.jay.example.aidl.Salary;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;  

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——client编写

Step 1:把服务端的AIDL文件拷贝下。拷贝后文件夹例如以下:

Step 2:编写简单的布局,再接着就是核心MainActvitiy的实现了

定义一个ServciceConnection对象,重写相应方法,和前面的普通数据的相似

再接着在bindService,然后再Button的点击事件中获取Salary对象并显示出来!

MainActivity.java

package com.jay.example.aidl_complexclient;  

import com.jay.example.aidl.ISalary;
import com.jay.example.aidl.Person;
import com.jay.example.aidl.Salary;  

import android.app.Activity;
import android.app.Service;
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.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;  

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);
    }  

}  

执行截图:

PS: 这里的代码是之前用Eclipse写的代码,Android Studio下自己定义类型有点问题,

临时没找到解决方法。假设知道的朋友请告知下!!!万分感激。!!

出现的问题例如以下:

两个实例的代码下载(基于Eclipse的):

1)使用AIDL完毕进程间的简单通信

2)传递复杂数据的AIDL Service的实现


3.直接通过Binder的onTransact完毕跨进程通信

上面讲过Android能够通过Binder的onTrensact方法来完毕通信。以下就来简单试下下,还是前面那个依据

序号查询名字的样例:

服务端实现

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

    private static final String DESCRIPTOR = "IPCService";
    private final String[] names = {"B神","艹神","基神","J神","翔神"};
    private MyBinder mBinder = new MyBinder();

    private class MyBinder extends Binder {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code){
                case 0x001: {
                    data.enforceInterface(DESCRIPTOR);
                    int num = data.readInt();
                    reply.writeNoException();
                    reply.writeString(names[num]);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    }

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

client实现

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private EditText edit_num;
    private Button btn_query;
    private TextView txt_result;
    private IBinder mIBinder;
    private ServiceConnection PersonConnection  = new ServiceConnection()
    {
        @Override
        public void onServiceDisconnected(ComponentName name)
        {
            mIBinder = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {
            mIBinder = service;
        }
    };

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

        //绑定远程Service
        Intent service = new Intent("android.intent.action.IPCService");
        service.setPackage("com.jay.ipcserver");
        bindService(service, PersonConnection, 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_result = (TextView) findViewById(R.id.txt_result);
    }

    @Override
    public void onClick(View v) {
        int num = Integer.parseInt(edit_num.getText().toString());
        if (mIBinder == null)
        {
            Toast.makeText(this, "未连接服务端或服务端被异常杀死", Toast.LENGTH_SHORT).show();
        } else {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            String _result = null;
            try{
                _data.writeInterfaceToken("IPCService");
                _data.writeInt(num);
                mIBinder.transact(0x001, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readString();
                txt_result.setText(_result);
                edit_num.setText("");
            }catch (RemoteException e)
            {
                e.printStackTrace();
            } finally
            {
                _reply.recycle();
                _data.recycle();
            }
        }
    }
}

执行截图:

代码比較简单,就不多解释了~用到自己改改就可以!

PS:代码參考于:Android aidl Binder框架浅析


4.Android 5.0后Service一些要注意的地方:

今天在隐式启动Service的时候,遇到这样一个问题

然后程序一启动就崩了,后来苦扣良久才发下是Android 5.0惹的祸,

原来5.0后有个新的特性,就是:

Service Intent must be explitict!

好吧,就是不能隐式去启动Service咯,解决办法也非常easy!

比方StartService的:

startService(new Intent(getApplicationContext(), “com.aaa.xxxserver”));

这样敲代码直接crash掉,要写成以下这样:

startService(new Intent(getApplicationContext(), LoadContactsService.class));

假设是BindService的:

Intent service = new Intent(“android.intent.action.AIDLService”);

的基础上,要加上包名:

service.setPackage(“com.jay.ipcserver”);

这样就能够了~

官方文档:http://developer.android.com/intl/zh-cn/guide/components/intents-filters.html#Types

文档说明处:


本节小结:

好的。关于Service的最后一节就到这里,本节解说了Binder的基本概念以及实现进程间通信的

两种方式:通过AIDL以及Binder.onTransact()来实现跨进程通信!最后还解说了下Android 5.0后

使用Service不能隐式启动的注意事项。就到这里,谢谢~

时间: 2024-10-10 05:42:45

Android基础新手教程——4.2.3 Service精通的相关文章

Android基础入门教程——4.2.3 Service精通

Android基础入门教程--4.2.3 Service精通 标签(空格分隔): Android基础入门教程 本节引言: 本节,我们继续来研究Service(服务)组件,本节将会学习下Android中的AIDL跨进程通信的一些 概念,并不深入到源码层次,暂时知道是什么,会用即可!开始本节内容~ 本节对应官方文档:Binder 1.Binder机制初涉 1)IBinder和Binder是什么鬼? 我们来看看官方文档怎么说: 中文翻译: IBinder是远程对象的基本接口,是饿了高性能而设计的轻量级

Android基础入门教程——4.2.1 Service初涉

Android基础入门教程--4.2.1 Service初涉 标签(空格分隔): Android基础入门教程 本节引言 好的,我们在前三节中对Android中的Activity进行了研究学习,相信大家获益良多吧! 本节开始我们继续来学习Android中的第二个组件:Service(服务), 好,废话不多说,开始本节内容! 1.线程的相关概念 在开始学习Service之前我们先来了解下线程的一些概念! 1)相关概念: 程序:为了完成特定任务,用某种语言编写的一组指令集合(一组静态代码) 进程:运行

Android基础新手教程——1.10 反编译APK获代替码&amp;amp;资源

Android基础新手教程--1.10 反编译APK获代替码&资源 标签(空格分隔): Android基础新手教程 本节引言: "反编译Apk".看上去好像好像非常高端的样子,事实上不然,就是通过某些反编译软件.对我们的APK进行反编译,从而获取程序的源代码,图片,XML资源等文件.不知道你有没有这样做过,看到一个别人的一个APP界面做得非常精美,或者你看上别人的图片素材,简单点的,我们能够下载别人的APK.然后改下后缀名,改成xxx.zip.然后解压: 笔者随便解压了一个AP

Android基础新手教程——3.8 Gestures(手势)

Android基础新手教程--3.8 Gesture(手势) 标签(空格分隔): Android基础新手教程 本节引言: 周六不歇息,刚剪完了个大平头回来.继续码字~ 好的,本节给大家带来点的是第三章的最后一节--Gesture(手势), 用过魅族手机的朋友相信对手势肯定是不陌生的.在home键两側像屏幕内滑动, 能够打开后台任务列表等等~在应用中通过手势来操作会大大提升用户体验. 比方Scroll手势在浏览器中个滚屏,Fling在浏览器中的换页等! 当然,有利也有弊,比方不当的手势操作引起AP

Android基础新手教程——3.7 AnsyncTask异步任务

Android基础新手教程--3.7 AnsyncTask异步任务 标签(空格分隔): Android基础新手教程 本节引言: 本节给大家带来的是Android给我们提供的一个轻量级的用于处理异步任务的类:AsyncTask.我们通常是 继承AsyncTask,然后在类中实现异步操作,然后将异步运行的进度.反馈给UI主线程~ 好吧,可能有些概念大家不懂,认为还是有必要解说下多线程的概念,那就先解释下一些概念性的东西吧! 1.相关概念 1)什么是多线程: 答:先要了解这几个名称:应用程序,进程,线

Android基础入门教程——4.2.2 Service进阶

Android基础入门教程--4.2.2 Service进阶 标签(空格分隔): Android基础入门教程 本节引言 上节我们学习了Service的生命周期,以及两种启动Service的两种方法, 本节继续来深入了解Service中的IntentService,Service的使用实例: 前台服务与轮询的实现! 1.IntentService的使用 在上一节后我们已经知道了如何去定义和启动Service,但是如果我们直接把 耗时线程放到Service中的onStart()方法中,虽然可以这样做

Android基础新手教程——4.3.2 BroadcastReceiver庖丁解牛

Android基础新手教程--4.3.2 BroadcastReceiver庖丁解牛 标签(空格分隔): Android基础新手教程 本节引言: 上节我们对BroadcastReceiver已经有了一个初步的了解了,知道两种广播类型:标准与有序, 动态或静态注冊广播接收者,监听系统广播,自己发送广播.已经满足我们的基本需求了~ 可是前面写的广播都是全局广播! 这相同意味着我们APP发出的广播,其他APP都会接收到, 或者其他APP发送的广播,我们的APP也相同会接收到,这样easy引起一些安全性

Android基础新手教程——1.2.1 使用Eclipse + ADT + SDK开发Android APP

Android基础新手教程--1.2.1 使用Eclipse + ADT + SDK开发Android APP 标签(空格分隔): Android基础新手教程 1.前言 这里我们有两条路能够选,直接使用封装好的用于开发Android的ADT Bundle,或者自己进行配置 由于谷歌已经放弃了ADT的更新,官网上也取消的下载链接.这里提供谷歌放弃更新前最新版本号的 ADT Bundle供大家下载! 2.直接使用打包好的Eclipse 32位版:adt-bundle-windows-x86-2014

2015年最新Android基础入门教程目录(完结版)

2015年最新Android基础入门教程目录(完结版) 标签(空格分隔): Android基础入门教程 前言: 关于<2015年最新Android基础入门教程目录>终于在今天落下了帷幕,全套教程 共148节已编写完毕,附上目录,关于教程的由来,笔者的情况和自学心得,资源分享 以及一些疑问等可戳:<2015最新Android基础入门教程>完结散花~ 下面是本系列教程的完整目录: 第一章:环境搭建与开发相关(已完结 10/10) Android基础入门教程--1.1 背景相关与系统架构