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的一个方法,它是抽象的。函数原型的代码如下:2>而最终实现bindService()方法的是ContextImpl这个类,代码如下:public abstract boolean bindService(Intent service, ServiceConnection conn, int flags);
这里先不讨论是如何实现bindService的,先来说一下他的三个参数@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; } }
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>步)
2)接下来,新建一个Person.aidl文件,代码很简单,就是指定一下package,定义这个类型(如4>步)Person getPeson(); //自定义数据类型,获得Cat的主人
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
* 优快云 Blog: http://blog.youkuaiyun.com/allan_bst
* Personal Website: http://mobiledream.top
* Personal Sina E-mail: jsp0369@163.com
*/
//业务接口的具体实现类
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
* 优快云 Blog: http://blog.youkuaiyun.com/allan_bst
* Personal Website: http://mobiledream.top
* Personal Sina E-mail: jsp0369@163.com
*/
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接口:实现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); } }
1)implements Parcelable
2)重写writeToParcel方法,将你的对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从 Parcel容器获取数据
3)重写describeContents方法,内容接口描述,默认返回0就可以
4)实例化静态内部对象CREATOR实现接口Parcelable.Creator
好了,就补充到这儿了,如果有什么疑惑的,可以给我留言