android中跨歷程通訊的4種方式

android中跨歷程通訊的4種方式


android中跨進程通訊的4種方式
由於android系統中應用程序之間不能共享內存。因此,在不同應用程序之間交互數據(跨進程通訊)就稍微麻煩一些。在android SDK中提供了4種用於跨進程通訊的方式。這4種方式正好對應於android系統中4種應用程序組件:Activity、Content Provider、Broadcast和Service。其中Activity可以跨進程調用其他應用程序的Activity;Content Provider可以跨進程訪問其他應用程序中的數據(以Cursor對象形式返回),當然,也可以對其他應用程序的數據進行增、刪、改操 作;Broadcast可以向android系統中所有應用程序發送廣播,而需要跨進程通訊的應用程序可以監聽這些廣播;Service和Content Provider類似,也可以訪問其他應用程序中的數據,但不同的是,Content Provider返回的是Cursor對象,而Service返回的是Java對象,這種可以跨進程通訊的服務叫AIDL服務。
完整示例請參閱本文提供的源代碼。

方式一:訪問其他應用程序的Activity
Activity既可以在進程內(同一個應用程序)訪問,也可以跨進程訪問。如果想在同一個應用程序中訪問Activity,需要指定Context對象和Activity的Class對象,代碼如下:


Intent intent = new  Intent(this , Test.class );  
startActivity(intent);  
      Activity的跨進程訪問與進程內訪問略有不同。雖然它們都需要Intent對象,但跨進程訪問並不需要指定Context對象和Activity的 Class對象,而需要指定的是要訪問的Activity所對應的Action(一個字符串)。有些Activity還需要指定一個Uri(通過 Intent構造方法的第2個參數指定)。
在android系統中有很多應用程序提供了可以跨進程訪問的Activity,例如,下面的代碼可以直接調用撥打電話的Activity。

Intent callIntent = new  Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" );  
startActivity(callIntent);  
執行上面的代碼後,系統會自動撥號。

        在調用撥號程序的代碼中使用了一個Intent.ACTION_CALL常量,該常量的定義如下:
public  static  final  String ACTION_CALL = "android.intent.action.CALL" ;  
        這個常量是一個字符串常量,也是我們在這節要介紹的跨進程調用Activity的關鍵。如果在應用程序中要共享某個Activity,需要爲這個 Activity指定一個字符串ID,也就是Action。也可以將這個Action看做這個Activity的key。在其他的應用程序中只要通過這個 Action就可以找到與Action對應的Activity,並通過startActivity方法來啓動這個Activity。

        下面先來看一下如何將應用程序的Activity共享出來,讀者可按如下幾步來共享Activity:
1.  在AndroidManifest.xml文件中指定Action。指定Action要使用<action>標籤,並在該標籤的android:name屬性中指定Action
2.  在AndroidManifest.xml文件中指定訪問協議。在指定Uri(Intent類的第2個參數)時需要訪問協議。訪問協議需要使 用<data>標籤的android:scheme屬性來指定。如果該屬性的值是“abc”,那麼Uri就應該是“abc://Uri的主體 部分”,也就是說,訪問協議是Uri的開頭部分。
3.  通過getIntent().getData().getHost()方法獲得協議後的Uri的主體部分。這個Host只是個稱謂,並不一定是主機名。讀者可以將其看成是任意的字符串。
4.  從Bundle對象中獲得其他應用程序傳遞過來的數據。
5.  這一步當然是獲得數據後做進一步的處理了。至於如何處理這些數據,就得根據具體的需求決定了。

        下面來根據這些步驟共享一個Activity。首先建立一個android工程(ActionActivity),工程的主Activity是Main。在 本例中我們會共享這個Main類。首先打開AndroidManifest.xml文件,添加一個<activity>標籤,並重新定義了 Main的相應屬性。AndroidManifest.xml文件的內容如下:

<!--  重新配置Main  -->  
<activity android:name=".Main"  android:label="@string/app_name" >  
    <intent-filter>      
        <action android:name="net.blogjava.mobile.MYACTION"  />  
        <data android:scheme="info"  />              
        <category android:name="android.intent.category.DEFAULT"  />  
    </intent-filter>  
</activity>  
       在配置AndroidManifest.xml時要注意,不能在同一個<activity>中配置多個動作,否則會覆蓋MAIN動作以使該程序無法正常啓動(雖然其他應用程序調用Main是正常的)。

         從上面的代碼可以看出,<action>標籤的android:name屬性值是 net.blogjava.mobile.MYACTION,這就是Main自定義的動作。<data>標籤指定了Url的協議。如果指定 了<data>標籤的android:scheme屬性值(info),則在調用Main時需要使用如下的URL:



info://任意字符串   
         一般<category>標籤的android:name屬性值可以設成android.intent.category.DEFAULT。

         下面來看看如何在Main類的onCreate方法中獲得其他應用程序傳遞過來的數據。



package  net.blogjava.mobile.actionactivity;  
... ...  
public  class  Main extends  Activity implements  OnClickListener  
{  
    private  EditText editText;  
    @Override   
    public  void  onClick(View view)  
    {  
        //  單擊按鈕,會顯示文本框中的內容(以Toast信息框形式顯示)   
        Toast.makeText(this , editText.getText().toString(), Toast.LENGTH_LONG)  
                .show();  
    }  
    @Override   
    public  void  onCreate(Bundle savedInstanceState)  
    {  
        super .onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        Button button = (Button) findViewById(R.id.button);  
        button.setOnClickListener(this );  
        editText = (EditText) findViewById(R.id.edittext);  
        //  獲得其他應用程序傳遞過來的數據   
        if  (getIntent().getData() != null )  
        {  
            //  獲得Host,也就是info://後面的內容   
            String host = getIntent().getData().getHost();  
            Bundle bundle = getIntent().getExtras();  
            //  其他的應用程序會傳遞過來一個value值,在該應用程序中需要獲得這個值   
            String value = bundle.getString("value" );  
            //  將Host和Value組合在一下顯示在EditText組件中   
            editText.setText(host + ":"  + value);  
            //  調用了按鈕的單擊事件,顯示Toast信息提示框   
            onClick(button);  
        }  
    }  
}  
        從上面的程序可以看出,首先通過getIntent().getData()來判斷其他的應用程序是否傳遞了Uri(getData方法返回了一個Uri 對象)。如果運行該程序,Uri爲null,因此,不會執行if語句裏面的代碼。當其他的應用程序傳遞了Uri對象後,系統會執行if語句裏面的代碼。當 運行ActionActivity後,在文本框中輸入“Running”,單擊“顯示文本框的內容”按鈕,會顯示如圖2所示的Toast提示信息框。



           下面來看一下其他的應用程序是如何調用ActionActivity中的Main。新建一個android工程(InvokeActivity),並添加一個按鈕,按鈕的單擊事件方法代碼如下:



public  void  onClick(View view)  
{  
    //  需要使用Intent類的第2個參數指定Uri   
    Intent intent = new  Intent("net.blogjava.mobile.MYACTION" , Uri  
            .parse("info://調用其他應用程序的Activity" ));  
    //  設置value屬性值   
    intent.putExtra("value" , "調用成功" );  
    //  調用ActionActivity中的Main   
    startActivity(intent);  
}  
       在運行InvokeActivity之前,先要運行ActionActivity以便在android模擬器中安裝該程序。然後單擊InvokeActivity中的按鈕,就會顯示如圖3所示的效果。



       當然,也可以使用startActivityForResult方法來啓動其他應用程序的Activity,以便獲得Activity的返回值。例如,可以將ActionActivity中Main類的onClick代碼修改爲下面的形式。





public  void  onClick(View view)  
{  
    Toast.makeText(this , editText.getText().toString(), Toast.LENGTH_LONG).show();  
    Intent intent = new  Intent();  
    //  設置要返回的屬性值   
    intent.putExtra("result" , editText.getText().toString());  
    //  設置返回碼和Intent對象   
    setResult(2 , intent);  
    //  關閉Activity   
    finish();  
}  
     然後在InvokeActivity中使用下面的代碼來調用Main。



intent = new  Intent("net.blogjava.mobile.MYACTION" , Uri  
        .parse("info://調用其他應用程序的Activity" ));  
//  傳遞數據   
intent.putExtra("value" , "調用成功" );  
startActivityForResult(intent, 1 );              //  1爲請求碼   
      要想接收Activity返回的值,需要覆蓋onActivityResult事件方法,代碼如下:



@Override   
protected  void  onActivityResult(int  requestCode, int  resultCode, Intent data)  
{  
    Toast.makeText(this , "返回值:"  + data.getExtras().getString("result" ),  
            Toast.LENGTH_LONG).show();  
}  
     當單擊InvokeActivity中的相應按鈕後,並且Main關閉後,會顯示如圖4所示的Toast信息提示框。



      從本節的介紹可以看出,跨進程訪問Activity(訪問其他應用程序中的Activity)主要是通過一個Action來完成的,如果要傳遞數據,還需 要指定一個Uri。當然,傳遞數據也可以通過Intent來完成。傳遞數據的過程可以是雙向的。如果要想從調用的Activity中返回數據,就需要使用 startActivityForResult方法來啓動Activity了。

方式二:Content Provider 
      Android應用程序可以使用文件或SqlLite數據庫來存儲數據。Content Provider提供了一種在多個應用程序之間數據共享的方式(跨進程共享數據)。應用程序可以利用Content Provider完成下面的工作

1. 查詢數據
2. 修改數據
3. 添加數據
4. 刪除數據

        雖然Content Provider也可以在同一個應用程序中被訪問,但這麼做並沒有什麼意義。Content Provider存在的目的向其他應用程序共享數據和允許其他應用程序對數據進行增、刪、改操作。
Android系統本身提供了很多Content Provider,例如,音頻、視頻、聯繫人信息等等。我們可以通過這些Content Provider獲得相關信息的列表。這些列表數據將以Cursor對象返回。因此,從Content Provider返回的數據是二維表的形式。

      對於訪問Content Provider的程序,需要使用ContentResolver對象。該對象需要使用getContentResolver方法獲得,代碼如下:



ContentResolver cr = getContentResolver();  
       與Activity一樣,Content Provider也需要與一個URI對應。每一個Content Provider可以控制多個數據集,在這種情況下,每一個數據集會對應一個單獨的URI。所有的URI必須以“content://”開頭。
爲了程序更容易維護,也爲了簡化程序代碼,一般將URI定義成一個常量。例如,下面的常量表示系統的聯繫人電話號碼。



android.provider.Contacts.Phones.CONTENT_URI   
       下面來看一下編寫Content Provider的具體步驟。


1.  編寫一個繼承於android.content.ContentProvider的子類。該類是ContentProvider的核心類。在該類中會實現 query、insert、update及delete方法。實際上調用ContentResolver類的這4個方法就是調用 ContentProvider類中與之要對應的方法。在本文中只介紹query。至於insert、update、delete和query的用法類 似。也是通過Uri傳遞參數,然後在這些方法中接收這些參數,並做進一步地處理。
2.  在AndroidManifest.xml文件中配置ContentProvider。要想唯一確定一個ContentProvider,需要指定這個 ContentProvider的URI,除此之外,還需要指定URI所對應的ContentProvider類。這有些象Servlet的定義,除了要 指定Servlet對應的Web地址,還要指定這個地址所對應的Servlet類。
現在來看一下Uri的具體格式,先看一下如圖5所示的URI。



          下面對圖5所示的URI的4個部分做一下解釋。

A:Content Provider URI的固定前綴,也就是說,所有的URI必須以content://開頭。
B:URI中最重要的部分。該部分是Content Provider的唯一標識。對於第三方應用程序來說,該部分最後使用完整的類名(包名+類名),以確保URI的唯一性。該部分需要在 AndroidManifest.xml文件中<provider>標籤中定義,代碼如下:



<provider name=".TransportationProvider"  authorities="com.example.transportationprovider"   
          . . .  >  
C:這部分是URI的路徑(path)。表示URI中各種被請求的數據。這部分是可選的, 如果Content Provider僅僅提供一種請求的數據,那麼這部分可以省略。如果Content Provider要提供多種請求數據。就需要添加多個路徑,甚至是子路徑。例如,“land/bus”、“land/train”、“sea/ship” 就指定了3種可能提供的數據。
D:這部分也是可選的。如果要傳遞一個值給Content Provider,可以通過這部分傳遞。當然,如果不需要傳值,這部分也可以省略,省略後的URI如下所示:

content://com.example.transportationprovider/trains   
    本例利用了《基於 android SDK1.5的英文電子詞典的實現》一文中實現的電子詞典程序。通過ContentProvider,將電子詞典的查詞功能共享成Cursor對象。這樣 其他的應用程序就可以通過ContentProvider來查詞英文單詞了。關於英文詞典的具體實現細節,讀者可以通過如下的地址查看《基於 android SDK1.5的英文電子詞典的實現》一文。





http://www. androidsdn.com/article/show/111   
      在電子詞典程序中需要一個DictionaryContentProvider類,該類是ContentProvider的子類。在該類中實現了 query方法,並根據不同的URI來返回不同的結果。讓我們先看一下DictionaryContentProvider類,然後再對這些代碼做一些解 釋。



... ...  
public  class  DictionaryContentProvider extends  ContentProvider  
{  
    private  static  UriMatcher uriMatcher;  
    private  static  final  String AUTHORITY = "net.blogjava.mobile.dictionarycontentprovider" ;  
    private  static  final  int  SINGLE_WORD = 1 ;  
    private  static  final  int  PREFIX_WORDS = 2 ;  
    public  static  final  String DATABASE_PATH = android.os.Environment  
    .getExternalStorageDirectory().getAbsolutePath()  
    + "/dictionary" ;  
    public  static  final  String DATABASE_FILENAME = "dictionary.db" ;  
    private  SQLiteDatabase database;  
    static   
    {  
        //  添加訪問ContentProvider的Uri   
        uriMatcher = new  UriMatcher(UriMatcher.NO_MATCH);  
        uriMatcher.addURI(AUTHORITY, "single" , SINGLE_WORD);  
        uriMatcher.addURI(AUTHORITY, "prefix/*" , PREFIX_WORDS);  
    }  
    //  該方法在Activity的onCreate方法之前調用   
    @Override   
    public  boolean  onCreate()  
    {  
        database = openDatabase();  
        return  true ;  
    }  
    //  在本例中只實現了query方法,其他的方法(insert、update和delete)與query方法的實現   
    //  類似   
    @Override   
    public  Cursor query(Uri uri, String[] projection, String selection,  
            String[] selectionArgs, String sortOrder)  
    {  
        Cursor cursor = null ;  
        switch  (uriMatcher.match(uri))  
        {  
            case  SINGLE_WORD:  
                //  查找指定的單詞   
                cursor = database.query("t_words" , projection, selection,  
                        selectionArgs, null , null , sortOrder);  
                break ;  
            case  PREFIX_WORDS:  
                String word = uri.getPathSegments().get(1 );  
                //  查找以指定字符串開頭的單詞集合   
                cursor = database  
                        .rawQuery(  
                                "select english as _id, chinese from t_words where english like ?" ,  
                                new  String[]  
                                { word + "%"  });  
                break ;  
  
            default :  
                throw  new  IllegalArgumentException("<"  + uri + ">格式不正確." );  
        }  
        return  cursor;  
    }  
    ... ...  
}  
      關於DictionaryContentProvider類的代碼需要做如下的解釋。


1.  在DictionaryContentProvider類的開頭定義的AUTHORITY是訪問ContentProvider的URI的前半部分。
2.  訪問ContentProvider的URI的後半部分由uriMatcher.addURI(...)方法指定。該方法的第1個參數就是 AUTHORITY(Uri的前半部分),第2個參數是Uri的後半部分,第3個參數是與第2個參數值對應的代碼。當其他的應用程序通過Uri訪問 ContentProvider時。系統解析Uri後,將addURI方法的第2個參數值轉換成與之對應的代碼(第3個參數值)。
3.  addURI的第2個參數值可以使用通配符。例如,prefix/*中的*表示所有字符。prefix/abc、prefix/xxx都會匹配成功。
4.  訪問ContentProvider的URI是addURI的第1個和第2個參數值的組件,例如,按着DictionaryContentProvider中設置的兩個URI,可以分別匹配下面的兩個URI。



content://net.blogjava.mobile.dictionarycontentprovider/single   
content://net.blogjava.mobile.dictionarycontentprovider/prefix/wo   

     要注意的是,訪問ContentProvider的URI必須以“content://”開頭。


5.  在query方法中建議使用SQLiteDatabase對象的query方法查詢。因爲query方法的參數正好和DictionaryContentProvider類中的query方法的參數對應,這樣使用起來比較方便。
6.  由於安裝了ContentProvider的應用程序會先調用ContentProvider的onCreate方法(該方法會在Activity的 onCreate方法之前調用),因此,只需要將打開或複製數據庫的方法(openDatabase)放在 DictionaryContentProvider類中,並在onCreate方法中調用即可。
7.  在DictionaryContentProvider類中只實現了query方法。在該方法中判斷了其他應用程序發送的是哪一個Uri。並進行相應的處理。這兩個Uri一個是查詢指定單詞的,另外一個是查詢以某個字符串開頭的所有單詞的(用於顯示單詞列表)。
下面在AndroidManifest.xml文件中配置DictionaryContentProvider類。



<provider android:name="DictionaryContentProvider"   
            android:authorities="net.blogjava.mobile.dictionarycontentprovider"  />    
       OK,現在來看看應用程序如何調用ContentProvider。調用ContentProvider的關鍵是使用 getContentResolver方法來獲得一個ContentResolver對象,並通過ContentResolver對象的query方法來 訪問ContentProvider。

        首先來定義兩個訪問ContentProvider的常量。

public  final  String DICTIONARY_SINGLE_WORD_URI = "content://net.blogjava.mobile.dictionarycontentprovider/single" ;  
public  final  String DICTIONARY_PREFIX_WORD_URI = "content://net.blogjava.mobile.dictionarycontentprovider/prefix" ;  
     然後在查詢按鈕的單擊事件中編寫如下的代碼來查詢單詞。





public  void  onClick(View view)  
{  
    Uri uri = Uri.parse(DICTIONARY_SINGLE_WORD_URI);  
    //  通過ContentProvider查詢單詞,並返回Cursor對象,然後的操作就和直接從數據中獲得   
    //  Cursor對象後的操作是一樣的了   
    Cursor cursor = getContentResolver().query(uri, null , "english=?" ,  
            new  String[]{ actvWord.getText().toString() }, null );  
    String result = "未找到該單詞." ;  
    if  (cursor.getCount() > 0 )  
    {  
        cursor.moveToFirst();  
        result = cursor.getString(cursor.getColumnIndex("chinese" ));  
    }  
    new  AlertDialog.Builder(this ).setTitle("查詢結果" ).setMessage(result)  
            .setPositiveButton("關閉" , null ).show();  
  
}  
下面是顯示單詞列表的代碼。





public  void  afterTextChanged(Editable s)  
{  
    if  ("" .equals(s.toString()))  
        return ;  
    Uri uri = Uri.parse(DICTIONARY_PREFIX_WORD_URI + "/"  + s.toString());  
    //  從ContentProvider中獲得以某個字符串開頭的所有單詞的Cursor對象   
    Cursor cursor = getContentResolver().query(uri, null , null , null , null );  
    DictionaryAdapter dictionaryAdapter = new  DictionaryAdapter(this ,  
            cursor, true );  
    actvWord.setAdapter(dictionaryAdapter);  
}  

     





方式三:廣播(Broadcast) 
      廣播是一種被動跨進程通訊的方式。當某個程序向系統發送廣播時,其他的應用程序只能被動地接收廣播數據。這就象電臺進行廣播一樣,聽衆只能被動地收聽,而不能主動與電臺進行溝通。
在應用程序中發送廣播比較簡單。只需要調用sendBroadcast方法即可。該方法需要一個Intent對象。通過Intent對象可以發送需要廣播的數據。

     先建一個android工程:sendbroadcast。在XML佈局文件中放兩個組件:EditText和Button,當單擊按鈕後,會彈出顯示 EditText組件中文本的對話框,關閉對話框後, 會使用sendBroadcast方法發送消息,並將EditText組件的文本通過Intent對象發送出去。完整的代碼如下:


package  net.blogjava.mobile.sendbroadcast;  
... ...  
public  class  Main extends  Activity implements  OnClickListener  
{  
    private  EditText editText;  
    @Override   
    public  void  onClick(View view)  
    {  
        new  AlertDialog.Builder(this ).setMessage(editText.getText().toString())  
                .setPositiveButton("確定" , null ).show();       
        //  通過Intent類的構造方法指定廣播的ID   
        Intent intent = new  Intent("net.blogjava.mobile.MYBROADCAST" );  
        //  將要廣播的數據添加到Intent對象中     
        intent.putExtra("text" , editText.getText().toString());  
        //  發送廣播     
        sendBroadcast(intent);  
    }  
    ... ...  
}  

       發送廣播並不需要在AndroidManifest.xml文件中註冊,但接收廣播必須在AndroidManifest.xml文件中註冊 receiver。下面來編寫一個接收廣播的應用程序。首先建立一個android工程:receiver。然後編寫一個MyReceiver類,該類是 BroadcastReceiver的子類,代碼如下:



package  net.blogjava.mobile.receiver;  
... ...  
public  class  MyReceiver extends  BroadcastReceiver  
{  
    //  當sendbroadcast發送廣播時,系統會調用onReceive方法來接收廣播   
    @Override   
    public  void  onReceive(Context context, Intent intent)  
{  
    //  判斷是否爲sendbroadcast發送的廣播   
        if  ("net.blogjava.mobile.MYBROADCAST" .equals(intent.getAction()))  
        {  
            Bundle bundle = intent.getExtras();  
            if  (bundle != null )  
            {  
                String text = bundle.getString("text" );  
                Toast.makeText(context, "成功接收廣播:"  + text, Toast.LENGTH_LONG).show();  
            }  
        }  
    }  
}  

     當應用程序發送廣播時,系統會調用onReceive方法來接收廣播,並通過intent.getAction()方法返回廣播的ID,也就是在發送廣播時Intent構造方法指定的字符串。然後就可以從Bundle對象中獲得相應的數據了。

     最後還需要在AndroidManifest.xml文件中註冊receiver,代碼如下:





<!--  註冊receiver   
<receiver android:name="MyReceiver" >  
    <intent-filter>  
        <action android:name="net.blogjava.mobile.MYBROADCAST"  />  
    </intent-filter>  
</receiver>  
       在註冊MyReceiver類時需要使用<receiver>標籤,android:name屬性指定MyReceiver類,<action>標籤的android:name指定了廣播的ID。

        首先運行receiver程序,然後就可以關閉receiver程序了。接收廣播並不依賴於程序的狀態。就算程序關閉了,仍然可以接收廣播。然後再啓動 sendbroadcast程序。並在文本框中輸入“android”,然後單擊按鈕,會彈出一個顯示文本框內容的對話框,如圖9所示。當關閉對話框後,會 顯示一個Toast信息提示框,這個信息框是由receiver程序彈出的。如圖10所示。



方式四:AIDL服務
       服務(Service)是android系統中非常重要的組件。Service可以脫離應用程序運行。也就是說,應用程序只起到一個啓動Service的作用。一但Service被啓動,就算應用程序關閉,Service仍然會在後臺運行。

       android系統中的Service主要有兩個作用:後臺運行和跨進程通訊。後臺運行就不用說了,當Service啓動後,就可以在Service對象中 運行相應的業務代碼,而這一切用戶並不會察覺。而跨進程通訊是這一節的主題。如果想讓應用程序可以跨進程通訊,就要使用我們這節講的AIDL服 務,AIDL的全稱是Android Interface Definition Language,也就是說,AIDL實際上是一種接口定義語言。通過這種語言定義接口後,Eclipse插件(ODT)會自動生成相應的Java代碼接 口代碼。下面來看一下編寫一個AIDL服務的基本步驟。

1.  在Eclipse工程的package目錄中建立一個擴展名爲aidl的文件。package目錄就是Java類所在的目錄。該文件的語法類似於Java代碼。aidl文件中定義的是AIDL服務的接口。這個接口需要在調用AIDL服務的程序中訪問。
2.  如果aidl文件的內容是正確的,Eclipse插件會自動生成一個Java接口文件(*.java)。
3.  建立一個服務類(Service的子類)。
4.  實現由aidl文件生成的Java接口。
5.  在AndroidManifest.xml文件中配置AIDL服務,尤其要注意的是,<action>標籤的android:name屬性值就是客戶端要引用該服務的ID,也就是Intent類構造方法的參數值。

      現在我們來編寫一個AIDL服務,首先建立一個android工程:aidlservice。在aidlservice工程中有一個Main類,在Main類所有的目錄建立一個IMyService.aidl文件,內容如下:



package  net.blogjava.mobile.aidlservice;  
interface  IMyService  
{  
    String getValue();  //  爲AIDL服務的接口方法,調用AIDL服務的程序需要調用該方法   
}  
      在保存IMyService.aidl文件後,ODT會在gen目錄下產生一個IMyService.java文件,讀者可以不必管這個文件中的內容,也 不需要修改該文件的內容。這個文件是由ODT自動維護的,只要修改了IMyService.aidl文件的內容,IMyService.java文件的內 容就會隨之改變。

        然後建立一個MyService類,該類是Service的子類,代碼如下:



package  net.blogjava.mobile.aidlservice;  
... ...  
public  class  MyService extends  Service  
{  
    //  IMyService.Stub類是根據IMyService.aidl文件生成的類,該類中包含了接口方法(getValue)   
    public  class  MyServiceImpl extends  IMyService.Stub  
    {  
        @Override   
        public  String getValue() throws  RemoteException  
        {  
            return  "從AIDL服務獲得的值." ;  
        }  
    }  
    @Override   
    public  IBinder onBind(Intent intent)  
{          
//  該方法必須返回MyServiceImpl類的對象實例   
        return  new  MyServiceImpl();  
    }  
}  
     最後需要在AndroidManifest.xml文件中配置MyService類,代碼如下:



<!--  註冊服務 -->  
<service android:name=".MyService" >  
    <intent-filter>  
        <!--  指定調用AIDL服務的ID  -->  
        <action android:name="net.blogjava.mobile.aidlservice.IMyService"  />  
    </intent-filter>  
</service>  
       下面來看看如何調用這個AIDL服務。首先建立一個android工程:aidlclient。然後將aidlservice工程中自動生成的 IMyService.java文件複製到aidlclient工程中。在調用AIDL服務之前需要先使用bindService方法綁定AIDL服務。 bindService方法需要一個ServiceConnection對象。ServiceConnection有一個 onServiceConnected方法,當成功綁定AIDL服務且,該方法被調用。並通過service參數返回AIDL服務對象。下面是調用 AIDL服務的完成代碼。





package  net.blogjava.mobile.aidlclient;  
... ...  
public  class  Main extends  Activity implements  OnClickListener  
{  
private  IMyService myService = null ;  
//  創建ServiceConnection對象   
    private  ServiceConnection serviceConnection = new  ServiceConnection()  
    {  
        @Override   
        public  void  onServiceConnected(ComponentName name, IBinder service)  
        {  
            // 獲得AIDL服務對象   
            myService = IMyService.Stub.asInterface(service);  
            try   
            {  
                //  調用AIDL服務對象中的getValue方法,並以對話框中顯示該方法的返回值   
                new  AlertDialog.Builder(Main.this ).setMessage(  
                        myService.getValue()).setPositiveButton("確定" , null )  
                        .show();  
            }  
            catch  (Exception e)  
            {  
            }  
        }  
        @Override   
        public  void  onServiceDisconnected(ComponentName name)  
        {  
        }  
    };  
    @Override   
    public  void  onClick(View view)  
{  
    //  綁定AIDL服務   
        bindService(new  Intent("net.blogjava.mobile.aidlservice.IMyService" ),  
                serviceConnection, Context.BIND_AUTO_CREATE);  
    }  
    ... ...  
}  

   在編寫AIDL服務和客戶端時要注意如下兩點:

1.  AIDL服務中的onBind方法必須返回AIDL接口對象(MyServiceImpl對象)。該對象也是onServiceConnected事件方法的第2個參數值。
2.  bindService方法的第1個參數是Intent對象,該對象構造方法的參數需要指定AIDL服務的ID,也就是在 AndroidManifest.xml文件中<service>標籤的<action>子標籤的android:name屬性 的值。

現在先運行aidlservice程序,以便安裝AIDL服務,然後運行aidlclient程序,並單擊按鈕,會顯示如圖11所示的對話框。對話框中的信息就是AIDL服務接口中getValue方法的返回值。



總結 
      本文介紹了4種跨進程通訊的方式:Activity、ContentProvider、Broadcast和AIDL Service。其中Activity可以跨進程調用其他應用程序的Activity;ContentProvider可以訪問其他應用程序返回的 Cursor對象;Broadcast採用的是被動接收的方法,也就是說,客戶端只能接收廣播數據,而不能向發送廣播的程序發送信息。AIDL Service可以將程序中的某個接口公開,這樣在其他的應用程序中就可以象訪問本地對象一樣訪問AIDL服務對象了。這4種跨進程通訊的方式可以應用在 不同的場合,例如,在需要顯示可視化的界面時可以用Activity,需要返回記錄集時可以用ContentProvider。至於在應用程序中具體要用 到哪一種或幾種方式進行跨進程通訊,讀者可以根據實際情況進行選擇。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章