天天看點

android中Service元件總結

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的界面展示如下:

android中Service元件總結

點選“開啟SERVER”按鈕,控制台輸出的log如下:

android中Service元件總結

再次點選“開啟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元件總結

到這裡,android的四大元件之一Service就介紹完畢,謝謝大家的閱讀!

繼續閱讀