我們手機中很多app在運行的過程中都能在后臺運行摔敛,例如邊聽歌邊打游戲,這一功能得益于服務(wù)全封,后臺功能屬于Android四大組件之一马昙。
下面通過音樂播放器的案列走進服務(wù):
服務(wù)(Service)是Android中的四大組件之一,它能夠長期在后臺運行切不提供用戶界面刹悴,即使用戶切換到另一應(yīng)用程序行楞,服務(wù)仍可在后臺運行敢伸。它的創(chuàng)建過程與創(chuàng)建Activity相似,只需繼承Service類即可恒削。
下面我們先來創(chuàng)建一個服務(wù):創(chuàng)建一個項目池颈,在項目中新建一個MyService類繼承自Service,然后會自動實現(xiàn)onBind()方法:
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
本服務(wù)目前沒有實現(xiàn)任何功能钓丰。需要注意的是琢歇,在MyService中有一個onBi()方法,該方法是Service類中唯一的抽象方法梦鉴,所以必須要在子類中實現(xiàn)李茫。
接下來我們需要在清單文件中配置,這是因為服務(wù)屬于Android四大組件之一肥橙,所以要在AndroidManifest.xml清單文件中進行注冊魄宏。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lxz.android0804">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
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>
<!--在此注冊服務(wù)信息-->
<service android:name=".MyService"/>
</application>
</manifest>
至此服務(wù)便創(chuàng)建成功了,需要注意到是不要忘記在清單文件中注冊存筏,否則服務(wù)不會生效宠互。
服務(wù)與其他組件不同的是:Service不能自己主動執(zhí)行味榛,需要調(diào)用相應(yīng)的方法來啟動。啟動服務(wù)的方法有兩個予跌,分別是Context.startService()和Context.bindService()搏色。使用不同方式開啟服務(wù),服務(wù)的生命周期也會不同券册。
1频轿、startService方式開啟服務(wù)的生命周期:
onCreate()(第一次創(chuàng)建服務(wù)時執(zhí)行)→onStartCommend()(啟動服務(wù)調(diào)用的方法)→onDestroy(服務(wù)被銷毀時執(zhí)行的方法)
2、bindService方式開啟服務(wù)的生命周期:
onCreate()(第一次創(chuàng)建服務(wù)時執(zhí)行)→onBind(啟動服務(wù)調(diào)用的方法)→onUnbind(斷開服務(wù)綁定時執(zhí)行的方法)→onDestroy(服務(wù)被銷毀時執(zhí)行的方法)
兩種方式都可以顯現(xiàn)編寫播放器的功能烁焙,但為了全面起見略吨,我們采用混合開啟服務(wù)的方式來寫,而且這樣也有很大的優(yōu)點考阱。
讓服務(wù)在后臺長期運行,又調(diào)用服務(wù)里的方法流程:
1鞠苟、先調(diào)用startService 開啟一個服務(wù)
2乞榨、然后調(diào)用BindService方法,獲取中間人(運行服務(wù)中的方法)
3当娱、unBindService方法
4吃既、調(diào)用StopService方法
接下來就正式進入音樂播放器的編寫:
首先在來寫我們的布局頁面:
<LinearLayout 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"
android:orientation="horizontal"
tools:context="com.lxz.android0804.MainActivity">
<SeekBar
android:id="@+id/seekbar"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:layout_marginTop="20dp"
/>
<ImageButton
android:id="@+id/btn_play"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="fitXY" //scaleType等比例縮放屬性
android:background="@null"
android:onClick="startmusic"
android:src="@drawable/start" />
</LinearLayout>
這里設(shè)置了一個進度條,和一個圖片按鈕跨细,同時給ImageButton設(shè)置了點擊事件鹦倚,采用線性布局,水平放置冀惭。這里在處理圖片按鈕時用到了一個圖片等比例縮放的屬性震叙,即:scaleType屬性。界面如圖顯示(主要用于功能的實現(xiàn)散休,所以沒有優(yōu)化界面的美觀度):
然后在MainActivity中來實現(xiàn)我們設(shè)置的點擊事件,同時開啟我們的服務(wù):
public class MainActivity extends AppCompatActivity {
MyConn conn;//聲明
MyService.MyBinder myBinder;
private ImageButton imageButton;
private SeekBar seekBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageButton=findViewById(R.id.btn_play);
seekBar=findViewById(R.id.seekbar);
Intent intent = new Intent(this, MyService.class);
startService(intent); //開啟服務(wù)
conn=new MyConn(); //
bindService(intent,conn,BIND_AUTO_CREATE);
}
class MyConn implements ServiceConnection{//自己定義一個MyConn類實現(xiàn)接口
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
myBinder=(MyService.MyBinder) service;//得到傳過來的中間人,因為放音樂也需要用中間人媒楼,所以把中間人設(shè)置為全局變量
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}
//播放音樂
public void startmusic(View view){
}
}
}
同時在MainActivity中得到我們設(shè)置imageButton跟seekBar。
因為在執(zhí)行Bindservice時我們需要實例化ServiceConnection接口的實現(xiàn)類戚丸,所以自己定義了一個MyConn類實現(xiàn)接口划址,重寫onServiceConnected()和onServiceDisconnected()方法。
那么接下來我們就要在MyService類里面完成我們想要實現(xiàn)的創(chuàng)建限府、播放夺颤、暫停最基本的功能:
public class MyService extends Service {
private static final String TAG = "MyService";
String path="mnt/sdcard/2.mp3";
MediaPlayer mediaPlayer;//全局變量
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
@Override
public void onCreate() {
Log.i(TAG, "onCreate: 準備播放");
super.onCreate();
//準備音樂播放器流程
mediaPlayer=new MediaPlayer();//局部變量,本方法有效
try {
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
//播放音樂
public void play(){
Log.i(TAG, "play: 開始播放");
mediaPlayer.start();
}
//暫停
public void pause(){
mediaPlayer.pause();
}
}
在這里為了簡單我們直接聲明了音樂文件的路徑胁勺,然后實現(xiàn)各個功能世澜,在過程中我們使用了MediaPlayer來實現(xiàn)播放音樂的功能。在Android中播放音頻文件一般都是使用MediaPlayer類來實現(xiàn)署穗。
下面對MediaPlayer進行簡單的介紹下其中常用的方法:
setAudioStreamType:指定音頻文件類型必須在prepare()方法之前調(diào)用宜狐。
setDateSource():設(shè)置要播放的音頻文件的位置势告。
prepare():在開始播放之前調(diào)用這個方法來完成準備工作。
start():開始或繼續(xù)音頻播放抚恒。
pause():暫停播放音頻咱台。
reset():將MediaPlayer重置到剛剛創(chuàng)建的狀態(tài)。
seekTo():從指定的位置開始播放音頻俭驮。
stop():停止播放音頻回溺,調(diào)用該方法后MediaPlayer對象無法再播放音頻。
release():釋放掉與MediaPlayer對象相關(guān)的資源混萝。
isplaying():判斷當(dāng)前MediaPlayer是否正在播放遗遵。
getDuration():獲取載入音頻文件時長。
getCurrentPosition():獲取當(dāng)前播放音頻文件的位置逸嘀。
以上就是最常用的幾個方法了车要。
因為我們要播放音樂所以我們肯定也需要獲取音樂的播放時長,進度崭倘,當(dāng)前狀態(tài)等功能翼岁,所以就用到上面的方法:我們將其加入到我們的服務(wù)類中。
//獲取歌曲時長(毫秒數(shù))
public int getDuration(){
return mediaPlayer.getDuration();
}
//查看當(dāng)前播放進度(毫秒數(shù))
public int getCurrentPosition(){
return mediaPlayer.getCurrentPosition();
}
//查看播放狀態(tài)
public boolean playstate(){
return mediaPlayer.isPlaying();
}
//定位音樂播放器
public void setPosition(int newPosition){
mediaPlayer.seekTo(newPosition);
}
因為Activity與服務(wù)時兩個不同的組件司光,所以當(dāng)我們在Activity中不能直接調(diào)用的服務(wù)中的方法琅坡,所以我們準備了一個中間人MyBinder來連接服務(wù)于Activity之間的關(guān)系,講各個方法放入進去残家。
class MyBinder extends Binder{ //定義內(nèi)部類榆俺,中間人
public void callMusic(){
play();
}
public void stopMusic(){
pause();
}
public boolean callPlayState(){
return playstate();
}
public int callDuration(){
return getDuration();
}
public int callCurrentPosition(){
return getCurrentPosition();
}
public void callSetPosition(int x){
setPosition(x);
}
}