概述
Service,乃Android四大组件之一,除Activity之外最常用的组件,经常配合Activity完成各项工作,是Activity的好基友。但同时,Service也有很多其它的CP,搭配使用干活不累。
内容
1、Service
Service被启动后,可以长时间在后台执行。每个服务类必须有一个相应的申明,在其AndroidManifest文件中。
服务有两种开启的状态:
-
启动状态
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。 -
绑定状态
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
定义一个Service:
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
onCreate: 当服务第一次被创建时,调用此方法。
onBind: 绑定服务时,会被调用,可多次调用,如果客户端不能绑定至服务,将返回空,否则返回IBinder类的实体。
onStartCommand: 当客户端使用startService开始服务时,会调用此方法,其返回参数表示当前开始请求的状体。
参数:
Intent:由startService给予,可以携带参数。
flags:请求开始的附加参数。可选值为:0,START_FLAG_REDELIVERY,START_FLAG_RETRY,0代表没有,它们具体含义如下:
>START_FLAG_REDELIVERY
这个值代表了onStartCommand方法的返回值为
START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。其中START_REDELIVER_INTENT意味着当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),此时Intent时有值的。
START_FLAG_RETRY
该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。
startId:开始特殊请求的参数。指明当前服务的唯一ID,与stopSelfResult (int startId)配合使用,stopSelfResult 可以更安全地根据ID停止服务。
实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值, START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义如下:
START_STICKY
当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
START_NOT_STICKY
当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
START_REDELIVER_INTENT
当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
onDestroy:
系统调用此方法,通知服务器再也不调用,应该清空回收资源,当其返回,就不会再被调用,然后服务就撒手人寰了。
在AndroidManifest文件中注册:
...
<service android:name=".server.MyService"
android:exported="true"
android:icon="@color/colorAccent"
android:isolatedProcess="true"
android:label="string source"
android:permission="string"
android:process=":remote">
<intent-filter android:icon="@mipmap/ic_launcher">
</intent-filter>
</service>
...
</application>
</manifest>
其中:
- name:Service类名
- exported:代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法被其他应用隐式调用。
- permission:权限申明。
- process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
- isolatedProcess:设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
- enabled:是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。
startService方式:
Intent intent = new Intent(this, MyService.class);
startService(intent);
随后,系统就会唤醒服务,如果是首次启动服务,则会调用其onCreate()方法,然后调用onStartCommand()方法,以接收客户端的消息。service会持续运行直至stopService()/stopSelf()被调用。
bindService方式:
public class MyService extends Service {
private MyBinder myBinder = new MyBinder();
public class MyBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return myBinder;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
上面的服务是一个可以bind的服务,onBind方法返回了一个自定义的Binder类型变量myBinder,用于返回服务本身。
public class TestMyServiceAct extends Activity {
private ServiceConnection mServiceConnection = new MyServiceConn();
private MyService.MyBinder myBinder;
private MyService myService;
private boolean mBound;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
private class MyServiceConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myService = myBinder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
}
private void executeUnbindService() {
if (mBound) {
//解绑服务
unbindService(mServiceConnection);
mBound = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
executeUnbindService();
}
}
通过ServiceConnection 监听器,可以得到服务的mybinder对象,从而获得服务本身的实例对象,那么就可以调用服务中的一些方法、变量等。
ServiceConnection是一个监听器接口,其中:
onServiceConnected(ComponentName name, IBinder service) :
可以回调onBind() 返回的IBinder变量,name参数是封装了组件的变量,如包名,组件描述等信息,较少使用该参数。
onServiceDisconnected(ComponentName name) :
Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。注意:当客户端取消绑定时,系统“绝对不会”调用该方法。
还有一个onBindingDied(ComponentName name):
这个方法可以不重写,当绑定到此连接挂掉时调用,这就意味着这个监听器不再有用,需要解绑服务并重新绑定激活。
当第一次绑定服务时,会调用onCreate,接着调用onBind,然后客户端的ServiceConnection#onServiceConnected()会被调用。一个组件多次调用bindService,服务的onBind方法只会在第一次被调用,解绑服务时会调用onUnBind,然后调用onDestroy,不管绑定多少次,解绑,一次就好。
2、BroadcastReceiver
BroadcastReceiver即广播接收器,是一个全局的监听器,Android四大组件之一,接收并处理sendBroadcast(Intent i)方法发送的Intend对象。分为广播发送者、广播接收者两个角色。
应用场景:
- 不同组件间的通信,同应用和不同应用之间。
- 多线程之间的通信。
- 与Android系统进程间的通信,电话。
广播用的是观察者模式,即发布者-消息中心-订阅者。
订阅者(即广播接收者)注册广播,它有动态注册和静态注册两种注册方式。
发布者(广播发送者),发送广播,通过消息中心(一个消息循环队列)处理,然后发送广播给订阅者。广播接收者得到消息后,通过onReceive回调。
Service和BroadcastReceiver
友情:★★★★★
爱情:★★★★★
婚姻:★★★★
亲情:★★★★
这两者一起配合的场景很多,比如一个构建音乐播放器:
简单音乐播放器
###3、Messenger
服务器与远程进程的通信,最简单的方式即Messenger。
Messenger作为进程间传递消息的一个封装。
####Service与Messenger
友情:★★★★★
爱情:★★★★★
婚姻:★★★★
亲情:★★★
原理图:
用于处理消息的服务:
/**
* 以下是 Messenger 使用的主要步骤:
* 1.服务实现一个 Handler,由其接收来自客户端的每个调用的回调
* 2.Handler 用于创建 Messenger 对象(对 Handler 的引用)
* 3.Messenger 创建一个 IBinder,服务通过 onBind() 使其返回客户端
* 4.客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用Messenger将 Message 对象发送给服务
* 5.服务在其 Handler 中(在 handleMessage() 方法中)接收每个 Message
*/
public class MessengerService extends Service {
static final int MSG_SAY_HELLO = 1;
private static final String TAG = "cwb";
class InComingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
//SAY_HELLO
Messenger client = msg.replyTo;
Message replyMsg = Message.obtain(null, MessengerService.MSG_SAY_HELLO);
Bundle bundle = new Bundle();
bundle.putString("reply", "I had received message from you!");
replyMsg.setData(bundle);
try {
client.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new InComingHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
用于处理消息的活动:
public class ActivityMessenger extends Activity {
Messenger mService = null;
boolean mBound;
private Messenger mReceiverReplyMsg = new Messenger(new ReceiverReplyMsgHandler());
private static class ReceiverReplyMsgHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_SAY_HELLO:
//receiver msg from service
break;
default:
super.handleMessage(msg);
}
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button btnBindService = null;
Button btnUnbindService = null;
Button btnSendMsg = null;
btnBindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(ActivityMessenger.this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
});
btnSendMsg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sayHello(v);
}
});
btnUnbindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
});
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
msg.replyTo = mReceiverReplyMsg;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
上面代码可知,服务器和客户端各持有一个Messenger对象,messenger对象各自映射一个handler,这个handler是用于处理消息的对象,用于传递给另一进程收发消息。比如,客户端绑定服务后,服务端会通过onBind返回一个IBinder:
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
客户端得到这个IBinder对象,封装成一个Messenger对象:
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);//获取Messenger
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};
客户端用从服务端获取的messenger,携带消息Message对象(msg包裹客户端自己的的Messenger),传递给服务端:
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
msg.replyTo = mReceiverReplyMsg;
try {
mService.send(msg);//发送消息
} catch (RemoteException e) {
e.printStackTrace();
}
在服务端得到messenger,可以用它跟客户端发消息:
Messenger client = msg.replyTo;//获得client的messenger
Message replyMsg = Message.obtain(null, MessengerService.MSG_SAY_HELLO);
Bundle bundle = new Bundle();
bundle.putString("reply", "I had received message from you!");
replyMsg.setData(bundle);
try {
client.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
然后客户端会在自己的handler中获取并处理来自服务端的消息:
private static class ReceiverReplyMsgHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_SAY_HELLO:
//receiver msg from service
break;
default:
super.handleMessage(msg);
}
}
}
看啊,真是不离不弃的一对儿。
3、AIDL
AIDL,即Android接口描述语言,是Android提供的一种进程间通讯机制(IPC)。
AIDL的使用
- 创建AIDL
1、创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化
2、新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
3、Make project ,生成与aidl文件对应的 Binder 的 Java 文件
- 服务端
创建Service,生成Binder实例对象,实现接口定义的方法;Binder实例对象在onBind中返回。
- 客户端
1、实现 ServiceConnection 接口,在其中拿到 AIDL 类
2、bindService()绑定服务
3、调用 AIDL 类中定义好的操作请求
Service与AIDL
友情:★★★★
爱情:★★★★
婚姻:★★★★★
亲情:★★★★★
AIDL的通信离不开Service,离不开Binder机制,是的,AIDL对Service的感情,已经成为一种依赖。
看一下具体的代码实现及操作:
我的目录:
正常情况下,是不应该把java文件放在aidl的目录下,但是便于移植,所以放在aidl目录,但是仅仅是这样的话make project编译是会报错的,会提示找不到GirlFriend.java这个文件,因为编译时,java文件时默认在java目录下找,还需要在gradle中声明:
//把java代码的访问路径设置为java、aidl包
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
1、构建操作类实体
public class GirlFriend implements Parcelable {
private String name;
private int type;
public static final int TYPE_BEAUTY = 0x20;
public static final int TYPE_CUTE = 0x21;
public static final int TYPE_SEXY = 0x22;
public GirlFriend(String name, int type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getType() {
return type;
}
public String getTypeDesc() {
if (type == TYPE_BEAUTY) {
return "BEAUTY";
}
else if (type == TYPE_CUTE) {
return "CUTE";
}
else if (type == TYPE_SEXY) {
return "SEXY";
}
else {
return "NULL";
}
}
public void setType(int type) {
this.type = type;
}
protected GirlFriend(Parcel in) {
this.name = in.readString();
this.type = in.readInt();
}
public static final Creator<GirlFriend> CREATOR = new Creator<GirlFriend>() {
@Override
public GirlFriend createFromParcel(Parcel in) {
return new GirlFriend(in);
}
@Override
public GirlFriend[] newArray(int size) {
return new GirlFriend[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(type);
}
@Override
public String toString() {
return "GirlFriend{" + "name:" + this.name + ",type:" + getTypeDesc() + "}";
}
}
2、创建与操作类实体对应的aidl文件GirlFriend.aidl,该文件必须和GirlFriend.java在同一目录下:
// GirlFriend.aidl
package com.example.chuwenbin.aidldemo.bean;
// Declare any non-default types here with import statements
parcelable GirlFriend;//和声明的java实体类在一个包里
3、创建接口aidl文件:
// IMyAidlInterface.aidl
package com.example.chuwenbin.aidldemo;
// Declare any non-default types here with import statements
import com.example.chuwenbin.aidldemo.bean.GirlFriend;
interface IMyAidlInterface {
/**
* 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)
*/
void makeGirlFriend(in GirlFriend gf);
GirlFriend getGirlFriend();
}
in 表示数据只能由客户端流向服务端;
out 表示数据只能由服务端流向客户端;
inout 则表示数据可在服务端与客户端之间双向流通
4、build->make project
生成对应java文件:
5、创建一个服务类
public class AIDLService extends Service {
public final String TAG = getClass().getSimpleName();
private List<GirlFriend> mGFs;
//make project生成的IMyAidlInterface.java文件在这里被用到
private IBinder mIBinder = new IMyAidlInterface.Stub() {
@Override
public void makeGirlFriend(GirlFriend gf) throws RemoteException {
Log.d(TAG, "make girl friend:" + gf);
mGFs.add(gf);
}
@Override
public GirlFriend getGirlFriend() throws RemoteException {
return mGFs.get(mGFs.size() - 1);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
mGFs = new ArrayList<>();
Log.d(TAG, "onBind");
return mIBinder;
}
}
6、客户端
public class ClientActivity extends Activity {
private Button mBtnMkGF;
private final String TAG = getClass().getSimpleName();
private IMyAidlInterface mAidlInterface;
private String[] gfNames = {"lily", "marry", "randy"};
private int[] gfTypes = {GirlFriend.TYPE_BEAUTY, GirlFriend.TYPE_CUTE, GirlFriend.TYPE_SEXY};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected");
mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mAidlInterface = null;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate");
Intent intent = new Intent(ClientActivity.this, AIDLService.class);
bindService(intent, mConnection, BIND_AUTO_CREATE);
mBtnMkGF = findViewById(R.id.btn_mk_gf);
mBtnMkGF.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Random random = new Random();
int index = Math.abs(random.nextInt()) % 3;
makeGF(gfNames[index], gfTypes[index]);
}
});
}
public void makeGF(String name, int type) {
GirlFriend girlFriend = new GirlFriend(name, type);
if (mAidlInterface == null) {
return;
}
try {
Log.d(TAG, "make gf:" + girlFriend.toString());
mAidlInterface.makeGirlFriend(girlFriend);
Log.d(TAG, "recent gf:" + mAidlInterface.getGirlFriend().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
运行结果
多次点击按钮,多次交女朋友,logcat输出如下:
每一次调用交女朋友的方法后,服务进程中该女友对象会被一个list集合存起来,当获取当前女友时,会得到你最近交的那个女朋友。这说明,交女朋友,一次当然只能交往一个啦!作为一个程序员,交不到女朋友怎么办呢?当然是多写代码,在程序里面找女朋友,各自类型的都有。
总结
下一篇要详细写AIDL,写个实战性的样例。