Android 進(jìn)程間通信實現(xiàn)原理分析
Android Service是分為兩種:
本地服務(wù)(Local Service): 同一個apk內(nèi)被調(diào)用
遠(yuǎn)程服務(wù)(Remote Service):被另一個apk調(diào)用
遠(yuǎn)程服務(wù)需要借助AIDL來完成。
AIDL 是什么
AIDL (Android Interface Definition Language) 是一種IDL 語言,用于生成可以在Android設(shè)備上兩個進(jìn)程之間進(jìn)行進(jìn)程間通信(interprocess communication, IPC)的代碼。如果在一個進(jìn)程中(例如Activity)要調(diào)用另一個進(jìn)程中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數(shù)。
AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現(xiàn)端傳遞數(shù)據(jù)。
AIDL 的作用
由于每個應(yīng)用程序都運行在自己的進(jìn)程空間,并且可以從應(yīng)用程序UI運行另一個服務(wù)進(jìn)程,而且經(jīng)常會在不同的進(jìn)程間傳遞對象。在Android平臺,一個進(jìn)程通常不能訪問另一個進(jìn)程的內(nèi)存空間,所以要想對話,需要將對象分解成操作系統(tǒng)可以理解的基本單元,并且有序的通過進(jìn)程邊界。
通過代碼來實現(xiàn)這個數(shù)據(jù)傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工作。
選擇AIDL的使用場合
官方文檔特別提醒我們何時使用AIDL是必要的:只有你允許客戶端從不同的應(yīng)用程序為了進(jìn)程間的通信而去訪問你的service,以及想在你的service處理多線程。
如果不需要進(jìn)行不同應(yīng)用程序間的并發(fā)通信(IPC),you should create your interface by implementing a Binder;或者你想進(jìn)行IPC,但不需要處理多線程的,則implement your interface using a Messenger。無論如何,在使用AIDL前,必須要理解如何綁定service——bindService。
下面將要講到的這個例子來自:http://www.cnblogs.com/lonkiss/archive/2012/10/23/2735548.html
下面用一個客戶端Activity操作服務(wù)端Service播放音樂的實例演示AIDL的使用。
服務(wù)端代碼結(jié)構(gòu)(左) 客戶端代碼結(jié)構(gòu)(右)被標(biāo)記的就是需要動手的。
服務(wù)端
新建一個android application project,命名為PlayerServer。 在res下的raw文件夾里面放入一個音樂文件,我這里放入的是Delta Goodrem的《Lost Without You》片段。如果不存在raw這個文件夾就自己新建一個,命名為raw。這個文件夾在raw文件夾下,與layout文件夾平級。raw中的文件遵守標(biāo)識符的命名規(guī)則,不要出現(xiàn)中文和空格,多個單詞可以用下劃線連接。
新建一個IRemoteServiice.aidl 文件,加入如下代碼
package pandafang.demo.playerserver;
interface IRemoteService {
void play();
void stop();
}
可見aidl文件的代碼跟java的interface一樣,但是aidl中不能加public等修飾符。Ctrl + S 保存后 ADT 會根據(jù)這個IRemoteService.aidl文件自動生成IRemoteService.java文件。如同R.java文件一樣在“gen/包名”下,代碼是自動生成的,不要手動修改。
接下來就是bound service的知識了。IRemoteService.java 中有一個Stub靜態(tài)抽象類extends Binder implements IRemoteService。自己動手寫一個PlayerService 用來播放音樂,播放音樂需要使用
android.media.MediaPlayer類。代碼如下
/**
* 播放音樂的服務(wù)
*/
public class PlayerService extends Service {
public static final String TAG = "PlayerService";
private MediaPlayer mplayer;
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG,"service onbind");
if(mplayer==null){
// 方法一說明
// 此方法實例化播放器的同時指定音樂數(shù)據(jù)源 ,若用此方法在,mplayer.start() 之前不需再調(diào)用mplayer.prepare()
// 官方文檔有說明 :On success, prepare() will already have been called and must not be called again.
// 譯文:一旦create成功,prepare已被調(diào)用,勿再調(diào)用 。查看源代碼可知create方法內(nèi)部已經(jīng)調(diào)用prepare方法。
// 方法一開始
// mplayer = MediaPlayer.create(this, R.raw.lost);
// 方法一結(jié)束
// 方法二說明
// 若用此方法,在mplayer.start() 之前需要調(diào)用mplayer.prepare()
// 方法二開始
mplayer = new MediaPlayer();
try {
FileDescriptor fd = getResources().openRawResourceFd(R.raw.lost).getFileDescriptor(); // 獲取音樂數(shù)據(jù)源
mplayer.setDataSource(fd); // 設(shè)置數(shù)據(jù)源
mplayer.setLooping(true); // 設(shè)為循環(huán)播放
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 方法二結(jié)束
Log.i(TAG,"player created");
}
return mBinder;
}
// 實現(xiàn)aidl文件中定義的接口
private IBinder mBinder = new IRemoteService.Stub() {
@Override
public void stop() throws RemoteException {
try {
if (mplayer.isPlaying()) {
mplayer.stop();
}
} catch (Exception e) {
// TODO: handle exception
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) {
// TODO: handle exception
e.printStackTrace();
}
}
};
@Override
public boolean onUnbind(Intent intent) {
if (mplayer != null) {
mplayer.release();
}
Log.i(TAG,"service onUnbind");
return super.onUnbind(intent);
}
}
服務(wù)編寫好以后,按照慣例在AndroidManifest.xml中加入聲明,代碼如下
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pandafang.demo.playerserver"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
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.example.playerserver.PlayerService"/>
</intent-filter>
</service>
</application>
</manifest>
需要加入的只是<service>...</service>那段,要注意的是 android:process=":remote" 和 intent-filter 。
運行服務(wù)端到設(shè)備上,準(zhǔn)備給客戶端調(diào)用
客戶端
新建一個android application project,命名為PlayerClient。將服務(wù)端放有aidl文件的包直接copy到客戶
端src目錄下,保留包中的aidl文件,其他刪除。
編寫MainActivity.java 代碼如下
/**
* 客戶端控制界面
*/
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
// 服務(wù)端 AndroidManifest.xml中的intent-filter action聲明的字符串
public static final String ACTION = "com.example.playerserver.PlayerService";
private Button playbtn, stopbtn;
private IRemoteService mService;
private boolean isBinded = false;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
isBinded = false;
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IRemoteService.Stub.asInterface(service);
isBinded = true;
}
};
@Override
public 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(clickListener);
stopbtn.setOnClickListener(clickListener);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@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;
}
}
private OnClickListener clickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (v.getId() == playbtn.getId()) {
// play
Log.i(TAG,"play button clicked");
try {
mService.play();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
// stop
Log.i(TAG,"stop button clicked");
try {
mService.stop();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
}
MainActivity是根據(jù)向?qū)ё詣由傻?,不需要在AndroidManifest.xml中注冊聲明了
運行客戶端到設(shè)備,按下按鈕可以播放/停止 效果如圖

如果想對更加詳細(xì)的實現(xiàn)原理進(jìn)行研究,可以參見這篇文章:
http://www.cnblogs.com/over140/archive/2011/03/08/1976890.html
- Android進(jìn)程通信之Messenger和AIDL使用詳解
- Android IPC機制利用Messenger實現(xiàn)跨進(jìn)程通信
- 深入理解Android中的Handler異步通信機制
- 深入Android Handler與線程間通信ITC的詳解
- Android Handler主線程和一般線程通信的應(yīng)用分析
- Android Socket通信詳解
- Android編程之客戶端通過socket與服務(wù)器通信的方法
- Android單片機與藍(lán)牙模塊通信實例代碼
- Android之網(wǎng)絡(luò)通信案例分析
- Android網(wǎng)絡(luò)編程之UDP通信模型實例
- Android開發(fā)使用Messenger及Handler進(jìn)行通信的方法示例
相關(guān)文章
Android應(yīng)用創(chuàng)建桌面快捷方式代碼
這篇文章主要為大家詳細(xì)介紹了Android應(yīng)用創(chuàng)建桌面快捷方式代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09Android編程獲取并設(shè)置Activity亮度的方法
這篇文章主要介紹了Android編程獲取并設(shè)置Activity亮度的方法,涉及Android針對屏幕亮度的相關(guān)操作技巧,需要的朋友可以參考下2015-12-12Android實現(xiàn)輪播圖無限循環(huán)效果
這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)輪播圖無限循環(huán)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02android中圖片的三級緩存cache策略(內(nèi)存/文件/網(wǎng)絡(luò))
實現(xiàn)圖片緩存也不難,需要有相應(yīng)的cache策略。這里我采用 內(nèi)存-文件-網(wǎng)絡(luò) 三層cache機制,其中內(nèi)存緩存包括強引用緩存和軟引用緩存(SoftReference),其實網(wǎng)絡(luò)不算cache,這里姑且也把它劃到緩存的層次結(jié)構(gòu)中2013-06-06Android中g(shù)ravity與layout_gravity的使用區(qū)別分析
本篇文章介紹了,在Android中g(shù)ravity與layout_gravity的使用區(qū)別分析。需要的朋友參考下2013-04-04