一、概念及說明
Android為了遮蔽程式的概念,利用不同的元件[Activity、Service]來表示程式之間的通訊!
元件間通訊的核心機制是Intent,通過Intent可以開啟一個Activity或Service,不論這個Activity或Service是屬於當前應用還是其它應用的!
本文如有bug,請指出啊!!大家一同進步!!
謝謝!!
Intent包含兩部分:
1、目的[action]–要往哪裡去
2、內容[category、data]–路上帶了些啥,區分性資料或內容性資料
Intent型別:
1、顯式–直接指定訊息目的地,只適合同一程式內的不同元件之間通訊
1、顯式–直接指定訊息目的地,只適合同一程式內的不同元件之間通訊
new Intent(this,Target.class)
2、隱式–AndroidMainifest.xml中註冊,一般用於跨程式通訊
2、隱式–AndroidMainifest.xml中註冊,一般用於跨程式通訊
new Intent(String action)
二、實現-Intent簡單程式間通訊
顯式的Intent較為簡單!
如何實現隱式Intent呢?
在AndroidManifest.xml檔案中定義<activity>
說明:
1、一個<activity>包括:
零個或多個<intent-filter>
在AndroidManifest.xml檔案中定義<activity>
說明:
1、一個<activity>包括:
零個或多個<intent-filter>
它主要是作為匹配的標準,能否匹配成功由<action>、<category>、<data>三個tag共同決定的。
2、一個<intent-filter>包括:
一個或多個 <action>
零個或多個 <category>
一個或多個 <action>
零個或多個 <category>
指定<activity>的分類特徵
eg:
<category android:name=”android.intent.category.LAUNCHER” />
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的視訊,
— 指定攜帶的資料的型別,使用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) ;
}
}
查詢上來的資料是多個記錄
<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” />
<data android:scheme=”http://localhost:8080/test.jsp” />
3、一個Intent對應多種匹配結果的處理說明
一個intent有多個可匹配的處理元件,系統如何處理?
分響應訊息的元件型別:
1)如果是service那麼這些service都可以啟動並處理訊息。
2)如果是Activity則會彈出一個對話方塊讓使用者進行選擇。
一個intent有多個可匹配的處理元件,系統如何處理?
分響應訊息的元件型別:
1)如果是service那麼這些service都可以啟動並處理訊息。
2)如果是Activity則會彈出一個對話方塊讓使用者進行選擇。
4、安全性問題
如果不同程式間的元件可以通過隱式訊息互相通訊,那程式不是可以輕易呼叫到其他的程式或者系統中的一些敏感程式的元件,這樣會不會很不安全呢?
其實Android在安全方面有一個統一,完備和輕便的安全策略模型。
如果不同程式間的元件可以通過隱式訊息互相通訊,那程式不是可以輕易呼叫到其他的程式或者系統中的一些敏感程式的元件,這樣會不會很不安全呢?
其實Android在安全方面有一個統一,完備和輕便的安全策略模型。
簡單一點說就是:許可權設定問題
我們可以自己定義permission,然後在需要的元件處設定該permission,那麼使用者要想該元件,必須要配置該permission,否則訪問失敗的!
我們可以自己定義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”
/>
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>
<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”/>
<uses-permission android:name=”com.myq.android.permission.DATETIME_SERVICE”/>
三、IPC機制
有了Intent這種基於訊息的程式內或程式間通訊模型,我們就可以通過Intent去開啟一個Service,可以通過Intent跳轉到另一個Activity,不論上面的Service或Activity是在當前程式還是其它程式內即不論是當前應用還是其它應用的Service或Activity,通過訊息機制都可以進行通訊!
有了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) ;
}
{
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;
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;
}
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 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.
|<——————–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