Android移动应用开发之服务
一、服务的创建
服务是一个长期运行在后台的用户组件,例如下载数据、播放音乐等。
程序包->New->Service->Service
package edu.hzuapps.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
onBind用于绑定服务。
二、服务的生命周期
- onCreate:第一次创建服务时执行的方法;
- onDestory:服务被销毁时执行的方法;
- onStartCommand:客户端通过startService方法启动服务时执行该方法;
- onBind:客户端通过调用bindService方法启动服务时执行该方法;
- onUnbind:客户端通过调用unBindService断开服务时执行该方法。
三、服务的启动方式
1、startService方式启动
该启动模式下服务会长期在后台运行,并且服务的状态和开启者的状态没有关系,即使启动服务的组件已经被销毁,服务也会依旧运行。
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/btn_stop"
android:layout_centerHorizontal="true"
android:layout_marginBottom="100dp"
android:background="#B0E0E6"
android:onClick="start"
android:text="开启服务"
android:textColor="#6C6C6C"
android:textSize="18sp" />
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/btn_start"
android:layout_alignLeft="@+id/btn_start"
android:layout_alignParentBottom="true"
android:layout_marginBottom="365dp"
android:background="#F08080"
android:onClick="stop"
android:text="关闭服务"
android:textColor="#6C6C6C"
android:textSize="18sp" />
</RelativeLayout>
重写Service生命周期中的方法MyService.java:
package edu.hzuapps.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void onCreate() {
super.onCreate();
Log.i("StartService", "onCreate()");
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("StartService", "onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
public void onDestroy() {
super.onDestroy();
Log.i("StartService", "onDestroy()");
}
}
交互界面MainActivity.java:
package edu.hzuapps.test;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button start;
private Button stop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.start = (Button) findViewById(R.id.btn_start);
this.stop = (Button) findViewById(R.id.btn_stop);
}
//开启服务的方法
public void start(View view) {
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
//关闭服务的方法
public void stop(View view) {
Intent intent = new Intent(this, MyService.class);
stopService(intent);
}
}
2、bindService方式启动
当一个组件通过bindService启动服务时,服务会与组件绑定,一个被绑定的服务提供一个客户端与服务器的接口,允许组件与服务交互,发送请求,得到结果,多个组件可以绑定一个服务,当调用onUnbind方法时,这个服务就会被摧毁。bindService的三个参数含义:
- Intent对象用于指定要启动的Service;
- ServiceConnection对象用于监听调用者与Service之间的连接状态,当调用者与Service连接成功时,将回调该对象的onServiceConnect方法,断开连接时将回调该对象的onServiceDisconnected方法;
- flags指定绑定时是否自动创建Service(如果还未创建),该参数可指定为0,即不自动创建,也可指定为BIND_AUTO_CREATE自动创建。
布局页面activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_above="@+id/btn_call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/btn_call"
android:layout_alignStart="@+id/btn_call"
android:layout_marginBottom="50dp"
android:background="#F5F5DC"
android:onClick="btnBind"
android:text="绑定服务"
android:textSize="16sp" />
<Button
android:id="@+id/btn_call"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/btn_unbind"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="50dp"
android:layout_marginEnd="100dp"
android:layout_marginRight="30dp"
android:background="#F5F5DC"
android:onClick="btnCall"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="调用服务中的方法"
android:textSize="16sp" />
<Button
android:id="@+id/btn_unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/btn_call"
android:layout_alignParentBottom="true"
android:layout_alignStart="@+id/btn_call"
android:layout_marginBottom="300dp"
android:background="#F5F5DC"
android:onClick="btnUnbind"
android:text="解绑服务"
android:textSize="16sp" />
</RelativeLayout>
Service类MyService.java:
扫描二维码关注公众号,回复:
12409001 查看本文章

package edu.hzuapps.bindservice;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
//创建服务的代理,调用服务中的方法
class MyBinder extends Binder {
public void callMethodInService() {
methodInService();
}
}
public void onCreate() {
Log.i("MyService", "创建服务,调用onCreate()");
super.onCreate();
}
public IBinder onBind(Intent intent) {
Log.i("MyService", "绑定服务,调用onBind()");
return new MyBinder();
}
public void methodInService() {
Log.i("MyService", "自定义方法methodInService()");
}
public boolean onUnbind(Intent intent) {
Log.i("MyService", "解绑服务,调用onUnbind()");
return super.onUnbind(intent);
}
}
界面交互代码MainActivity.java:
package edu.hzuapps.bindservice;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private MyService.MyBinder myBinder;
private Myconn myconn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//创建MyConn类,用于实现连接服务
private class Myconn implements ServiceConnection {
//当成功绑定到服务时调用的方法,返回MyService里面的Ibinder对象
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
Log.i("MainActivity", "服务成功绑定,内存地址为"+myBinder.toString());
}
//当服务失去连接时调用的方法
public void onServiceDisconnected(ComponentName name) {
Log.i("MainActivity", "服务失去连接");
}
}
//绑定服务
public void btnBind(View view) {
if (myconn == null) {
myconn = new Myconn();
Intent intent = new Intent(this, MyService.class);
bindService(intent, myconn, BIND_AUTO_CREATE);
}
}
//解绑服务
public void btnUnbind(View view) {
if (myconn != null) {
unbindService(myconn);
myconn = null;
}
}
//调用服务中的方法
public void btnCall(View view) {
myBinder.callMethodInService();
}
}
四、服务的通信
1、通信方式
(1)本地服务通信
绑定方式开启服务的案例实际上就是用来本地服务通信,首先创建一个Service类,该类会提供一个onBind方法,onBind方法返回一个IBinder对象,作为参数传递给ServiceConnection中的onServiceConnected,即通过IBinder对象与Service进行通信。
(2)远程服务通信
在Android中,每个应用程序都运行在自己的进程中,如果想要完成不同进程之间的通信,就需要使用远程服务通信,远程服务通信是通过AIDL:
- AIDL定义接口的源代码必须以.aidl结尾
- AIDL接口中用到的数据类型,除了基本的数据类型及String、List、Map、CharSequence外,其他类型需要导入包。
2、实战演练:音乐播放器
布局文件activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/et_inputpath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Music/失眠症.mp3" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_play"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="3dp"
android:drawableTop="@drawable/play"
android:gravity="center"
android:text="播放" />
<TextView
android:id="@+id/tv_stop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="3dp"
android:drawableTop="@drawable/stop"
android:gravity="center"
android:text="暂停" />
</LinearLayout>
</LinearLayout>
服务类MusicService.java:
package edu.hzuapps.musicplayer;
import android.app.Service;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
public class MusicService extends Service {
private static final String TAG = "MusicService";
public MediaPlayer mediaPlayer;
class MyBinder extends Binder {
//播放音乐
public void play(String path) {
try {
if (mediaPlayer == null) {
//创建一个MediaPlayer播放器
mediaPlayer = new MediaPlayer();
//指定参数为音频文件
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//指定播放路径
mediaPlayer.setDataSource(path);
//准备播放
mediaPlayer.prepare();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
//开始播放
mediaPlayer.start();
}
});
} else {
int position = getCurrentProgress();
mediaPlayer.seekTo(position);
try {
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaPlayer.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//暂停播放
public void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
} else if (mediaPlayer != null && (!mediaPlayer.isPlaying())) {
mediaPlayer.start();
}
}
}
public void onCreate() {
super.onCreate();
}
//获取当前进度
public int getCurrentProgress() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
return mediaPlayer.getCurrentPosition();
} else if (mediaPlayer != null && (!mediaPlayer.isPlaying())) {
return mediaPlayer.getCurrentPosition();
}
return 0;
}
public void onDestroy() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
}
- setAudioStreamType:指定音频文件的类型,必须在prepare之前调用;
- setDataSource:设置要播放的音频文件的位置,URI路径;
- prepare:准备播放;
- start:开始或继续播放音频;
- pause:暂停播放音频;
- seekTo:从指定位置开始播放音频;
- release:释放掉与MediaPlayer对象相关的资源;
- isPlaying:判断当前MediaPlayer是否正在播放音频;
- getCurrentPosition:获取当前播放音频文件的位置。
交互界面MainActivity.java:
package edu.hzuapps.musicplayer;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText path;
private Intent intent;
private myConn conn;
MusicService.MyBinder binder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
path = (EditText) findViewById(R.id.et_inputpath);
findViewById(R.id.tv_play).setOnClickListener(this);
findViewById(R.id.tv_stop).setOnClickListener(this);
conn = new myConn();
intent = new Intent(this, MusicService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
private class myConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
binder = (MusicService.MyBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
@Override
public void onClick(View view) {
String pathway = path.getText().toString().trim();
File SDpath = Environment.getExternalStorageDirectory();
File file = new File(SDpath, pathway);
String path = file.getAbsolutePath();
switch (view.getId()) {
case R.id.tv_play:
if (file.exists() && file.length()>0) {
binder.play(path);
} else {
Toast.makeText(this, "找不到音乐文件!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.tv_stop:
binder.pause();
break;
default:
break;
}
}
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
[!]Android版本为6.0时注意权限问题,需要在Activity中动态申请敏感权限。