部落格概述
在android項目中,使用activity與service的場景非常多,但是根據android的現有設計,如何實作這兩種元件協作的最佳實踐呢?本部落格就根據部落客在項目中的使用經驗來記錄個人了解的項目最佳實踐。
關鍵技術點
activity
activity直譯為活動,是android的四大元件之一,也是最重要用的最多的。他提供一個能讓使用者操作并與之互動的界面。一個應用有多個界面,也就有多個activity。打電話,發短信,拍照都是通過activity來做的。個人了解:activity就類似于servlet+jsp的組合體,xml是界面顯示,代碼具體控制行為。
service
service是一個應用元件,他用來在背景完成一個時間跨度比較大的工作任務,且不關聯任何的界面。類似mysql服務,不依賴任何界面。同理,activity沒了,沒有界面了,但是服務還在。如果背景關了,服務就完犢子了。服務并不是在分線程裡面運作,而是在背景運作。service的生命周期方法是在主線程裡面運作的。應用雖然退出(退到背景),但是service仍然在運作。背景指的是不跟頁面關聯。
一個service可以完成以下工作:播放音樂,完成io操作,通路網絡,大資料量的資料庫操作。
服務的特點:service在背景運作,不用與使用者進行互動。即使應用退出(背景),服務也不會停止。需要注意的是,service中的耗時操作一定要放在分線程。
最佳實踐
從service的的生命周期方法中,有一個關鍵的方法onStartCommand(),這個方法在service的生命周期中十分關鍵。每次activity或者其他元件調用startService()方法的時候,該方法(onStartCommand)都會被觸發。針對這種機制,可以很好套用某種設計模式的思路(指令模式),來實作service元件對外界指很好的執行,友善功能擴充。
對于具體應用,對某個規固定service的操作是有窮的,也就是說可以通過枚舉來指定service可以接受的指令,然後通過廣播把指令的執行結果進行傳回,這裡需要對執行結果做一下封裝。需要注意的是,這種廣播的方式可以針對通訊頻率不太高的場景(0.5s以上),對于高頻率的通訊場景,部落客沒有做測試。
最佳實踐的案例代碼
藍牙指令
/*
此對象用于activity發的指令
*/
public enum BluetoothCommand {
FIRST__LOGIN("FIRST__LOGIN"), //第一使用者登入
FIRST__LOGOUT("FIRST__LOGOUT"), //第一使用者退出
SECOND__LOGIN("SECOND__LOGIN"), //第二使用者登入
SECOND__LOGOUT("SECOND__LOGOUT");//第二使用者退出
private String value = "";
private BluetoothCommand(String value) { //必須是private的,否則編譯錯誤
this.value = value;
}
public static BluetoothCommand getEnumByString(String value) { //手寫的從string到enum的轉換函數
switch (value) {
case "FIRST__LOGIN":
return FIRST__LOGIN;
case "FIRST__LOGOUT":
return FIRST__LOGOUT;
case "SECOND__LOGIN":
return SECOND__LOGIN;
case "SECOND__LOGOUT":
return SECOND__LOGOUT;
default:
return null;
}
}
public String value() {
return this.value;
}
}
指令回報
*
此消息用于藍牙發廣播,在這裡定義msgtype,比如使用者登入成功,
*/
public class BluetoothMessage {
//心率類型
public static final int HEART_BEAT = 1;
//消息類型
private int msgType;
//字元串附加物
private String attachment;
public int getMsgType() {
return msgType;
}
public void setMsgType(int msgType) {
this.msgType = msgType;
}
public String getAttachment() {
return attachment;
}
public void setAttachment(String attachment) {
this.attachment = attachment;
}
//快速生成心率對象
public static BluetoothMessage heartBeatMsg(String attachment){
BluetoothMessage bluetoothMessage = new BluetoothMessage();
bluetoothMessage.setMsgType(HEART_BEAT);
bluetoothMessage.setAttachment(attachment);
return bluetoothMessage;
}
@Override
public String toString() {
return "BluetoothMessage{" +
"msgType=" + msgType +
", attachment='" + attachment + '\'' +
'}';
}
}
service的指令處理方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.d("收到了command");
//不支援BLE藍牙的裝置,下面就都不執行了
// if (!isBluetoothUseful){
//
// return super.onStartCommand(intent, flags, startId);
// }
String command = intent.getStringExtra("command");
if (command == null) return super.onStartCommand(intent, flags, startId); //初始化的時候
LogUtil.d("command != null ");
BluetoothCommand bluetoothCommand = BluetoothCommand.getEnumByString(command);
switch (bluetoothCommand){
case FIRST__LOGIN:
scanDevice();
break;
case FIRST__LOGOUT:
//下線所有裝置
disConnect();
break;
case SECOND__LOGIN:
LogUtil.d("onStartCommand-SECOND__LOGIN");
LoginBiz.getInstance().COUNT_DOWN_LATCH.countDown();
break;
}
return super.onStartCommand(intent, flags, startId);
}
總結
- 對于某些耗時操作要在子線程裡面完成。因為雖然service不跟任何activity綁定,但是他還是運作在主線程裡面的。
- 對于service的執行狀态要分情況讨論的場景,可以使用内部枚舉類,枚舉出service的status狀态,然後在執行指令時,判斷是否處于合适的狀态,如果不處于,那麼就不能執行指令。