android四大元件之service總結
Service通常總是稱之為“後臺服務”,其中“後臺”一詞是相對於前臺而言的,具體是指其本身的執行並不依賴於使用者可視的UI介面,因此,從實際業務需求上來理解,Service的適用場景應該具備以下條件:
1.並不依賴於使用者可視的UI介面(當然,這一條其實也不是絕對的,如前臺Service就是與Notification介面結合使用的);
2.具有較長時間的執行特性。 常見於下載等耗時任務。
一,service 和其他三個元件一樣,在應用時首先需要在AndroidMainfest檔案中註冊。
<service android:enabled=[“true” | “false”]
android:exported=[“true” | “false”]
android:icon=“drawable resource”
android:isolatedProcess=[“true” | “false”]
android:label=“string resource”
android:name=“string”
android:permission=“string”
android:process=“string” >
android:name對應Service類名,android:permission是許可權宣告,android:process設定具體的程序名稱,android:exported 是service是否可以被外部引用。
二,service的使用
service有兩種使用方式
1,startService 和stopService
開始service
intentService = new Intent(MainActivity.this,MyService.class);
intentService.putExtra(“name”,“我是 service!”);
startService(intentService);
關閉service stopService(intentService);在Service內部,也可以通過stopSelf(…)方式停止其本身。
涉及的service生命週期方法有
@Override
public void onCreate() {
super.onCreate();
Log.e(tag,“in onCreate”);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(tag, “in onStartCommand”);
String name = intent.getStringExtra(“name”);
Log.w(tag, “name:” + name);
return START_STICKY;
}
@Override
public void onDestroy() {
Log.e(tag,“onDestroy”);
super.onDestroy();
}
第一次啟動startService 生命週期方法呼叫情況如下:
2018-12-23 22:13:41.156 2276-2276/com.example.lcq.myapp E/MyService: in onCreate
2018-12-23 22:13:41.156 2276-2276/com.example.lcq.myapp W/MyService: in onStartCommand
2018-12-23 22:13:41.157 2276-2276/com.example.lcq.myapp W/MyService: name:我是 service!
再次點選 start後呼叫如下
2018-12-23 22:16:01.469 2276-2276/com.example.lcq.myapp W/MyService: in onStartCommand
2018-12-23 22:16:01.469 2276-2276/com.example.lcq.myapp W/MyService: name:我是 service!
由此可見 oncreate 方法只調用一次,而onStartCommand 可多次呼叫。
執行stopService方法呼叫方法如下:
2018-12-23 22:18:28.528 2276-2276/com.example.lcq.myapp E/MyService: onDestroy
public int onStartCommand(Intent intent, int flags, int startId) 返回一個int型的值
具體的可選值及含義如下:
START_NOT_STICKY:當Service因為記憶體不足而被系統kill後,接下來未來的某個時間內,即使系統記憶體足夠可用,系統也不會嘗試重新建立此Service。除非程式中Client明確再次呼叫startService(…)啟動此Service。
START_STICKY:當Service因為記憶體不足而被系統kill後,接下來未來的某個時間內,當系統記憶體足夠可用的情況下,系統將會嘗試重新建立此Service,一旦建立成功後將回調onStartCommand(…)方法,但其中的Intent將是null,pendingintent除外。
START_REDELIVER_INTENT:與START_STICKY唯一不同的是,回撥onStartCommand(…)方法時,其中的Intent將是非空,將是最後一次呼叫startService(…)中的intent。
2,bind 和unbind方式
這種方式要藉助於ServiceConnection 這個介面將使用者繫結後臺服務後,可獲取後臺服務代理物件的回撥,我們可以通過該回調,拿到後臺服務的代理物件,並呼叫後臺服務定義的方法,也就實現了後臺服務和前臺的互動,程式碼試下如下:
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.w(tag, “in MyServiceConnection onServiceConnected”);
myBinder = (MyService.MyBinder) service;
myService = myBinder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.w(tag, “in MyServiceConnection onServiceDisConnected”);
mBound = false;
}
}
private MyBinder myBinder = new MyBinder();
public class MyBinder extends Binder {
public MyService getService() {
Log.e(tag,“getService”);
return MyService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.e(tag,“in onBind”);
return myBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(tag,“unbind”);
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.e(tag,“onDestroy”);
super.onDestroy();
}
首次點選onbind log顯示如下:
2018-12-23 22:39:24.093 2276-2276/com.example.lcq.myapp E/MyService: in onCreate
2018-12-23 22:39:24.093 2276-2276/com.example.lcq.myapp E/MyService: in onBind
2018-12-23 22:39:24.094 2276-2276/com.example.lcq.myapp W/MainActivity: in MyServiceConnection onServiceConnected
2018-12-23 22:39:24.094 2276-2276/com.example.lcq.myapp E/MyService: getService
再次點選bind 不再執行 周期函式方法,說明MyBindService沒有進行任何回撥。
點選unbind 執行如下:
2018-12-23 22:42:01.418 2276-2276/com.example.lcq.myapp E/MyService: unbind
2018-12-23 22:42:01.418 2276-2276/com.example.lcq.myapp E/MyService: onDestroy
通過bind和unbind方法我們可以使用Messager來進行通訊,通過Messenger方式返回Binder物件可以不用考慮Clinet - Service是否屬於同一個程序的問題,並且,可以實現Client - Service之間的雙向通訊。極大方便了此類業務需求的實現。
public class SecondActivity extends BaseActivity {
private static final String tag =“SecondActivity”;
private boolean mBound;
private Messenger mServerMessager;
private Handler mClientHander = new MyClientHandler();
private Messenger mClientMessager = new Messenger(mClientHander);
class MyClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if(msg.what == MyMessagerService.MSG_FROM_SERVER_TO_CLIENT) {
Log.e(tag,“receive msg from server”);
Toast.makeText(SecondActivity.this,“從server 傳過來的訊息”,Toast.LENGTH_SHORT).show();
}
}
}
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(tag,“onServiceConnected”);
mServerMessager = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBound= false;
}
};
public void bindService(View view) {
Log.e(tag,“bindService”);
Intent intent = new Intent(SecondActivity.this,MyMessagerService.class);
bindService(intent,sc,Context.BIND_AUTO_CREATE);
}
public void unbindService(View view) {
Log.e(tag,“unbindService”);
excuteUnbindService();
}
public void sendMessager(View view) {
Log.e(tag,“sendMessager”);
sendMsg();
}
private void sendMsg() {
if(!mBound) {
return;
}
Message msg = Message.obtain(null, MyMessagerService.MSG_FROM_CLIENT_TO_SERVER,0,0);
msg.replyTo = mClientMessager;
try {
mServerMessager.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void excuteUnbindService() {
if (mBound) {
unbindService(sc);
mBound = false;
}
}
}
service類中的程式碼如下:
public class MyMessagerService extends Service {
public static final String TAG = “MyMessengerService”;
public static final int MSG_FROM_CLIENT_TO_SERVER = 1;
public static final int MSG_FROM_SERVER_TO_CLIENT = 2;
private Messenger mClientMessager;
private Messenger mSericeMessager = new Messenger(new ServerBinder());
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG,“onBind”);
return mSericeMessager.getBinder();
}
public class ServerBinder extends Handler {
@Override
public void handleMessage(Message msg) {
Log.w(TAG, “thread name:” + Thread.currentThread().getName());
switch (msg.what) {
case MSG_FROM_CLIENT_TO_SERVER:
mClientMessager = msg.replyTo;
Message toClinetMsg = Message.obtain(null,MSG_FROM_SERVER_TO_CLIENT);
try {
Log.w(TAG, “server begin send msg to client”);
Toast.makeText(MyMessagerService.this,“從Client 傳過來的訊息”,Toast.LENGTH_SHORT).show();
mClientMessager.send(toClinetMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
super.handleMessage(msg);
}
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG,“onUnbind”);
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.e(TAG,“onDestroy”);
super.onDestroy();
}
}
首先在service 和activity各定義了一個messager,然後通過bindService 將mServerMessager 通過ServiceConnection 繫結到activity,再點選sendMsg時又將activity的mClientMessager通過mServerMessager send到service,service 獲取到mClientMessager後又將mServerMessager send到activity 從而實現了activity和activity的雙向通訊。
3,IntentService
IntentService是專門用來解決Service中不能執行耗時操作這一問題的,建立一個IntentService也很簡單,只要繼承IntentService並覆寫onHandlerIntent函式,在該函式中就可以執行耗時操作了。
class MyIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass’s constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
//TODO 在這裡執行耗時操作
}
}
4,常見系統服務
系統服務提供了很多便捷服務,比如查詢Wifi、網路狀態、查詢電量、查詢音量、查詢包名、查詢Application資訊等等等相關多的服務,
1,判斷手機是否開啟wifi服務
WifiManager wifi = (WifiManager) getSystemService(WIFI_SERVICE);
boolean enabled = wm.isWifiEnabled();
2,獲取當前音量和最大音量
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
int current = am.getStreamMaxVolume(AudioManager.STREAM_RING);
int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
3,判斷網路是否連線
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
boolean isAvailable = info.isAvailable();