Service作为安卓的四大组件之一,适合长期在后台运行而不需要用户界面的操作,Service默认运行在UI线程中,因此Service中同样不适合做耗时操作,如果需要做耗时操作需要开启子线程。
常用Service的开启包括start和bind两种启动方式。
- 创建自定义Service
public class ServiceTest extends Service {
/**
* 继承Service时必须要实现的方法
*bind调用时,获取Intent传递的参数,或执行绑定后的操作
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Service首次运行时会执行该方法
* 如果Service已经启动则不会调用(在onStartCommand()和onBind()之前调用)
*/
@Override
public void onCreate() {
super.onCreate();
}
/**
* 通过start调用时执行该方法,每调用一次,执行一次该方法
* @param intent
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
/**
* Service方法销毁时,执行该方法
*/
@Override
public void onDestroy() {
super.onDestroy();
}
}
以上演示代码的注释已很详细的讲解Service每个方法的含义,自定义Service其实很简单,只需要继承Service方法即可,然后根据业务需要重写在onStartCommand()或onBind()执行相应的操作。下面简述每个方法的含义。
- onCreate():Service启动前调用的方法,在在onStartCommand()和onBind()之前调用,处理一些初始化的操作;
- onBind():由于该方法在Service设置为abstract,因此继承Service必须实现的方法,同时也是通过bind调用时的回调方法;
- onStartCommand():如果通过start方式启动,必须重写该方法,在该回调方法中处理相应的业务逻辑,每次调用都会执行该方法;
- onDestroy():Service销毁时执行该方法,可处理一些资源关闭或释放的操作。
2 注册Service
作为四大组件之一,同样在使用Service的使用,需要继承Service类,重写onBind方法,并在AndroidManifest.xml中注册新创建的Servcie对象。
<service android:name=".ServiceTest"
android:enabled="true"
android:exported="true"
android:isolatedProcess="true"
android:process=":procressName"/>
- name:表示新创建的Service的类名称;
- enabled:是否被实例化,默认为true;
- exported:表示该Service是否支持隐式调用,其默认值是由Service中是否包含intent-filter决定,如果包含intent-filter则默认为true,否则为false,即使设置为true也无法隐式调用,因为如果不添加intent-filter,当通过Intent调用时,安卓就无法知道具体那个控件(Service/Activity/Broadcast Receiver)来响应该请求;
- isolatedProcess:如果设置为true,服务将会在特殊的进程中运行(独立应用的进程),通过start或bind与其进行数据通信;
- process:进程设置,是否需要在单独的进程中运行,在设置该参数时需注意,如果设置为“:procressName”和“procressName”,前者表示“App-package:procressName”而后者表示“procressName”进程,两者表示不同的进程。
3 Service启动
Service的启动包括两种方式,一种是通过start方式,还有一种是bind方式,下面具体介绍该两种方式的不同含义。
3.1 Bind方式
使用流程如下
- 创建自定义Servcie类,重写onBind()对象,返回自定义的Binder类并传入自定义的Service对象;
- 创建自定义Binder类,编写针对Service的处理方法;
- 在bindServcie时,传入ServiceConnection对象,获取是否连接状态。
创建自定义的Service类,编写绑定成功后的方法类。
public class ServiceTest extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MyBinder(this); //返回自定义的Binder类
}
/**
* Service的处理方法
* @param str
*/
public void serviceMethod(String str){
Log.i(TAG, "setMethod: str "+str);
}
创建自定义Binder类,编写针对Service的处理方发
public class MyBinder extends Binder {
private static final String TAG = "MyBinder";
private ServiceTest mServiceTest;
public MyBinder(ServiceTest serviceTest) {
mServiceTest = serviceTest;
}
//Binder操作后的方法
public void testMethod(String str){
Log.i(TAG, "testMethod: "+"XIAOHAN "+str);
mServiceTest.serviceMethod(str);
mMyBinderInterface.binderCall(str+"recall");
}
private MyBinderInterface mMyBinderInterface;
public void setMyBinderInterface(MyBinderInterface myBinderInterface) {
mMyBinderInterface = myBinderInterface;
}
public interface MyBinderInterface {
void binderCall(String msg);
}
}
实际使用时,bindService时,需要传入一个ServiceConnection参数,在onServiceConnected可以获取到自定义的Binder类对象,通过该对象可以获取自定义Binder类中的回调处理方法,拿到自定义的Binder类后,说明bind成功,可通过binder对象,传入参数,间接调用自定义的Service中的处理方法。
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyBinder = (MyBinder) service;
mMyBinder.setMyBinderInterface(new MyBinder.MyBinderInterface() {
@Override
public void binderCall(String msg) {
Log.i(TAG, "XIAOHAN binderCall: "+msg);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent = new Intent(MainActivity.this,ServiceTest.class);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "XIAOHAN onClick: "+mMyBinder);
mMyBinder.testMethod("hangzhou");
}
});
执行流程打印日志如下:
02-24 02:13:53.165 4172-4172/com.example.xiaohan.test22 I/MainActivity: onCreate: XIAOHAN serviceConnection
02-24 02:13:53.175 4172-4172/com.example.xiaohan.test22 I/MainActivity: onCreate: XIAOHAN bindService
02-24 02:13:53.205 4172-4172/com.example.xiaohan.test22 I/ServiceTest: XIAOHAN onBind:
02-24 02:13:53.205 4172-4172/com.example.xiaohan.test22 I/MyBinder: XIAOHAN MyBinder:
执行顺序:创建serviceConnection对象–bindService—调用onBind方法----创建MyBinder对象
当点击onclick时,即执行mMyBinder.testMethod(“hangzhou”)时的执行日志如下
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MainActivity: XIAOHAN onClick: com.example.xk.test22.MyBinder@4a77e7f4
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MyBinder: testMethod: XIAOHAN hangzhou
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/ServiceTest: XIAOHAN setMethod: hangzhou
02-24 02:16:20.115 4172-4172/com.example.xiaohan.test22 I/MainActivity: XIAOHAN binderCall: hangzhourecall
执行顺序:自定义Biner的执行方法–Service的绑定处理方法—ServiceConnection的onServiceConnected()回调
注意:如果在使用bind调用Service时,如果在AndroidManifest.xml中对绑定的组件(activity或servcie)设置了进程名,但未设置自定义的Service的相同的进程,则会报如下错误:
java.lang.ClassCastException:android.os.BinderProxy
处理的方法见该文章。
3.2 Start方式
可通过其他组件(如Activity)调用startServce方法来开启Service,每次调用一次start方法,都会回调一次 onStartCommand()方法,但只要执行一次stopService方法,就会关闭service方法,因此该执行的顺序是:
onCreate()—onStartCommand() (n次)—onDestroy()
对于该流程的操作相信大家都已熟悉,但实际使用中我们更多的是关注onStartCommand()方法的使用。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
在查看该方法前,我们先通过查看源代码,获取该方法的参数类型和返回值类型,点击源码,可以查看如下代码块,通过该代码块可以看到我们需要的参数类型。
public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
方法参数的设置:
- Intent:启动Service传递过来的Intent对象,也可通过Intent传递数值;
- flags:包括两个参数:START_FLAG_REDELIVERY和START_FLAG_RETRY,其中START_FLAG_REDELIVERY:表示之前已经传递过Intent信息,但是由于Service被kill,再次重传时保留之前Intent的值,表示之前的传递还未结束,START_FLAG_RETRY:如果onStartCommand()回调一直没有返回值会重新调用onStartCommand()方法;
- startId:指明当前Service的唯一对象,与stopSelfResult (int startId)方法配合使用,可更安全的停止服务。
返回值的类型:
- START_STICKY_COMPATIBILITY:与START_STICKY类似,主要是兼容低版本的作用;
- START_STICKY:线程被杀死,会尝试再次创建该服务,并会回调onStartCommand()方法,但Intent参数可能为空,因此回调该方法时需要做非空判断;
- START_NOT_STICKY:线程被杀死时,不会尝试重启该服务,除非程序检测被杀后,重新开启,但已经不是被杀调之前的状态了(创建新的服务对象);
- START_REDELIVER_INTENT:如果线程被杀,重新创建,并传递最后一个服务传递的Intent的值调用onStartCommand()方法,与START_STICKY不同,该传递的Intent值是非空的。
startService启动Service的方式包括两种,一种是通过显示启动,一种是通过隐式启动,两种启动方式分析如下
1.显示启动:
在需要的地方启动该服务。
Intent intent = new Intent(MainActivity.this, ServiceTest.class);
// intent.putExtra("xiaohan","杭州");//添加数据
Bundle bundle = new Bundle(); //通过Bundle方式传递
bundle.putString("xiaohan","杭州");
intent.putExtras(bundle);
startService(intent);
在自定义的Service的onStartCommand方法中处理回调。
//回调中处理该数据
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
/* String xiaohan = intent.getStringExtra("xiaohan");
Log.i(TAG, "onStartCommand: "+xiaohan);*/
Bundle extras = intent.getExtras();
String xiaohan = extras.getString("xiaohan");
Log.i(TAG, "onStartCommand: "+xiaohan);
return START_STICKY;
}
该方式是我们比较常见的启动方式,启动时可通过Intent传递参数(支持基本数据类型(及其对应的数组)、String和序列化后的对象),如果包含多个参数,可通过Bundle传递,但需要注意通过该种方式传递的数据不能太大(不超过1M)。
2.隐式启动:
在AndroidManifest.xml中声明自定义的Service,添加action和category。
<service android:name=".ServiceTest">
<intent-filter >
<action android:name="com.example.xiaohan.service"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
采用Bind的方式
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMyBinder = (MyBinder) service;
mMyBinder.setMyBinderInterface(new MyBinder.MyBinderInterface() {
@Override
public void binderCall(String msg) {
Log.i(TAG, "XIAOHAN binderCall: "+msg);
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent service = new Intent();
service.setAction("com.example.xiaohan.service"); //service 的 action 值
service.setPackage("com.example.xiaohan.test22"); //远程服务所在包名
//绑定服务
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
//启动服务
// startService(service);