Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> 深入探討 Android 傳感器:隨處監控您的環境

深入探討 Android 傳感器:隨處監控您的環境

編輯:Android開發實例

  簡介

  對於 Java™ 開發人員來說,Android 平台是通過使用硬件傳感器創建創新應用程序的理想平台。我們將學習一些可用於 Android 應用程序的接口連接選項,包括使用傳感器子系統和錄制音頻片段。

  利用配備 Android 的設備的硬件功能可以構建哪些應用程序呢?任何需要電子監視和監聽的應用程序都可以構建。嬰兒監視器、安全系統,甚至地震儀都可以。理論上講,您不能同時出現在兩個地方,但 Android 可以利用一些可行的方法實現這一點。縱觀本文始末,您必須記住,使用的 Android 設備不僅僅局限於 “手機”,還可以是部署在固定位置、具有無線網絡連接的設備,比如 EDGE 或 WiFi。

  Android 傳感器功能

  使用 Android 平台有一個很新穎的地方,那就是您可以在設備內部訪問一些 “好工具”。過去,訪問設備底層硬件的能力一度讓移動開發人員感到非常棘手。盡管 Android Java 環境的角色仍然是您和設備的橋梁,但 Android 開發團隊讓許多硬件功能浮出了水面。該平台是一個開源平台,因此您可以自由地編寫代碼實現您的任務。

  如果尚未安裝 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 展示了該應用程序的運行情況。


圖 1. 監視加速和方向


  使用 Eclipse 環境和 Android Developer Tools 插件創建的應用程序。清單 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 顯示了輸入、應用程序邏輯、通知方法或輸出之間的簡單視圖。


圖 2. 以 Android 為中心的傳感器系統的方塊圖


  該架構很靈活;應用程序邏輯可以劃分為本地 Android 設備和服務器端資源(可以實現更大的數據庫和計算功能)。例如,本地 Android 設備上錄制的音軌可以 POST 到 Web 服務器,其中將根據音頻模式數據庫比較數據。很明顯,這僅僅是冰山一角。希望您能更深入地研究,讓 Android 平台超越移動電話的范疇。

  結束語

  在本文中,我們介紹了 Android 傳感器。樣例應用程序度量了方向和加速,以及使用 MediaRecorder 類與錄制功能進行交互。對於構建實際系統,Android 是一個靈活、有吸引力的平台。Android 領域發展迅速,並且不斷壯大。請務必關注該平台。

  

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved