Service是android四大元件之一,它與Activity非常類似,最大的差別就是Activity在前台運作,主要作用于界面的互動,而Service是在背景運作的一個服務,它沒有界面。
Service的建立步驟:
(1)定義一個繼承Service的子類;
(2)在AndroidManifest.xml檔案中配置該Service。
與activity類似,Service也有自己的生命周期方法,具體如下:
abstract IBinder onBind(Intent intent):該方法是重寫Service必須實作的一個方法,方法中傳回一個Binder對象,我們可以通過該Binder對象與Service進行通信。
void onCreate ():當Service第一次被建立成功後,将會回調該方法,主要做一些初始化操作。
void onDestroy():當Service被關閉之前會調用改方法,主要做一些Service的清理與儲存工作。
void onStartCommand():每次用戶端調用startService(Intent)後方法啟動Service時,Service都會回調改方法進行相關操作。
boolean onUnbind():當綁定改Service上的所有用戶端全都斷開連接配接時,将會調用改回回調方法。
下面舉例子介紹一下Service元件的簡單應用
1、Service的建立、配置、啟動與停止
MyService類:這個類隻是重寫了一些方法,并列印一些log,為了清楚說明Service啟動時的方法回調
<span style="font-family:FangSong_GB2312;font-size:18px;">import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service{
private String TAG = "MyService";
//必須實作,傳回IBinder對象,用于與Service元件進行通信,此處傳回null,具體後面詳細介紹
@Override
public IBinder onBind(Intent intent) {
return null;
}
//Service被建立時回調的方法
@Override
public void onCreate(){
Log.i(TAG, "建立Service調用onCreate()");
}
//Service被啟動時回調的方法
@Override
public int onStartCommand(Intent intent, int flags, int startId){
Log.i(TAG, "啟動Service調用onStartCommand");
/*
* 這裡傳回狀态有三個值,分别是:
* 1、START_STICKY:當服務程序在運作時被殺死,系統将會把它置為started狀态,但是不儲存其傳遞的Intent對象,之後,系統會嘗試重新建立服務;
* 2、START_NOT_STICKY:當服務程序在運作時被殺死,并且沒有新的Intent對象傳遞過來的話,系統将會把它置為started狀态,
* 但是系統不會重新建立服務,直到startService(Intent intent)方法再次被調用;
* 3、START_REDELIVER_INTENT:當服務程序在運作時被殺死,它将會在隔一段時間後自動建立,并且最後一個傳遞的Intent對象将會再次傳遞過來。
*/
return START_STICKY;
}
//Service銷毀時回調的方法
@Override
public void onDestroy(){
super.onDestroy();
Log.i(TAG, "銷毀Service調用onDestroy()");
}
}</span>
定義了Service之後需要在AndroidManifest.xml檔案中配置該Service,即在AndroidManifest.xml檔案中application标簽内增加如下的配置片段來配置service:
<span style="font-family:FangSong_GB2312;font-size:18px;"><!-- 配置一個service元件 -->
<service android:name=".MyService">
<intent-filter>
<!-- 為該元件的intent-filter配置action -->
<action android:name="com.example.MY_SERVER"/>
</intent-filter>
</service></span>
當service開發完成之後,接下來就可以在程式中運作service了,android中運作service有如下兩種方法:
1)通過Context的startService()方法來啟動service,用該方法啟動的service,通路者與改Service沒有關聯,當通路者退出了,Service仍然運作。
2)通過Context的bindService()方法啟動service,用該方法啟動service,通路者與Service綁定在一起,通路者一旦退出,service也就終止。
下面程式使用activity作為service的通路者,界面中有兩個按鈕,布局檔案很簡單,這裡就不給出,一個按鈕用于啟動service,一個按鈕用于關閉service
代碼如下MainActivity:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class MainActivity extends Activity implements OnClickListener{
private Button start_service;
private Button stop_service;
private TextView text;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
text = (TextView) findViewById(R.id.text);
start_service = (Button) findViewById(R.id.start_service);
stop_service = (Button) findViewById(R.id.stop_service);
start_service.setOnClickListener(this);
stop_service.setOnClickListener(this);
//建立啟動service的Intent
intent = new Intent(MainActivity.this, MyService.class);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.start_service:
//啟動service
startService(intent);
text.setText("start_service");
break;
case R.id.stop_service:
//停止service
stopService(intent);
text.setText("stop_service");
break;
}
}
}</span>
activity的界面展示如下:
點選“開啟SERVER”按鈕,控制台輸出的log如下:
再次點選“開啟SERVER”按鈕,控制台會列印啟動"Service調用onStartCommand";
點選“停止SERVICE”按鈕,控制台會列印“銷毀Service調用onDestroy()”。
這裡就不截圖了,讀者可以自己運作上面代碼
2、綁定本地的Service并與之通信
在現實應用中,我們經常需要在Service執行一些操作,并在activity中擷取操作的傳回值,也就是要求能與Service進行通信。
我們前面寫的程式通過startService()和stopService()啟動、關閉Service時,Service和通路者之間基本不存在太多的關聯,是以Service和通路者之間也就無法進行通信和資料交換。
如果我們需要通路者和Service之間能進行通信,則可以使用bindService()和unbindService()方法啟動和關閉服務。
bindService方法:bindService(Intent service, ServiceConnection conn, int flags),該方法有三個參數,下面對這三個參數逐一介紹:
1)service:該參數通過Intent指向要啟動的Service。
2)conn:該參數是一個ServiceConnection對象,該對象用于監聽通路者與Service之間的連接配接情況。當連接配接成功是會回調該對象的onServiceConnected(ComponentName name, IBinder service)方法,其中service就是Service類OnBind的傳回值;當連接配接斷開時将會回調該對象的onServiceDiscinnected(ComponentName name)方法。
3)flags:指定綁定時是否自建立Service(如果Service還未建立)。0為不建立、BIND_AUTO_CREATE為自建立。
注意上面第二個參數對象的onServiceConnected方法有個IBinder對象,該對象即可實作與綁定的Server進行通訊。
在實際開發中,我們通常會繼承Binder(IBinder的實作類)來實作自己的IBinder對象,下面我們來看一個執行個體:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class BindService extends Service{
private int count;
private boolean quit;
private MyBinder binder = new MyBinder();
public class MyBinder extends Binder{
public int getCount(){
//擷取Service中的count
return count;
}
}
//必須實作的方法,傳回IBinder對象
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
Log.i("BindService", "onCreate");
super.onCreate();
count = 0;
quit = false;
//啟動一個線程,動态更新count的值
new Thread(){
@Override
public void run(){
while(!quit){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
//Log.i("BindService", count+"-------");
}
}
}.start();
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
this.quit = true;
super.onDestroy();
}
}</span>
上面的Service類中我們必須實作onBind方法傳回一顆可以通路Service的count值得IBinder對象,該對象會被Service傳回給調用者。在oncreate方法中,我們建立另一個線程,用時更新count的值。值得注意的事,由于server也是運作在主線程的,是以不能做耗時的操作,如果需要執行耗時的操作,需要建立子線程開完成。
下面我們來看一下通過Activity綁定Service,具體代碼如下:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class SeconActivity extends Activity implements View.OnClickListener{
private TextView text;
private Button bind;
private Button unbind;
private Intent intent;
private boolean quit = false;
private BindService.MyBinder binder;
private Handler hander = new Handler(){
@Override
public void handleMessage(Message msg) {
//收到消息後更新UI(關于Handler,後面的部落格會具體講解)
text.setText(msg.arg1+"");
}
};
private ServiceConnection conn = new ServiceConnection() {
//通路者與Service連接配接成功時将回調此方法,得到IBinder對象來與Service進行通信
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("Activity", "---onServiceConnected----");
quit = true;
binder = (MyBinder) service;
//拿到binder後,啟動一個新線程,來每個一秒從Service取出count的值
new Thread(action).start();
}
//通路者與Service斷開連接配接将回調此方法
@Override
public void onServiceDisconnected(ComponentName name) {
quit = false;
Log.i("Activity", "---onServiceDisconnected----");
}
};
private Runnable action = new Runnable() {
@Override
public void run() {
while(quit){
try {
Thread.sleep(1000);
if(binder != null){
//取到count值後,向主線程發送一個消息,讓主線程來更新UI
Message message = new Message();
message.arg1 = binder.getCount();
hander.sendMessage(message);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
initView();
}
private void initView() {
intent = new Intent();
intent.setAction("com.example.MY_SERVER");
bind = (Button) findViewById(R.id.bind);
unbind = (Button) findViewById(R.id.unbind);
text = (TextView) findViewById(R.id.text);
bind.setOnClickListener(this);
unbind.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind:
//綁定Service
if (!bindService(intent, conn, Service.BIND_AUTO_CREATE)) {
Log.e("bindService", "Could not bind to Bluetooth AVRCP CT Service");
}
else
{
Log.d("bindService","bind successfully"+binder);
}
//bindService(intent, conn, Service.BIND_AUTO_CREATE);
break;
case R.id.unbind:
text.setText("取消綁定");
unbindService(conn);
break;
default:
break;
}
}
}</span>
代碼中有詳細的解釋,大家可以研讀一下。代碼主要做的事情就是通過Activity綁定一個Service,然後通過得到的Binder來通路Service中的資源。
對于Service的onBind()方法所傳回的IBinder對象來講,他可以被當成該Service元件遂傳回的回調對象,Service允許用戶端使用者通過IBinder對象來通路内部的資料,這樣就可以實作用戶端與Service之間通信。
3.跨程序調用Service
Android中,各應用程式都運作在自己的程序中,程序之間一般無法直接進行資料交換的,我們上面是現在的代碼是指Activity和Service在同一個程序,這樣就可以友善的通信,可如果不在一個程序,那麼要進行通信就不是那麼容易了。
android跨程序通信的方式很多,比如Bundle、檔案共享、AIDL、Messenger、ContentProvider、Socket以及Broadcast方式。其中AIDL和Messenger用的是比較多的,他們都是基于binder實作的,具體細節這裡就不多講了,我以後會專門出類似的部落格,請關注。
由于AIDL比較複雜,這裡我選用Messenger來實作跨程序Server通信,Messenger實際是對AIDL的一次封裝。
首先我們看一下服務端,我們建立一個Service來處理用戶端請求,并且收到使用者請求後向使用者傳回一條消息,具體代碼如下:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class MessengerService extends Service{
private Handler MessengerHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i("MessengerService", msg.getData().getString("msg"));
//拿到用戶端傳過來的Messenger對象
Messenger client = msg.replyTo;
//封裝要反悔給用戶端的消息
Message replyMessage = Message.obtain(null, 2);
Bundle bundle = new Bundle();
bundle.putString("reply", "恩,收到您的消息!");
replyMessage.setData(bundle);
try {
//利用用戶端的Messenger為用戶端發送消息
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
};
//建立服務端Messenger
private final Messenger mMessenger = new Messenger(MessengerHandler);
@Override
public IBinder onBind(Intent intent) {
Log.i("Service", "onBind");
//向用戶端傳回Ibinder對象,用戶端利用該對象通路服務端
return mMessenger.getBinder();
}
@Override
public void onCreate() {
Log.i("Service", "onCreate");
super.onCreate();
}
}</span>
然後注冊Service,讓其在單獨的程序中運作
<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="white-space:pre"> </span><service android:name=".Messager.MessengerService"
android:process=":remote">
<intent-filter>
<action android:name="com.example.MESSENGER_SERVER"/>
</intent-filter>
</service> </span>
接下來就看用戶端了,用戶端實作也比較簡單,首先綁定遠端程序的Service,綁定成功後,根據Service傳回的IBinder對象建立Messenger對象,并使用此對象發送消息,為了能收到Service端傳回的消息,用戶端建立了一個資深的Messenger發送給Service端,Service端就可以通過用戶端的Messenger想用戶端發送消息了,具體代碼如下:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class MessengerActivity extends Activity{
private Messenger mService;
private Button bind;
private TextView text;
private Intent intent;
private ServiceConnection conn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//根據得到的IBinder對象建立Messenger
mService = new Messenger(service);
Message msg = Message.obtain(null, 1);
Bundle bundle = new Bundle();
bundle.putString("msg", "您好,Service");
msg.setData(bundle);
msg.replyTo = mGetMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//為了收到Service的回複,用戶端需要建立一個接收消息的Messenger和Handler
private Handler MessengerHander = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 2:
Log.i("MessengerActivity", msg.getData().getString("reply"));
text.setText(msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
};
private Messenger mGetMessenger = new Messenger(MessengerHander);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
init();
}
private void init() {
intent = new Intent(MessengerActivity.this, MessengerService.class);
bind = (Button) findViewById(R.id.bind);
text = (TextView) findViewById(R.id.text);
bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//bindService(intent, conn, Context.BIND_AUTO_CREATE);
if (!bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
Log.e("bindService", "Could not bind to Bluetooth AVRCP CT Service");
}
else
{
Log.d("bindService","bind successfully");
}
}
});
}
@Override
protected void onDestroy(){
unbindService(conn);
super.onDestroy();
}
}
</span>
代碼看起來比較長,可是很簡單,相信讀者都能讀懂。我們運作程式後,看一下log,很閑人用戶端向Service發送了一條消息,也收到了Service傳回的消息,這說明我們跨程序和Service通信實作成功。
到這裡,android的四大元件之一Service就介紹完畢,謝謝大家的閱讀!