Android 是一個面嚮應用程式開發的富平台,它擁有許多具有吸引力的使用者介面元素和資料管理功能。Android 還提供了一組豐富的介面選項。在本文中,學習如何配合使用 Android 的各種感應器選項監控您的環境。範例代碼展示了如何在 Android 電話中錄製音頻。想構建自己的嬰兒監視器嗎?想用聲音來電話中或者開啟房門嗎?請學習如何利用配備有 Android 的裝置的硬體功能。
簡介
對於 Java 開發人員來說,Android 平台是通過使用硬體感應器建立創新應用程式的理想平台。我們將學習一些可用於 Android 應用程式的介面串連選項,包括使用感應器子系統和錄製音頻片段。
利用配備 Android 的裝置的硬體功能可以構建哪些應用程式呢?任何需要電子監視和監聽的應用程式都可以構建。嬰兒監視器、安全系統,甚至地震儀都可以。理論上講,您不能同時出現在兩個地方,但 Android 可以利用一些可行的方法實現這一點。縱觀本文始末,您必須記住,使用的 Android 裝置不僅僅局限於 “手機”,還可以是部署在固定位置、具有無線網路串連的裝置,比如 EDGE 或 WiFi。下載 本文樣本的源檔案。
Android 感應器功能
使用 Android 平台有一個很新穎的地方,那就是您可以在裝置內部訪問一些 “好工具”。過去,訪問裝置底層硬體的能力一度讓移動開發人員感到非常棘手。儘管 Android Java 環境的角色仍然是您和裝置的橋樑,但 Android Team Dev讓許多硬體功能浮出了水面。該平台是一個開源平台,因此您可以自由地編寫代碼實現您的任務。
如果尚未安裝 Android,您可以 下載 Android SDK。您還可以 瀏覽 android.hardware 包的內容並參考本文的樣本。android.media 包 包含了一些提供有用和新穎功能的類。
Android SDK 中包含的一些面向硬體的功能描述如下。
表 1. Android SDK 中提供的面向硬體的特性
| 特性 |
描述 |
| android.hardware.Camera |
允許應用程式與相機互動的類,可以截取照片、擷取預覽螢幕的映像,修改用來治理相機操作的參數。 |
| android.hardware.SensorManager |
允許訪問 Android 平台感應器的類。並非所有配備 Android 的裝置都支援 SensorManager 中的所有感應器,雖然這種可能性讓人非常興奮。(可用感應器的簡介見下文) |
| android.hardware.SensorListener |
在感應器值即時更改時,希望接收更新的類要實現的介面。應用程式實現該介面來監視硬體中一個或多個可用感應器。例如,本文中的 代碼 包含實現該介面的類,實現後可以監視裝置的方向和內建的加速表。 |
| android.media.MediaRecorder |
用於錄製媒體範例的類,對於錄製特定位置(比如嬰兒保育)的音頻活動非常有用。還可以分析音頻片段以便在訪問控制項或安全應用程式時進行身份評鑑。例如,它可以協助您通過聲音開啟門,以節省時間,不需要從房產經紀人處擷取鑰匙。 |
| android.FaceDetector |
允許對人臉(以位元影像形式包含)進行基本識別的類。不可能有兩張完全一樣的臉。可以使用該類作為鎖定裝置方法,無需記密碼 — 這是手機的生物識別特徵識別功能。 |
| android.os.* |
包含幾個有用類的包,可以與作業環境互動,包括電源管理、檔案查看器、處理器和訊息類。和許多可行動裝置一樣,支援 Android 的電話可能會消耗大量電能。讓裝置在正確的時間 “醒來” 以監視感興趣的事件是在設計時需要首先關注的方面。 |
java.util.Date java.util.Timer java.util.TimerTask |
當測量實際的事件時,資料和時間往往很重要。例如,java.util.Date 類允許您在遇到特定的事件或狀況時擷取時間戳記。您可以使用 java.util.Timer 和 java.util.TimerTask 分別執行循環性任務或時間點任務。 |
android.hardware.SensorManager 包含幾個常量,這表示 Android 感應器系統的不同方面,包括:
-
感應器類型
-
方向、加速表、光線、磁場、臨近性、溫度等。
-
採樣率
-
最快、遊戲、普通、使用者介面。當應用程式請求特定的採樣率時,其實只是對感應器子系統的一個提示,或者一個建議。不保證特定的採樣率可用。
-
準確性
-
高、低、中、不可靠。
SensorListener 介面是感應器應用程式的中心。它包括兩個必需方法:
- onSensorChanged(int sensor,float values[]) 方法在感應器值更改時調用。該方法只對受此應用程式監視的感應器調用(更多內容見下文)。該方法的參數包括:一個整數,指示更改的感應器;一個浮點值數組,表示感應器資料本身。有些感應器只提供一個資料值,另一些則提供三個浮點值。方向和加速表感應器都提供三個資料值。
- 當感應器的準確性更改時,將調用 onAccuracyChanged(int sensor,int accuracy) 方法。參數包括兩個整數:一個表示感應器,另一個表示該感應器新的準確值。
要與感應器互動,應用程式必須註冊以偵聽與一個或多個感應器相關的活動。註冊使用 SensorManager 類的 registerListener 方法完成。本文中的 程式碼範例 示範了如何註冊和登出 SensorListener。
記住,並非所有支援 Android 的裝置都支援 SDK 中定義的所有感應器。如果某個感應器無法在特定的裝置上使用,您的應用程式就會適當地降級。
感應器樣本
範例應用程式僅監控對方向和加速表感應器的更改(原始碼見 下載)。當收到更改時,感應器值在 TextView 小組件的螢幕上顯示。圖 1 展示了該應用程式的運行情況。
使用 Eclipse 環境和 Android Developer Tools 外掛程式建立的應用程式。(關於使用 Eclipse 開發 Android 應用程式的資訊,請參見 參考資料。)清單 1 展示了該應用程式的代碼。
清單 1. IBMEyes.java
package com.msi.ibm.eyes;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.widget.TextView;import android.hardware.SensorManager;import android.hardware.SensorListener;public class IBMEyes extends Activity implements SensorListener { final String tag = "IBMEyes"; SensorManager sm = null; TextView xViewA = null; TextView yViewA = null; TextView zViewA = null; TextView xViewO = null; TextView yViewO = null; TextView zViewO = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // get reference to SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE); setContentView(R.layout.main); xViewA = (TextView) findViewById(R.id.xbox); yViewA = (TextView) findViewById(R.id.ybox); zViewA = (TextView) findViewById(R.id.zbox); xViewO = (TextView) findViewById(R.id.xboxo); yViewO = (TextView) findViewById(R.id.yboxo); zViewO = (TextView) findViewById(R.id.zboxo); } public void onSensorChanged(int sensor, float[] values) { synchronized (this) { Log.d(tag, "onSensorChanged: " + sensor + ", x: " + values[0] + ", y: " + values[1] + ", z: " + values[2]); if (sensor == SensorManager.SENSOR_ORIENTATION) { xViewO.setText("Orientation X: " + values[0]); yViewO.setText("Orientation Y: " + values[1]); zViewO.setText("Orientation Z: " + values[2]); } if (sensor == SensorManager.SENSOR_ACCELEROMETER) { xViewA.setText("Accel X: " + values[0]); yViewA.setText("Accel Y: " + values[1]); zViewA.setText("Accel Z: " + values[2]); } } } public void onAccuracyChanged(int sensor, int accuracy) { Log.d(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy); } @Override protected void onResume() { super.onResume(); // register this class as a listener for the orientation and accelerometer sensors sm.registerListener(this, SensorManager.SENSOR_ORIENTATION |SensorManager.SENSOR_ACCELEROMETER, SensorManager.SENSOR_DELAY_NORMAL); } @Override protected void onStop() { // unregister listener sm.unregisterListener(this); super.onStop(); } } |
編寫應用程式必須基於常見的活動,因為它只是利用從感應器擷取的資料更新螢幕。在裝置可能在前台執行其他活動的應用程式中,將應用程式構建為服務可能更加合適。
該活動的 onCreate 方法可以引用 SensorManager,其中包含所有與感應器有關的函數。onCreate 方法還建立了對 6 個 TextView 小組件的引用,您需要使用感應器資料值更新這些小組件。
onResume() 方法使用對 SensorManager 的引用通過 registerListener 方法註冊感應器更新:
- 第一個參數是實現 SensorListener 接
- 第二個參數是所需感應器的位元遮罩。在本例中,應用程式從 SENSOR_ORIENTATION 和 SENSOR_ACCELEROMETER 請求資料。
- 第三個參數是一個系統提示,指出應用程式更新感應器值所需的速度。
應用程式(活動)暫停後,需要登出接聽程式,這樣以後就不會再收到感應器更新。這通過 SensorManager 的 unregisterListener 方法實現。惟一的參數是 SensorListener 的執行個體。
在 registerListener 和 unregisterListener 方法調用中,應用程式使用關鍵字 this。注意類定義中的 implements 關鍵字,其中聲明了該類實現 SensorListener 介面。這就是要將它傳遞到 registerListener 和 unregisterListener 的原因。
SensorListener 必須實現兩個方法 onSensorChange 和 onAccuracyChanged。應用程式範例不關心感應器的準確度,但關注感應器當前的 X、Y 和 Z 值。onAccuracyChanged 方法實質上不執行任何操作;它只在每次調用時添加一個日誌項。
似乎經常需要調用 onSensorChanged 方法,因為加速表和方向感應器正在快速發送資料。查看第一個參數確定哪個感應器在發送資料。確認了發送資料的感應器之後,將使用方法第二個參數傳遞的浮點值數組中所包含的資料更新相應的 UI 元素。該樣本只是顯示這些值,但在更加進階的應用程式中,還可以分析這些值,比較原來的值,或者設定某種模式識別演算法來確定使用者(或外部環境)的行為。
現在您已經瞭解了感應器子系統,接下來的部分將回顧一個在 Android 手機上錄製音訊代碼範例。該範例運行在 DEV1 開發裝置上。
使用 MediaRecorder
android.media 包包含與媒體子系統互動的類。使用 android.media.MediaRecorder 類進行媒體採樣,包括音頻和視頻。MediaRecorder 作為狀態機器運行。您需要設定不同的參數,比如源裝置和格式。設定後,可執行任何時間長度的錄製,直到使用者停止。
清單 2 包含的代碼在 Android 裝置上錄製音頻。顯示的代碼不包括應用程式的 UI 元素(完整原始碼見 下載)。
清單 2. 錄製音頻片段
MediaRecorder mrec ;File audiofile = null;private static final String TAG="SoundRecordingDemo";protected void startRecording() throws IOException { mrec.setAudioSource(MediaRecorder.AudioSource.MIC); mrec.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mrec.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); if (mSampleFile == null) { File sampleDir = Environment.getExternalStorageDirectory(); try { audiofile = File.createTempFile("ibm", ".3gp", sampleDir); } catch (IOException e) { Log.e(TAG,"sdcard access error"); return; } } mrec.setOutputFile(audiofile.getAbsolutePath()); mrec.prepare(); mrec.start();}protected void stopRecording() { mrec.stop(); mrec.release(); processaudiofile(audiofile.getAbsolutePath());}protected void processaudiofile() { ContentValues values = new ContentValues(3); long current = System.currentTimeMillis(); values.put(MediaStore.Audio.Media.TITLE, "audio" + audiofile.getName()); values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000)); values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/3gpp"); values.put(MediaStore.Audio.Media.DATA, audiofile.getAbsolutePath()); ContentResolver contentResolver = getContentResolver(); Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Uri newUri = contentResolver.insert(base, values); sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));} |
在 startRecording 方法中,執行個體化並初始化 MediaRecorder 的執行個體:
- 輸入源被設定為麥克風(MIC)。
- 輸出格式被設定為 3GPP(*.3gp 檔案),這是行動裝置專用的媒體格式。
- 編碼器被設定為 AMR_NB,這是音頻格式,採樣率為 8 KHz。NB 表示窄頻。SDK 文檔 解釋了不同的資料格式和可用的編碼器。
音頻檔案儲存體在儲存卡而不是記憶體中。External.getExternalStorageDirectory() 返回儲存卡位置的名稱,在該目錄中將建立一個臨時檔案名稱。然後,通過調用 setOutputFile 方法將檔案關聯到 MediaRecorder 執行個體。音頻資料將儲存到該檔案中。
調用 prepare 方法完成 MediaRecorder 的初始化。準備開始錄製流程時,將調用 start 方法。在調用 stop 方法之前,將對儲存卡上的檔案進行錄製。release 方法將釋放分配給 MediaRecorder 執行個體的資源。
音頻採樣完成之後,需要採取以下步驟:
- 向裝置的媒體庫添加該音頻。
- 執行一些模式識別步驟確定聲音:
- 這是嬰兒的啼哭聲嗎?
- 這是所有人的聲音嗎?是否要解鎖手機?
- 這是 “芝麻開門” 嗎?是否要開啟通往 “秘密通道” 的大門?
- 自動將音頻檔案上傳到網路位置以便處理。
在該代碼範例中,processaudiofile 方法將音頻添加到媒體庫。使用 Intent 通知裝置上的媒體應用程式有新內容可用。
關於該程式碼片段最後要注意的是:如果您試用,它一開始不會錄製音頻。您將看到建立的檔案,但是沒有任何音頻。您需要向 AndroidManifest.xml 檔案添加許可權:
<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission> |
現在,您已經學了一點關於與 Android 感應器和錄製音頻相關的內容。下一節將更全面的介紹與資料擷取和報告系統有關的應用程式架構。
Android 作為感應器平台
Android 平台包含各種用於監視環境的感應器選項。有了輸入或類比選項數組,以及進階計算和互聯功能,Android 成為構建實際系統的最佳平台。圖 2 顯示了輸入、應用程式邏輯、通知方法或輸出之間的簡單視圖。
該架構很靈活;應用程式邏輯可以劃分為本地 Android 裝置和伺服器端資源(可以實現更大的資料庫和計算功能)。例如,本地 Android 裝置上錄製的音軌可以 POST 到 Web 服務器,其中將根據音頻模式資料庫比較資料。很明顯,這僅僅是冰山一角。希望您能更深入地研究,讓 Android 平台超越行動電話的範疇。
結束語
在本文中,我們介紹了 Android 感應器。範例應用程式度量了方向和加速,以及使用 MediaRecorder 類與錄製功能進行互動。對於構建實際系統,Android 是一個靈活、有吸引力的平台。Android 領域發展迅速,並且不斷壯大。口的類的執行個體。