Android:Service(三)——Aidl綁定遠程服務

遠程服務

筆者一連寫了幾篇關於服務的博文,不是因爲篇幅太長,而是因爲偷懶~純屬爲了完成csdn的持之以恆每月四篇博客~~~
哈哈~好了回到正題,什麼是遠程服務呢?遠程服務筆者覺得是相對於本地服務來說的。本地服務就是運行在同一個進程上的服務,而遠程服務就是運行在其它進程上的服務。
不同進程間的數據一般都是獨立的,若要在不同進程間通訊就要通過特定的方法去溝通。
android就提供了aild(以下一段解釋來自百度百科):
AIDL:Android Interface Definition Language,即Android接口定義語言
Android系統中的進程之間不能共享內存,因此,需要提供一些機制在不同進程之間進行數據通信。
爲了使其他的應用程序也可以訪問本應用程序提供的服務,Android系統採用了遠程過程調用(Remote Procedure Call,RPC)方式來實現。與很多其他的基於RPC的解決方案一樣,Android使用一種接口定義語言(Interface Definition Language,IDL)來公開服務的接口。我們知道4個Android應用程序組件中的3個(Activity、BroadcastReceiver和ContentProvider)都可以進行跨進程訪問,另外一個Android應用程序組件Service同樣可以。因此,可以將這種可以跨進程訪問的服務稱爲AIDL(Android Interface Definition Language)服務。.

好了~看完這段簡單的描述~我們先去看看怎樣綁定遠程服務。
要實現這個示例,可以這麼做:
1.在一個android項目上定義一個運行在別的進程的service。
2.新建兩個android項目,一個用於綁定,一個作爲遠程服務被綁定。由於兩個不同的android項目,一般會運行在兩個不同的進程上的。

筆者就使用第二種方式講解


綁定遠程服務

首先新建一個android項目,作爲被綁定那個項目。然後在裏面新建一個Service:

public class RemoveService extends Service {

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return new MyBind();
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		Log.e("remoteService", "遠程服務創建了");
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		Log.e("remoteService", "遠程服務銷燬了");
	}

	private void sayHello() {
		Log.e("remote", "hello");
	}

	public class MyBind extends IService.Stub {
		public void callMethodInService() {
			sayHello();
		}
	}

}


步驟與綁定本地服務有點相似,也是在service裏面新建一個內部class但是這個內部類要繼承一個IService.Stub。這個IService不是系統自帶的,而是我們自己寫的~
接下來看看這個IService是怎樣寫的,首先新建一個接口文件IService,因爲我們是要通過aidl調用這個Service的方法,所以我們在這個接口文件上定義一個
void callMethodInService();的方法,方便遠程調用Service裏的方法,具體調用過程:在負責調用的那個app,通過MyBind的對象調用void callMethodInService();這個方法,然後void callMethodInService();這個方法可以再調用Service裏定義的方法。

package com.example.remoteservice;

  interface IService {
	 void callMethodInService();
}


好了現在IService已經定義出來了,那麼上文的MyBind繼承的是Stup,這個Stup在哪呢?
我們寫好IService之後,到IService的物理路徑上看,可以看到這是一個擴展名爲.java的文件,我們把它的擴展名改成aidl。然後回到eclipse(本文是基於eclipse,由於studio貌似不支持同時顯示多個項目,爲了方便操作,筆者決定使用eclipse),由衆多大神所寫的eclipse豈是浪得虛名的?如無意外eclipse又開始發神經了~我們剛纔修改的IService文件依然還是.java後綴名(或者已經消失了),沒事!我們按F5刷新一下,close project,open project,clean project都試過之後總會正常顯示的~尷尬我表示很無奈

變成aidl文件的IService:


然後我們再去項目中的gen目錄下看,可以看到多了一個IService.java的文件(如果沒有出現請繼續折騰~在gen目錄下刷新,clean,close,open)
點開這個新生成的文件:

public static abstract class Stub extends android.os.Binder implements com.example.remoteservice.IService
{
private static final java.lang.String DESCRIPTOR = "com.example.remoteservice.IService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
看到裏面正有suub類,而且已經幫我們繼承Binder並實現IService了~
最後在AndroidManifest.xml文件裏爲該服務定義一個action,方便遠程調用該service。

 <service android:name="com.example.remoteservice.RemoveService">
            <intent-filter>
                <action android:name="com.javy.remoteService"/>
            </intent-filter>
        </service>



好了,至此遠程服務寫完了。接下來就看綁定的那一端~呃~~有點拗口~我們就稱之爲客戶端吧~

同樣也是新建一個項目,然後由於方便演示,筆者寫了幾個按鈕


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_vertical"
    tools:context="com.example.bindremoteservice.MainActivity" >

   <Button 
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/bind"
       android:text="綁定遠程服務"
       />
   <Button 
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/unbind"
       android:text="解除綁定遠程服務"
       />
   <Button 
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/call"
       android:text="調用遠程服務的方法"
       />
   

</LinearLayout>
佈局很簡單~也不用看了~

想要進行aidl通訊則也要在客戶端的定義一個aidl文件,而這個aidl文件必須跟遠程服務端的一抹一眼,所在的包名也要一樣~



這個不再多說,接下來看MainActivity.java

package com.example.bindremoteservice;

import com.example.remoteservice.IService;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {
	private Button bind;
	private Button unbind;
	private Button call;
	private MyConn conn;
	private IService is;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}

	private void initView() {
		bind = (Button) findViewById(R.id.bind);
		unbind = (Button) findViewById(R.id.unbind);
		call = (Button) findViewById(R.id.call);
		bind.setOnClickListener(this);
		unbind.setOnClickListener(this);
		call.setOnClickListener(this);
		conn = new MyConn();
	}

	@Override
	public void onClick(View v) {
		if (v.equals(bind)) {
			Intent intent = new Intent();
			intent.setAction("com.javy.remoteService");
			intent.setPackage("com.example.remoteservicedemo");
			bindService(intent, conn, BIND_AUTO_CREATE);
		}
		if (v.equals(unbind)) {
			unbindService(conn);
		}
		if (v.equals(call)) {
			try {
				is.callMethodInService();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	private class MyConn implements ServiceConnection {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.e("bindlog", "已綁定");
			is = IService.Stub.asInterface(service);
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.e("", "已解綁");

		}

	}

}

嗯~挺長的代碼~其實也很簡單~忽略掉findViewById,忽略掉setOnclickListener剩下的代碼也不多了。
我們看onClick裏面的代碼:
if (v.equals(bind)) {
			Intent intent = new Intent();
			intent.setAction("com.javy.remoteService");
			intent.setPackage("com.example.remoteservicedemo");
			bindService(intent, conn, BIND_AUTO_CREATE);
		}
綁定的操作與綁定本地服務的操作差不多,不過也就是多了個setAction與setPackage。
這個setAction所傳進去的正是剛纔在服務端說定義的action,而setPackage則是服務端的包名,注意不是服務端的Service所在的包名,而是服務端的app包名。
這裏多說兩句的就是~在4.4之前只需要setAction就可以綁定遠程服務了,但是5.0之後就不能夠這樣綁定了,原因是什麼不安全~爲了兼容我們也寫上setpackage

解綁操作與解綁本地服務差不多這裏不說了。

最後就是獲取IService的對象並調用其中的方法。獲取IService對象的時候與本地綁定的獲取方式不同,本地綁定獲取的時候只需要版對象強轉就可以了,當事者這裏是不行的。這裏需要使用特定的方法:
is = IService.Stub.asInterface(service);

最後就是利用is調用相關的方法了。

我們來分別運行整兩個程序,我們主要看客戶端的界面:


(留意剛纔的代碼哪些地方有log)現在筆者點擊綁定遠程服務:


可以看到log顯示已經創建遠程服務了

然後現在點擊“調用遠程服務的方法”:


筆者在服務端的方法裏確實寫了一句log hello的代碼
現在點擊解除綁定
與綁定本地服務一樣,一旦解除綁定,對應的服務也會銷燬。
最後再試試是不是與之綁定的activity銷燬,遠程服務也隨之銷燬。現在筆者先點擊綁定然後退出客戶端:


可以看到確實如此。
好了,由於篇幅比較長,首尾難顧,因此筆者在此給出源碼DEMO:

DEMO下載!!!!!!!!!!!!














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章