640 likes | 953 Views
Chapter 9. 手機各項服務及通訊網路的使用. Intent. Intent 簡介. Android 應用程式中的活動、服務或廣播接收器可經由訊息啟動,然而包含這些資訊的訊息稱為 Intent 。 Intent 是一種 runtime binding 機制,可以在兩個元件之間進行連接 。 例如說:有一個活動要開啟網頁瀏覽器查看網頁,那此活動只需發出 WEB_SEARCH_ACTION 請求給 Android , Android 會根據 Intent 的請求內容,查詢各元件註冊時的 IntentFilter ,找到網頁瀏覽器活動來瀏覽網頁。.
E N D
Chapter 9 手機各項服務及通訊網路的使用
Intent簡介 Android應用程式中的活動、服務或廣播接收器可經由訊息啟動,然而包含這些資訊的訊息稱為Intent。 Intent是一種runtime binding機制,可以在兩個元件之間進行連接。 例如說:有一個活動要開啟網頁瀏覽器查看網頁,那此活動只需發出WEB_SEARCH_ACTION請求給Android,Android會根據Intent的請求內容,查詢各元件註冊時的IntentFilter,找到網頁瀏覽器活動來瀏覽網頁。
Intent的Componentname屬性 元件名稱是指Intent目標元件的名稱或是一個元件名稱對象。這種對象名稱是目標元件類別名稱和目標元件所在應用程式套件名稱的組合。 元件中封包名稱不一定要和manifest文件中的封包名稱完全符合。 元件名稱通常是一個選項,如果Intent訊息中指定了目標元件的名稱,這就稱為顯式訊息,Intent會傳送給指定的元件。 如果目標元件名稱並沒有指定,這就稱為隱式訊息,Android會透過Intent內的其他訊息和已註冊的IntentFilter比較來選擇合適的目標元件。
Intent的Action屬性 動作是描述Intent所觸發動作名稱的字串常數,對於BroadcastIntent來說,動作所指的是被廣播出去的動作。 理論上動作可以為任何字串,而與Android系統應用有關的動作,通常是以靜態字串常數的方式定義在了Intent類別當中。 下頁表列出目前Android系統中常見的Activity Action Intent的動作,其餘可參照Android SDK。
Intent的Action屬性 Activity Action Intent:
Intent的Action屬性 Broadcast Intent:
Intent的Data屬性 • Data描述Intent要操作的資料URI和資料類型。有的動作需要對相應的Data進行處理。 • 例如說:對於動作ACTION_EDIT來說,它的Data可以是聯絡人、簡訊等等可編輯的URI。而對於ACTION_CALL來說,它的Data可以是一個tel://格式的電話號碼URI。 • 正確設定Intent的Data對於Android尋找系統中符合Intent要求的元件是非常重要的。 • 如果你使用了ACTION_CALL,但是Data卻設定為mailto://格式的URI,那麼原本想執行「啟動撥打電話」的動作,會因沒有與之相對應的應用程式而不會被執行。所以每次使用Intent時,都應注意與設置Action相關的資料類型和格式。
Intent的Category屬性 Category是對被請求元件的額外描述訊息。 Android也在Intent類別中定義了一些Category字串常數,下表列出了常見的Category字串常數。
Intent的Extra屬性 Extra屬性為使用Intent連接不同的元件時,有時需要在Intent中附加額外的訊息,以便將資料傳送給目標。 舉例來說:ACTION_TIMEZONE_CHANGED需要帶有附加訊息以表示新的時區。
Intent的Extra屬性 Extra屬性用鍵值關聯結構保存在Intent當中,Intent透過呼叫putExtras()和getExtras()方法來儲存和取得Extra。 Extra是以Bundle物件的形式來保存的,Bundle物件提供了一系列put和get方法來設定、取得相應鍵值訊息。 在Intent類別中也為Android系統應用的一些Exrta的鍵值定義了靜態的字串常數。下頁表列出常見的Extra常數。
Intent簡介 • 在前面第三章有介紹到Android有活動、服務和廣播接收器,分別也有不同的Intent傳送方式。 • 要啟動一個新的活動,或讓現有的活動執行新的操作,可以透過呼叫Context.startActivity()或Activity.startActivityForResult()方法。 • 這兩個方法需要傳入的Intent參數稱為活動行為意圖(Activity Action Intent),根據Intent對目標活動描述的不同,來啟動與Intent符合的活動或傳遞信息。
Intent簡介 • 要啟動一個新的服務,或者向一個已有的服務傳送新指令,呼叫Context.startService()或Context.bindService()兩個方法,將啟動此方法的上下文對象與Service連結。 • 透過Context.sendBroadcast()、Context.sendOrderBroadcast()和Context.send-StickBroadcast()這三個方法可以發送BroadcastIntent。 • BroadcastIntent發送後,經系統比對後,擁有與BroadcastIntent相符合的IntentFilter會通知它的BroadcastReceiver啟動。 • 這種機制被廣泛運用於設備或系統狀態變化的通知。 • 舉例來說,當Android的電池電量過低時,系統會發送Action為BATTERY_LOW的廣播,接著任何符合該Action的IntentFilter所註冊的BroadcastReceiver都會啟動自訂處理程序,例如說:關閉手機的Wi-FI與GPS以節省電池耗損。
Intent簡介 • 當Intent送出後,Android都會準確找到相符合的一個或多個活動、服務或廣播接收器作為回應。因此不同類型的Intent訊息不會出現重複的。 • BroadcastIntent訊息只會傳送給廣播接收器,不會發送給活動或服務。 • 由startActivity()傳送的Intent也只會送給活動 • 由startService()傳送的Intent只會發送給服務。
Intent的屬性 • Intent抽象描述了要執行的動作,其描述的基本內容可以分為: • 元件名稱(Component name) • 動作(Action) • 資料(Data) • 類別(Category) • 附加訊息(Extra) • 標號(Flag) • 將於下頁詳細的介紹。
如何使用IntentFilter 應用程式的元件為了告訴Android本身能對應、處理哪些隱式Intent的請求,可以定義一個或多個IntentFilter。 每個IntentFilter描述該元件所能對應的Intent請求能力及接收何種類型的請求行為、資料。 舉例來說,網頁瀏覽器的IntentFilter應定義它所希望接收的Intent Action是WEB_SEARCH_ACTION,以及相關的請求資料是網址URI格式。
如何使用IntentFilter 一般來說,最常定義IntentFilter的方法是在AndroidManifest.xml文件中用標籤<Intent-Filter>描述元件的IntentFilter。 隱式Intent和IntentFilter進行比較時的三個條件是Intent的Action、Data以及Category。 實際上,一個隱式Intent要求要能夠傳送給目標元件,必要透過這三個條件的檢查,如果任何一個條件不符合,Android都不會將隱式Intent傳送給目標元件。
如何使用IntentFilter 1.動作驗證 <intent-filter>標籤中可以包括<action>子標籤, 範例如下: 一個<intent-filter>標籤至少包含一個<action>標籤,否則任何Intent請求都無法與該<intent-filter>相符合。 如果Intent請求的Action和<intent-filter>中個某一個<action>相符,該Intent就符合了這個<intent-filter>的動作驗證。 如果Intent請求或<intent-filter>中沒有說明具體的Action類型,那麼會出現下面兩種情況。
如何使用IntentFilter 2.類別驗證 <intent-filter>標籤中可以包括<category>子標籤, 範例如下: 只有當Intent請求中所有的Category與元件中某一個IntentFilter的<category>完全相符時,才會讓該Intent請求通過驗證,IntentFilter中多餘的<category>聲明並不會導致比對失敗。 一個沒有指定任何類別驗證的IntentFilter只會匹配沒有設置類別的Intent請求。
如何使用IntentFilter 3.資料驗證 資料在<intent-filter>中的範例如下: <data>標籤指定了希望接受Intent請求的資料URI和資料類型,URI被分成三部分來比對:scheme、authority和path。 其中,用setData()設定Intent請求的URI資料類型和scheme必須與IntentFilter中所指定的一致。若IntentFilter中還指定了authority或path,它們也需要相符合才會通過驗證。 講解完Intent基本概念之後,接下來就透過使用Intent啟動Android本身的電話撥號程式,來驗證上述所講述的概念。
Intent範例演練 前面介紹的內容讓讀者了解Intent的意義,在此就透過一個範例來講解如何在應用程式中使用Intent。 在這個範例中使用一個Intent啟動電話撥號程式,其中Intent的行為是ACTION_DIAL,同時在Intent中傳送聯絡人的電話號碼,範例如下頁圖所示。
Intent範例演練 程式範例圖:
Intent範例演練 布局文件(res/layout/main.xml): <?xml version="1.0" encoding="utf-8"?> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" > <EditText android:id = "@+id/phoneNumber" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id = "@+id/btnDial" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/dial" /> </LinearLayout>
Intent範例演練 程式碼(IntentEX.java): public class IntentEX extends Activity { private String strNumber; private Button btnDial; private EditText phoneNumber; /** Called when the Activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnDial = (Button)findViewById(R.id.btnDial); phoneNumber = (EditText)findViewById(R.id.phoneNumber); btnDial.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { strNumber = phoneNumber.getText().toString(); /* 建立Intent */ Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel://" + strNumber)); /* 啟動活動 */ startActivity(intent); } }); } }
Intent範例演練 此範例使用EditText與Button,在EditText中輸入號碼,在點選撥號鍵,就會呼叫內建的撥號程式進行撥打。 上述的範例是使用Android內建的撥號程式,想必讀者一定會想知道如何不使用內建的撥號程式進行撥打,其實這部分就是需要使用到IntentFilter。
Intent範例演練 原本在AndroidManifest.xml中只有一個<intent-filter>: 目前只有一個IntentFilter,它的動作名稱是Action.MAIN,類別名稱是Category.LAUNCHER。 有了這個IntentFilter,IntentEX的圖示才出現在應用程式的選單裡。
Intent範例演練 接著需在AndroidManifest.xml中加入以下程式碼:
Intent範例演練 更改後的效果如下頁圖所示,當按下模擬器右側鍵盤的綠色撥號鍵時,系統會彈出一個視窗,可以選擇啟動IntentEX或Android內建的撥號程式。 這個範例說明了隱式Intent的用法,IntentEX定義本身的IntentFilter行為是ACTION.CALL_BUTTON,每次使用者按下撥號鍵時,Android系統都會將撥號鍵的Intent和所有定義過ACTION.CALL_BUTTON的IntentFilter進行比較,然後將符合的元件提供給使用者選擇。
Intent範例演練 程式範例圖:
Camera 關於Android上的相機應用,可分為兩部分:取景和拍攝照片的功能,一般來說拍照功能相對於取景來說是較簡單的,以下這個範例將會講解如何實作這兩部分。範例程式如圖所示。
Camera 首先要在Androidmanifest.xml中加入相機權限: 並加入寫入SDCard之權限:
Camera 布局文件(res/layout/main.xml): <?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" android:id="@+id/layout"> <TextView android:text="CameraEX" android:textSize="22sp" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <FrameLayout android:id="@+id/wPreview" android:layout_weight="1" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <Button android:id="@+id/btnClick" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click" android:layout_gravity="center" /> </LinearLayout>
Camera 程式碼-1(Preview.java): public void surfaceDestroyed(SurfaceHolder holder) { /* 停止Preview */ camera.stopPreview(); camera = null; } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { /* 建立Camera.Parameters物件 */ Camera.Parameters parameters = camera.getParameters(); /* 設定預覽畫面大小 */ parameters.setPreviewSize(320, 240); /* 設定圖像格式 */ parameters.setPictureFormat(PixelFormat.JPEG); /* 將上述設定之參數給Camera */ camera.setParameters(parameters); /* 立即執行Preview */ camera.startPreview(); } 由於程式碼過多,完整程式碼請參考光碟中CameraEX.java、Preview.java Preview(Context context) { super(context); /* 取得holder */ surfaceHolder = getHolder(); surfaceHolder.addCallback(this); /* 設定預覽Buffer Type */ surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { /* 若相機非在預覽模式,則開啟相機 */ camera = Camera.open(); try { /* setPreviewDisplay唯一的參數為SurfaceHolder */ camera.setPreviewDisplay(holder); camera.setPreviewCallback(new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera arg1) { /* 在此可針對預覽圖像作一些優化 */ } }); } catch (IOException e) { e.printStackTrace(); } }
Sensor • 有關手機上感測器的開發,Android平台將許多硬體部分整合地更方便使用,首先要先了解的就是Android提供了哪些感測器的常數給開發程式使用。 • 感測器類型 • 方向、加速表、光線、磁場、距離、溫度等。 • 取樣率 • 最快、遊戲、普通、使用者界面。當應用程式請求特定的取樣率時,只是對感測器系統的一個建議,並不保證特定的取樣率可用。 • 準確性 • 高、低、中、不可靠。
Sensor • SensorEventListener 是感測器應用程式的入口。它包括兩個方法: • onSensorChanged(SensorEvent e)方法在感測器值變更時呼叫。 • 該方法只對受此應用程式監視的感測器呼叫。 • 每種感測器所提供的資料不一定一樣多,像是方向和加速表傳感器都提供三個資料。 • 當感測器的準確性變更時,將會呼叫 onAccuracyChanged(Sensor s, int accuracy) 方法。 • 參數包括兩個參數:一個表示感測器,另一個表示該感測器的新準確值。
Sensor 當需要與感測器互動時,應用程式必須向系統註冊,藉此監視一個或多個與感測器相關的活動。 關於註冊的方法是使用 SensorManager 類別中的 registerListener 方法。 不過在此需要注意的是,並非所有 Android 的設備都支援 SDK 中所定義的感測器。 假設某種感測器無法在特定的設備上使用,系統會自動降級。
Sensor 範例程式將以重力加速度感應器作測試,如圖所示。
Sensor 布局文件(res/layout/main.xml): <?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:id="@+id/output" android:layout_width="fill_parent" android:text="@string/hello" android:textSize="24sp" android:layout_height="fill_parent" android:layout_margin="30dp" /> </LinearLayout>
Sensor 程式碼-1(SensorEX.java): public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 取得感測器服務 */ mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); /* 註冊加速度監聽器 */ mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST); textView = new TextView(this); setContentView(textView); }
Sensor 程式碼-2(SensorEX.java): private final SensorEventListener mSensorListener = new SensorEventListener() { public void onSensorChanged(SensorEvent se) { /* 取得x,y,z加速度值 */ x = se.values[0]; y = se.values[1]; z = se.values[2]; textView.setText("x: "+ x +", y: "+ y +", z: " + z); } public void onAccuracyChanged(Sensor sensor, int accuracy) { } };
Sensor 程式碼-3(SensorEX.java): 完整程式碼請參考光碟中SensorEX.java protected void onResume() { super.onResume(); mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST); } protected void onStop() { /* 註銷監聽器 */ mSensorManager.unregisterListener(mSensorListener); super.onStop(); }
前言 Android當中提供了許多API,對於手機上的控制及管理都非常好用,一般來說,像是SIM卡資訊、聯絡人、手機模式、通話紀錄…等等,都可由Android API管理控制。 藉由本章的講解,讓讀者可以更輕易地了解到手機控制與管理的便利性。
PhoneNumberUtils PhoneNumberUtils顧名思義就是與電話號碼有關的工具集,因此裡面包含了許多對於電話格式的一些方法。 此範例延伸前面章節所使用的Intent程式,範例如圖所示。
PhoneNumberUtils 布局文件(res/layout/main.xml): <?xml version="1.0" encoding="utf-8"?> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" > <EditText android:id = "@+id/phoneNumber" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id = "@+id/btnDial" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/dial" /> </LinearLayout>
PhoneNumberUtils 程式碼(PhoneNumberUtilsEX.java): 完整程式碼請參考光碟中PhoneNumberUtilsEX.java public void onClick(View v) { strNumber = phoneNumber.getText().toString(); /* 判斷電話號碼是否為正確 */ if ( PhoneNumberUtils.isGlobalPhoneNumber(strNumber) ) { Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel://" + strNumber)); startActivity(intent); } else { Toast.makeText(PhoneNumberUtilsEX.this, R.string.message, Toast.LENGTH_LONG).show(); } }
PhoneNumberUtils 程式碼(PhoneNumberUtilsEX.java): 完整程式碼請參考光碟中PhoneNumberUtilsEX.java public void onClick(View v) { strNumber = phoneNumber.getText().toString(); /* 判斷電話號碼是否為正確 */ if ( PhoneNumberUtils.isGlobalPhoneNumber(strNumber) ) { Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel://" + strNumber)); startActivity(intent); } else { Toast.makeText(PhoneNumberUtilsEX.this, R.string.message, Toast.LENGTH_LONG).show(); } }
ServiceState ServiceState包含手機狀態及服務相關資訊,與TelephonyManager類似,範例如圖所示。