Service的start和bind启动服务

start启动服务

功能:点击start按钮开启一个服务,点击stop按钮停止一个按钮。

代码:
布局的xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">


<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/start"
android:text="startService"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/stop"
android:text="destroyService"
android:onClick="doClick"/>


</LinearLayout>

和Activity一样,Service也需要在配置文件中注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demoniaccube.chobits.pracetice_service" >


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >

<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service android:name=".MyService"></service>

</application>

</manifest>

一个Activity类,用于启动服务和提供按钮界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainActivity extends AppCompatActivity
{

private Intent intent1;
@Override
protected void onCreate(Bundle savedInstanceState)
{

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

}
public void doClick(View v)
{

switch (v.getId())
{
case R.id.start:
intent1 = new Intent(MainActivity.this, MyService.class);
startService(intent1);
break;
case R.id.stop:
stopService(intent1);
break;
}
}
}

一个自定义的Service子类,用于继承Service类,用于实现Service的具体内容,这里只是简单的打印了文字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MyService extends Service
{

@Override
public void onCreate()
{

super.onCreate();
Log.d("ZXB", "Service_onCreate()");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{

Log.d("ZXB", "Service_onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy()
{

Log.d("ZXB", "Service_onDestroy()");
super.onDestroy();
}

@Override
public IBinder onBind(Intent intent)//Service是抽象类,而onBind()方法则是他的抽象方法,必须实现。
{

Log.d("ZXB", "Service_onBind()");
return null;
}
}

启动一个服务很简单,总结一下:
1、继承Service类,现实其方法。
2、在配置文件中注册Service。
3、在Service的启动源(这里是Activity)中使用startService()方法启动Service,stopService停止服务。这和跳转Activity的方法类似,用了Intent的显示用法。

bind启动服务

功能:
点击bind按钮绑定服务,unbind按钮解除绑定。点击播放、暂停等按钮打印对应的日志。这里的播放、暂停等方法是写在Service里的。这里用bind使Service和Activity建立了联系。

代码:
xml布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">


<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/start"
android:text="startService"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/stop"
android:text="destroyService"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/bind"
android:text="bindService"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/unbind"
android:text="unbindService"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/play"
android:text="播放"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/pause"
android:text="暂停"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/previous"
android:text="上一首"
android:onClick="doClick"/>

<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/next"
android:text="下一首"
android:onClick="doClick"/>


</LinearLayout>

Activity类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public class MainActivity extends AppCompatActivity
{

private Intent intent1;
private Intent intent2;
MyBindService myBindService;
ServiceConnection conn = new ServiceConnection()//ServiceConnection匿名类起到连接启动源和Service的作用
{
@Override//当启动源和Service成功连接后会调用这个方法
public void onServiceConnected(ComponentName name, IBinder service)
{

myBindService = ((MyBindService.MyBinder)service).getService();
}

@Override
//当启动源跟Service的连接意外丢失的时候会调用这个方法(Service崩溃,强行杀死等),如果和启动源取消了绑定则不会回调这个方法
public void onServiceDisconnected(ComponentName name)
{

Log.d("ZXB","error");
}
};//在方法之外,属于语句,注意这个逗号不能丢!

@Override
protected void onCreate(Bundle savedInstanceState)
{

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent1 = new Intent(MainActivity.this, MyService.class);
intent2 = new Intent(MainActivity.this, MyBindService.class);

}
public void doClick(View v)
{

switch (v.getId())
{
case R.id.start:
startService(intent1);
break;
case R.id.stop:
stopService(intent1);
break;
case R.id.bind:
bindService(intent2, conn, BIND_AUTO_CREATE);
//bind多次也只会调用一次onBind方法
//这里有三个参数:第一个是Intent,即从启动源到服务的意图;第二个conn是上面实例化的ServiceConnection;第三个BIND_AUTO_CREATE表示在活动和服务进行绑定后自动创建服务。也可以是其他模式,比如绑定后不创建服务等,具体查看相应api。
break;
case R.id.unbind:
unbindService(conn);
// 这边如果重复unBind会报错,提示该服务没有注册的错误——IllegalArgumentException:
// Service not registered: null
// 所以一般会设置一个flag去看这个service
// bind后有没有被unBind过,没有unBind过才能调用unBind方法
break;
case R.id.play:
myBindService.play();
//这里如果没有先启动服务,直接播放;会报java.lang.
//IllegalStateException: Could not execute method for and
//roid:onClick不能执行这个点击事件的错误,可以写个判断服
//务是否启动,这里就不写了O(∩_∩)O哈!和上面的异常一样需要处理。
break;
case R.id.pause:
myBindService.pause();
break;
case R.id.previous:
myBindService.previous();
break;
case R.id.next:
myBindService.next();
break;



}
}
}

Service子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class MyBindService extends Service
{

class MyBinder extends Binder//Binder实现了Ibinder接口,所以可以作为onBind的返回值
{

public MyBindService getService()
{

return MyBindService.this;
}
}
@Override
public void onCreate()
{

super.onCreate();
Log.d("ZXB", "BindService_onCreate()");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{

Log.d("ZXB", "BindService_onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}

@Override
public IBinder onBind(Intent intent)
{

Log.d("ZXB", "BindService_onBind()");
return new MyBinder();
}

@Override
public boolean onUnbind(Intent intent)
{

Log.d("ZXB", "BindService_onUnbind()");
return super.onUnbind(intent);

}

@Override
public void onDestroy()
{

super.onDestroy();
Log.d("ZXB", "BindService_onDestroy()");
}

public void play()
{

Log.d("ZXB", "播放");
}
public void pause()
{

Log.d("ZXB", "暂停");
}
public void previous()
{

Log.d("ZXB", "上一首");
}
public void next()
{

Log.d("ZXB", "下一首");
}

}

总结:
bind启动的服务的流程和start类似,都是要先继承Service类,现实其方法;然后在配置文件中注册Service;在启动源中用bindService绑定并启动服务,unbindService解除绑定服务。而与start不同的是,在继承Service的时候,要实现IBinder接口(即继承Binder类,写个内部类),将得到的Binder子类实例作为onBind的返回值。而在启动源(Service)中需要实例化ServiceConnection类,这个类起到连接Service和Activity的作用,Activity可以通过ServiceConnection得到Service实例,从而控制Service。
注意:如果不使用ServiceConnection的话,绑定后直接退出应用或解除绑定会报错。

另外我试了一下。
1、当我把使用start和bind混合启动Service后,又解除了绑定,依然可以使用Service的播放、暂停等方法。这个地方我有点奇怪,既然已经解除绑定为什么还可以调用服务里的方法?
2、当我把使用start和bind混合启动后,又stop了Service,依然可以使用Service的播放、暂停等方法。

关于服务的详细博文
引用一下他讲的比较好的地方:

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!

Service的生命周期

1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。
4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

特别注意:
1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);
2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;
3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;