由于android系統(tǒng)中應(yīng)用程序之間不能共享內(nèi)存淹办。因此眉枕,在不同應(yīng)用程序之間交互數(shù)據(jù)(跨進(jìn)程通訊)就稍微麻煩一些。在android SDK中提供了4種用于跨進(jìn)程通訊的方式怜森。這4種方式正好對(duì)應(yīng)于android系統(tǒng)中4種應(yīng)用程序組件:Activity速挑、Content Provider、Broadcast和Service副硅。
其中Activity可以跨進(jìn)程調(diào)用其他應(yīng)用程序的Activity姥宝;
Content Provider可以跨進(jìn)程訪問其他應(yīng)用程序中的數(shù)據(jù)(以Cursor對(duì)象形式返回),當(dāng)然想许,也可以對(duì)其他應(yīng)用程序的數(shù)據(jù)進(jìn)行增伶授、刪断序、改操 作流纹;
Broadcast可以向android系統(tǒng)中所有應(yīng)用程序發(fā)送廣播,而需要跨進(jìn)程通訊的應(yīng)用程序可以監(jiān)聽這些廣播违诗;
Service和Content Provider類似漱凝,也可以訪問其他應(yīng)用程序中的數(shù)據(jù),但不同的是诸迟,Content Provider返回的是Cursor對(duì)象茸炒,而Service返回的是Java對(duì)象愕乎,這種可以跨進(jìn)程通訊的服務(wù)叫AIDL服務(wù)。
完整示例請(qǐng)參閱本文提供的源代碼壁公。
方式一:訪問其他應(yīng)用程序的Activity
Activity既可以在進(jìn)程內(nèi)(同一個(gè)應(yīng)用程序)訪問感论,也可以跨進(jìn)程訪問。如果想在同一個(gè)應(yīng)用程序中訪問Activity紊册,需要指定Context對(duì)象和Activity的Class對(duì)象比肄,代碼如下:
Intent intent =newIntent(this, Test.class);
startActivity(intent);
Activity的跨進(jìn)程訪問與進(jìn)程內(nèi)訪問略有不同。雖然它們都需要Intent對(duì)象囊陡,但跨進(jìn)程訪問并不需要指定Context對(duì)象和Activity的 Class對(duì)象芳绩,而需要指定的是要訪問的Activity所對(duì)應(yīng)的Action(一個(gè)字符串)。有些Activity還需要指定一個(gè)Uri(通過 Intent構(gòu)造方法的第2個(gè)參數(shù)指定)撞反。
在android系統(tǒng)中有很多應(yīng)用程序提供了可以跨進(jìn)程訪問的Activity妥色,例如,下面的代碼可以直接調(diào)用撥打電話的Activity遏片。
Intent callIntent =newIntent(Intent.ACTION_CALL, Uri.parse("tel:12345678");
startActivity(callIntent);
執(zhí)行上面的代碼后嘹害,系統(tǒng)會(huì)自動(dòng)撥號(hào),界面如圖1所示丁稀。
在調(diào)用撥號(hào)程序的代碼中使用了一個(gè)Intent.ACTION_CALL常量吼拥,該常量的定義如下:
publicstaticfinalString ACTION_CALL = "android.intent.action.CALL" ;
這個(gè)常量是一個(gè)字符串常量,也是我們?cè)谶@節(jié)要介紹的跨進(jìn)程調(diào)用Activity的關(guān)鍵线衫。如果在應(yīng)用程序中要共享某個(gè)Activity凿可,需要為這個(gè) Activity指定一個(gè)字符串ID,也就是Action授账。也可以將這個(gè)Action看做這個(gè)Activity的key枯跑。在其他的應(yīng)用程序中只要通過這個(gè) Action就可以找到與Action對(duì)應(yīng)的Activity,并通過startActivity方法來啟動(dòng)這個(gè)Activity白热。
下面先來看一下如何將應(yīng)用程序的Activity共享出來敛助,讀者可按如下幾步來共享Activity:
1.? 在AndroidManifest.xml文件中指定Action。指定Action要使用標(biāo)簽屋确,并在該標(biāo)簽的android:name屬性中指定Action
2.? 在AndroidManifest.xml文件中指定訪問協(xié)議纳击。在指定Uri(Intent類的第2個(gè)參數(shù))時(shí)需要訪問協(xié)議。訪問協(xié)議需要使 用標(biāo)簽的android:scheme屬性來指定攻臀。如果該屬性的值是“abc”焕数,那么Uri就應(yīng)該是“abc://Uri的主體 部分”,也就是說刨啸,訪問協(xié)議是Uri的開頭部分堡赔。
3.? 通過getIntent().getData().getHost()方法獲得協(xié)議后的Uri的主體部分。這個(gè)Host只是個(gè)稱謂设联,并不一定是主機(jī)名善已。讀者可以將其看成是任意的字符串灼捂。
4.? 從Bundle對(duì)象中獲得其他應(yīng)用程序傳遞過來的數(shù)據(jù)。
5.? 這一步當(dāng)然是獲得數(shù)據(jù)后做進(jìn)一步的處理了换团。至于如何處理這些數(shù)據(jù)悉稠,就得根據(jù)具體的需求決定了。
下面來根據(jù)這些步驟共享一個(gè)Activity艘包。首先建立一個(gè)android工程(ActionActivity)偎球,工程的主Activity是Main。在 本例中我們會(huì)共享這個(gè)Main類辑甜。首先打開AndroidManifest.xml文件衰絮,添加一個(gè)標(biāo)簽,并重新定義了 Main的相應(yīng)屬性磷醋。AndroidManifest.xml文件的內(nèi)容如下:
在配置AndroidManifest.xml時(shí)要注意猫牡,不能在同一個(gè)中配置多個(gè)動(dòng)作,否則會(huì)覆蓋MAIN動(dòng)作以使該程序無法正常啟動(dòng)(雖然其他應(yīng)用程序調(diào)用Main是正常的)邓线。
從上面的代碼可以看出淌友,標(biāo)簽的android:name屬性值是 net.blogjava.mobile.MYACTION,這就是Main自定義的動(dòng)作骇陈。標(biāo)簽指定了Url的協(xié)議震庭。如果指定 了標(biāo)簽的android:scheme屬性值(info),則在調(diào)用Main時(shí)需要使用如下的URL:
info://任意字符串
一般標(biāo)簽的android:name屬性值可以設(shè)成android.intent.category.DEFAULT你雌。
下面來看看如何在Main類的onCreate方法中獲得其他應(yīng)用程序傳遞過來的數(shù)據(jù)器联。
packagenet.blogjava.mobile.actionactivity;
... ...publicclassMainextendsActivityimplementsOnClickListener
{privateEditText editText;
@OverridepublicvoidonClick(View view)
{//單擊按鈕,會(huì)顯示文本框中的內(nèi)容(以Toast信息框形式顯示)Toast.makeText(this, editText.getText().toString(), Toast.LENGTH_LONG)
.show();
}
@OverridepublicvoidonCreate(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);//獲得其他應(yīng)用程序傳遞過來的數(shù)據(jù)if(getIntent().getData() !=null)
{//獲得Host婿崭,也就是info://后面的內(nèi)容String host =getIntent().getData().getHost();
Bundle bundle=getIntent().getExtras();//其他的應(yīng)用程序會(huì)傳遞過來一個(gè)value值拨拓,在該應(yīng)用程序中需要獲得這個(gè)值String value = bundle.getString("value");//將Host和Value組合在一下顯示在EditText組件中editText.setText(host + ":" +value);//調(diào)用了按鈕的單擊事件,顯示Toast信息提示框onClick(button);
}
}
}
從上面的程序可以看出氓栈,首先通過getIntent().getData()來判斷其他的應(yīng)用程序是否傳遞了Uri(getData方法返回了一個(gè)Uri 對(duì)象)渣磷。如果運(yùn)行該程序,Uri為null授瘦,因此醋界,不會(huì)執(zhí)行if語句里面的代碼。當(dāng)其他的應(yīng)用程序傳遞了Uri對(duì)象后提完,系統(tǒng)會(huì)執(zhí)行if語句里面的代碼形纺。當(dāng) 運(yùn)行ActionActivity后,在文本框中輸入“Running”氯葬,單擊“顯示文本框的內(nèi)容”按鈕挡篓,會(huì)顯示如圖2所示的Toast提示信息框婉陷。
下面來看一下其他的應(yīng)用程序是如何調(diào)用ActionActivity中的Main帚称。新建一個(gè)android工程(InvokeActivity),并添加一個(gè)按鈕楼吃,按鈕的單擊事件方法代碼如下:
publicvoidonClick(View view)
{//需要使用Intent類的第2個(gè)參數(shù)指定UriIntent intent =newIntent("net.blogjava.mobile.MYACTION", Uri
.parse("info://調(diào)用其他應(yīng)用程序的Activity"));//設(shè)置value屬性值intent.putExtra("value", "調(diào)用成功");//調(diào)用ActionActivity中的MainstartActivity(intent);
}
在運(yùn)行InvokeActivity之前躬窜,先要運(yùn)行ActionActivity以便在android模擬器中安裝該程序。然后單擊InvokeActivity中的按鈕,就會(huì)顯示如圖3所示的效果。
當(dāng)然,也可以使用startActivityForResult方法來啟動(dòng)其他應(yīng)用程序的Activity,以便獲得Activity的返回值。例如,可以將ActionActivity中Main類的onClick代碼修改為下面的形式养盗。
publicvoidonClick(View view)
{
Toast.makeText(this, editText.getText().toString(), Toast.LENGTH_LONG).show();
Intent intent=newIntent();//設(shè)置要返回的屬性值intent.putExtra("result", editText.getText().toString());//設(shè)置返回碼和Intent對(duì)象setResult(2, intent);//關(guān)閉Activityfinish();
}
然后在InvokeActivity中使用下面的代碼來調(diào)用Main。
intent =newIntent("net.blogjava.mobile.MYACTION", Uri
.parse("info://調(diào)用其他應(yīng)用程序的Activity"));//傳遞數(shù)據(jù)intent.putExtra("value", "調(diào)用成功");
startActivityForResult(intent,1);//1為請(qǐng)求碼
要想接收Activity返回的值,需要覆蓋onActivityResult事件方法,代碼如下:
@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode, Intent data)
{
Toast.makeText(this, "返回值:" + data.getExtras().getString("result"),
Toast.LENGTH_LONG).show();
}
當(dāng)單擊InvokeActivity中的相應(yīng)按鈕后藏研,并且Main關(guān)閉后业踏,會(huì)顯示如圖4所示的Toast信息提示框伐脖。
從本節(jié)的介紹可以看出场勤,跨進(jìn)程訪問Activity(訪問其他應(yīng)用程序中的Activity)主要是通過一個(gè)Action來完成的,如果要傳遞數(shù)據(jù)骤竹,還需 要指定一個(gè)Uri蒙揣。當(dāng)然懒震,傳遞數(shù)據(jù)也可以通過Intent來完成递宅。傳遞數(shù)據(jù)的過程可以是雙向的英融。如果要想從調(diào)用的Activity中返回?cái)?shù)據(jù),就需要使用 startActivityForResult方法來啟動(dòng)Activity了矢赁。
方式二:Content Provider
Android應(yīng)用程序可以使用文件或SqlLite數(shù)據(jù)庫(kù)來存儲(chǔ)數(shù)據(jù)给涕。Content Provider提供了一種在多個(gè)應(yīng)用程序之間數(shù)據(jù)共享的方式(跨進(jìn)程共享數(shù)據(jù))。應(yīng)用程序可以利用Content Provider完成下面的工作
1. 查詢數(shù)據(jù)
2. 修改數(shù)據(jù)
3. 添加數(shù)據(jù)
4. 刪除數(shù)據(jù)
雖然Content Provider也可以在同一個(gè)應(yīng)用程序中被訪問,但這么做并沒有什么意義够庙。Content Provider存在的目的向其他應(yīng)用程序共享數(shù)據(jù)和允許其他應(yīng)用程序?qū)?shù)據(jù)進(jìn)行增恭应、刪、改操作耘眨。
Android系統(tǒng)本身提供了很多Content Provider昼榛,例如,音頻剔难、視頻胆屿、聯(lián)系人信息等等。我們可以通過這些Content Provider獲得相關(guān)信息的列表偶宫。這些列表數(shù)據(jù)將以Cursor對(duì)象返回非迹。因此,從Content Provider返回的數(shù)據(jù)是二維表的形式纯趋。
對(duì)于訪問Content Provider的程序憎兽,需要使用ContentResolver對(duì)象。該對(duì)象需要使用getContentResolver方法獲得吵冒,代碼如下:
ContentResolver cr = getContentResolver();
與Activity一樣纯命,Content Provider也需要與一個(gè)URI對(duì)應(yīng)。每一個(gè)Content Provider可以控制多個(gè)數(shù)據(jù)集痹栖,在這種情況下扎附,每一個(gè)數(shù)據(jù)集會(huì)對(duì)應(yīng)一個(gè)單獨(dú)的URI。所有的URI必須以“content://”開頭结耀。
為了程序更容易維護(hù)留夜,也為了簡(jiǎn)化程序代碼,一般將URI定義成一個(gè)常量图甜。例如碍粥,下面的常量表示系統(tǒng)的聯(lián)系人電話號(hào)碼。
android.provider.Contacts.Phones.CONTENT_URI
下面來看一下編寫Content Provider的具體步驟黑毅。
1.? 編寫一個(gè)繼承于android.content.ContentProvider的子類嚼摩。該類是ContentProvider的核心類。在該類中會(huì)實(shí)現(xiàn) query矿瘦、insert枕面、update及delete方法。實(shí)際上調(diào)用ContentResolver類的這4個(gè)方法就是調(diào)用 ContentProvider類中與之要對(duì)應(yīng)的方法缚去。在本文中只介紹query潮秘。至于insert、update易结、delete和query的用法類 似枕荞。也是通過Uri傳遞參數(shù)柜候,然后在這些方法中接收這些參數(shù),并做進(jìn)一步地處理躏精。
2.? 在AndroidManifest.xml文件中配置ContentProvider渣刷。要想唯一確定一個(gè)ContentProvider,需要指定這個(gè) ContentProvider的URI矗烛,除此之外辅柴,還需要指定URI所對(duì)應(yīng)的ContentProvider類。這有些象Servlet的定義瞭吃,除了要 指定Servlet對(duì)應(yīng)的Web地址碌嘀,還要指定這個(gè)地址所對(duì)應(yīng)的Servlet類。
現(xiàn)在來看一下Uri的具體格式虱而,先看一下如圖5所示的URI筏餐。
下面對(duì)圖5所示的URI的4個(gè)部分做一下解釋开泽。
A:Content Provider URI的固定前綴牡拇,也就是說,所有的URI必須以content://開頭穆律。
B:URI中最重要的部分惠呼。該部分是Content Provider的唯一標(biāo)識(shí)。對(duì)于第三方應(yīng)用程序來說峦耘,該部分最后使用完整的類名(包名+類名)剔蹋,以確保URI的唯一性。該部分需要在 AndroidManifest.xml文件中標(biāo)簽中定義辅髓,代碼如下:
C:這部分是URI的路徑(path)泣崩。表示URI中各種被請(qǐng)求的數(shù)據(jù)。這部分是可選的洛口, 如果Content Provider僅僅提供一種請(qǐng)求的數(shù)據(jù)矫付,那么這部分可以省略。如果Content Provider要提供多種請(qǐng)求數(shù)據(jù)第焰。就需要添加多個(gè)路徑买优,甚至是子路徑。例如挺举,“l(fā)and/bus”杀赢、“l(fā)and/train”、“sea/ship” 就指定了3種可能提供的數(shù)據(jù)湘纵。
D:這部分也是可選的脂崔。如果要傳遞一個(gè)值給Content Provider,可以通過這部分傳遞梧喷。當(dāng)然脱篙,如果不需要傳值娇钱,這部分也可以省略,省略后的URI如下所示:
content://com.example.transportationprovider/trains
本例利用了《基于androidSDK1.5的英文電子詞典的實(shí)現(xiàn)》一文中實(shí)現(xiàn)的電子詞典程序绊困。通過ContentProvider文搂,將電子詞典的查詞功能共享成Cursor對(duì)象。這樣 其他的應(yīng)用程序就可以通過ContentProvider來查詞英文單詞了秤朗。關(guān)于英文詞典的具體實(shí)現(xiàn)細(xì)節(jié)煤蹭,讀者可以通過如下的地址查看《基于androidSDK1.5的英文電子詞典的實(shí)現(xiàn)》一文。
http://www.ophonesdn.com/article/show/111
在電子詞典程序中需要一個(gè)DictionaryContentProvider類取视,該類是ContentProvider的子類硝皂。在該類中實(shí)現(xiàn)了 query方法,并根據(jù)不同的URI來返回不同的結(jié)果作谭。讓我們先看一下DictionaryContentProvider類稽物,然后再對(duì)這些代碼做一些解 釋。
... ...publicclassDictionaryContentProviderextendsContentProvider
{privatestaticUriMatcher uriMatcher;privatestaticfinalString AUTHORITY = "net.blogjava.mobile.dictionarycontentprovider";privatestaticfinalintSINGLE_WORD = 1;privatestaticfinalintPREFIX_WORDS = 2;publicstaticfinalString DATABASE_PATH =android.os.Environment
.getExternalStorageDirectory().getAbsolutePath()+ "/dictionary";publicstaticfinalString DATABASE_FILENAME = "dictionary.db";privateSQLiteDatabase database;static{//添加訪問ContentProvider的UriuriMatcher =newUriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY,"single", SINGLE_WORD);
uriMatcher.addURI(AUTHORITY,"prefix/*", PREFIX_WORDS);
}//該方法在Activity的onCreate方法之前調(diào)用@OverridepublicbooleanonCreate()
{
database=openDatabase();returntrue;
}//在本例中只實(shí)現(xiàn)了query方法折欠,其他的方法(insert贝或、update和delete)與query方法的實(shí)現(xiàn)//類似@OverridepublicCursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
Cursor cursor=null;switch(uriMatcher.match(uri))
{caseSINGLE_WORD://查找指定的單詞cursor = database.query("t_words", projection, selection,
selectionArgs,null,null, sortOrder);break;casePREFIX_WORDS:
String word= uri.getPathSegments().get(1);//查找以指定字符串開頭的單詞集合cursor =database
.rawQuery("select english as _id, chinese from t_words where english like ?",newString[]
{ word+ "%"});break;default:thrownewIllegalArgumentException("<" + uri + ">格式不正確.");
}returncursor;
}
... ...
}
關(guān)于DictionaryContentProvider類的代碼需要做如下的解釋。
1.? 在DictionaryContentProvider類的開頭定義的AUTHORITY是訪問ContentProvider的URI的前半部分锐秦。
2.? 訪問ContentProvider的URI的后半部分由uriMatcher.addURI(...)方法指定咪奖。該方法的第1個(gè)參數(shù)就是 AUTHORITY(Uri的前半部分),第2個(gè)參數(shù)是Uri的后半部分酱床,第3個(gè)參數(shù)是與第2個(gè)參數(shù)值對(duì)應(yīng)的代碼羊赵。當(dāng)其他的應(yīng)用程序通過Uri訪問 ContentProvider時(shí)。系統(tǒng)解析Uri后扇谣,將addURI方法的第2個(gè)參數(shù)值轉(zhuǎn)換成與之對(duì)應(yīng)的代碼(第3個(gè)參數(shù)值)昧捷。
3.? addURI的第2個(gè)參數(shù)值可以使用通配符。例如罐寨,prefix/*中的*表示所有字符靡挥。prefix/abc、prefix/xxx都會(huì)匹配成功衩茸。
4.? 訪問ContentProvider的URI是addURI的第1個(gè)和第2個(gè)參數(shù)值的組件芹血,例如,按著DictionaryContentProvider中設(shè)置的兩個(gè)URI楞慈,可以分別匹配下面的兩個(gè)URI幔烛。
content://net.blogjava.mobile.dictionarycontentprovider/singlecontent://net.blogjava.mobile.dictionarycontentprovider/prefix/wo
要注意的是,訪問ContentProvider的URI必須以“content://”開頭囊蓝。
5.? 在query方法中建議使用SQLiteDatabase對(duì)象的query方法查詢饿悬。因?yàn)閝uery方法的參數(shù)正好和DictionaryContentProvider類中的query方法的參數(shù)對(duì)應(yīng),這樣使用起來比較方便聚霜。
6.? 由于安裝了ContentProvider的應(yīng)用程序會(huì)先調(diào)用ContentProvider的onCreate方法(該方法會(huì)在Activity的 onCreate方法之前調(diào)用)狡恬,因此珠叔,只需要將打開或復(fù)制數(shù)據(jù)庫(kù)的方法(openDatabase)放在 DictionaryContentProvider類中,并在onCreate方法中調(diào)用即可弟劲。
7.? 在DictionaryContentProvider類中只實(shí)現(xiàn)了query方法祷安。在該方法中判斷了其他應(yīng)用程序發(fā)送的是哪一個(gè)Uri。并進(jìn)行相應(yīng)的處理兔乞。這兩個(gè)Uri一個(gè)是查詢指定單詞的汇鞭,另外一個(gè)是查詢以某個(gè)字符串開頭的所有單詞的(用于顯示單詞列表)。
下面在AndroidManifest.xml文件中配置DictionaryContentProvider類庸追。
OK霍骄,現(xiàn)在來看看應(yīng)用程序如何調(diào)用ContentProvider。調(diào)用ContentProvider的關(guān)鍵是使用 getContentResolver方法來獲得一個(gè)ContentResolver對(duì)象淡溯,并通過ContentResolver對(duì)象的query方法來 訪問ContentProvider读整。
首先來定義兩個(gè)訪問ContentProvider的常量。
publicfinalString DICTIONARY_SINGLE_WORD_URI = "content://net.blogjava.mobile.dictionarycontentprovider/single";publicfinalString DICTIONARY_PREFIX_WORD_URI = "content://net.blogjava.mobile.dictionarycontentprovider/prefix";
然后在查詢按鈕的單擊事件中編寫如下的代碼來查詢單詞咱娶。
publicvoidonClick(View view)
{
Uri uri=Uri.parse(DICTIONARY_SINGLE_WORD_URI);//通過ContentProvider查詢單詞米间,并返回Cursor對(duì)象,然后的操作就和直接從數(shù)據(jù)中獲得//Cursor對(duì)象后的操作是一樣的了Cursor cursor = getContentResolver().query(uri,null, "english=?",newString[]{ actvWord.getText().toString() },null);
String result= "未找到該單詞.";if(cursor.getCount() > 0)
{
cursor.moveToFirst();
result= cursor.getString(cursor.getColumnIndex("chinese"));
}newAlertDialog.Builder(this).setTitle("查詢結(jié)果").setMessage(result)
.setPositiveButton("關(guān)閉",null).show();
}
下面是顯示單詞列表的代碼豺总。
publicvoidafterTextChanged(Editable s)
{if("".equals(s.toString()))return;
Uri uri= Uri.parse(DICTIONARY_PREFIX_WORD_URI + "/" +s.toString());//從ContentProvider中獲得以某個(gè)字符串開頭的所有單詞的Cursor對(duì)象Cursor cursor = getContentResolver().query(uri,null,null,null,null);
DictionaryAdapter dictionaryAdapter=newDictionaryAdapter(this,
cursor,true);
actvWord.setAdapter(dictionaryAdapter);
}
現(xiàn)在來運(yùn)行本例车伞,會(huì)看到如圖6所示的界面择懂。當(dāng)查詢單詞時(shí)會(huì)顯示如圖7所示的單詞列表喻喳,查詢出結(jié)果后,會(huì)顯示如圖8所示的界面困曙。
方式三:廣播(Broadcast)
廣播是一種被動(dòng)跨進(jìn)程通訊的方式表伦。當(dāng)某個(gè)程序向系統(tǒng)發(fā)送廣播時(shí),其他的應(yīng)用程序只能被動(dòng)地接收廣播數(shù)據(jù)慷丽。這就象電臺(tái)進(jìn)行廣播一樣蹦哼,聽眾只能被動(dòng)地收聽,而不能主動(dòng)與電臺(tái)進(jìn)行溝通要糊。
在應(yīng)用程序中發(fā)送廣播比較簡(jiǎn)單纲熏。只需要調(diào)用sendBroadcast方法即可。該方法需要一個(gè)Intent對(duì)象锄俄。通過Intent對(duì)象可以發(fā)送需要廣播的數(shù)據(jù)局劲。
先建一個(gè)android工程:sendbroadcast。在XML布局文件中放兩個(gè)組件:EditText和Button奶赠,當(dāng)單擊按鈕后鱼填,會(huì)彈出顯示 EditText組件中文本的對(duì)話框,關(guān)閉對(duì)話框后毅戈, 會(huì)使用sendBroadcast方法發(fā)送消息苹丸,并將EditText組件的文本通過Intent對(duì)象發(fā)送出去愤惰。完整的代碼如下:
packagenet.blogjava.mobile.sendbroadcast;
... ...publicclassMainextendsActivityimplementsOnClickListener
{privateEditText editText;
@OverridepublicvoidonClick(View view)
{newAlertDialog.Builder(this).setMessage(editText.getText().toString())
.setPositiveButton("確定",null).show();//通過Intent類的構(gòu)造方法指定廣播的IDIntent intent =newIntent("net.blogjava.mobile.MYBROADCAST");//將要廣播的數(shù)據(jù)添加到Intent對(duì)象中intent.putExtra("text", editText.getText().toString());//發(fā)送廣播sendBroadcast(intent);
}
... ...
}
發(fā)送廣播并不需要在AndroidManifest.xml文件中注冊(cè),但接收廣播必須在AndroidManifest.xml文件中注冊(cè) receiver赘理。下面來編寫一個(gè)接收廣播的應(yīng)用程序宦言。首先建立一個(gè)android工程:receiver。然后編寫一個(gè)MyReceiver類商模,該類是 BroadcastReceiver的子類蜡励,代碼如下:
packagenet.blogjava.mobile.receiver;
... ...publicclassMyReceiverextendsBroadcastReceiver
{//當(dāng)sendbroadcast發(fā)送廣播時(shí),系統(tǒng)會(huì)調(diào)用onReceive方法來接收廣播@OverridepublicvoidonReceive(Context context, Intent intent)
{//判斷是否為sendbroadcast發(fā)送的廣播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();
}
}
}
}
當(dāng)應(yīng)用程序發(fā)送廣播時(shí)阻桅,系統(tǒng)會(huì)調(diào)用onReceive方法來接收廣播凉倚,并通過intent.getAction()方法返回廣播的ID,也就是在發(fā)送廣播時(shí)Intent構(gòu)造方法指定的字符串嫂沉。然后就可以從Bundle對(duì)象中獲得相應(yīng)的數(shù)據(jù)了稽寒。
最后還需要在AndroidManifest.xml文件中注冊(cè)receiver,代碼如下:
? ? ? ? ? ? ? ? -->
在注冊(cè)MyReceiver類時(shí)需要使用標(biāo)簽,android:name屬性指定MyReceiver類,標(biāo)簽的android:name指定了廣播的ID须揣。
首先運(yùn)行receiver程序杭攻,然后就可以關(guān)閉receiver程序了。接收廣播并不依賴于程序的狀態(tài)孤页。就算程序關(guān)閉了,仍然可以接收廣播。然后再啟動(dòng) sendbroadcast程序谅河。并在文本框中輸入“android”,然后單擊按鈕确丢,會(huì)彈出一個(gè)顯示文本框內(nèi)容的對(duì)話框绷耍,如圖9所示。當(dāng)關(guān)閉對(duì)話框后鲜侥,會(huì) 顯示一個(gè)Toast信息提示框褂始,這個(gè)信息框是由receiver程序彈出的。如圖10所示描函。
方式四:AIDL服務(wù)
服務(wù)(Service)是android系統(tǒng)中非常重要的組件崎苗。Service可以脫離應(yīng)用程序運(yùn)行。也就是說舀寓,應(yīng)用程序只起到一個(gè)啟動(dòng)Service的作用胆数。一但Service被啟動(dòng),就算應(yīng)用程序關(guān)閉基公,Service仍然會(huì)在后臺(tái)運(yùn)行幅慌。
android系統(tǒng)中的Service主要有兩個(gè)作用:后臺(tái)運(yùn)行和跨進(jìn)程通訊。后臺(tái)運(yùn)行就不用說了轰豆,當(dāng)Service啟動(dòng)后胰伍,就可以在Service對(duì)象中 運(yùn)行相應(yīng)的業(yè)務(wù)代碼齿诞,而這一切用戶并不會(huì)察覺。而跨進(jìn)程通訊是這一節(jié)的主題骂租。如果想讓應(yīng)用程序可以跨進(jìn)程通訊祷杈,就要使用我們這節(jié)講的AIDL服 務(wù),AIDL的全稱是Android Interface Definition Language渗饮,也就是說但汞,AIDL實(shí)際上是一種接口定義語言。通過這種語言定義接口后互站,Eclipse插件(ODT)會(huì)自動(dòng)生成相應(yīng)的Java代碼接 口代碼私蕾。下面來看一下編寫一個(gè)AIDL服務(wù)的基本步驟。
1.? 在Eclipse工程的package目錄中建立一個(gè)擴(kuò)展名為aidl的文件胡桃。package目錄就是Java類所在的目錄踩叭。該文件的語法類似于Java代碼。aidl文件中定義的是AIDL服務(wù)的接口翠胰。這個(gè)接口需要在調(diào)用AIDL服務(wù)的程序中訪問容贝。
2.? 如果aidl文件的內(nèi)容是正確的,Eclipse插件會(huì)自動(dòng)生成一個(gè)Java接口文件(*.java)之景。
3.? 建立一個(gè)服務(wù)類(Service的子類)斤富。
4.? 實(shí)現(xiàn)由aidl文件生成的Java接口。
5.? 在AndroidManifest.xml文件中配置AIDL服務(wù)锻狗,尤其要注意的是满力,標(biāo)簽的android:name屬性值就是客戶端要引用該服務(wù)的ID,也就是Intent類構(gòu)造方法的參數(shù)值屋谭。
現(xiàn)在我們來編寫一個(gè)AIDL服務(wù)脚囊,首先建立一個(gè)android工程:aidlservice龟糕。在aidlservice工程中有一個(gè)Main類桐磁,在Main類所有的目錄建立一個(gè)IMyService.aidl文件,內(nèi)容如下:
packagenet.blogjava.mobile.aidlservice;interfaceIMyService
{
String getValue();//為AIDL服務(wù)的接口方法讲岁,調(diào)用AIDL服務(wù)的程序需要調(diào)用該方法}
在保存IMyService.aidl文件后我擂,ODT會(huì)在gen目錄下產(chǎn)生一個(gè)IMyService.java文件,讀者可以不必管這個(gè)文件中的內(nèi)容缓艳,也 不需要修改該文件的內(nèi)容校摩。這個(gè)文件是由ODT自動(dòng)維護(hù)的,只要修改了IMyService.aidl文件的內(nèi)容阶淘,IMyService.java文件的內(nèi) 容就會(huì)隨之改變衙吩。
然后建立一個(gè)MyService類,該類是Service的子類溪窒,代碼如下:
packagenet.blogjava.mobile.aidlservice;
... ...publicclassMyServiceextendsService
{//IMyService.Stub類是根據(jù)IMyService.aidl文件生成的類坤塞,該類中包含了接口方法(getValue)publicclassMyServiceImplextendsIMyService.Stub
{
@OverridepublicString getValue()throwsRemoteException
{return"從AIDL服務(wù)獲得的值.";
}
}
@OverridepublicIBinder onBind(Intent intent)
{//該方法必須返回MyServiceImpl類的對(duì)象實(shí)例returnnewMyServiceImpl();
}
}
最后需要在AndroidManifest.xml文件中配置MyService類冯勉,代碼如下:
下面來看看如何調(diào)用這個(gè)AIDL服務(wù)。首先建立一個(gè)android工程:aidlclient摹芙。然后將aidlservice工程中自動(dòng)生成的 IMyService.java文件復(fù)制到aidlclient工程中灼狰。在調(diào)用AIDL服務(wù)之前需要先使用bindService方法綁定AIDL服務(wù)。 bindService方法需要一個(gè)ServiceConnection對(duì)象浮禾。ServiceConnection有一個(gè) onServiceConnected方法交胚,當(dāng)成功綁定AIDL服務(wù)且,該方法被調(diào)用盈电。并通過service參數(shù)返回AIDL服務(wù)對(duì)象蝴簇。下面是調(diào)用 AIDL服務(wù)的完成代碼。
packagenet.blogjava.mobile.aidlclient;
... ...publicclassMainextendsActivityimplementsOnClickListener
{privateIMyService myService =null;//創(chuàng)建ServiceConnection對(duì)象privateServiceConnection serviceConnection =newServiceConnection()
{
@OverridepublicvoidonServiceConnected(ComponentName name, IBinder service)
{//獲得AIDL服務(wù)對(duì)象myService =IMyService.Stub.asInterface(service);try{//調(diào)用AIDL服務(wù)對(duì)象中的getValue方法匆帚,并以對(duì)話框中顯示該方法的返回值newAlertDialog.Builder(Main.this).setMessage(
myService.getValue()).setPositiveButton("確定",null)
.show();
}catch(Exception e)
{
}
}
@OverridepublicvoidonServiceDisconnected(ComponentName name)
{
}
};
@OverridepublicvoidonClick(View view)
{//綁定AIDL服務(wù)bindService(newIntent("net.blogjava.mobile.aidlservice.IMyService"),
serviceConnection, Context.BIND_AUTO_CREATE);
}
... ...
}
在編寫AIDL服務(wù)和客戶端時(shí)要注意如下兩點(diǎn):
1.? AIDL服務(wù)中的onBind方法必須返回AIDL接口對(duì)象(MyServiceImpl對(duì)象)军熏。該對(duì)象也是onServiceConnected事件方法的第2個(gè)參數(shù)值。
2.? bindService方法的第1個(gè)參數(shù)是Intent對(duì)象卷扮,該對(duì)象構(gòu)造方法的參數(shù)需要指定AIDL服務(wù)的ID荡澎,也就是在 AndroidManifest.xml文件中標(biāo)簽的子標(biāo)簽的android:name屬性 的值。
現(xiàn)在先運(yùn)行aidlservice程序晤锹,以便安裝AIDL服務(wù)摩幔,然后運(yùn)行aidlclient程序,并單擊按鈕鞭铆,會(huì)顯示如圖11所示的對(duì)話框或衡。對(duì)話框中的信息就是AIDL服務(wù)接口中g(shù)etValue方法的返回值。
總結(jié)
本文介紹了4種跨進(jìn)程通訊的方式:Activity车遂、ContentProvider封断、Broadcast和AIDL Service。其中Activity可以跨進(jìn)程調(diào)用其他應(yīng)用程序的Activity舶担;ContentProvider可以訪問其他應(yīng)用程序返回的 Cursor對(duì)象坡疼;Broadcast采用的是被動(dòng)接收的方法,也就是說衣陶,客戶端只能接收廣播數(shù)據(jù)柄瑰,而不能向發(fā)送廣播的程序發(fā)送信息。AIDL Service可以將程序中的某個(gè)接口公開剪况,這樣在其他的應(yīng)用程序中就可以象訪問本地對(duì)象一樣訪問AIDL服務(wù)對(duì)象了教沾。這4種跨進(jìn)程通訊的方式可以應(yīng)用在 不同的場(chǎng)合,例如译断,在需要顯示可視化的界面時(shí)可以用Activity授翻,需要返回記錄集時(shí)可以用ContentProvider。至于在應(yīng)用程序中具體要用 到哪一種或幾種方式進(jìn)行跨進(jìn)程通訊,讀者可以根據(jù)實(shí)際情況進(jìn)行選擇堪唐。