1. 程式人生 > >Activity呼叫Service裡的方法

Activity呼叫Service裡的方法

Service是不能被例項化的,那麼我們如果想呼叫裡邊的方法,該怎樣進行呢?

首先看一個Serviece類

public class SingService extends Service{

	@Override
	public IBinder onBind(Intent intent) {
		// TODO 自動生成的方法存根
		return null;
	}
	@Override
	public void onCreate() {
		// TODO 自動生成的方法存根
		super.onCreate();
		System.out.print("服務啟動,開始唱歌了");
	}
	@Override
	public void onDestroy() {
		// TODO 自動生成的方法存根
		super.onDestroy();
		System.out.print("服務結束,停止唱歌了");
	}
	//自定義的方法
	public void ChangeSong(String songName){
		Toast.makeText(getApplicationContext(), "切換歌曲", 0).show();
	}
	
}
Activity的程式碼部分如下:由於這些方法都是按鈕的點選事件,所以傳入引數View(例如在XML檔案中android:click = "start")
public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}	
	/*
	 * 開啟服務
	 * */
	public void start(View view){
		Intent intent = new Intent(this, SingService.class);
		//採用api建立服務,服務物件是被系統(Android 框架)new 出來的
		startService(intent);
	}
	/*
	 * 停止服務
	 * */
	public void stop(View view){
		Intent intent = new Intent(this, SingService.class);
		stopService(intent);
	}
	/*
	 * 呼叫服務裡的方法,換首歌,由於Service是系統建立的,我們不能NEW物件呼叫方法
	 * */
	public void change(View view){
		//由於系統框架在建立服務的時候,會建立與之對應的上下文,下面的程式碼是錯誤的
//		SingService service = new SingService();
//		service.ChangeSong("月亮之上");
		
	}
}
那麼我們怎樣才能呼叫Service裡的方法呢?可以採用代理人模式,不直接呼叫,而是通過與之有關係的代理人來實現。

我們啟動Service,除了StartService之外,還可以通過bindService的方法,得到代理人物件。

首先看Service類的寫法有什麼不一樣

public class SingService extends Service{

	@Override
	<strong>//步驟二:服務在成功繫結的時候會調動onBind方法,返回一個IBinder物件</strong>
<span style="font-size:12px;">	</span><span style="font-size:14px;"><span style="color:#ff0000;">public IBinder onBind(Intent intent) {
		// 當服務被成功繫結的時候,系統自動呼叫該方法
		System.out.print("服務成功被繫結");
		//返回自定義的代理人物件,這個很關鍵
		return new MyBinder();
	}</span>
	
<span style="color:#ff0000;">	public class MyBinder extends Binder{
		//簡介的利用代理人,呼叫了唱歌的方法
		<strong>public  void CallChangeSong(String name){
			ChangeSong(name);</strong>
		}
	}</span>	</span>
	@Override
	public void onCreate() {
		// TODO 自動生成的方法存根
		super.onCreate();
		System.out.print("服務啟動,開始唱歌了");
	}
	@Override
	public void onDestroy() {
		// TODO 自動生成的方法存根
		super.onDestroy();
		System.out.print("服務結束,停止唱歌了");
	}
	//自定義的方法
	public void ChangeSong(String songName){
		Toast.makeText(getApplicationContext(), "切換歌曲", 0).show();
	}
}
Activity裡的寫法:
public class MainActivity extends Activity {
	<strong>//步驟四:在Activity裡面得到服務IBinder物件的引用</strong>
	private SingService.MyBinder myBinder;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}	
	
	public void change(View view){
	<strong>//步驟五:利用iBinder物件間接的呼叫了服務裡的方法</strong>
		myBinder.CallChangeSong("月亮之上");
	}
	
	<strong>//步驟一:採用繫結服務的方式開啟服務</strong>
	public void bind(View view){
		Intent intent = new Intent(this, SingService.class);
		bindService(intent, new MyConn(), BIND_AUTO_CREATE);
	}
	
	//建立一個內部類,代理人
	private class MyConn implements ServiceConnection{
		//在服務被成功繫結時候呼叫的方法
		@Override
		public void onServiceConnected(ComponentName name,
				IBinder service) {
			System.out.println("把代理人返回來了");
			<strong>//第三步:服務返回的IBinder物件會被傳遞給myConn的回撥方法</strong>
			myBinder = (MyBinder) service;
			
		}
		//在服務失去繫結時候呼叫的方法,正常情況下不會被呼叫,只有在程序異常終止時候才會呼叫。
		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO 自動生成的方法存根
			
		}
		
	}
	
}
整個的流程如下:

但是在實際的開發中,我們不可能這麼直接去寫,把代理人寫在Service裡,那肯定是不行的,而且不能直接定義成public讓外部去訪問,我們通常是建立一個介面,通過暴露介面的方式來實現。

public interface Iservice {
	public void callChangeSong(String name);
}
單獨寫一個介面,統一規範作用

在Service中,把MyBinder類去實現這個介面

	private class MyBinder extends Binder implements Iservice{
		@Override
		public void callChangeSong(String name) {
			// TODO 自動生成的方法存根
			ChangeSong(name);
		}
	}

在Activity中做如下修改:

private Iservice myBinder;
myBinder = (Iservice) service;

如果我們沒有繫結服務,那麼Activity退出了,服務還在,但是如果綁定了,那麼Activity退出,服務跟著也馬上退出。

服務的混合開啟:

需求:既要想保證服務長期在後臺執行,又想去呼叫服務裡的方法,怎麼辦呢?

技巧:1、先開啟服務——這樣就可以保證服務長期執行(不繫結)   2、繫結服務——這樣就可以呼叫裡邊的方法了    

服務只會被建立一次,也就是當我們如果先開啟了服務,會執行OnCreate方法,當再繫結服務的時候,onCreate方法不會再執行,直接執行onBind方法。

注意事項:

當我們繫結服務了之後,如果直接退出,會報出異常的錯誤,為什麼呢?因為我們綁定了服務,直接退出會顯示異常,怎樣解決呢?也就是在我們退出的時候,要把繫結的服務解除掉。

定義一個方法:

	//解除繫結服務的方法
	public void unBind(View view){
		unbindService(conn);
	}
這時候在Service中會呼叫onUnbind方法,如下:
	public boolean onUnbind(Intent intent) {
		// TODO 自動生成的方法存根
		System.out.println("解除繫結");
		return super.onUnbind(intent);
	}

服務一旦被繫結,通過StopService方法是不能停止掉的,只能通過解綁之後,才能停止,服務先解綁,然後直接onDestory了,所以綁定了服務,記得要解綁。如果點選多次繫結服務,只會呼叫一次onBind方法,但是如果呼叫多次解綁方法,程式第一次會呼叫解綁,後邊就會報錯。

為了防止忘記解綁服務,我們通常在Activity的onDestory中呼叫解綁服務的方法,這樣就能保證應用程式在退出的時候,自動把程式解綁掉,但是如果程式設計師比較勤奮,之前已經解綁過了,那如果再解綁就會報錯,怎麼辦呢?通常情況下我們可以用try_catch方法包圍解綁,這樣就比較安全了。