Service是android4大组件之一,它主要用于处理后台的一些耗时逻辑,以及实现需要长期运行在后台的任务。
Service 分类:
1.1. 按运行地点分类:
本地服务,服务依附在主进程上
远程服务,独立进程
1.2. 按运行类型分类
前台服务
后台服务
1.3. 按启动方式分类:
startService
bindService
startService + bindService
Service与Thread区别
1.Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位,Thread运行在子线程上,独立于主线程。
2. Service:Service 是android的一种机制, Service 是运行在主进程的 main 线程上的。
Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。
Service的生命周期方法
onCreate():Service创建时调用
onStartCommand():Service启动时调用
onBind():Service绑定时调用
onRebind():Service重绑定时调用
onUnbind():Service解绑定时调用
onDestroy():Service销毁时调用
Service的操作方法
startService
调用startService时,如果此时Service没有启动则会调用onCreate->
onStartCommand.如果此时Service已经启动,则不会再调用onCreate,onCreate函数只在服务未启动时启动服务调用,但是会继续调用onStartCommand,调用次数与startService调用次数对应。
对于多个Activity启动Service的情况,这样的结论也适合,当Activity已经启动过该Service,在另外Activity启动该Service时,不会创建Service,但会调用onStartCommand。
当Activity使用startService启动服务时,退出未停止服务,重新进入Activity调用startService时不会调用onCreate,但会调用onStartCommand。
stopService
当调用stopService停止服务时,Service会调用onDestory停止服务。
对于多个Activity,当Service已经使用startService启动完成之后,如果在另外的Activity调用stopService结束该Service,会调用onDestory。如果之后返回到最初启动的Activity再调用stopService,那么此时不会再触发onDestory,因为之前启动的Service已经结束了。由此可知,调用startService启动服务,可以在另外的地方调用stopService结束服务。
其中onDestory是在另外Activity调用stopService触发的。
bindService
当一个service没有启动,直接调用bindService时,则会触发Service的onCreate和onBind方法。
对于多个Activity绑定同一个Service,如果Service已经被启动,则再次调用bindService时,Service的onCreate和onBind都不会继续调用,此时会回调ServiceConnection的onServiceConnected方法。
Activity通过调用unbindService来与一个已经绑定的Service解除绑定,该方法会触发Service的onUnbind和onDestory方法,当该Service只与当前Activity绑定时,测试效果如下:
一个Activity如果调用了bindService,那么在结束之前需要调用unbindService解除与服务的绑定,否则会抛出android.app.ServiceConnectionLeaked异常。此时仍会触发Service的onUnbind和onDestory,Service仍然会结束。
unbindService
一个Activity只能调用一次unbindService来解除与Service的绑定,多次调用则会抛出java.lang.IllegalArgumentException:Service not registered…的异常。
如果想Service可以绑定,并且退出当前绑定Activity时服务仍然继续运行,可以在Activity启动的时候先调用startService,之后再调用bindService进行绑定服务。如果解除绑定之后再次启动Activity进行绑定Service,那么此时onBind将不会继续调用,但是会回调ServiceConnection的onServiceConnected方法。
Service中含有一个onRebind的方法,当Service中的onUnbind方法返回true,并且Service调用unbindService之后并没有销毁,此时重新绑定时将会触发onRebind方法,该方法不会重复调用,类似于onBind,但是和onBind不一样的是,初次绑定Service时还是会调用onBind,之后才会调用onRebind。onRebind在Service未被绑定并且Sevice之前已经被解除绑定时调用onBind触发。
Service常见操作流程
假设有两个Activity,分别为a1和a2.
a1.StartService->a1.stopService
Service回调:onCreate->onStartCommand->onDestory
a1.StartService->a2.startService->a2.stopService
onCreate->onStartCommand->onStartCommand->onDestory
stopService先调先停止
a1.bindService->a1.unbindService
onCreate->onBind->a1.onServiceConnected->onUnbind->onDestory
同一Activity多次调用bindService效果一样
a1.bindService->a2.bindService->a2.unbindService->a1.unbindService
onCreate->onBind->a1.onServiceConnected->a2.onServiceConnected
->onUnbind->onDestory
5.a1.StartService->a1.bindService->a1.unbindService->a1.stopService
onCreate->onStartCommand->onBind->a1.onServiceConnected
->onUnbind->onDestory
6.a1.bindService->a1.StartService->a1.stopService->a1.unbindService
onCreate-> onBind->onStartCommand->a1.onServiceConnected
->onUnbind->onDestory
Service的启动和停止
Service首先需要在AndroidManifest.xml里注册,然后通过startService和stopService可以实现基本启动和停止功能。
Service的绑定
Service需要在AndroidManifest.xml里注册,Service需要实现onBind方法,在onBind里返回一个Binder实例。在调用bindService方法时需要传递一个ServiceConnection对象,用来监听与Service连接和丢失连接的状态。Service 取消绑定时使用unbindService方法实现。
注意问题
1.使用 startService 启动服务之后,需要使用 stopService停止服务;
2.调用 bindService 绑定到Service的时候,应该使用unbindService 解除绑定,bindService 和unbindService 应该是成对出现的;
3.在 sdk 2.0 及其以后的版本中,应当使用 onStartCommand 而不是onStart。
4.使用 startService 与 bindService 启动服务,要使Service 的终止,unbindService与stopService都需要调用,才能停止Service;
5.当手机屏幕横竖屏的时候,使用 bindService 建立的连接便会断开,服务的生命周期与上述相同。
6.Service.onBind如果返回null,则调用 bindService 会启动 Service,但不会连接上 Service,因此 ServiceConnection.onServiceConnected不会被调用,但任然需要使用 unbindService 函数断开它,这样 Service 才会停止。
7.使用 startForeground ,如果 id 为 0 ,那么notification 将不会显示。
8.OnBind onCreate onRebind onUnbind方法不能重复被调用
测试代码
TestService.java
public class TestService extends Service {
private int num = 0;
@Override
public void onCreate() {
super.onCreate();
System.out.println("-----------------> TestService: onCreate");
}
@Override
public int onStartCommand(Intent intent, int fTAGs, int startId) {
System.out.println("-----------------> TestService: onStartCommand");
return super.onStartCommand(intent, fTAGs, startId);
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("-----------------> TestService: onBind");
return binder;
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
System.out.println("-----------------> TestService: onRebind");
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("-----------------> TestService: onUnbind");
// return super.onUnbind(intent);
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("-----------------> TestService: onDestroy");
}
public int getNum() {
int a = num ++;
return a;
}
public IBinder binder = new TestBinder();
public class TestBinder extends Binder{
public TestService getService(){
return TestService.this;
}
}
}
MainActivity.java
public class MainActivity extends Activity {
private Button btStart = null;
private Button btStop = null;
private Button btBind= null;
private Button btUnbind = null;
private Button btOther = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
// 控件初始化
private void initView() {
btStart = (Button) findViewById(R.id.bt_start);
btStart.setOnClickListener(clickListener);
btStop = (Button) findViewById(R.id.bt_stop);
btStop.setOnClickListener(clickListener);
btBind = (Button) findViewById(R.id.bt_bind);
btBind.setOnClickListener(clickListener);
btUnbind = (Button) findViewById(R.id.bt_unbind);
btUnbind.setOnClickListener(clickListener);
btOther = (Button) findViewById(R.id.bt_other);
btOther.setOnClickListener(clickListener);
}
private OnClickListener clickListener = new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.bt_start: // start
startService(new Intent(MainActivity.this, TestService.class));
break;
case R.id.bt_stop: // stop
stopService(new Intent(MainActivity.this, TestService.class));
break;
case R.id.bt_bind: // bind
bindService(new Intent(MainActivity.this, TestService.class), serviceConnection, Context.BIND_AUTO_CREATE);
break;
case R.id.bt_unbind: // unbind
unbindService(serviceConnection);
break;
case R.id.bt_other: //
Intent it = new Intent();
it.setClass(MainActivity.this, MainActivity.class);
startActivity(it);
break;
default:
break;
}
}
};
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
System.out.println("----------------> onServiceConnected");
TestBinder sBinder = (TestBinder)service;
System.out.println("----------------> num: " + sBinder.getService().getNum());
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
System.out.println("----------------> onServiceDisconnected");
}
};
}
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:id="@+id/bt_start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="startService" />
<Button
android:id="@+id/bt_stop"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="stopService" />
<Button
android:id="@+id/bt_bind"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="bindService" />
<Button
android:id="@+id/bt_unbind"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="myClick"
android:text="unbindService" />
<Button
android:id="@+id/bt_other"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="otherActivity" />
</LinearLayout>
AndroidMainfest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.testservice.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.example.testservice.TestService" ></service>
</application>
</manifest>