因爲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);
[java] view plaincopy
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);
[java] view plaincopy
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678");
startActivity(callIntent);
執行上面的代碼後,系統會自動撥號,界面如圖1所示。
在調用撥號程序的代碼中使用了一個Intent.ACTION_CALL常量,該常量的定義以下:
public static final String ACTION_CALL = "android.intent.action.CALL" ;
[java] view plaincopy
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>
[java] view plaincopy
<!-- 從新配置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://任意字符串
[java] view plaincopy
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);
}
}
}
[java] view plaincopy
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);
}
[java] view plaincopy
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();
}
[java] view plaincopy
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爲請求碼
[java] view plaincopy
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();
}
[java] view plaincopy
@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();
[java] view plaincopy
ContentResolver cr = getContentResolver();
與Activity同樣,Content Provider也須要與一個URI對應。每個Content Provider能夠控制多個數據集,在這種狀況下,每個數據集會對應一個單獨的URI。全部的URI必須以「content://」開頭。
爲了程序更容易維護,也爲了簡化程序代碼,通常將URI定義成一個常量。例如,下面的常量表示系統的聯繫人電話號碼。
android.provider.Contacts.Phones.CONTENT_URI
[java] view plaincopy
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"
. . . >
[java] view plaincopy
<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
[java] view plaincopy
content://com.example.transportationprovider/trains
本例利用了《基於android SDK1.5的英文電子詞典的實現》一文中實現的電子詞典程序。經過ContentProvider,將電子詞典的查詞功能共享成Cursor對象。這樣 其餘的應用程序就能夠經過ContentProvider來查詞英文單詞了。關於英文詞典的具體實現細節,讀者能夠經過以下的地址查看《基於android SDK1.5的英文電子詞典的實現》一文。
http://www. androidsdn.com/article/show/111
[java] view plaincopy
http://www.ophonesdn.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;
}
... ...
}
[java] view plaincopy
... ...
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
[java] view plaincopy
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" />
[java] view plaincopy
<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" ;
[java] view plaincopy
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();
}
[java] view plaincopy
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);
}
[java] view plaincopy
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);
}
如今來運行本例,會看到如圖6所示的界面。當查詢單詞時會顯示如圖7所示的單詞列表,查詢出結果後,會顯示如圖8所示的界面。
方式三:廣播(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);
}
... ...
}
[java] view plaincopy
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();
}
}
}
}
[java] view plaincopy
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>
[java] view plaincopy
<!-- 註冊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服務的程序須要調用該方法
}
[java] view plaincopy
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();
}
}
[java] view plaincopy
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>
[java] view plaincopy
<!-- 註冊服務-->
<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);
}
... ...
}
[java] view plaincopy
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。至於在應用程序中具體要用 到哪種或幾種方式進行跨進程通信,讀者能夠根據實際狀況進行選擇。