Android移動應用開發之服務
- 一、服務的建立
- 二、服務的生命周期
- 三、服務的啟動方式
-
- 1、startService方式啟動
- 2、bindService方式啟動
- 四、服務的通信
-
- 1、通信方式
-
- (1)本地服務通信
- (2)遠端服務通信
- 2、實戰演練:音樂播放器
一、服務的建立
服務是一個長期運作在背景的使用者元件,例如下載下傳資料、播放音樂等。
程式包->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:
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中動态申請敏感權限。