Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中:Activity、Content Provider、Broadcast和AIDLService4種跨歷程通訊的方式

Android中:Activity、Content Provider、Broadcast和AIDLService4種跨歷程通訊的方式

編輯:關於Android編程

由於android系統中應用程序之間不能共享內存。因此,在不同應用程序之間交互數據(跨進程通訊)就稍微麻煩一些。在android SDK中提供了4種用於跨進程通訊的方式。

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

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

Activity的Class對象,代碼如下:

1. Intent intent = new Intent(this , Test.class );

2. startActivity(intent);

Activity的跨進程訪問與進程內訪問略有不同。雖然它們都需要Intent對象,但跨進程訪問並不需要指定Context對象和Activity的 Class對象,而需要指定的是要訪問的Activity所對應的Action(一個字符串)。有些Activity還需要指定一個Uri(通過 Intent構造方法的第2個參數指定)。

在android系統中有很多應用程序提供了可以跨進程訪問的Activity,例如,下面的代碼可以直接調用撥打電話的Activity。

1. Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:1234567 8" );

2. startActivity(callIntent);

執行上面的代碼後,系統會自動撥號,界面如圖1所示。

\

在調用撥號程序的代碼中使用了一個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要使用標簽,並在該標簽的android:name屬性中指定Action


2. 在AndroidManifest.xml文件中指定訪問協議。在指定Uri(Intent類的第2個參數)時需要訪問協議。訪問協議需要使 用標簽的android:scheme屬性來指定。如果該屬性的值是“abc”,那麼Uri就應該是“abc://Uri的主體 部分”,也就是說,訪問協議是Uri的開頭部分。


3. 通過getIntent().getData().getHost()方法獲得協議後的Uri的主體部分。這個Host只是個稱謂,並不一定是主機名。讀者可以將其看成是任意的字符串。


4. 從Bundle對象中獲得其他應用程序傳遞過來的數據。


5. 這一步當然是獲得數據後做進一步的處理了。至於如何處理這些數據,就得根據具體的需求決定了。


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






 

在配置AndroidManifest.xml時要注意,不能在同一個中配置多個動作,否則會覆蓋MAIN動作以使該程序無法正常啟動(雖然其他應用程序調用Main是正常的)。

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

info://任意字符串

一般標簽的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方法獲得,代碼如下:

1. ContentResolver cr = getContentResolver();
與Activity一樣,Content Provider也需要與一個URI對應。每一個Content Provider可以控制多個數據集,在這種情況下,每一個數據集會對應一個單獨的URI。所有的URI必須以“content://”開頭。


為了程序更容易維護,也為了簡化程序代碼,一般將URI定義成一個常量。例如,下面的常量表示系統的聯系人電話號碼。

1. 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傳遞參數,然後在這些方法中接收這些參數,並


 

 

 


方式三:廣播(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

 

 

在注冊MyReceiver類時需要使用標簽,android:name屬性指定MyReceiver類,標簽的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服務,尤其要注意的是,標簽的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類,代碼如下:

 

 

下面來看看如何調用這個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文件中標簽的子標簽的android:name屬性 的值。

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

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved