Android 進程間通訊之AIDL

#前言 因爲android系統中應用程序之間不能共享內存。所以,在不一樣應用程序之間交互數據(跨進程通信)就稍微麻煩一些。html

在android SDK中提供了4種用於跨進程通信的方式。這4種方式正好對應於android系統中4種應用程序組件:Activity、Content Provider、Broadcast和Service。java

  • Activity能夠跨進程調用其餘應用程序的Activity;
  • Content Provider能夠跨進程訪問其餘應用程序中的數據(以Cursor對象形式返回),固然,也能夠對其餘應用程序的數據進行增、刪、改操 做;
  • Broadcast能夠向android系統中全部應用程序發送廣播,而須要跨進程通信的應用程序能夠監聽這些廣播;
  • Service和Content Provider相似,也能夠訪問其餘應用程序中的數據,但不一樣的是,Content Provider返回的是Cursor對象,
  • Service返回的是Java對象,這種能夠跨進程通信的服務叫AIDL服務。

#AIDL介紹android

##AIDL 是什麼   AIDL (Android Interface Definition Language) 是一種IDL 語言,用於生成能夠在Android設備上兩個進程之間進行進程間通訊(interprocess communication, IPC)的代碼。若是在一個進程中(例如Activity)要調用另外一個進程中(例如Service)對象的操做,就可使用AIDL生成可序列化的參數。   AIDL IPC機制是面向接口的,像COM或Corba同樣,可是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。 ##AIDL 的做用   因爲每一個應用程序都運行在本身的進程空間,而且能夠從應用程序UI運行另外一個服務進程,並且常常會在不一樣的進程間傳遞對象。在Android平臺,一個進程一般不能訪問另外一個進程的內存空間,因此要想對話,須要將對象分解成操做系統能夠理解的基本單元,而且有序的經過進程邊界。   經過代碼來實現這個數據傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工做。git

##選擇AIDL的使用場合   github

官方文檔特別提醒咱們什麼時候使用AIDL是必要的:只有你容許客戶端從不一樣的應用程序爲了進程間的通訊而去訪問你的service,以及想在你的service處理多線程。   若是不須要進行不一樣應用程序間的併發通訊(IPC),you should create your interface by implementing a Binder;或者你想進行IPC,但不須要處理多線程的,則implement your interface using a Messenger。不管如何,在使用AIDL前,必需要理解如何綁定service——bindService。多線程

#實踐部分 Android Service是分爲兩種:併發

  本地服務(Local Service): 同一個apk內被調用   遠程服務(Remote Service):被另外一個apk調用app

遠程服務須要藉助AIDL來完成。ide

下面學習一下 做者:Panda Fang 的例子 連接工具

這是篇不錯的AIDL學習例子 下面用一個客戶端Activity操做服務端Service播放音樂的實例演示AIDL的使用。

##服務端 新建一個Android工程,命名爲AIDLPlayerserver。 包名爲com.ylbf.aidlplayerserver在res下的raw文件夾裏面放入一個音樂文件,我這裏放入的是 TheFatRat - Monody (feat. Laura Brehm).mp3的一個片斷。若是不存在raw這個文件夾就本身新建一個,命名爲raw。這個文件夾在raw文件夾下,與layout文件夾平級。raw中的文件遵照標識符的命名規則,不要出現中文和空格,多個單詞能夠用下劃線鏈接,我這裏改成monody.mp3

###目錄結構 這裏寫圖片描述

###IRemoteServiice.aidl 文件 新建一個IRemoteServiice.aidl 文件,加入以下代碼

package com.ylbf.aidlplayerserver;

interface IRemoteService {
 void play();
 void stop();
}

可見aidl文件的代碼跟java的interface同樣,可是aidl中不能加public等修飾符。Ctrl + S 保存後 ADT 會根據這個IRemoteService.aidl文件自動生成IRemoteService.java文件。如同R.java文件同樣在gen/包名下,代碼是自動生成的,不要手動修改。

接下來就是bound service的知識了。IRemoteService.java 中有一個Stub靜態抽象類extends Binder implements IRemoteService。本身動手寫一個PlayerService 用來播放音樂,播放音樂須要使用android.media.MediaPlayer類。

###PlayerService 代碼以下

/**
 * @category 播放音樂的服務
 * @author ylbf
 * @version 2016-02-18 14:35:48
 */
public class PlayerService extends Service {
	public static final String TAG = "PlayerService";
	private MediaPlayer mPlayer;
	private IBinder mBinder = new IRemoteServiice.Stub() {

		@Override
		public void stop() throws RemoteException {
			try {
				if (mPlayer.isPlaying()) {
					mPlayer.stop();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		@Override
		public void play() throws RemoteException {
			try {
				if (mPlayer.isPlaying()) {
					return;
				}
				// start以前須要prepare。
				// 若是前面實例化mplayer時使用方法一,則第一次play的時候直接start,不用prepare。
				// 可是stop一次以後,再次play就須要在start以前prepare了。
				// 前面使用方法二 這裏就簡便了, 不用判斷各類情況
				mPlayer.prepare();
				mPlayer.start();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};

	@Override
	public IBinder onBind(Intent arg0) {
		Log.i(TAG, "service onbind");
		if (mPlayer == null) {
			// 方法一說明
			// 此方法實例化播放器的同時指定音樂數據源 ,若用此方法在,mPlayer.start()
			// 以前不需再調用mplayer.prepare()
			// 官方文檔有說明 :On success, prepare() will already have been called and
			// must not be called again.
			// 譯文:一旦create成功,prepare已被調用,勿再調用 。查看源代碼可知create方法內部已經調用prepare方法。
			// 方法一開始
			// mPlayer = MediaPlayer.create(this, R.raw.monody);
			// 方法一結束

			// 方法二說明
			// 若用此方法,在mplayer.start() 以前須要調用mplayer.prepare()
			// 方法二開始
			mPlayer = new MediaPlayer();
			try {
				FileDescriptor fd = getResources().openRawResourceFd(R.raw.monody).getFileDescriptor(); // 獲取音樂數據源
				mPlayer.setDataSource(fd); // 設置數據源
				mPlayer.setLooping(true); // 設爲循環播放
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// 方法二結束
			Log.i(TAG, "player created");
		}
		return mBinder;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		if (mPlayer != null) {
			mPlayer.release();
		}
		Log.i(TAG, "service onUnbind");
		return super.onUnbind(intent);
	}

}

###AndroidManifest文件 服務編寫好之後,按照慣例在AndroidManifest.xml中加入聲明,代碼以下 須要加入的只是<service>...</service>那段,要注意的是 android:process=":remote"intent-filter

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ylbf.aidlplayerserver"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <service
            android:name=".PlayerService"
            android:process=":remote" >
            <intent-filter>
                <action android:name="com.ylbf.aidlplayerserver.PlayerService" />
            </intent-filter>
        </service>
    </application>

</manifest>

安裝運行服務端到設備上,準備給客戶端調用

安裝運行日誌

[2016-02-18 15:25:46 - AIDLPlayerserver] ------------------------------
[2016-02-18 15:25:46 - AIDLPlayerserver] Android Launch!
[2016-02-18 15:25:46 - AIDLPlayerserver] adb is running normally.
[2016-02-18 15:25:46 - AIDLPlayerserver] No Launcher activity found!
[2016-02-18 15:25:46 - AIDLPlayerserver] The launch will only sync the application package on the device!
[2016-02-18 15:25:46 - AIDLPlayerserver] Performing sync
[2016-02-18 15:25:46 - AIDLPlayerserver] Automatic Target Mode: Unable to detect device compatibility. Please select a target device.
[2016-02-18 15:25:50 - AIDLPlayerserver] Uploading AIDLPlayerserver.apk onto device 'xxxxxxxxxxxx'
[2016-02-18 15:25:50 - AIDLPlayerserver] Installing AIDLPlayerserver.apk...
[2016-02-18 15:25:53 - AIDLPlayerserver] Success!
[2016-02-18 15:25:53 - AIDLPlayerserver] \AIDLPlayerserver\bin\AIDLPlayerserver.apk installed on device
[2016-02-18 15:25:53 - AIDLPlayerserver] Done!

##客戶端

新建一個Android工程,命名爲AIDLPlayerClient,包名爲com.ylbf.aidlplayerclient。將服務端放有aidl文件的包直接copy到客戶端src目錄下,保留包中的aidl文件,其餘刪除。 ###目錄結構以下 這裏寫圖片描述 ###MainActivity.java代碼 編寫MainActivity.java 代碼以下

/**
 * 客戶端控制界面
 * 
 * @author ylbf
 * @version 2016-02-18 15:44:45
 */
public class MainActivity extends Activity implements OnClickListener {
	public static final String TAG = "MainActivity";

	/**
	 * 服務端 AndroidManifest.xml中的intent-filter action聲明的字符串
	 */
	public static final String ACTION = "com.ylbf.aidlplayerserver.PlayerService";
	private IRemoteServiice mService;
	private boolean isBinded = false;
	private Button playbtn, stopbtn;

	private ServiceConnection conn = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
			isBinded = false;
			mService = null;
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mService = IRemoteServiice.Stub.asInterface(service);
			isBinded = true;
		}
	};

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

	private void initViews() {
		playbtn = (Button) findViewById(R.id.button1);
		stopbtn = (Button) findViewById(R.id.button2);
		playbtn.setOnClickListener(this);
		stopbtn.setOnClickListener(this);
	}

	@Override
	protected void onDestroy() {
		doUnbind();
		super.onDestroy();
	}

	/**
	 * 綁定服務
	 */
	public void doBind() {
		Intent intent = new Intent(ACTION);
		bindService(intent, conn, Context.BIND_AUTO_CREATE);
	}

	/**
	 * 解綁服務
	 */
	public void doUnbind() {
		if (isBinded) {
			unbindService(conn);
			mService = null;
			isBinded = false;
		}
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == playbtn.getId()) {
			// play
			Log.i(TAG, "play button clicked");
			try {
				mService.play();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		} else {
			// stop
			Log.i(TAG, "stop button clicked");
			try {
				mService.stop();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}
}

運行客戶端到設備,按下按鈕能夠播放/中止 就能夠了

#項目源代碼 項目源代碼

#參考連接 Android Service學習之AIDL, Parcelable和遠程服務 2011-04-07;

相關文章
相關標籤/搜索