Service技术
Q7nl1s admin

主要内容:Service服务3种实现 + MediaPlayer编程

image-20230418133549931

Service概述

◼ Service:是一种可长时间运行在后台,而不提供界面的应用组件。
◼ Service可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。
◼ 当应用程序进程被杀掉后,所有依赖于该进程的Service也会停止运行。
◼ Service是Android系统四大组件之一。

image-20230418133707335

Service和Thread区别

◼ 简单地说,Service 和 Thread 没有关系,是两个不同概念。
◼ Thread 是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行,而 Service 是运行在主线程里的后台程序。(Service 不是子线程)
◼ Why Service?
◼ 如果没有 Service,在 Activity 启动子线程后,子线程的运行就独立于 Activity,当 Activity 被销毁之后,就无法再重新获取到之前创建的子线程(”野线程”)。另外在一个 Activity 中创建的子线程,其他 Activity 无法对其进行操作。
◼ Service 就不同了,所有的 Activity 都可以与 Service 进行绑定,即使 Activity 被销毁了,之后只要重新与 Service 重新绑定,就又能获取原来 Service 实例。
◼ 因此,使用 Service 来处理后台任务(创建子线程),Activity 就可以放心地 finish,完全不需要担心无法对后台任务进行控制的情况。

Service类型

◼ Service类型有3种:

◼ 后台服务:执行用户不会直接注意到的操作(普通启动型)
◼ 前台服务:执行一些用户能注意到的操作(必须显示通知)
◼ 绑定服务:提供客户端-服务端接口,以便组件与服务进行交互

后台服务(也称启动型服务)

◼ 执行用户不会直接注意到的操作
◼ 后台服务优先级相对较低,当系统内存不足时,后台服务有可能被回收
◼ 启动和终止服务:
• 应用组件通过 startService(intent) 启动服务
• 终止服务:在服务内部调用 stopSelf() ,或由其他组件在外部调用 stopService(intent) 来停止 // 必须和 startService(intent) 是同一个 intent
• 无论调用了多少次 startService(),只需调用一次 stopService() 来停止

前台服务

◼ 前台服务必须显示一个通知,以使用户意识到服务正在后台执行。
◼ 启动和终止服务:
• 组件通过 startForegroundService(intent) 启动服务 + startForeground(通知id, 通知) 发送通知
• 外部终止服务:stopService(intent) // 会移除前台服务的通知
• 在服务内部主动移除通知:stopForeground(true)
• 除非服务被终止或主动移除通知,否则通知无法关闭 ==强制结束APP也不行==

绑定服务

◼ 绑定服务是客户端-服务器接口中的服务器
◼ 绑定服务通常只在为其他应用组件提供服务时处于活动状态,通常不用无限期在后台运行
◼ 启动和终止服务:
• 组件通过调用 bindService(intent, conn, flag) 与服务绑定
• 外部终止服务:unbindService(conn) 关闭服务 // 用于服务连接对象
• 服务一旦启用,调用者就与服务绑定在一起,调用者一旦退出,服务也就终止
• 多个组件可以同时绑定到一个服务,当全部绑定解除后,服务才被销毁。
• 借助绑定服务,组件可以绑定到服务、发送请求、接收响应,以及执行进程间通信 (IPC)

Service生命周期

image-20230418134404932

Service实现

– 音乐播放服务为例

◼ 创建服务

◼ 服务实现

image-20230418134451958

创建服务

◼ 新建 Module:如 ServiceDemo
◼ MainActivity 界面布局如下

activity_main.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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="100dp"
android:text="启动服务"
android:textSize="16sp"
app:icon="@android:drawable/ic_media_play"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="56dp"
android:layout_marginTop="100dp"
android:layout_marginEnd="32dp"
android:text="结束服务"
android:textSize="16sp"
app:icon="@android:drawable/ic_media_pause"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_start"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

创建 raw 资源文件夹

◼ 在 res 文件夹下新建 raw 资源文件夹

image-20230418134750642

说明:raw 文件夹通常存放资源文件,如音频、视频等,通过 R.raw.资源id 可定位资源

注意:资源文件名要求首字母小写或以下划线开头,不要出现中文和空格,也不要以全数字命名

添加音乐资源

◼ 将音乐文件(mp3格式)复制到 raw 文件夹中

image-20230418154902422

新建服务

image-20230418154920033

服务配置

image-20230418154943734

自动生成的服务框架:

1
2
3
4
5
6
7
8
9
10
// 一个服务类要继承Service,并实现onBind()方法
public class MusicService extends Service {
public MusicService() { // 默认构造函数(基本不用,可删除)
}

@Override
public IBinder onBind(Intent intent) { // 绑定型服务需要重写此方法,启动型服务一般只需保持默认代码即可
throw new UnsupportedOperationException("Not yet implemented");
}
}

服务的配置:

1
2
3
4
5
<service 
android:name=".MusicService"
android:enabled="true" <!-- 如果为false就不能被调用 -->
android:exported="true" > <!-- 如果为false就不能被外部调用 -->
</service>

服务实现

◼ 3种服务实现方法:
◼ 后台服务实现
◼ 前台服务实现
◼ 绑定服务实现

后台型服务实现

◼ 将服务业务写在不同生命周期回调函数中
◼ 在服务中使用 MediaPlayer 播放音乐
◼ 外部按钮:启动和结束服务

(1)服务添加生命周期回调函数:

先熟悉一下服务的生命周期

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
public class MusicService extends Service {
public MusicService() {
}

// • onCreate只在服务首次创建时执行,且只执行1次,常用来执行一些服务初始化操作
// • 如果服务已在运行,则不会再调用此方法。onCreate在onStartCommand之前执行
@Override
public void onCreate() {
super.onCreate();
Log.d("flag","onCreate");
}

// • 服务的主体功能通常放在 onStartCommand() 方法中
// • 服务启动后,多次调用 startService() 会导致多次调用onStartCommand() 方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) { // 获得启动服务时传来的intent
Log.d("flag","onStartCommand");
return super.onStartCommand(intent, flags, startId);
}

// • 使用 stopService() 或 selfStop() 终止服务前会调用此方法
// • 这是服务生命周期的最后一个调用
// • 此方法常用来清理资源,如线程、注册的侦听器、接收器等
@Override
public void onDestroy() {
super.onDestroy();
Log.d("flag","onDestroy");
}

// onBind()方法用于绑定型服务,启动型服务只需保留即可
@Override
public IBinder onBind(Intent intent) {
Log.d("flag","onBind");
throw new UnsupportedOperationException("Not yet implemented");
}
}

(2)启动和结束服务:

◼ 启动服务:

1
2
Intent intent = new Intent( MainActivity.this, MusicService.class ); //定义Intent (本例是显示Intent)
startService(intent); //启动服务

◼ 结束服务:

1
stopService(intent); //结束服务,要求和启动时是同一个Intent,会执行服务的onDestroy方法

MainActivity.java

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
public class MainActivity extends AppCompatActivity {
Button btn_start;
Button btn_stop;
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent( MainActivity.this, MusicService.class );
intent.putExtra("msg", "hello"); // 通过intent给service传递数据(单个数据可以不用bundle)
// service端在onStartCommand()中可获得intent及其数据:intent.getStringExtra("data"));
switch( v.getId() ){
case R.id.btn_start:
startService(intent); //启动服务
break;
case R.id.btn_stop:
stopService(intent); //停止服务
break;
}
}
};

public void initView() {
btn_start = (Button) findViewById(R.id.btn_start);
btn_stop = (Button) findViewById(R.id.btn_stop);
btn_start.setOnClickListener(listener);
btn_stop.setOnClickListener(listener);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
}

先测试一下:服务的生命周期

image-20230418155800404

(3)在服务中添加音乐播放功能

◼ MediaPlayer:Android内置的音频和视频播放器
◼ MediaPlayer常用方法:

方法名 功能描述
setDataSource() 设置要播放的音频文件的位置。
prepare() 在开始播放之前调用这个方法完成准备工作。
start() 开始或继续播放音频。
pauseO) 暂停播放音频。
reset() 将MediaPlayer对象重置到刚刚创建的状态。
seekTo() 从指定的位置开始播放音频。
stop() 停止播放音频。调用这个方法后的MediaPlayer对象无法再播放音频。
release() 释放掉与MediaPlayer对象相关的资源。
isPlayingO 判断当前MediaPlayer是否正在播放音频。
getDuration() 获取载入的音频文件的时长。
getCurrentPosition() 获取音乐播放的当前位置

音乐播放具体实现:

MusicService.java

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
public class MusicService extends Service {
MediaPlayer mediaPlayer; // 定义媒体播放器

public void init() { // 自定义初始化
if (mediaPlayer == null) {
// 音乐资源来自 raw 文件夹
mediaPlayer = MediaPlayer.create( getApplicationContext(), R.raw.she ); //创建播放器并加载音乐
mediaPlayer.setLooping(true); //重复播放
}
}
public void start() { // 自定义播放
mediaPlayer.seekTo(0); //播放器从头开始
mediaPlayer.start(); //播放
}

public void stop() { // 结束播放
if (mediaPlayer != null) {
mediaPlayer.stop(); //停止播放
mediaPlayer.release(); //回收
}
}

public MusicService() { // 默认的构造函数(可删除)
}

@Override
public void onCreate() { // 在onCreate()中完成播放器初始化
super.onCreate();
Log.d("flag", "onCreate");
init();
}

@Override
public int onStartCommand( Intent intent, int flags, int startId ) { // 服务的主体功能放在此处
Log.d("flag", "onStartCommand");
Log.d("flag", intent.getStringExtra("msg") ); // 获取intent传递给服务的数据
start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() { // 在服务终止前结束音乐播放
super.onDestroy();
Log.d("flag", "onDestroy");
stop();
}
@Override
public IBinder onBind(Intent intent) {
Log.d("flag", "onBind");
throw new UnsupportedOperationException("Not yet implemented");
}
}

运行结果:

image-20230418160924563

前台服务实现

◼ 前台服务必须显示一个通知,以使用户意识到服务正在后台执行。
◼ 前台服务的启动和结束:

1
2
Intent intent = new Intent( MainActivity.this, MusicService.class ); //定义Intent(本例是显示Intent)
startForegroundService(intent); //启动前台服务
1
stopService(intent); //结束服务,要求是同一个Intent,会执行服务的onDestroy方法

◼ 通知的发送和清除:

1
2
startForeground(通知ID, notification); //前台服务需要发送通知
stopForeground(true); //前台服务主动清除通知

注:除非服务被终止 stopService() 或 stopForeground(true) 主动移除通知,否则通知无法关闭

具体实现:
(1)新建:MainActivity2 和 MusicService2(服务)
(2)申请2个权限:
• 前台服务的权限:FOREGROUND_SERVICE
• 通知发送的权限:POST_NOTIFICATIONS

AndroidManifest.xml

1
2
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

(3)MainActivity2配置:启动程序 + 单例启动模式

AndroidManifest.xml

1
2
3
4
5
6
7
8
9
<activity
android:name=".MainActivity2"
android:launchMode="singleInstance" <!-- Activity设置为单例启动模式默认值是standard--> <!--作用:从通知返回到Activity时,不会重新创建Activity实例 -->
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

MainActivity2.java

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
public class MainActivity2 extends AppCompatActivity {
//多权限申请
private final ActivityResultLauncher permissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
// 判断两个权限是否获取
if (result.get(Manifest.permission.POST_NOTIFICATIONS) != null
&& result.get(Manifest.permission.FOREGROUND_SERVICE) != null) {
if (Objects.requireNonNull(result.get(Manifest.permission.POST_NOTIFICATIONS)).equals(true)
&& Objects.requireNonNull(result.get(Manifest.permission.FOREGROUND_SERVICE)).equals(true)) {
//权限全部获取到之后的动作
Log.d("flag", "获得所有相关权限");
} else {
//有权限没有获取到的动作
}
}
}
}
);

//在需要的时候启动权限请求
private void requestPermission() {
//多个权限 或直接 new String[]{…}
String[] permissions = { Manifest.permission.POST_NOTIFICATIONS, Manifest.permission.FOREGROUND_SERVICE }; // 两个权限
permissionLauncher.launch(permissions); //启动权限申请
}
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
// 启动/终止前台服务
Intent intent = new Intent( MainActivity2.this, MusicService2.class );
switch( v.getId() ){
case R.id.btn_start:
startForegroundService(intent); //启动服务(外部):初次启动会调用服务的onCreate()
break;
case R.id.btn_stop:
stopService(intent); //停止服务(外部):终止前会执行服务的onDestroy()
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( R.layout.activity_main ); // 说明:MainActivity2仍然使用MainActivity的界面

requestPermission(); //申请权限

// 说明:最好放在权限申请回调函数中
Button btn_start = (Button) findViewById(R.id.btn_start);
Button btn_stop = (Button) findViewById(R.id.btn_stop);
btn_start.setOnClickListener(listener);
btn_stop.setOnClickListener(listener);
}
}

前台服务的实现:MusicService2

MusicService2.java

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
75
76
77
public class MusicService2 extends Service {
MediaPlayer mediaPlayer;
// 通知的相关成员定义
// 增加通知编程模块
private static final int NOTIFY_ID = 1; //通知的id (自拟)
private static final String CHANNEL_ID = "123"; //通知渠道的id (自拟)
private static final String CHANNEL_Name = "mychannel"; //通知渠道的名称(自拟)
NotificationManager manager; //通知管理类
Notification notification; //通知类
NotificationChannel channel; //通知渠道
PendingIntent pendingIntent; //延迟Intent
Bitmap bitmap; //通知的LargeIcon

//发送前台服务通知
public void sendNotity() {
manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // 创建通知管理器
channel = new NotificationChannel(CHANNEL_ID, CHANNEL_Name, NotificationManager.IMPORTANCE_HIGH); // 创建通知渠道
manager.createNotificationChannel(channel);
Intent intent = new Intent(MusicService2.this, MainActivity2.class); //点击通知将返回MainActivity2
pendingIntent = PendingIntent.getActivity(MusicService2.this, 0, intent, PendingIntent.FLAG_IMMUTABLE); // 创建PendingIntent
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.gu); // 创建大图标的Bitmap
// 创建通知,注:第二个参数是Channel的id,一定要和通知渠道的id保持一致,否则不然通知不会显示
notification = new Notification.Builder(MusicService2.this, CHANNEL_ID)
.setContentTitle("提醒") //通知标题
.setContentText("通知内容…") //通知内容
.setWhen(System.currentTimeMillis()) //通知产生的时间
.setShowWhen(true) //显示时间
.setSmallIcon(R.drawable.twitter) //小图标
.setLargeIcon(bitmap) //大图标Bitmap
.setAutoCancel(true) //通知点击后自动删除(注:前台服务的通知不起作用)
.setContentIntent(pendingIntent) //设置通知点击的Intent
.build(); //构建通知
startForeground(NOTIFY_ID, notification); //发送前台服务通知 不用原版通知发送:manager.notify(NOTIFY_ID,notification);
}

public void init() {
if ( mediaPlayer == null ) {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.she); //创建音乐播放器
mediaPlayer.setLooping(true); //重复播放
}
}
public void start() {
mediaPlayer.seekTo(0); //从头开始
mediaPlayer.start(); //播放
}
public void stop() {
if ( mediaPlayer != null ) {
mediaPlayer.stop(); //停止播放
mediaPlayer.release(); //回收
}
}
public MusicService2() {
}

@Override
public void onCreate() {
super.onCreate();
sendNotity(); //前台服务通知只需发送1次,所以放在onCreate中
init(); //播放器初始化
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
start(); //播放
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true); //服务结束前主动移除通知
stop(); //停止播放
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}

运行结果:

image-20230418162124384

绑定服务实现

◼ 服务代理框架
◼ 服务的绑定和解除
◼ 服务端实现
◼ 客户端实现

服务代理框架

◼ 什么是代理:
◼ 代理是访问对象和目标对象之间的中介,当无法或不想直接引用目标对象或访问目标对象存在困难时,可以通过代理来间接访问。

◼ 代理模式优点:
◼ 在客户端与目标对象之间起到中介作用和保护目标对象的作用
◼ 代理对象可以扩展目标对象的功能
◼ 能将客户端与目标对象分离,在一定程度上降低了系统耦合度,增加程序扩展性

服务代理框架:假设服务类名为 AService

◼ 服务端:创建 Binder 代理来获取服务实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AService extends Service {
private final IBinder binder = new LocalBinder(); //创建一个Binder代理实例

// 内部类
public class LocalBinder extends Binder {
public AService getService() {
return AService.this; // 自定义一个方法用于获取Service实例
}
}
@Override
public IBinder onBind(Intent intent) {
return binder; // 在onBind()中返回服务代理(暴露代理)
}
// … 其他服务业务代码(见后面示例)
}

注:IBinder 是接口,Binder 是接口实现类,LocalBinder 自定义类是继承 Binder 并扩展

服务的绑定和解除 (启动和结束绑定服务)
1
2
3
4
// 绑定服务
Intent intent = new Intent(MainActivity.this, AService.class);
intent.putExtra("data","hello"); //传递数据(视需求而定)
bindService( intent, connection, BIND_AUTO_CREATE); //绑定服务有3个参数

BIND_AUTO_CREATE:当 bindService 时,如果服务不存在则自动创建该服务,并执行 onCreate–>onBind,如果服务已存在(已绑定),则只会调用 onBind

1
2
3
// 解除绑定
unbindService( connection ); //注:参数不是intent,而是服务绑定对象
mBound = false; //绑定状态恢复为无绑定

image-20230418162945729

服务绑定图示

image-20230418163009962

服务端实现:新建MusicService3服务(播放、暂停、销毁)
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
public class MusicService3 extends Service {
MediaPlayer mediaPlayer;

// 创建服务代理
private final IBinder binder = new LocalBinder();
public class LocalBinder extends Binder {
public MusicService3 getService() {
return MusicService3.this;
}
}
public void init() {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.she); //创建音乐播放器
mediaPlayer.setLooping(true); //重复播放
mediaPlayer.seekTo(0); //从头开始
}
}

// 音乐服务的一些业务功能
public void start()
{
if ( !mediaPlayer.isPlaying() ) {
mediaPlayer.start(); //播放
}
}
public void pause()
{
if ( mediaPlayer.isPlaying() ) {
mediaPlayer.pause(); //暂停
}
}
public void stop() {
if ( mediaPlayer != null ) {
mediaPlayer.stop(); //停止
mediaPlayer.release(); //回收
}
}

// 服务的生命周期函数
@Override
public void onCreate() {
super.onCreate();
Log.d("flag", "onCreate");
init(); //初始化
}
@Override
public IBinder onBind(Intent intent) {
Log.d("flag", "onBind");
//获取传入的信息
Log.d("flag", intent.getStringExtra("data"));
return binder; // 在onBind中返回服务代理
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("flag", "onUnbind");
stop(); // 注:回收工作放在onUnbind更及时
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("flag", "onDestroy");
}
} //end service
客户端:新建MainActivity3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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"
tools:context=".MainActivity3">
<ImageButton
android:id="@+id/btn_start"
android:layout_width="100dp"
android:layout_height="60dp"
android:layout_marginStart="8dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="8dp"
android:backgroundTint="@android:color/holo_orange_dark"
android:scaleType="fitCenter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@android:drawable/ic_media_play" />
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity3.java

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
public class MainActivity3 extends AppCompatActivity {
ImageButton btn_start;
private MusicService3 musicService; // 要调用的服务(从服务代理中获取)
private boolean mBound = false; // 服务连接状态

// 服务连接对象
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
MusicService3.LocalBinder binder = (MusicService3.LocalBinder) iBinder; //iBinder获取服务代理
musicService = binder.getService(); //获取代理端服务实例(实现服务绑定)
mBound = true; //设置连接状态为true
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false; //设置连接状态为true
}
};


View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start:
boolean isPlaying = musicService.mediaPlayer.isPlaying();
if ( !isPlaying ) {
musicService.start(); // 调用服务的API
btn_start.setImageDrawable(getDrawable(android.R.drawable.ic_media_pause)); // 按钮根据播放状态切换不同图片
} else {
musicService.pause(); // 调用服务的API
btn_start.setImageDrawable(getDrawable(android.R.drawable.ic_media_play)); // 按钮根据播放状态切换不同图片
}
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
Log.d("flag", "onCreate");
btn_start = (ImageButton) findViewById(R.id.btn_start);
btn_start.setOnClickListener(listener);

// 本例在Activity启动时就进行服务绑定
Intent intent = new Intent(MainActivity3.this, MusicService3.class);
intent.putExtra("data", "hello");
bindService( intent, connection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("myflag", "onDestroy");
if( mBound ) {
// 在Activity销毁时解除服务绑定
unbindService(connection);
mBound = false;
}
}
}

运行结果:将 MainActivity3 设置为启动程序

image-20230418163607384

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View