天天看點

Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信

Android移動應用開發之服務

  • 一、服務的建立
  • 二、服務的生命周期
  • 三、服務的啟動方式
    • 1、startService方式啟動
    • 2、bindService方式啟動
  • 四、服務的通信
    • 1、通信方式
      • (1)本地服務通信
      • (2)遠端服務通信
    • 2、實戰演練:音樂播放器

一、服務的建立

服務是一個長期運作在背景的使用者元件,例如下載下傳資料、播放音樂等。

程式包->New->Service->Service

Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信
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用于綁定服務。

二、服務的生命周期

Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信
  • 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);
    }
}
           
Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信
Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信
Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信

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();
    }
}

           
Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信
Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信

四、服務的通信

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移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信
Android移動應用開發之服務一、服務的建立二、服務的生命周期三、服務的啟動方式四、服務的通信

[!]Android版本為6.0時注意權限問題,需要在Activity中動态申請敏感權限。