service相關

本篇仍以問題爲驅動android

1、什麼時Service?

Service是Android程序中四大基礎組件之一,它和Activity同樣都是Context的子類,只不過它沒有UI界面,是在後臺運行的組件。Service是Android中實現程序後臺運行的解決方案,它很是適用於去執行那些不須要和用戶交互並且還要求長期運行的任務。Service默認並不會運行在子線程中,它也不運行在一個獨立的進程中,它一樣執行在UI線程中,所以,不要在Service中執行耗時的操做,除非你在Service中建立了子線程來完成耗時操做。git

 

2、Service種類

按運行地點分類:github

按運行類型分類:編程

按使用方式分類:安全

 

3、Service生命週期

OnCreate()
系統在service第一次建立時執行此方法,來執行只運行一次的初始化工做。若是service已經運行,這個方法不會被調用。多線程

onStartCommand()
每次客戶端調用startService()方法啓動該Service都會回調該方法(屢次調用)。一旦這個方法執行,service就啓動而且在後臺長期運行。經過調用stopSelf()或stopService()來中止服務。架構

OnBind()
當組件調用bindService()想要綁定到service時(好比想要執行進程間通信)系統調用此方法(一次調用,一旦綁定後,下次再調用bindService()不會回調該方法)。在你的實現中,你必須提供一個返回一個IBinder來以使客戶端可以使用它與service通信,你必須老是實現這個方法,可是若是你不容許綁定,那麼你應返回null。異步

OnUnbind()
當前組件調用unbindService(),想要解除與service的綁定時系統調用此方法(一次調用,一旦解除綁定後,下次再調用unbindService()會拋出異常)。ide

OnDestory()
系統在service再也不被使用並要銷燬時調用此方法(一次調用)。service應在此方法中釋放資源,好比線程,已註冊的偵聽器,接收器等等.這是service收到的最後一個調用。工具

下面介紹三種不一樣狀況下Service的生命週期狀況。

1.startService / stopService

生命週期順序:onCreate->onStartCommand->onDestroy

若是一個Service被某個Activity 調用 Context.startService方法啓動,那麼不論是否有Activity使用bindService綁定或unbindService解除綁定到該Service,該Service都在後臺運行,直到被調用stopService,或自身的stopSelf方法。固然若是系統資源不足,android系統也可能結束服務,還有一種方法能夠關閉服務,在設置中,經過應用->找到本身應用->中止。

注意點:

①第一次 startService 會觸發 onCreate 和 onStartCommand,之後在服務運行過程當中,每次 startService 都只會觸發 onStartCommand

②不論 startService 多少次,stopService 一次就會中止服務

2.bindService / unbindService

生命週期順序:onCreate->onBind->onUnBind->onDestroy

若是一個Service在某個Activity中被調用bindService方法啓動,不論bindService被調用幾回,Service的onCreate方法只會執行一次,同時onStartCommand方法始終不會調用。

當創建鏈接後,Service會一直運行,除非調用unbindService來接觸綁定、斷開鏈接或調用該Service的Context不存在了(如Activity被Finish——即經過bindService啓動的Service的生命週期依附於啓動它的Context),系統在這時會自動中止該Service。

注意點:

第一次 bindService 會觸發 onCreate 和 onBind,之後在服務運行過程當中,每次 bindService 都不會觸發任何回調

3.混合型(上面兩種方式的交互)

當一個Service在被啓動(startService)的同時又被綁定(bindService),該Service將會一直在後臺運行,而且無論調用幾回,onCreate方法始終只會調用一次,onStartCommand的調用次數與startService調用的次數一致(使用bindService方法不會調用onStartCommand)。同時,調用unBindService將不會中止Service,必須調用stopService或Service自身的stopSelf來中止服務。

在什麼狀況下使用 startService 或 bindService 或 同時使用startService 和 bindService?

①若是你只是想要啓動一個後臺服務長期進行某項任務那麼使用 startService 即可以了。

②若是你想要與正在運行的 Service 取得聯繫,那麼有兩種方法,一種是使用 broadcast ,另外是使用 bindService ,前者的缺點是若是交流較爲頻繁,容易形成性能上的問題,而且 BroadcastReceiver 自己執行代碼的時間是很短的(也許執行到一半,後面的代碼便不會執行),然後者則沒有這些問題,所以咱們確定選擇使用 bindService(這個時候你便同時在使用 startService 和 bindService 了,這在 Activity 中更新 Service 的某些運行狀態是至關有用的)。

③若是你的服務只是公開一個遠程接口,供鏈接上的客服端(android 的 Service 是C/S架構)遠程調用執行方法。這個時候你能夠不讓服務一開始就運行,而只用 bindService ,這樣在第一次 bindService 的時候纔會建立服務的實例運行它,這會節約不少系統資源,特別是若是你的服務是Remote Service,那麼該效果會越明顯(固然在 Service 建立的時候會花去必定時間,你應當注意到這點)。

 

4、Service與Activity怎麼實現通訊

1.Binder:

  • 多個客戶端能夠所有鏈接到該服務。可是,系統只會在第一個客戶端綁定時調用您的服務的onBind()方法來檢索IBinder。系統而後將相同的IBinder傳遞給綁定的任何其餘客戶端,而無需再次調用onBind()。
  • 當最後一個客戶端解除綁定服務時,系統會銷燬該服務(除非該服務也由startService()啓動)。
  • 實現綁定服務時,最重要的部分是定義onBind()回調方法返回的接口。您能夠經過幾種不一樣的方法來定義服務的IBinder接口,如下部分將討論每種技術。

(1)擴展Binder類

簡述:若是您的Service對您本身的應用程序是私有的,而且與客戶端在相同的進程中運行(這是常見的),則應該經過擴展Binder類並建立其實例,onBind()返回該實例。 客戶端收到Binder,可使用它直接訪問Binder實現或甚至Service中可用的公共方法。

實現以下:

經過 Binder 接口的形式實現,當 Activity 綁定 Service 成功的時候 Activity 會在 ServiceConnection 的類 的 onServiceConnected()回調方法中獲取到 Service 的 onBind()方法 return 過來的 Binder 的子類,而後經過對象調用方法。
  這裏以音樂播放service爲例
  1. 新建一個繼承自Service的類MusicService,而後在AndroidManifest.xml裏註冊這個Service
  2. Activity裏面使用bindService方式啓動MyService,也就是綁定了MyService
    (到這裏實現了綁定,Activity與Service通訊的話繼續下面的步驟)
  3. 新建一個繼承自Binder的類MusicBinder(通常爲service內部類)
  4. 在MusicService裏實例化一個MusicBinder對象mBinder,並在onBind回調方法裏面返回這個mBinder對象
  5. 第2步bindService方法須要一個ServiceConnection類型的參數,在ServiceConnection裏能夠取到一個IBinder對象,就是第4步onBinder返回的mBinder對象(也就是在Activity裏面拿到了Service裏面的mBinder對象)
  6. 在Activity裏面拿到mBinder以後就能夠調用這個binder裏面的方法了(也就是能夠給Service發消息了),須要什麼方法在MyBinder類裏面定義實現就好了。若是須要Service給Activity發消息的話,經過這個binder註冊一個自定義回調便可。
public class MusicService extends Service {
    //實現一個播放音樂的servicepublic MediaPlayer mediaPlayer;private MusicBinder mBinder = new MusicBinder(this);

    public MusicService() {
        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource("/storage/emulated/0/netease/cloudmusic/Music/雷軍 - Are You OK.MP3");
            mediaPlayer.prepare();
            mediaPlayer.setLooping(true);
        } catch (Exception e) {

        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId);
    }

public void playOrPause() {
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        } else {
            mediaPlayer.start();
        }
    }

    public void stop() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            try {
                mediaPlayer.reset();
                mediaPlayer.setDataSource(Environment.getExternalStorageState() 
+ "/storage/emulated/0/netease/cloudmusic/Music/雷軍 - Are You OK.MP3"); mediaPlayer.prepare(); mediaPlayer.seekTo(0); } catch (Exception e) { } } } public class MusicBinder extends Binder { MusicService musicService; private OnTestListener mListener; public MusicBinder(MusicService service) { this.musicService = service; } public MusicService getService() { return musicService; } public void playOrPause() { musicService.playOrPause(); mListener.onTest(musicService.mediaPlayer.isPlaying() ? "play" : "pause"); } public void stop() { musicService.stop(); mListener.onTest("stop"); } // MyBinder 裏面提供一個註冊回調的方法 public void setOnTestListener(OnTestListener listener) { this.mListener = listener; } } //自定義一個回調接口 public interface OnTestListener { void onTest(String str); } }

 activity代碼以下:

public class MusicActivity extends Activity implements MusicService.OnTestListener {

    private MusicService.MusicBinder mBinder;
    private MusicService mService;
    private ServiceConnection sc = new MusicServiceConnection();
    private TextView textView;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_btns);
        init();
    }

    private void init() {
        textView = (TextView) findViewById(R.id.tv_music);
        findViewById(R.id.btn_1).setOnClickListener(v -> {
            //綁定Service
            Intent intent = new Intent(this, MusicService.class);
            startService(intent);
            bindService(intent, sc, Context.BIND_AUTO_CREATE);

        });

        findViewById(R.id.btn_2).setOnClickListener(v -> {
            //解除綁定
            unbindService(sc);
        });
        findViewById(R.id.btn_3).setOnClickListener(v -> {
            //也能夠直接經過binder獲取的service實例,操做播放暫停方法
//            mService.playOrPause();
            //經過binder實例操做binder內部的service方法
            mBinder.playOrPause();
        });
        findViewById(R.id.btn_4).setOnClickListener(v -> {
//            mService.stop();
            mBinder.stop();
        });
    }

    @Override
    public void onTest(String str) {
        //接口回調,提供給service向activity的通訊,實現雙向通訊
        textView.setText(str);
    }

    class MusicServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mBinder = (MusicService.MusicBinder) iBinder;
            mService = mBinder.getService();
            mBinder.setOnTestListener(MusicActivity.this);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mService = null;
        }
    }
}
 
(2)使用Messenger:(進程間通訊)

簡述:若是須要Service 和客戶端位於不一樣的進程,則可使用Messenger爲服務建立一個interface。 以這種方式,服務定義響應不一樣類型的Message對象的Handler。 該Handler是Messenger的基礎,能夠與客戶端共享IBinder,容許客戶端使用Message對象向服務發送命令。 此外,客戶端能夠定義本身的Messenger,所以服務能夠發回消息。

這是執行進程間通訊(IPC)的最簡單的方法,由於Messenger將全部請求排隊到單個線程中,以便使用者沒必要將服務設計爲線程安全。

它引用了一個Handler對象,以便others可以向它發送消息(使用mMessenger.send(Message msg)方法)。該類容許跨進程間基於Message的通訊(即兩個進程間能夠經過Message進行通訊),在服務端使用Handler建立一個Messenger,客戶端持有這個Messenger就能夠與服務端通訊了。一個Messeger不能同時雙向發送,兩個就就能雙向發送了
 
直接在onServiceConnected回調中像本地服務同樣轉換會報強轉錯誤

 

使用:若是您須要服務與遠程進程通訊,那麼可使用Messenger爲服務提供接口。這種技術容許執行進程間通訊(IPC),而無需使用AIDL。

如下是Messenger使用以下:

A、Service實現一個Handler,接收客戶端發送的消息。

B、Handler用於建立一個Messenger對象(這是對Handler的引用)。

C、Messenger建立一個IBinder,Service將IBinder 從onBind()返回給客戶端。

D、客戶端使用IBinder來實例化Messenger (引用服務的Handler),客戶端利用Messenger將Message對象發送到服務。

E、Service接收 Message在其Handler的handleMessage()方法中進行處理。

 

service實現代碼

a. service建立Handler對象。

  * 處理來自客戶端的消息
    */
    private Handler mHandler = new Handler() {
    //建立Handler對象
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello,remote service", Toast.LENGTH_SHORT).show();
                    //經過message對象獲取客戶端傳遞過來的Messenger對象。
                    Messenger messenger = msg.replyTo;
                    if (messenger != null) {
                        Message messg = Message.obtain(null, MSG_SAY_HELLO);
                        try {
                            //向客戶端發送消息
                            messenger.send(messg);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case MSG_MUSIC_PLAY:
                    //播放/暫停音樂
                    playOrPause();
                    break;
                case MSG_MUSIC_STOP:
                    //中止播放
                    stop();
                    break;
                default:
                    break;
            }
        }
    };

 

b.建立Messenger對象

    //建立Mesenger 對象
    public Messenger mMessenger;

 

public RemoteMusicService() {
        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource("/storage/emulated/0/netease/cloudmusic/Music/雷軍 - Are You OK.MP3");
            mediaPlayer.prepare();
            mediaPlayer.setLooping(true);
        } catch (Exception e) {

        }
        mMessenger = new Messenger(mHandler);
    }

 

c.onBind()返回IBinder對象

 
    
private MusicBinder mBinder = new MusicBinder(this);
 @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //經過Mesenger對象獲取IBinder實例,並返回
        return mMessenger.getBinder();
    }

 

d.服務端接收客戶端發送的消息,並在Handler對象的hanleMessage方法中進行處理。

 

完整service代碼以下

public class RemoteMusicService extends Service {
    //實現一個播放音樂的service
    public static final int MSG_SAY_HELLO = 101;
    public static final int MSG_MUSIC_PLAY = 102;
    public static final int MSG_MUSIC_STOP = 103;
    public MediaPlayer mediaPlayer;
    /**
     * 處理來自客戶端的消息
    */
    private Handler mHandler = new Handler() {
    //建立Handler對象
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello,remote service", Toast.LENGTH_SHORT).show();
                    //經過message對象獲取客戶端傳遞過來的Messenger對象。
                    Messenger messenger = msg.replyTo;
                    if (messenger != null) {
                        Message messg = Message.obtain(null, MSG_SAY_HELLO);
                        try {
                            //向客戶端發送消息
                            messenger.send(messg);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case MSG_MUSIC_PLAY:
                    //播放/暫停音樂
                    playOrPause();
                    break;
                case MSG_MUSIC_STOP:
                    //中止播放
                    stop();
                    break;
                default:
                    break;
            }
        }
    };
    //建立Mesenger 對象
    public Messenger mMessenger;
    private MusicBinder mBinder = new MusicBinder(this);

    public RemoteMusicService() {
        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource("/storage/emulated/0/netease/cloudmusic/Music/雷軍 - Are You OK.MP3");
            mediaPlayer.prepare();
            mediaPlayer.setLooping(true);
        } catch (Exception e) {

        }
        mMessenger = new Messenger(mHandler);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //經過Mesenger對象獲取IBinder實例,並返回
        return mMessenger.getBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    public void playOrPause() {
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        } else {
            mediaPlayer.start();
        }
    }

    public void stop() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            try {
                mediaPlayer.reset();
                mediaPlayer.setDataSource(Environment.getExternalStorageState() + "/storage/emulated/0/netease/cloudmusic/Music/雷軍 - Are You OK.MP3");
                mediaPlayer.prepare();
                mediaPlayer.seekTo(0);
            } catch (Exception e) {
            }
        }
    }

    public class MusicBinder extends Binder {
        RemoteMusicService musicService;

        public MusicBinder(RemoteMusicService service) {
            this.musicService = service;
        }

        public void playOrPause() {
            musicService.playOrPause();
        }

        public void stop() {
            musicService.stop();
        }

    }
}
View Code

 

 

AndroidManifest文件

 <service
            android:name=".service.musicService.RemoteMusicService"
            android:enabled="true"
            android:exported="true"
            android:icon="@drawable/ic_menu_camera"
            android:label="music"
            android:process=":music" />

exported要設置爲true,不然別的進程不能使用該Service

 

客戶端activity實現

a.綁定Service

b. 鏈接成功回調

實現ServiceConnection,必須覆蓋兩個回調方法:

findViewById(R.id.btn_5).setOnClickListener(v -> {
            Intent intent = new Intent(this, RemoteMusicService.class);    
            bindService(intent, new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                    // 當與服務端鏈接成功時,回調該方法。
                    //經過IBinder參數獲取Messenger
                    mRemoteMessenger = new Messenger(iBinder);
                    textView.setText("connected");
                }

                @Override
                public void onServiceDisconnected(ComponentName componentName) {
                    mRemoteMessenger = null;
                    textView.setText("disconnected");
                }
            }, Context.BIND_AUTO_CREATE);
            textView.setText("connecting");
        });

c. 向服務端發送消息 
Message屬性replyTo不是必須的,若是但願Service向客戶端發送消息則須要。

private void sendMsg(int what) {
        if (mRemoteMessenger == null) return;    //建立消息,設置what屬性
        Message msg = Message.obtain(null, what);    //replyTo不是必須的,若是但願Service向客戶端發送消息則須要設置。
        msg.replyTo = mMessenger;
        try {        //發送消息
            mRemoteMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

d. 接收Service的回覆

 private Messenger mRemoteMessenger;
    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    textView.setText("hello");
                    break;
                default:
                    break;
            }
        }
    };
    private Messenger mMessenger = new Messenger(mHandler);

e. 解除綁定

須要斷開與服務的鏈接時,請調用unbindService()。

 

完整客戶端activity代碼以下

public class MusicActivity extends Activity {
private TextView textView;
    private Messenger mRemoteMessenger;
    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    textView.setText("hello");
                    break;
                default:
                    break;
            }
        }
    };
    private Messenger mMessenger = new Messenger(mHandler);

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_btns);
        init();
    }

    private void init() {
        textView = (TextView) findViewById(R.id.tv_music);
        findViewById(R.id.btn_5).setOnClickListener(v -> {
            Intent intent = new Intent(this, RemoteMusicService.class);    
            bindService(intent, new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                    // 當與服務端鏈接成功時,回調該方法。
                    //經過IBinder參數獲取Messenger
                    mRemoteMessenger = new Messenger(iBinder);
                    textView.setText("connected");
                }

                @Override
                public void onServiceDisconnected(ComponentName componentName) {
                    mRemoteMessenger = null;
                    textView.setText("disconnected");
                }
            }, Context.BIND_AUTO_CREATE);
            textView.setText("connecting");
        });
        findViewById(R.id.btn_6).setOnClickListener(v -> {
            sendMsg(MSG_SAY_HELLO);
        });
        findViewById(R.id.btn_7).setOnClickListener(v -> {
            sendMsg(MSG_MUSIC_PLAY);
        });
        findViewById(R.id.btn_8).setOnClickListener(v -> {
            sendMsg(MSG_MUSIC_STOP);
        });
    }

    private void sendMsg(int what) {
        if (mRemoteMessenger == null) return;    //建立消息,設置what屬性
        Message msg = Message.obtain(null, what);    //replyTo不是必須的,若是但願Service向客戶端發送消息則須要設置。
        msg.replyTo = mMessenger;
        try {        //發送消息
            mRemoteMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

}
 
View Code

 

 

(3)使用AIDL:

簡述:AIDL(Android Interface Definition Language)執行全部的工做,將對象分解爲基元,操做系統能夠在進程之間瞭解和編組它們以執行IPC。 以前使用的Messenger技術其實是基於AIDL做爲其底層結構。 如上所述,Messenger在單個線程中建立全部客戶端請求的隊列,所以服務一次接收一個請求。 可是,若是您但願您的服務同時處理多個請求,則能夠直接使用AIDL。 在這種狀況下,您的服務必須可以進行多線程並創建線程安全。

要直接使用AIDL,您必須建立一個定義編程接口的.aidl文件。 Android SDK工具使用此文件生成一個實現接口並處理IPC的抽象類,而後您能夠在服務中擴展它。

注意:大多數應用程序不該該使用AIDL建立綁定的服務,由於它可能須要多線程功能,並可能致使更復雜的實現。所以,AIDL不適用於大多數應用程序。

aidl 比較適合當客戶端和服務端不在同一個應用下的場景。
(4)經過廣播進行交互
(5)使用rxBus(輕量級通訊,缺點,發生錯誤難以debug)

5、IntentService有什麼優勢(與Service區別)

IntentService是Service的子類,是一個異步的,會自動中止的服務,很好解決了傳統的Service中處理完耗時操做忘記中止並銷燬Service的問題

  • 會建立獨立的worker線程來處理全部的Intent請求;
  • 會建立獨立的worker線程來處理onHandleIntent()方法實現的代碼,無需處理多線程問題;
  • 全部請求處理完成後,IntentService會自動中止,無需調用stopSelf()方法中止Service;
  • 爲Service的onBind()提供默認實現,返回null;
  • 爲Service的onStartCommand提供默認實現,將請求Intent添加到隊列中;
  • IntentService不會阻塞UI線程,而普通Serveice會致使ANR異常
  • Intentservice若未執行完成上一次的任務,將不會新開一個線程,是等待以前的任務完成後,再執行新的任務,等任務完成後再次調用stopSelf()

 

 

 

簡單實現

相關文章
相關標籤/搜索