3G,全稱為3rd Generation,中文含義就是指第三代數字通信。
所謂3G,是指将無線通信與國際網際網路等多媒體通信結合的新一代移動通信系統。 3G隻是一種通信技術标準,符合這個标準的技術有WCDMA、CDMA2000、TD-SCDMA三種制式。中國聯通使用的是WCDMA(世界上大部分 3G網絡都采用的是該标準) ;中國電信使用的是CDMA2000 (日、韓和北美使用);中國移動使用的是具有自主知識産權的TD-SCDMA(隻有中國才使用) 。相對第一代模拟制式手機(1G)和第二代GSM、 CDMA等數字手機(2G),3G網絡能處理圖像、音樂、視訊等多種媒體形式,提供包括網頁浏覽、電話會議、電子商務等多種資訊服務。第三代與前兩代的主要差別是在傳輸聲音和資料的速度上有很大的提升。
由于3G商用需要相當浩大的工程,要從目前的2G邁向3G不可能一下就銜接得上,是以前幾年2.5G的手機就出現了。符合2.5G标準的技術有 CDMA2000 1X和GPRS,中國聯通使用的是CDMA2000 1X标準,中國移動使用的是GPRS标準。目前,我們可以把2.5G移動通信技術看作是2G邁向3G的銜接性技術,在2.5G網絡下出現了如WAP、藍牙 (Bluetoot) 等技術。
Android 應用程式架構
src/ java原代碼存放目錄
gen/ 自動生成目錄
gen 目錄中存放所有由Android開發工具自動生成的檔案。目錄中最重要的就是R.java檔案。 這個檔案由Android開發工具自動産生的。Android開發工具會自動根據你放入res目錄的xml界面檔案、圖示與常量,同步更新修改 R.java檔案。正因為R.java檔案是由開發工具自動生成的,是以我們應避免手工修改R.java。R.java在應用中起到了字典的作用,它包含了界面、圖示、常量等各種資源的id,通過R.java,應用可以很友善地找到對應資源。另外編繹器也會檢查R.java清單中的資源是否被使用到,沒有被使用到的資源不會編繹進軟體中,這樣可以減少應用在手機占用的空間。
res/ 資源(Resource)目錄
在這個目錄中我們可以存放應用使用到的各種資源,如xml界面檔案,圖檔或資料。具體請看ppt下方備注欄。
AndroidManifest.xml 功能清單檔案
這個檔案列出了應用程式所提供的功能,在這個檔案中,你可以指定應用程式使用到的服務(如電話服務、網際網路服務、短信服務、GPS服務等等)。另外當你新添加一個Activity的時候,也需要在這個檔案中進行相應配置,隻有配置好後,才能調用此Activity。
default.properties 項目環境資訊,一般是不需要修改此檔案
res/drawable 專門存放png、jpg等圖示檔案。在代碼中使用getResources().getDrawable(resourceId)擷取該目錄下的資源。
res/layout 專門存放xml界面檔案,xml界面檔案和HTML檔案一樣,主要用于顯示使用者操作界面。
res/values 專門存放應用使用到的各種類型資料。不同類型的資料存放在不同的檔案中,如下:
· strings.xml 定義字元串和數值,在Activity中使用getResources().getString(resourceId) 或getResources().getText(resourceId)取得資源。它的作用和 struts中的國際化資源檔案一樣。
<?xml version="1.0" encoding="UTF-8"?>
< resources>
< string name="android"> android</string>
< /resources>
· arrays.xml 定義數組。
<?xml version="1.0" encoding="utf-8"?>
< resources>
<string-array name="colors">
<item>red</item>
<item>yellow</item>
<item>green</item>
<item>blue</item>
</string-array>
< /resources>
· colors.xml 定義顔色和顔色字串數值,你可以在Activity中使用getResources().getDrawable(resourceId) 以及getResources().getColor(resourceId)取得這些資源。例子如下:
<?xml version="1.0" encoding="UTF-8"?>
< resources>
< color name="contents_text">#ff000000</color>
< /resources>
· dimens.xml 定義尺寸資料,在Activity中使用getResources().getDimension(resourceId) 取得這些資源
<?xml version="1.0" encoding="UTF-8"?>
< resources>
< dimen name="key_height">50dip</dimen>
< /resources>
· styles.xml 定義樣式。
<?xml version="1.0" encoding="utf-8"?>
< resources>
<style name="androidText" parent="@style/Text">
<item name="android:textSize">18px</item>
<item name="android:textColor">#008</item>
</style>
< /resources>
res/anim/ 編譯成幀動畫的XML檔案。
res/xml/ 在Activity中使用getResources().getXML()讀取該目錄下的XML資源檔案。
res/raw/ 該目錄下的檔案将直接被複制到裝置上。編譯軟體時,這些資料不會被編譯,它們被直接加入到程式安裝包裡。 為了在程式中使用這些資源,你可以調用getResources().openRawResource(ID) , 參數ID形式:R.raw. somefilename。
電話拔号器
因為應用要使用手機的電話服務,是以要在清單檔案AndroidManifest.xml中添加電話服務權限:
< ?xml version="1.0" encoding="utf-8"?>
< manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.android.action"
android:versionCode="1"
android:versionName="1.0">
略....
<uses-sdk android:minSdkVersion=“6" />
<uses-permission android:name="android.permission.CALL_PHONE"/>
< /manifest>
界面布局:
< ?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/inputmobile"/>
<EditText android:layout_width="fill_parent" android:layout_height="wrap_content"
android:id="@+id/mobile"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/button"
android:id="@+id/button"/>
< /LinearLayout>
LinearLayout (線性布局)、AbsoluteLayout(絕對布局)、RelativeLayout(相對布局)、TableLayout(表格布局)、FrameLayout(幀布局)
Activity:
public class DialerAction extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
EditText editText = (EditText)findViewById(R.id.mobile);
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+ editText.getText()));
DialerAction.this.startActivity(intent);
}
});
}
}
測試步驟:
1>在Eclipse中運作此應用
2>在Dos視窗中進入android SDK安裝路徑的tools目錄,輸入以下指令再開啟一個Android模拟器:
emulator -data android
注:android為使用者資料存取檔案,如果該檔案不存在,預設在tools目錄建立該檔案
3>在電話擾号器中輸入上圖現顯的電話号碼
“ 尚未注冊網絡 ” 錯誤資訊的解決辦法
打開Android模拟器時,出現無信号,拔打電話或發短信時,提示“尚未注冊網絡”錯誤資訊的解決方案如下。
l 場景一:你的電腦沒有連接配接上網際網路,同時也沒有在區域網路。
解決辦法:右鍵點選網路上的芳鄰,選擇"屬性",在網絡連接配接視窗中右鍵點選"本地連接配接",選擇"屬性",設定TCP/IP屬性如下:
IP位址:192.168.1.100
子網路遮罩:255.255.255.0
預設網關:192.168.1.100
首選DNS 伺服器:192.168.1.100
l 場景二:你的電腦沒有連接配接上網際網路,但在區域網路。
解決辦法:右鍵點選網路上的芳鄰,選擇"屬性",在網絡連接配接視窗中右鍵點選"本地連接配接",選擇"屬性",設定TCP/IP屬性如下:
IP位址:設定成你所在區域網路的IP,如:192.168.1.100
子網路遮罩:設定成你所在區域網路的掩碼,如:255.255.255.0
預設網關:設定成你所在區域網路的網關,一般網關的IP格式為:*.*.*.1,如:192.168.1.1
首選DNS 伺服器:設定成你所在區域網路的路由器IP,一般路由器的IP格式為:*.*.*.1,如:192.168.1.1
最後一種解決方案是:讓你的電腦連接配接上網際網路。
短信發送器
因為應用要使用手機的短信服務,是以要在清單檔案AndroidManifest.xml中添加短信服務權限:
< ?xml version="1.0" encoding="utf-8"?>
< manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.android.sms"
android:versionCode="1"
android:versionName="1.0">
略....
<uses-sdk android:minSdkVersion=“4" />
<uses-permission android:name="android.permission.SEND_SMS"/>
< /manifest>
界面布局:
< ?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical“ android:layout_width="fill_parent“ android:layout_height="fill_parent" >
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/inputmobile"/>
<EditText android:layout_width="fill_parent" android:layout_height="wrap_content"
android:id="@+id/mobile"/>
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/content"/>
<EditText android:layout_width="fill_parent" android:layout_height="wrap_content"
android:minLines="3"
android:id="@+id/content"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/button"
android:id="@+id/button"/>
< /LinearLayout>
Activity主要代碼:
String mobile = mobileView.getText().toString();
String content = contentView.getText().toString();
SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent = PendingIntent.getBroadcast(SMSSender.this, 0, new Intent(), 0);
if(content.length()>70){//如果字數超過70,需拆分成多條短信發送
List<String> msgs = smsManager.divideMessage(content);
for(String msg : msgs){
smsManager.sendTextMessage(mobile, null, msg, sentIntent, null);
//最後二個參數為短信已發送的廣播意圖,最後一個參數為短信對方已收到短信的廣播意圖
}
}else{
smsManager.sendTextMessage(mobile, null, content, sentIntent, null);
}
Toast.makeText(SMSSender.this, "短信發送完成", Toast.LENGTH_LONG).show();
對應用進行單元測試
第一步:首先在AndroidManifest.xml中加入下面紅色代碼: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.android.action “ android:versionCode="1“ android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <uses-library android:name="android.test.runner" /> .... </application> <uses-sdk android:minSdkVersion="6" /> <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="cn.android.action" android:label="Tests for My App" /> </manifest> 上面targetPackage指定的包要和應用的package相同。 第二步:編寫單元測試代碼(選擇要測試的方法,右鍵點選 “ Run As ” -- “ Android Junit Test ” ): import android.test.AndroidTestCase; import android.util.Log; public class XMLTest extends AndroidTestCase { public void testSomething() throws Throwable { Assert.assertTrue(1 + 1 == 3); } }
使用檔案進行資料存儲
首先給大家介紹使用檔案如何對資料進行存儲,Activity提供了openFileOutput()方法可以用于把資料輸出到檔案中,具體的實作過程與在J2SE環境中儲存資料到檔案中是一樣的。
public class FileActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
...
FileOutputStream outStream = this.openFileOutput("android.txt", Context.MODE_PRIVATE);
outStream.write("安桌".getBytes());
outStream.close();
}
}
openFileOutput()方法的第一參數用于指定檔案名稱,不能包含路徑分隔符“/” ,如果檔案不存在,Android 會自動建立它。建立的檔案儲存在/data/data/<package name>/files目錄,如: /data/data/cn.android.action/files/android.txt ,通過點選Eclipse菜單“Window”-“Show View”-“Other”,在對話視窗中展開android檔案夾,選擇下面的File Explorer視圖,然後在File Explorer視圖中展開/data/data/<package name>/files目錄就可以看到該檔案。
openFileOutput()方法的第二參數用于指定操作模式,有四種模式,分别為: Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:為預設操作模式,代表該檔案是私有資料,隻能被應用本身通路,在該模式下,寫入的内容會覆寫原檔案的内容,如果想把新寫入的内容追加到原檔案中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式會檢查檔案是否存在,存在就往檔案追加内容,否則就建立新檔案。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用來控制其他應用是否有權限讀寫該檔案。
MODE_WORLD_READABLE:表示目前檔案可以被其他應用讀取; MODE_WORLD_WRITEABLE:表示目前檔案可以被其他應用寫入。
如果希望檔案被其他應用讀和寫,可以傳入:
openFileOutput("android.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
android有一套自己的安全模型,當應用程式(.apk)在安裝時系統就會配置設定給他一個userid,當該應用要去通路其他資源比如檔案的時候,就需要userid比對。預設情況下,任何應用建立的檔案,sharedpreferences,資料庫都應該是私有的(位于/data/data /<package name>/files),其他程式無法通路。除非在建立時指定了Context.MODE_WORLD_READABLE或者 Context.MODE_WORLD_WRITEABLE ,隻有這樣其他程式才能正确通路。
讀取檔案内容
如果要打開存放在/data/data/<package name>/files目錄應用私有的檔案,可以使用Activity提供openFileInput()方法。
FileInputStream inStream = this.getContext().openFileInput("android.txt");
Log.i("FileTest", readInStream(inStream));
readInStream()的方法請看本頁下面備注。
或者直接使用檔案的絕對路徑:
File file = new File("/data/data/cn.android.action/files/android.txt");
FileInputStream inStream = new FileInputStream(file);
Log.i("FileTest", readInStream(inStream));
注意:上面檔案路徑中的“cn.android.action”為應用所在包,當你在編寫代碼時應替換為你自己應用使用的包。
對于私有檔案隻能被建立該檔案的應用通路,如果希望檔案能被其他應用讀和寫,可以在建立檔案時,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE權限。
Activity還提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于擷取/data/data/<package name>/cache目錄
getFilesDir()方法用于擷取/data/data/<package name>/files目錄
public static String readInStream(FileInputStream inStream){
try {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = -1;
while((length = inStream.read(buffer)) != -1 ){
outStream.write(buffer, 0, length);
}
outStream.close();
inStream.close();
return outStream.toString();
} catch (IOException e) {
Log.i("FileTest", e.getMessage());
}
return null;
}
把檔案存放在 SDCard
使用Activity的openFileOutput()方法儲存檔案,檔案是存放在手機空間上,一般手機的存儲空間不是很大,存放些小檔案還行,如果要存放像視訊這樣的大檔案,是不可行的。對于像視訊這樣的大檔案,我們可以把它存放在SDCard。 SDCard是幹什麼的?你可以把它看作是移動硬碟或U盤。
在模拟器中使用SDCard,你需要先建立一張SDCard卡(當然不是真的SDCard,隻是鏡像檔案)。建立SDCard可以在Eclipse建立模拟器時随同建立,也可以使用DOS指令進行建立,如下:
在Dos視窗中進入android SDK安裝路徑的tools目錄,輸入以下指令建立一張容量為2G的SDCard,檔案字尾可以随便取,建議使用.img:
mksdcard 2048M D:\AndroidTool\sdcard.img
在程式中通路SDCard,你需要申請通路SDCard的權限。
在AndroidManifest.xml中加入通路SDCard的權限如下:
< !-- 在SDCard中建立與删除檔案權限 -->
< uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
< !-- 往SDCard寫入資料權限 -->
< uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
要往SDCard存放檔案,程式必須先判斷手機是否裝有SDCard,并且可以進行讀寫。
注意:通路SDCard必須在AndroidManifest.xml中加入通路SDCard的權限
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
File sdCardDir = Environment.getExternalStorageDirectory();//擷取SDCard目錄
File saveFile = new File(sdCardDir, “android.txt”);
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("安桌".getBytes());
outStream.close();
}
Environment.getExternalStorageState()方法用于擷取SDCard的狀态,如果手機裝有SDCard,并且可以進行讀寫,那麼方法傳回的狀态等于Environment.MEDIA_MOUNTED。
Environment.getExternalStorageDirectory()方法用于擷取SDCard的目錄,當然要擷取SDCard的目錄,你也可以這樣寫:
File sdCardDir = new File("/sdcard"); //擷取SDCard目錄
File saveFile = new File(sdCardDir, "android.txt");
//上面兩句代碼可以合成一句: File saveFile = new File("/sdcard/android.txt");
FileOutputStream outStream = new FileOutputStream(saveFile);
outStream.write("安桌test".getBytes());
outStream.close();
|
|
|
|
|
|
|
|
cuihai | 2011-03-03 12:15 |
三、開發工具箱 Android 設計哲學 即使平台之間有很大的不同,但是如何利用API 建立應用程式的學習過程是大同小異的。一般來說,有兩個步驟:首先,應該知道怎麼用API 實作你的功能。其次,要了解平台間的細微差别。換句話說,首先你應該學會如何建立應用程式(了解應用程式的基本結構等),然後就要學會根據具體情況實作這個應用程式。 相比而言,第二階段(學習使用正确的方法來實作應用程式)通常需要很長一段時間,在這個過程中你會不斷地寫代碼,犯錯誤,然後從錯誤中吸取教訓。顯然,這不是一個有效的學習方法,本小節和下面的一些連接配接針對這向你伸出援助之手,教你怎麼學習建立你的應用程式。 在此之前,先講一個要點:成功的應用程式往往提供一個突出的使用者體驗。當Android團隊建構了一個有着健壯核心的系統時,大多數的使用者體驗将來源于使用者和應用程式之間的的互動。是以,我們鼓勵你們花時間去建構應用程式優秀的使用者體驗。 顯著的使用者體驗展現在三個核心特征上:1、快速;2、響應;3、無縫。當然,自從計算機出現以後,每一個平台都曾經有過類似的三種性質。盡管如此,每個平台實作這些特性的方式也有所不同;下面将會簡單的介紹在Android 平台下面你的應用程式将如何達到這些要求。 快速(Fast) Android 程式執行應該是很快的。當然,準确來說它的程式應該執行的很有效率(有效率才會快)。在目前的計算機世界裡喲這樣一個傾向:假設摩爾定律能夠最終解決我們所有的問題。當這種傾向遇到嵌入式應用程式的時候,摩爾定律就變得有些複雜了。 與桌面和服務應用程式不一樣,摩爾定律在移動裝置應用程式上面不是真正适用的。摩爾定律實際上是關于電子半導體內建密度的,它原本的含義是:随着時間的流逝,在給定的電路尺寸上面可以內建更多的電路。對于桌面和服務應用陳旭來說,它的含義是: 你可以打包更多的“速度”在一個大小差不多的晶片上面,速度的提高,其他相應的一些性能也會顯著的提高。對以像手機這樣的嵌入式應用程式來說,相反的,使用摩爾定律是為了使晶片變得更小。這樣,随着晶片密度的提高,相同功能的晶片會變得越來越小,功耗會越來越低,進而使手機做的越來越小,電池的持續的時間越來越長。這樣就導緻手持嵌入式裝置相對于桌面系統來說正在以一種比較慢二實際的速度在增漲。因而,對于嵌入式裝置來說,摩爾定律就意味着更多的功能和更少的功耗,速度隻是次要的。這就是我們要寫出高效代碼的原因:你不能天真的認為電話在速度上面的增漲速度和桌面、服務應用程式是一樣的。一般來說,高效的代碼意味着最小的記憶體占用,意味着緊湊的風格,意味着避免了因為某種語言和編碼習慣對性能的影響。我們用面向對象這個概念來了解,大部分這樣的工作都是在方法層面上,與實際的代碼,循環等等相類似。 在 “編寫高效Android” 一文中,我們會對此做詳細的介紹。 響應(Responsive) 我們有可能能夠編寫赢得世界上所有的性能測試的代碼,但是使用者使用起來會感到很惱火,這是因為應用程式沒有足夠的響應性——讓人感覺反映遲鈍,在關鍵的時刻失靈,或者處理輸入太慢。在Android 平台下,那些響應性不夠的應用程式會經常彈出"Application Not Responding" (ANR)這樣的緻命消息。 通常,這會在應用程式不能響應使用者的輸入的情況下發生。例如,你的應用程式在一些I/O 操作上(如網絡接口調用)阻塞,這是主線程将不會處理使用者的輸入事件,一段時間之後應用系統就會認為你的程式挂起了,就會給一個選項給使用者詢問是否結束它。同樣的,如果你的應用程式花費很多時間去建構記憶體中的一個結構、或者計算遊戲的下一步,這時系統同樣會認為程式已經挂起。當碰到上面情況的時候,要確定計算的高效性,但是即使是最高效的代碼也需要花費時間。 在上面兩個例子中,問題的解決方案是建立一個子線程來處理大部分的工作,這樣就能保證你的主線程(響應使用者界面事件)一直運作,這樣防止系統認為你的程式已經僵化。因為這種線程的實作一般在“類”(CLASS)這個層次上,你可以把響應當成是類的問題來處理(這裡,可以和方法層次描述的基本性能相比較)。 這裡隻是一個簡單的介紹,在 “建構響應Android 應用程式” 一文中對于應用程式的響應性有詳細的介紹。 無縫性(Seamless) 即使是你的應用程式執行很快,并且具有很高的響應性,它仍然有可能讓使用者苦惱。一個常見的例子是背景程序(比如Android 的 Service 和 BroadcastReceiver)對某些事件可能會突然彈出一個UI 響應。這似乎是無關緊要的,并且一般開發者會認為這這是正常的,因為他們花費了戴亮時間去測試和使用自己的應用程式。可是,Android 應用程式模型的建構是能夠允許使用者在不同的應用程式之間進行流暢的切換。這就意味着,當你的背景程序實際上彈出那個UI 的時候,使用者可能正在系統的其他部分中,做一些其他的事情,如在接電話。想像一下,如果SMS 服務每次都會在文本消息傳入時彈出一個對話框,這很快就會使使用者崩潰。這就是為什麼Android 标準對于這些事件使用的是通知(Notifications)機制;這使使用者能夠自己控制。 這僅僅是一個例子,相似的例子數不勝數。比如,如果Activities 沒有正确的實作onPause()方法和其他生命周期方法,這将會導緻資料丢失。或者如果你的應用程式有意的暴露資料給其他應用程式使用,你應該使用一個ContentProvider,而不是用一個路人皆可見的未加工過的檔案或者資料庫。 這些例子有一個共同的特點,他們都涉及到程式與程式或則程式與系統之間的互動。系統被設計為将多個應用程式視為一種松耦合元件的聯合,而不是大塊的代碼黑盒。這就允許作為開發人員的你将整個系統看作是一個這些元件的大聯合。這允許你幹淨地封裝,無縫地和其他應用程式結合,因而你能設計自己喜歡的程式。 這使一種元件層次的概念(與性能和響應的類層次和方法層次相對應)。至于怎樣編寫無縫性能很高的代碼, “與系統相結合” 一文中将會對此做出介紹,提供代碼提示和最佳執行個體。 建構自定義元件 Android 中,你的應用程式程式與View 類元件有着一種固定的聯系,例如按鈕(Button)、文本框(TextView), 可編輯文本框(EditText), 清單框(ListView), 複選框(CheckBox),單選框(RadioButton), 滾動條(Gallery), 微調器(Spinner), 等等,還有一些比較先進的有着特殊用途的View 元件,例如 AutoCompleteTextView, ImageSwitcher 和TextSwitcher。除此之外,種類繁多的像 線性布局(LinearLayout), 架構布局(FrameLayout), 這樣的布局元件(Layout)也被認為是View 元件,他們是從View類派生過來的。 你的應用程式就是這些控制元件和布局元件以某種方式結合顯示在螢幕上,一般來說這些元件對你來說基本夠用,但是你也應該知道你是可以通過類繼承建立屬于自己的元件,一般可以繼承像View、Layouts(布局元件)這樣的元件,甚至可以是一些比較進階的控制類元件。下面我們說一下為什麼要繼承: · 你可以為實作某種功能建立一個完全自定義風格的元件,例如用二維的圖形建立控制元件實作聲音的控制,就像電子控制一樣。 · 你可以把幾種元件結合形成一個新的元件,你的元件可能同時包含ComboBox (一個能輸入的文本清單)和dual-pane selector control(左右兩個List 視窗, 你可以配置設定視窗每一項的從屬關系)等等。 · 你可以建立自己的布局元件(Layout)。SDK 中的布局元件已經提供了一系列的選項讓你打造屬于自己的應用程式,但是進階的開發人員會發現根據現有的 Layout 元件開發新的Layout 元件是很有必要的,甚至是完全從底層開發新的組 件。 · 你可以覆寫一個現有元件的顯示或功能。例如,改變EditText(可編輯文本)元件在螢幕上的顯示方式(可以參考Notepad 的例子,裡面教你如何建立一個下劃線的顯示頁面)。 · 你可以捕獲像按鍵按下這樣的事件,以一些通用的方法來處理這些事件(一個遊戲的例子)。 為了實作某種目标你可能很有必要擴充一個已經存在的View 元件,下面我們結合一些例子教你如何去做。 基本方法(The Basic Approach ) 下面的一些步驟都比較概括,教你如何建立自己的元件: 1. 讓你的類(Class)繼承一個現有的View 類或View 的子類。 2. 重載父類的一些方法:需要重載的父類方法一般以‘on’開頭,如onDraw(), onMeasure()和 onKeyDown()等等。 o 這個在Activity 或則 ListActivity 派生中同樣适用,你需要重載一些生命 周期函數和一些其他功能性的HOOK 函數。 3. 使用你的繼承類:一旦你的繼承類建立完成,你可以在基類能夠使用的地方使用你的繼承類,但完成功能就是你自己編寫的了。 繼承類能夠定義在activities 裡面,這樣你能夠友善的調用,但是這并不是必要的(或許在你的應用程式中你希望建立一個所有人都可以使用的元件)。 完全自定義元件(Fully Customized Components) 完全自定義元件的方法可以建立一些用于顯示的圖形元件(graphical components),也許是一個像電壓表的圖形計量器,或者想卡拉OK 裡面顯示歌詞的小球随着音樂滾動。 無論那種方式,你也不能單純的利用元件的結合完成,無論你怎麼結合這些現有的元件。 幸運的是,你可以以你自己的要求輕松地建立完全屬于自己的元件,你會發現不夠用的隻是你的想象力、螢幕的尺寸和處理器的性能(記住你的應用程式最後隻會在那些性能低于桌面電腦的平台上面運作)。 下面簡單介紹如何打造完全自定義的元件: 1. 最為通用的VIEW 類的父類毫無疑問是View 類,是以,最開始你要建立一個基于此類的一個子類。 2. 你可以寫一個構造函數從XML 檔案中提取屬性和參數,當然你也可以自己定義這些屬性和參數(也許是圖形計量器的顔色和尺寸,或者是指針的寬度和幅度等等) 3. 你可能有必要寫自己的事件監聽器,屬性的通路和修改函數和一些元件本身的功能上的代碼。 4. 如果你希望元件能夠顯示什麼東西,你很有可能會重載 onMeasure() 函數,因而你就不得不重載 onDraw() 函數。當兩個函數都用預設的,那麼 onDraw()函數将不會做任何事情,并且預設的 onMeasure() 函數自動的設定了一個100x100 —的尺寸,這個尺寸可能并不是你想要的。 5. 其他有必要重載的on... 系列函數都需要重新寫一次。 onDraw()和onMeasure() onDraw()函數将會傳給你一個 Canvas 對象,通過它你可以在二維圖形上做任何事情,包括其他的一些标準和通用的元件、文本的格式,任何你可以想到的東西都可以通過它實作。 注意: 這裡不包括三維圖形如果你想使用三維的圖形,你應該把你的父類由View 改為SurfaceView 類,并且用一個單獨的線程。可以參考GLSurfaceViewActivity 的例子。 onMeasure() 函數有點棘手,因為這個函數是展現元件和容器互動的關鍵部分,onMeasure()應該重載,讓它能夠有效而準确的表現它所包含部分的測量值。這就有點複雜了,因為我們不但要考慮父類的限制(通過onMeasure()傳過來的),同時我們應該知道一旦測量寬度和高度出來後,就要立即調用setMeasuredDimension() 方法。 概括的來講,執行onMeasure()函數分為一下幾個階段: 1. 重載的onMeasure()方法會被調用,高度和寬度參數同時也會涉及到 (widthMeasureSpec 和heighMeasureSpec 兩個參數都是整數類型),同時你應該考慮你産品的尺寸限制。這裡詳細的内容可以參考View.onMeasure(int,int) (這個連接配接内容詳細的解釋了整個measurement 操作)。 2. 你的元件要通過onMeasure()計算得到必要的measurement 長度和寬度進而來顯示你的元件,它應該與規格保持一緻,盡管它可以實作一些規格以外的功能(在這個例子裡,父類能夠選擇做什麼,包括剪切、滑動、送出異常或者用不同的參數又一次調用onMeasure()函數)。 3. 一旦高度和寬度計算出來之後,必須調用setMeasuredDimension(int width, int height),否則就會導緻異常。 一個自定義元件的例子(A Customized Component Example) 在 API Demos 中的CustomView 提供了以一個自定義元件的例子,這個自定義元件在LabelView 類中定義。 LabelView 例子涉及到了自定義元件的方方面面: · 首先讓自定義元件從View 類中派生出來。 · 編寫帶參數的構造函數(參數可以來源于XML 檔案)。這裡面的一些處理都已經在View 父類中完成,但是任然有些Labelview 使用的自定義元件特有的新的參數需要處理。 · 一些标準的Public 函數,例如setText(), setTextSize(), setTextColor() · 重載onMeasure()方法來确定元件的尺寸(注意:在LabelView 中是通過一個私有函數measureWidth()來實作的) · 重載onDraw()函數把Lable 顯示在提供的canvas 上。 在例子中,你可以通過custom_view_1.xml 看到自定義元件LabelView 的用法。在XML檔案中特别要注意的是android:和app:兩個參數的混合運用,app:參數表示應用程式中被認為是LabelView 元件的個體,這些也會作為資源在R 類中定義。 元件混合技術Compound Components (or Compound Controls) 如果你不想建立一個完全自定義的元件,而是由幾個現有元件的組合産生的新的元件,那麼混合元件技術就更加适合。簡單的來說,這樣把幾個現有的元件融合到一個邏輯組合裡面可以封裝成一個新的元件。例如,一個Combo Box 元件可以看作是是一個EditText 和一個帶有彈出清單的Button 元件的混合體。如果你點選按鈕為清單選擇一項, 在Android 中,其實還有其他的兩個View 類可以做到類似的效果: Spinner 和 AutoCompleteTextView,,但是Combo Box 作為一個例子更容易讓人了解。 下面簡單的介紹如何建立組合元件: 1. 一般從Layout 類開始,建立一個Layout 類的派生類。也許在Combo box 我們會選擇水準方向的LinearLayout 作為父類。記住,其他的Layout 類是可以嵌套到裡面的,是以混合元件可以是任何元件的混合。注意,正如Activity 一樣,你既可以使用外部XML 檔案來聲明你的元件,也可以嵌套在代碼中。 2. 在新的混合元件的構造函數中,首先,調用所有的父類的構造函數,傳入對應的參數。然後可以設定你的混合元件的其他的一些方面,在哪建立EditText 元件,又在哪建立PopupList 元件。注意:你同時也可以在XML 檔案中引入一些自己的屬性和參數,這些屬性和參數也可以被你的混合元件所使用。 3. 你也可以建立時間監聽器去監聽新元件中View 類觸發的事件,例如,對List 選項單擊事件的監聽,你必須在此時間發生後更新你EditText 的值。 4. 你可能建立自己的一些屬性,帶有通路和修改方法。例如,允許設定EditText 初始值并且提供通路它的方法。 5. 在Layout 的派生類中,你沒有必要去重載onDraw()和onMeasure()方法,因為Layout 會有比較好的預設處理。但是,如果你覺得有必要你也可以重載它。 6. 你也可能重載一些on 系列函數,例如通過onKeyDown()的重載,你可以通過按某個鍵去選擇清單中的對應的值。 總之,把Layout 類作為基類有下面幾個優點: · 正如activity 一樣,你也可以通過XML 檔案去聲明你的新元件,或者你也可以在代碼中嵌套。 · onDraw()函數和onMeasure()函數是沒有必要重載的,兩個函數已經做得 很好了。 · 你可以很快的建立你的混合元件,并且可以像單一元件那樣使用。 混合元件的例子(Examples of Compound Controls) In the API Demos project 在API Demos 工程中,有兩個List 類的例子——Example 4 和Example 6,裡面的SpeechView 元件是從LinearLayout 類派生過來,實作顯示演講顯示功能,對應的原代碼是List4.java 和List6.java。 調整現有元件(Tweaking an Existing Component) 在某些情況下,你可能有更簡單的方法去建立你的元件。如果你應經有了一個非常類似的元件,你所要做的隻是簡單的從這個元件派生出你的元件,重在其中一些有必要修改的方法。通過完全自定義元件的方法你也可以同樣的實作,但通過沖View 派生産生新的元件,你可以簡單擷取一些已經存在的處理機制,這些很可能是你所想要的,而沒有必要從頭開始。 例如,在SDK 中有一個NotePad 的例子(NotePad application )。該例子示範了很多Android 平台實用的細節,例如你會學到從EditView 派生出能夠自動換行的記事本。這還不是一個完美的例子,因為相比早期的版本來說,這些API 已經感變了很多,但它确實說明了一些問題。 如果你還未檢視該程式,現在你就可以在Eclipse 中導入記事本例程(或僅通過提供的連結檢視相應的源代碼)。特别是檢視NoteEditor.java 中的MyEditText 的定義。 下面有幾點要注意的地方: 1. 聲明(The Definition) 這個類是通過下面一行代碼來定義的: public static class MyEditText extends EditText o 它是定義在NoteEditor activity 類裡面的,但是它是共有的 (public),是以如果有必要,它可以通過NoteEditor.MyEditText 從 NoteEditor 外面來調用。 o 它是static 類(靜态類),意味着不會出現所謂的通過父類通路資料的 “虛态方法”, 這樣就使該類成為一個可以不嚴重依賴NoteEditor 的單獨 類。對于不需要從外部類通路的内聯類的建立,這是一個很清晰地思路,保 證所産生的類很小,并且允許它可以被其他的類友善的調用。 o 它是EditText 類的擴充,它是我們選擇的用來自定義的父類。當我們 完成以後,新的類就可以作為一個普通的EditText 來使用。 2. 類的初始化 一般來說,父類是首先調用的。進一步來說,這不是一個預設的構造函數,而是 一個帶參數的構造函數。因為EditText 是使用從XML 布局檔案提取出來的參 數進行建立,是以我們的構造函數也要取出參數并且将這些參數傳遞給父類。 3. 方法重載 在本例中,僅對onDraw()一個方法進行重載。但你可以很容易地為你的定制組 件重載其他需要的方法。 對于記事本例子來說,通過重載onDraw()方法我們可以在EidtView 的畫布 (canvas)上繪制藍色的線條(canvas 類是通過重寫的onDraw()方法傳遞)。 該函數快要結束時要調用super.onDraw()函數。父類的方法是應該調用,但 是在這個例子裡面,我們是在我們劃好了藍線之後調用的。 4. 使用定制元件 現在,我們已經有自己定制的元件了,但是應該怎樣使用它呢?在記事本例子中, 定制的元件直接在預定義的布局檔案中使用,讓我們看一看res/layout 目錄 中的note_editor.xml 檔案。 <view xmlns:android="http://schemas.android.com/apk/res/android" class="com.android.notepad.NoteEditor$MyEditText" id="@+id/note" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:drawable/empty" android:padding="10dip" android:scrollbars="vertical" android:fadingEdge="vertical" /> o 該自定義元件在XML 中是作為一個一般的View 類來建立的,并且是通過 全路徑包來描述的。注意這裡内聯類是通過NoteEditor$MyEditText 來 表示的,這是Java 程式設計中引用内聯類的标準方法。 o 在定義中的其他屬性和參數将傳遞給定制元件的構造函數,然後才傳到 EditText 構造函數中,是以這些參數也是你使用EditText 元件的參數。注意, 這裡你也可以增加你自己的參數,我們将在下面讨論這個問題。 這就是你全部需要做的,誠然這是一個簡單的例子。但問題的關鍵是:你的需求有多複雜,那麼你的自定義元件就有多麼複雜。 一個更為複雜的元件可能需要重載更多的on 系列函數,并且還要很多特有的函數來充分實作自定義元件的功能。唯一的限制就是你的想象力和你需要元件去執行什麼工作。 現在開始你的元件化之旅吧 如你所見,Android 提供了一種精巧而又強大的元件模型,讓你盡可能的完成你的工作。從簡單的元件調整到元件混合,甚至完全自定義元件,靈活的運用這些技術,你應該可以得到一個完全符合你外觀要求的的Android 程式 Android 平台的可選API Android 适用于各種各樣的手機,從最低端直到最高端的智能手機。核心的Android API在每部手機上都可使用,但任然有一些API 接口有一些特别的适用範圍:這就是所謂的“可選API”。這些API 之是以是“可選的”,主要是因為一個手持裝置并不一定要完全支援這類API,甚至于完全不支援。例如,一個手持裝置可能沒有GPS 或Wi-FI 的硬體。在這個條件下,這類功能的API 任然存在,但不會以相同的方式來工作。例如Location API 任然在沒有GPS 的裝置上存在,但極有可能完全沒有安裝功能提供者,意味着這類API 就不能有效地使用。 你的應用應該無障礙地運作或連接配接在一個可能不支援你API 的裝置,因為你的裝置上有這些上層接口(the classes)。當然執行起來可能什麼也不會做,或者抛出一個異常。 每個API 會做些什麼我們可以參考這些API 的說明文檔,你應該編寫你的程式來适當的處理這類問題。 Wi-Fi API Wi-Fi API 為應用程式提供了一種與那些帶有Wi-FI 網絡接口的底層無線堆棧互相交流的手段。幾乎所有的請求裝置資訊都是可利用的,包括網絡的連接配接速度、IP 位址、目前狀态等等,還有一些其他可用網絡的資訊。一些可用的互動操作包括掃描、添加、儲存、結束和發起連接配接。 Wi-Fi API 在 android.net.wifi 包中。 定位服務(Location-Based Services) 定位服務允許軟體擷取手機目前的位置資訊。這包括從全球定位系統衛星上擷取地理位置,但相關資訊不限于此。例如,未來其他定位系統可能會營運,屆時,對其相應的API 接口也會加入到系統中。定位服務的API 在android.location 包中。 多媒體API(Media APIs) 多媒體API 主要用于播放媒體檔案。這同時包括對音頻(如播放MP3 或其他音樂檔案以及遊戲聲音效果等)和視訊(如播放從網上下載下傳的視訊)的支援,并支援"播放URI位址"(Note:URI 即是統一資源識别位址)模式-在網絡上直接播放的流媒體。技術上來說,多媒體API 并不是“可選的”,因為它總是要用到。但是不同的硬體環境上面可能有不同的編解碼的硬體機制,因而它又是“可選的”。 多媒體API 在 android.media 包中。 基于OpenGL 的3D 圖形(3D Graphics with OpenGL) Android 的主要使用者接口架構是一個典型的面向控件的類繼承系統。但不要讓表面的情況迷惑了你,因為在它下面是一種非常快的2D 和3D 組合的圖形引擎,并且支援硬體加速。用來通路平台3D 功能的API 接口是OpenGL ES API。和多媒體API 一樣,OpenGL 也不是嚴格意義上的“可選”,因為這些API 會總是存在并且實作那些固定的功能。但是,一些裝置可能有硬體加速環節,使用它的時候就會影響你的應用程式的表現。 l 用于HTTP請求中的常用頭 • Accept: text/html,image/* • Accept-Charset: ISO-8859-1 • Accept-Encoding: gzip,compress • Accept-Language: en-us,zh-cn • Host: www.it315.org:80 • If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT • Referer: http://www.it315.org/index.jsp • User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) • Cookie • Connection: close/Keep-Alive • Date: Tue, 11 Jul 2000 18:23:51 GMT l HTTP請求中的常用響應頭 • Location: http://www.it315.org/index.jsp • Server:apache tomcat • Content-Encoding: gzip • Content-Length: 80 • Content-Language: zh-cn • Content-Type: text/html; charset=GB2312 • Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT • Refresh: 1;url=http://www.it315.org • Content-Disposition: attachment; filename=aaa.zip • Transfer-Encoding: chunked • Set-Cookie:SS=Q0=5Lb_nQ; path=/search • Expires: -1 • Cache-Control: no-cache • Pragma: no-cache • Connection: close/Keep-Alive • Date: Tue, 11 Jul 2000 18:23:51 GMT l 通用資訊頭指既能用于請求,又能用于響應的一些消息頭。 • Cache-Control: no-cache • Pragma: no-cache • Connection: close/Keep-Alive • Date: Tue, 11 Jul 2000 18:23:51 GMT 資料的存儲與通路 接口程式設計會對軟體有影響,少用接口程式設計。 多用内部類,少建立類。 Androd的檔案輸出流:context.openFileOutput(“檔案名稱(不能帶有路徑)”,操作模式); 操作模式:context.MODE_PRIVATE:如果有新的檔案内容會覆寫原來的檔案内容,而且隻能被建立這個檔案的應用所通路。其它應用是不能被通路,因為私有了。而且預設也是這種私有的操作模式。如果檔案不存在,會自動建立。如果這個檔案已經存在,這個模式會把新的幾個問題覆寫舊的内容。 openFileOutput()方法的第一參數用于指定檔案名稱,不能包含路徑分隔符“/” ,如果檔案不存在,Android 會自動建立它。建立的檔案儲存在/data/data/<package name>/files目錄,如: /data/data/cn.itcast.action/files/itcast.txt ,通過點選Eclipse菜單“Window”-“Show View”-“Other”,在對話視窗中展開android檔案夾,選擇下面的File Explorer視圖,然後在File Explorer視圖中展開/data/data/<package name>/files目錄就可以看到該檔案。 SAX解析xml文檔: //創 建SAX工廠 SAXParserFactory factory = SAXParserFactory.newInstance(); //建立解析器 SAXParser saxParser = factory.newSAXParser(); 建立一個類myDefaultHandler,繼承DefaultHandler saxParser.parse(inputStream,handler); 在myDefaultHandler類中重寫charactors()方法、startDocument()方法、startElement()方法、endElement()方法。 在startDocument方法中建立一個存xml檔案資料的集合對象。 在startElement方法中判斷開始标簽是否為我們要的資料的開始标簽,如果是就建立對象來存取。還要用一個變量記住所經過的标簽。 在charactors方法中,判斷startElement元素的标簽是否為内容字元串的開始标簽,如果是就可以new String();獲得裡面的資料。 在endElement方法中,把記住标簽的變量置空。最後還要判斷結束标簽是否為一個完整對象的結束标簽,如果是就可以把這個對象加到集合中,再把這個對象置空。 DOM解析xml文檔 先用獲得讀取xml檔案的流。 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document dom = builder.parser(inputStream); Pull解析xml文檔 Pull解析器也是基于事件驅動解析。 思路:首先獲得pull解析器,然後設定讀入的流setIput(inputStream,”UTF-8”);就開始觸發第一個事件。得到一個int常量,然後就是用while循環,判斷是否為結束文檔事件,隻要不是結束文檔事件,就不要結束。在這個循環中完成資料的獲得。 代碼如下: public static List<Person> readXML(InputStream inStream) { XmlPullParser parser = Xml.newPullParser(); try { parser.setInput(inStream, "UTF-8"); int eventType = parser.getEventType(); Person currentPerson = null; List<Person> persons = null; while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_DOCUMENT://文檔開始事件,可以進行資料初始化處理 persons = new ArrayList<Person>(); break; case XmlPullParser.START_TAG://開始元素事件 String name = parser.getName(); if (name.equalsIgnoreCase("person")) { currentPerson = new Person(); currentPerson.setId(new Integer(parser.getAttributeValue(null, "id"))); } else if (currentPerson != null) { if (name.equalsIgnoreCase("name")) { currentPerson.setName(parser.nextText());// 如果後面是Text元素,即傳回它的值 } else if (name.equalsIgnoreCase("age")) { currentPerson.setAge(new Short(parser.nextText())); } } break; case XmlPullParser.END_TAG://結束元素事件 if (parser.getName().equalsIgnoreCase("person") && currentPerson != null) { persons.add(currentPerson); currentPerson = null; }break; } eventType = parser.next(); } inStream.close(); return persons; } catch (Exception e) { e.printStackTrace(); } return null; } 生成xml檔案 代碼如下: public static String writeXML(List<Person> persons, Writer writer){ XmlSerializer serializer = Xml.newSerializer(); try { //也可以輸出要指定的SD卡檔案 File file = new File(Enviroment.getExternalStorageDirectory(),”XXX.xml”); FileOutputStream out = new FileOutputStream(file); serializer.setOutput(writer); serializer.startDocument("UTF-8", true); //第一個參數為命名空間,如果不使用命名空間,可以設定為null serializer.startTag("", "persons"); for (Person person : persons){ serializer.startTag("", "person"); serializer.attribute("", "id", person.getId().toString()); serializer.startTag("", "name"); serializer.text(person.getName()); serializer.endTag("", "name"); serializer.startTag("", "age"); serializer.text(person.getAge().toString()); serializer.endTag("", "age"); serializer.endTag("", "person"); } serializer.endTag("", "persons"); serializer.endDocument(); return writer.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } 第六天 第三個視訊: 使用廣播: 要繼承廣播接收者類:BroadcastReceiver,如: ,隻要繼承這個類,就能完成對短信的竊聽。 訂閱廣播接收者有兩種方式:其一: SmsMessage能把PDU(移動的資料格式)資料,轉換成短能被閱讀的資訊。代碼如: 短信接收權限: 廣播被分為兩種不同的類型:“普通廣播(Normal broadcasts)”和“有序廣播(Ordered broadcasts)”。前者是完全異步的,所有接收者(邏輯上)都在同一時刻運作,對消息傳遞的效率而言這是很好的做法,但缺點是:接收者不能傳遞處理結果給下一個接收者,并且無法終止廣播Intent的傳播;然而後者是逐個執行接收者——系統會按照接收者聲明的優先級别(聲明在intent-filter元素的android:priority屬性中,數越大優先級别越高,取值範圍:-1000到1000。也可以調用IntentFilter對象的setPriority()進行設定),按順序逐次執行。 Context.sendBroadcast() 發送的是普通廣播,所有訂閱者都有機會獲得并進行處理。 Context.sendOrderedBroadcast() 發送的是有序廣播,系統會根據接收者聲明的優先級别按順序逐個執行接收者,前面的接收者有權終止廣播,如果廣播被前面的接收者終止,後面的接收者就再也無法擷取到廣播。對于有序廣播,前面的接收者可以将處理結果存放進廣播Intent,然後傳給下一個接收者。 在Android中,程式的響應(Responsive)被活動管理器(Activity Manager)和視窗管理器(Window Manager)這兩個系統服務所監視。當BroadcastReceiver在10秒内沒有執行完畢,Android會認為該程式無響應。是以在BroadcastReceiver裡不能做一些比較耗時的操作,否側會彈出ANR(Application No Response)的對話框。如果需要完成一項比較耗時的工作,應該通過發送Intent給Service,由Service來完成。而不是使用子線程的方法來解決,因為BroadcastReceiver的生命周期很短(在onReceive() 執行後BroadcastReceiver 的執行個體就會被銷毀),子線程可能還沒有結束它就先結束了。當然如果BroadcastReceiver結束了,它的宿主程序還在運作,子線程還會繼續執行。但宿主程序此時很容易在系統需要記憶體時被優先殺死,因為它屬于空程序(沒有任何活動元件的程序)。 每次廣播消息到來時都會建立BroadcastReceiver執行個體并執行onReceive() 方法。 開發調用者跟服務進行通信的應用: 開發線上音樂播放器,與伺服器進行播放和停止的操作,就可以用到。 比如:多線程上傳下載下傳。 |