[轉Android進程間通信--消息機制及IPC機制實現 ]

一、概念及說明
Android爲了屏蔽進程的概念,利用不同的組件[Activity、Service]來表示進程之間的通信!
組件間通信的核心機制是Intent,通過Intent可以開啓一個Activity或Service,不論這個Activity或Service是屬於當前應用還是其它應用的!
                                                 
                              本文如有bug,請指出啊!!大家一同進步!!
                                                             謝謝!!

Intent包含兩部分:
1、目的[action]--要往哪裏去
2、內容[category、data]--路上帶了些啥,區分性數據或內容性數據
Intent類型:
1、顯式--直接指定消息目的地,只適合同一進程內的不同組件之間通信
new Intent(this,Target.class)
2、隱式--AndroidMainifest.xml中註冊,一般用於跨進程通信
new Intent(String action)
 
二、實現-Intent簡單進程間通信
顯式的Intent較爲簡單!
 
如何實現隱式Intent呢?
在AndroidManifest.xml文件中定義<activity>
說明:
1、一個<activity>包括:
零個或多個<intent-filter>

它主要是作爲匹配的標準,能否匹配成功由<action>、<category>、<data>三個tag共同決定的。
 
2、一個<intent-filter>包括:
一個或多個 <action>
零個或多個 <category>  
指定<activity>的分類特徵
eg:
<category android:name="android.intent.category.LAUNCHER" />
--說明該<activity>是該project運行的第一個界面

<category android:name="android.intent.category.HOME" />
--說明該<activity>可以作爲Launcher的,即系統操作界面

<category android:name="android.intent.category.DEFAULT" />
 --缺省情況

零個或一個 <data>
-- 指定攜帶的數據的類型,使用MIME類型描述方式來描述
eg:
<data android:mimeType="video/mpeg" />
video/mpeg表示編碼格式爲mpeg的視頻,
也可以使用通配符video/*表示任意格式的視頻文件類型;
 
在查詢ContentProvider時,可以使用
<data android:mimeType="vnd.android.cursor.dir/vnd.myq.note" />
查詢上來的數據是多個記錄
<data android:mimeType="vnd.android.cursor.item/vnd.myq.note" />
查詢上來的數據是單個記錄
如上設置,要重寫SQLiteOpenHelper的getType(Uri uri)方法
eg:
 @Override
 public String getType(Uri uri) {
  final int match = sUriMatcher.match(uri) ;
  switch(match)
  {
  case NOTES :
  case LIVE_FOLDER_NOTES:
   return "vnd.android.cursor.dir/vnd.myq.note" ;
   
  case NOTES_ID :
   return "vnd.android.cursor.item/vnd.myq.note" ;
   
  default:
   throw new IllegalArgumentException("invalid uri : " + uri) ;
  }
 }
 
數據的URI由scheme(協議),host,port,path四部分:scheme://host:port/path
<data android:scheme="http://localhost:8080/test.jsp " />

3、一個Intent對應多種匹配結果的處理說明
 一個intent有多個可匹配的處理組件,系統如何處理?
分響應消息的組件類型:
1)如果是service那麼這些service都可以啓動並處理消息。
2)如果是Activity則會彈出一個對話框讓用戶進行選擇。
 
4、安全性問題
 如果不同進程間的組件可以通過隱式消息互相通信,那程序不是可以輕易調用到其他的程序或者系統中的一些敏感程序的組件,這樣會不會很不安全呢?
其實Android在安全方面有一個統一,完備和輕便的安全策略模型。

簡單一點說就是:權限設置問題
我們可以自己定義permission,然後在需要的組件處設置該permission,那麼用戶要想該組件,必須要配置該permission,否則訪問失敗的!

eg:
1、定義permission
<permission-group android:name="android.permission-group.MYQ_INFO"/>
<permission
     android:name="com.myq.android.permission.DATETIME_SERVICE"
     android:permissionGroup="android.permission-group.MYQ_INFO"
     android:protectionLevel="normal"
     />
 
2、配置permission
<service android:name=".DateTimeService" android:permission="com.myq.android.permission.DATETIME_SERVICE">
   <intent-filter>
 <action android:name="com.myq.android.MultiProcessTest.DATETIMESERVICE_ACTION" />
   </intent-filter>
</service>
 
3、使用permission
<uses-permission android:name="com.myq.android.permission.DATETIME_SERVICE"/>
 
三、IPC機制
有了Intent這種基於消息的進程內或進程間通信模型,我們就可以通過 Intent去開啓一個Service,可以通過Intent跳轉到另一個Activity,不論上面的Service或Activity是在當前進程還 是其它進程內即不論是當前應用還是其它應用的Service或Activity,通過消息機制都可以進行通信!

但是通過消息機制實現的進程間通信,有一個弊端就是,如果我們的Activity與Service之間的交往不是簡單的 Activity開啓Service操作,而是要隨時發一些控制請求,那麼必須就要保證Activity在Service的運行過程中隨時可以連接到 Service。

eg :音樂播放程序
後臺的播放服務往往獨立運行,以方便在使用其他程序界面時也能聽到音樂。同時這個後臺播放服務也會定義一個控制接口,比如播放,暫停,快進等方法,任何時候播放程序的界面都可以連接到播放服務,然後通過這組控制接口方法對其控制。
 
如上的需求僅僅通過Intent去開啓Service就無法滿足了!從而Android的顯得稍微笨重的IPC機制就出現了,然而它的出現只適 用於Activity與Service之間的通信,類似於遠程方法調用,就像是C/S模式的訪問,通過定義AIDL接口文件來定義一個IPC接 口,Server端實現IPC接口,Client端調用IPC接口的本地代理。

由於IPC調用是同步的,如果一個IPC服務需要超過幾毫秒的時間才能完成的話,你應該避免在Activity的主線程中調用,否則IPC調用會掛起應用程序導致界面失去響應。在 這種情況下,應該考慮單起一個線程來處理IPC訪問。

兩個進程間IPC看起來就象是一個進程進入另一個進程執行代碼然後帶着執行的結果返回。

IPC機制鼓勵我們“儘量利用已有功能,利用IPC和包含已有功能的程序協作完成一個完整的項目”
 
IPC實現demo:
我的
project -- MultiProcessTest
package -- com.myq.android.MultiProcessTest
 
1、AIDL文件,我是放在package下,
文件名稱爲:
IDateTimeService.aidl
文件內容爲:
package com.myq.android.MultiProcessTest ;
interface IDateTimeService
{
 String getCurrentDateTime(in String format) ;
}
 
如果正確配置,會在gen下,生成同名的java文件
簡單摘要:
//我們需要實現的類Stub
public interface IDateTimeService extends android.os.IInterface
{
...
public static abstract class Stub
extends android.os.Binder
implements com.myq.android.MultiProcessTest.IDateTimeService
{
 ...
//獲取實例的方法asInterface
public static com.myq.android.MultiProcessTest.IDateTimeService asInterface (android.os.IBinder obj)
{
  ...
}
 ...
}
//我們自己的業務方法,需要實現的
public java.lang.String getCurrentDateTime (java.lang.String format) throws android.os.RemoteException;
}
 
2、Service中實現IDateTimeService.Stub
eg:
package com.myq.android.MultiProcessTest;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
 
public class DateTimeService extends Service {
 
 public static final String DATETIME_SERVICE_ACTION = "com.myq.android.MultiProcessTest.DATETIMESERVICE_ACTION" ;
 
 private static final String TAG = "--------DateTimeService-------" ;
 
 private  SimpleDateFormat sdf ;
 
 private final IDateTimeService.Stub stub = new IDateTimeService.Stub()
 {
  
  public String getCurrentDateTime(String format) throws RemoteException {
   return getCurrentDateTimeString(format) ;
  }
 } ;
 
 private synchronized String getCurrentDateTimeString(String format)
 {
     sdf = new SimpleDateFormat(format) ;
     final String temp = sdf.format(new Date()) ;
   Log.i(TAG,"getCurrentDateTimeString--" + Thread.currentThread() + "--" + temp) ;
   return temp ;
 }
 
 public IBinder onBind(Intent arg0)
 {
  Log.i(TAG, "onBind--" + Thread.currentThread()) ;
  return stub;
 }
}

3、Client端代碼實現
private ServiceConnection mServiceConn = new ServiceConnection()
 {
  
  public void onServiceConnected(ComponentName name, IBinder service) {
   mDateTimeService = IDateTimeService.Stub.asInterface(service) ;
  }
  
  public void onServiceDisconnected(ComponentName name) {
   mDateTimeService = null ;
  }
 } ;
 
說明:
網上的好多資料都沒有涉及IPC調用的AIDL的具體說明!
它本質上是Server端和Client端都具有相同的AIDL文件,要位於相同的包下,即package的包名藥一樣,然後才能正確的通過proxy訪問,否則client與server的aidl文件處於不同package會出錯的。
 
aidl模型如下:
                |<--------------------aidl---------------------->|
 client端-->proxy  ----------parcel數據包-------- stub<---server端
從而proxy+parcel+stub構成了aidl.
只不過,proxy運行在客戶進程,而stub運行在服務端進程。
當你通過aidl去訪問服務端時,客戶端會阻塞在proxy,服務端處理完後,通知proxy返回。
 
四、附件及說明
1、
附件是我測試所用的demo,我用的系統是ubuntu9,Android2.2版本
基本功能:
可以根據用戶選擇的不同輸出格式輸出當前系統的時間。
2、
運行順序:
先運行Server端:MultiProcessTest
再運行Client端:MultiProcessTestClient
 
3、
注意點:
Server和Client端的AIDL文件必須要位於同package下,否則會出錯
安全性問題實現,權限控制--定義、配置、使用
異步處理問題--Handler
 
 

本文出自 “苗運齊的博客 ” 博客,請務必保留此出處http://myqdroid.blog.51cto.com/2057579/394189

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