當前位置: 首頁 > 工業(yè)電子產品 > 集成電路(ICs) > IC傳感器
發(fā)布日期:2022-05-11 點擊率:131
簡介
上一節(jié)制作了一個傳感器的應用,應用程序獲取傳感器數據代碼流程大致如下
源碼分析
如上所示,在應用層調用幾個常用接口就能夠獲取到傳感器數據了,主要接口包括
下面對這四個接口為主線來學習源代碼,理解傳感器的框架
getSystemService
該接口是一個用來獲取系統(tǒng)服務的接口,SensorManager是一個抽象類,為應用層提供傳感器接口,SystemSensorManager繼承SensorManager實現了這些接口功能,我們來看一下SystemSensorManager的創(chuàng)建.
SystemSensorManager在SystemServiceRegistry的靜態(tài)初始化塊當中,因此在系統(tǒng)初始階段加載SystemServiceRegistry時,SystemSensorManager就會被創(chuàng)建,來看一下它的構造函數
調用nativeCreate創(chuàng)建Native層的SensorManager,在創(chuàng)建過程中會循環(huán)等待SensorService被創(chuàng)建,與其建立binder關系,接著調用SensorSerivce的getSensorList,來獲取硬件傳感器列表信息.
上面SensorManager會等待SensorService注冊,將它保存在成員變量mSensorServer中.小結一下這一段流程UML圖
SensorManager功能都來自于SensorService,它在Android的Sensor框架中占據核心位置,接下來看一下分析一下SensorService的相關代碼.
當內核啟動后執(zhí)行init 程序,該程序解析 init.rc文件(zygote包含在init.${ro.zygote}.rc中),rc文件中指定的應用程序在app_main.cpp中,調用AndroidRuntime的start方法,接著通過JNI調用Zyoteinit.java中的main函數,從這里開始追蹤.
上面代碼可知ZygoteInit的main函數執(zhí)中調用startSystemServer函數,調用forkSystemServer創(chuàng)建系統(tǒng)服務,跳轉到SystemServer的main函數
在 SystemServer 的main函數中,會創(chuàng)建SystemService對象,并調用run方法,在run方法中調用startBootstrapServices(),接著調用Native方法startSensorService(),
在android_server_SystemServer_startSensorService中創(chuàng)建線程start_sensor_service來實例化SensorService,然后將SensorService添加到ServiceManager當中(SensorManager從ServiceManager里獲取SensorService).當SensorService被創(chuàng)建時,onFirstRef被調用,這個初始化函數完成了一些重要的初始化過程,我們來看一下
在 SensorService 的 onFirstRef 接口中,獲取SensorDevice(單例類)的引用,這里看一下SensorDevice的構造函數
SensorDevice調用 hw_get_module接口加載HAL的Sensor庫庫,接著調用HAL層提供的open接口,執(zhí)行HAL層的初始化,同時返回硬件訪問接口,接著調用 Sensor HAL提供的 get_sensors_list 接口,獲取所支持的 Sensor列表.
接著上面SensorService::onFirstRef接口,通過SensorDevice獲取傳感器列表信息后,調用registerSensor分別將傳感器添加到mSensors中.然后創(chuàng)建 Looper , Looper用來監(jiān)聽傳感器數據的上報和分發(fā),接著調用run方法,啟動threadLoop,輪詢HAL層傳感器數據的上報.來看一下SensorService的threadLoop
SensorService的流程圖
getDefaultSensor/getSensorList
SensorManager.java中的getDefaultSensor方法是依賴getSensorlist實現的,來看一下getSensorlist方法.
registerListener
SensorManager.java向應用提供的registerListener方法,最終會調到SystemSensorManager中的registerListenerImpl方法,
創(chuàng)建一個與looper,listener相關聯的SensorEventQueue,看一下SensorEventQueue的構造函數,
接著看它的基類baseEventQueue和它的nativeInitbaseEventQueue方法.
nativeInitSensorEventQueue中,有兩件事要注意,1.創(chuàng)建SensorEventQueue對象2.創(chuàng)建Receiver.先看一下SensorEventQueue的創(chuàng)建
SensorManager通過mSensorServer(它是在SensorManager構造函數里getService獲得)的createSensorEventConnection接口,創(chuàng)建一個SensorEventConnection對象,它是SensorService的內部類,在SensorService::enable中被添加,用來在接收到的HAL層的數據發(fā)送出去的重要通道.接著創(chuàng)建一個與這個SensorEventConnection對象關聯的SensorEventQueue對象,并返回.Native層的SensorEventQueue是非常重要的數據通道,一方面接收Native層SensorService發(fā)送過來的數據(SensorService的數據來自HAL層),另一方面將數據分發(fā)到framewrok層SystemSensorManager的baseEventQueue子類對象中.后面會通過代碼說明這個過程如何發(fā)生.接著看Receiver類,Receiver構造函數
Receiver構造函數中,主要建立與SensorService和MessageQueue之間的關聯,接著看它的onFirstRef.
Native層的MessageQueue調用Looper類的addFd方法,添加對SensorEventQueue的監(jiān)聽,看一下looper的addFd方法.
Receiver繼承LooperCallback并實現了handleEvent函數,addFD時會將callback傳入Looper.Looper的addFd如果被監(jiān)控的文件描述符,已經存在用EPOLL_CTL_MOD參數調用epoll_ctl修改,否則,EPOLL_CTL_ADD來添加.(Native層的Looper是以epoll為核心實現的,對Looper不熟悉的可以看一下epoll的簡介).Looper在添加文件描述符后,后續(xù)通過pollOnce或pollAll接口來訪問,最終都會調用pollInner來等待這些文件描述符被寫入數據,我們來看一下pollInner
在Looper中epoll_wait來監(jiān)聽文件描述符是否有數據寫入,當有數據寫入后,調用callback的handleEvent方法來進行處理,這里是調用了Receiver的handleEvent方法,handleEvent中依賴于SensorEventQueue中的mSensorChannel(mSensorChannel是在SensorEventConnection構造時創(chuàng)建的)來實現數據的讀取的,mSensorChannel是一個BitTube對象,BitTube提供全雙工的跨進程的通訊管道(這里對它做了一個說明,有興趣可以了解一下),handleEvent依賴它來獲取發(fā)送過來的數據(數據從哪發(fā)來?我們后面再說),我們看一下Receiver的handleEvent接口
上面會根據不同類型的傳感器將數據進行處理,常用傳感器通過 gbaseEventQueueClassInfo.dispatchSensorEvent進行分發(fā),這里實際上是調用的framework層的SensorEventQueue類(繼承baseEventQueue)的dispatchSensorEvent方法.
dispatchSensorEvent里,通過handle找到對應的sensor,將數據通過SensorEventListener的onSensorChanged將數據給應用程序.
回顧一下前面的流程,Looper通過epoll_wait將數據讀出,回調Receiver的handleEvent函數,handleEvent調用SensorEventQueuem的read方法讀取數據,然后通過JNI回調framework層SensorEventQueue的dispatchSensorEvent方法,在dispatchSensorEvent方法中回調SensorEventListener的onSensorChanged方法,這個方法為應用注冊的監(jiān)聽方法,將數據返回到應用層.可是是誰將數據寫入Looper監(jiān)控的文件描述符中的?我們接著看一下這個過程.之前說過”handleEvent調用SensorEventQueue的read方法讀取數據”,讀取和寫入都依賴于BitTube對象,它在SensorEventConnection構造函數中創(chuàng)建(mChannel),然后SensorEventQueue通過getSensorChannel()接口獲取該對象,(這里可以按照mSensorChannel的線索,反推寫入數據的地方,接下來直接描述結果),SensorService在onFirstRef最后執(zhí)行了run,threadLoop開始運行,這個函數上面分析過了,在最后時
SensorEventConnection的sendEvents方法會將數據寫入BitTube中
到這里傳感器數據的寫入,讀取流程分析完畢.
這里在簡述一下數據的完整流程:SensorService的threadLoop線程,循環(huán)通過SensorDevice的poll接口,從HAL層獲取傳感器數據,然后調用已建立好的傳感器連接通道SensorEventConnection的sendEvents方法,寫入到BitTube的socketpai通道中,Looper函數的epoll_wait被喚醒,然后回調Receiver的handleEvent方法,在該方法中SensorEventQueue會讀取socketpai通道里的數據,調用JNI調用framework層的SensorEventQueue的dispatchSensorEvent方法,在此方法中回調SensorEventListener的onSensorChanged方法和onAccuracyChanged,這兩個方法就是應用程序注冊的方法.
傳感器HAL層的AOSP部分包括sensor.h,接下來看一下這個文件內容.
HAL層關鍵結構和接口
HAL層中關鍵的結構和接口存在sensor.h文件中,包括:sensor_module_t、sensors_poll_device_1、get_sensors_list等
sensor.h
sensor_module_t
sensors_poll_device_1
sensors_poll_device_1_t兼容老1.0版本sensors_poll_device_t,并提供了新版本接口batch,flush,inject_sensor_data接口.
- get_sensors_list
獲取所有sensor的列表,由*list指向,傳遞給上層使用.又的平臺是靜態(tài)方式注冊list,有的平臺動態(tài)方式注冊.
set_operation_mode
模式設置接口,用來將sensor service注入的數據返回.因傳感器數據來自于底層硬件,這個方式多用于調試或特殊功能,多數平臺不實現這個接口.
sensor_t
這個結構體在實現hal層代碼時,參數已又英文注釋,重點關注handle、type、minDelay、flags.
sensors_event_t
傳感器數據上報的結構體,注意sensor和sensors_vec_t,sensor變量對應的是handle,如果不匹配將無法被上層enable,sensors_vec_t中的status默認為0,需要設置為大于0的狀態(tài),否則上層會將數據丟棄,status可選參數如下
HAL層以下部分各SOC廠商實現方式不同(有些走input,有些走IIO,有些走share memory),這里不繼續(xù)追述.
官方文檔中,Android平臺支持三大類的傳感器,它們分別是:
a. Motion sensors
b. Environmental sensors
c. Position sensors
android姿態(tài)傳感器源代碼,非常簡單的源代碼,直接運行,很適合初學了解Android傳感器
從另一個角度劃分,安卓的傳感器又可以分為基于硬件的和基于軟件的。基于硬件的傳感器往往是通過物理組件去實現的,他們通常是通過去測量特殊環(huán)境的屬性獲取數據,比如:重力加速度、地磁場強度或方位角度的變化。而基于軟件的傳感器并不依賴物理設備,盡管它們是模仿基于硬件的傳感器的。基于軟件的傳感器通常是通過一個或更多的硬件傳感器獲取數據,并且有時會調用虛擬傳感器或人工傳感器等等,線性加速度傳感器和重力傳感器就是基于軟件傳感器的例子。
轉自:
Android 是一個面向應用程序開發(fā)的富平臺,它擁有許多具有吸引力的用戶界面元素和數據管理功能。Android 還提供了一組豐富的接口選項。
在本文中,學習如何配合使用 Android 的各種傳感器選項監(jiān)控您的環(huán)境。樣例代碼展示了如何在 Android 電話中錄制音頻。想構建自己的監(jiān)視器嗎?想用聲音來接聽電話或者打開房門嗎?請學習如何利用配備有 Android 的設備的硬件功能。
對于 Java? 開發(fā)人員來說,Android 平臺是通過使用硬件傳感器創(chuàng)建創(chuàng)新應用程序的理想平臺。我們將學習一些可用于 Android 應用程序的接口連接選項,包括使用傳感器子系統(tǒng)和錄制音頻片段。
利用配備 Android 的設備的硬件功能可以構建哪些應用程序呢?任何需要電子監(jiān)視和監(jiān)聽的應用程序都可以構建。嬰兒監(jiān)視器、安全系統(tǒng),甚至地震儀都可以。理論上講,您不能同時出現在兩個地方,但 Android 可以利用一些可行的方法實現這一點。縱觀本文始末,您必須記住,使用的 Android 設備不僅僅局限于 “手機”,還可以是部署在固定位置、具有無線網絡連接的設備,比如 EDGE 或 WiFi。請下載附件中本文示例的源代碼。
Android 傳感器功能
使用 Android 平臺有一個很新穎的地方,那就是您可以在設備內部訪問一些“好工具”。過去,訪問設備底層硬件的能力一度讓移動開發(fā)人員感到非常棘手。盡管 Android Java 環(huán)境的角色仍然是您和設備的橋梁,但Android 開發(fā)團隊讓許多硬件功能浮出了水面。該平臺是一個開源平臺,因此您可以自由地編寫代碼實現您的任務。
如果尚未安裝 Android,您可以 下載 Android SDK。您還可以 瀏覽 android.hardware 包的內容并參考本文的示例。android.media 包 包含了一些提供有用和新穎功能的類。
Android SDK 中包含的一些面向硬件的功能描述如下。
表 1. Android SDK 中提供的面向硬件的特性
特性
描述
android.hardware.Camera
允許應用程序與相機交互的類,可以截取照片、獲取預覽屏幕的圖像,修改用來治理相機操作的參數。
android.hardware.SensorManager
允許訪問 Android 平臺傳感器的類。并非所有配備 Android 的設備都支持 SensorManager 中的所有傳感器,雖然這種可能性讓人非常興奮。(可用傳感器的簡介見下文)
android.hardware.SensorListener
在傳感器值實時更改時,希望接收更新的類要實現的接口。應用程序實現該接口來監(jiān)視硬件中一個或多個可用傳感器。例如,本文中的 代碼 包含實現該接口的類,實現后可以監(jiān)視設備的方向和內置的加速表。
android.media.MediaRecorder
用于錄制媒體樣例的類,對于錄制特定位置(比如嬰兒保育)的音頻活動非常有用。還可以分析音頻片段以便在訪問控件或安全應用程序時進行身份鑒定。例如,它可以幫助您通過聲音打開門,以節(jié)省時間,不需要從房產經紀人處獲取鑰匙。
android.FaceDetector
允許對人臉(以位圖形式包含)進行基本識別的類。不可能有兩張完全一樣的臉。可以使用該類作為設備鎖定方法,無需記密碼 — 這是手機的生物特征識別功能。
android.os.*
包含幾個有用類的包,可以與操作環(huán)境交互,包括電源管理、文件查看器、處理器和消息類。和許多可移動設備一樣,支持 Android 的電話可能會消耗大量電能。讓設備在正確的時間 “醒來” 以監(jiān)視感興趣的事件是在設計時需要首先關注的方面。
java.util.Date
java.util.Timer
java.util.TimerTask
當測量實際的事件時,數據和時間往往很重要。例如,java.util.Date 類允許您在遇到特定的事件或狀況時獲取時間戳。您可以使用 java.util.Timer 和 java.util.TimerTask 分別執(zhí)行周期性任務或時間點任務。
android.hardware.SensorManager 包含幾個常量,這表示 Android 傳感器系統(tǒng)的不同方面,包括:傳感器類型方向、加速表、光線、磁場、臨近性、溫度等。采樣率最快、游戲、普通、用戶界面。當應用程序請求特定的采樣率時,其實只是對傳感器子系統(tǒng)的一個提示,或者一個建議。不保證特定的采樣率可用。準確性高、低、中、不可靠。
SensorListener 接口是傳感器應用程序的中心。它包括兩個必需方法:
onSensorChanged(int sensor,float values[]) 方法在傳感器值更改時調用。該方法只對受此應用程序監(jiān)視的傳感器調用(更多內容見下文)。該方法的參數包括:一個整數,指示更改的傳感器;一個浮點值數組,表示傳感器數據本身。有些傳感器只提供一個數據值,另一些則提供三個浮點值。方向和加速表傳感器都提供三個數據值。
當傳感器的準確性更改時,將調用 onAccuracyChanged(int sensor,int accuracy) 方法。參數包括兩個整數:一個表示傳感器,另一個表示該傳感器新的準確值。
要與傳感器交互,應用程序必須注冊以偵聽與一個或多個傳感器相關的活動。注冊使用 SensorManager 類的 registerListener 方法完成。本文中的源代碼中演示了如何注冊和注銷 SensorListener。
記住,并非所有支持 Android 的設備都支持 SDK 中定義的所有傳感器。如果某個傳感器無法在特定的設備上使用,您的應用程序就會適當地降級。
傳感器示例
樣例應用程序僅監(jiān)控對方向和加速表傳感器的更改(見源代碼)。當收到更改時,傳感器值在 TextView 小部件的屏幕上顯示。
圖 1 展示了該應用程序的運行情況。
圖 1. 監(jiān)視加速和方向
使用 Eclipse 環(huán)境和 Android Developer Tools 插件創(chuàng)建的應用程序。清單 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;
@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();
}
}
編寫應用程序必須基于常見的活動,因為它只是利用從傳感器獲取的數據更新屏幕。在設備可能在前臺執(zhí)行其他活動的應用程序中,將應用程序構建為服務可能更加合適。
該活動的 onCreate 方法可以引用 SensorManager,其中包含所有與傳感器有關的函數。onCreate 方法還建立了對 6 個 TextView 小部件的引用,您需要使用傳感器數據值更新這些小部件。
onResume() 方法使用對 SensorManager 的引用通過 registerListener 方法注冊傳感器更新:
第一個參數是實現 SensorListener 接口的類的實例。
第二個參數是所需傳感器的位掩碼。在本例中,應用程序從 SENSOR_ORIENTATION 和 SENSOR_ACCELEROMETER 請求數據。
第三個參數是一個系統(tǒng)提示,指出應用程序更新傳感器值所需的速度。
應用程序(活動)暫停后,需要注銷偵聽器,這樣以后就不會再收到傳感器更新。這通過 SensorManager 的 unregisterListener 方法實現。惟一的參數是 SensorListener 的實例。
在 registerListener 和 unregisterListener 方法調用中,應用程序使用關鍵字 this。注意類定義中的 implements 關鍵字,其中聲明了該類實現 SensorListener 接口。這就是要將它傳遞到 registerListener 和 unregisterListener 的原因。
SensorListener 必須實現兩個方法 onSensorChange 和 onAccuracyChanged。示例應用程序不關心傳感器的準確度,但關注傳感器當前的 X、Y 和 Z 值。onAccuracyChanged 方法實質上不執(zhí)行任何操作;它只在每次調用時添加一個日志項。
似乎經常需要調用 onSensorChanged方法,因為加速表和方向傳感器正在快速發(fā)送數據。查看第一個參數確定哪個傳感器在發(fā)送數據。確認了發(fā)送數據的傳感器之后,將使用方法第二個參數傳遞的浮點值數組中所包含的數據更新相應的 UI元素。該示例只是顯示這些值,但在更加高級的應用程序中,還可以分析這些值,比較原來的值,或者設置某種模式識別算法來確定用戶(或外部環(huán)境)的行為。
現在您已經了解了傳感器子系統(tǒng),接下來的部分將回顧一個在 Android 手機上錄制音頻的代碼樣例。該樣例運行在 DEV1 開發(fā)設備上。
使用 MediaRecorder
android.media 包包含與媒體子系統(tǒng)交互的類。使用 android.media.MediaRecorder 類進行媒體采樣,包括音頻和視頻。MediaRecorder 作為狀態(tài)機運行。您需要設置不同的參數,比如源設備和格式。設置后,可執(zhí)行任何時間長度的錄制,直到用戶停止。
清單 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() 返回存儲卡位置的名稱,在該目錄中將創(chuàng)建一個臨時文件名。然后,通過調用 setOutputFile 方法將文件關聯到 MediaRecorder 實例。音頻數據將存儲到該文件中。
調用 prepare 方法完成 MediaRecorder 的初始化。準備開始錄制流程時,將調用 start 方法。在調用 stop 方法之前,將對存儲卡上的文件進行錄制。release 方法將釋放分配給 MediaRecorder 實例的資源。
音頻采樣完成之后,需要采取以下步驟:
向設備的媒體庫添加該音頻。
執(zhí)行一些模式識別步驟確定聲音:
這是嬰兒的啼哭聲嗎?
這是所有人的聲音嗎?是否要解鎖手機?
這是 “芝麻開門” 嗎?是否要打開通往 “秘密通道” 的大門?
自動將音頻文件上傳到網絡位置以便處理。
在該代碼樣例中,processaudiofile 方法將音頻添加到媒體庫。使用 Intent 通知設備上的媒體應用程序有新內容可用。
關于該代碼片段最后要注意的是:如果您試用,它一開始不會錄制音頻。您將看到創(chuàng)建的文件,但是沒有任何音頻。
您需要向 AndroidManifest.xml 文件添加權限:
現在,您已經學了一點關于與 Android 傳感器和錄制音頻相關的內容。下一節(jié)將更全面的介紹與數據采集和報告系統(tǒng)有關的應用程序架構。
Android 作為傳感器平臺
Android 平臺包含各種用于監(jiān)視環(huán)境的傳感器選項。有了輸入或模擬選項數組,以及高級計算和互聯功能,Android 成為構建實際系統(tǒng)的最佳平臺。
圖 2 顯示了輸入、應用程序邏輯、通知方法或輸出之間的簡單視圖。
圖 2. 以 Android 為中心的傳感器系統(tǒng)的方塊圖
該架構很靈活;應用程序邏輯可以劃分為本地 Android 設備和服務器端資源(可以實現更大的數據庫和計算功能)。
例如,本地 Android 設備上錄制的音軌可以 POST 到 Web 服務器,其中將根據音頻模式數據庫比較數據。很明顯,這僅僅是冰山一角。希望您能更深入地研究,讓 Android 平臺超越移動電話的范疇。
結束語
在本文中,我們介紹了 Android 傳感器。樣例應用程序度量了方向和加速,以及使用 MediaRecorder 類與錄制功能進行交互。對于構建實際系統(tǒng),Android 是一個靈活、有吸引力的平臺。Android 領域發(fā)展迅速,并且不斷壯大。請務必關注該平臺。
private long lastUpdateTime;
// 手機上一個位置時重力感應坐標
private float lastX;
private float lastY;
private float lastZ;
Vibrator vibrator;
// 速度閾值,當搖晃速度達到這值后產生作用
private static final int SPEED_SHRESHOLD=1000;
// 兩次檢測的時間間隔
private static final int UPTATE_INTERVAL_TIME=70;
private DevicePolicyManager deviceManager;
// private boolean isAdmin;
private PowerManager pm;
public void onSensorChanged(SensorEvent event) {
// 現在檢測時間
long currentUpdateTime= System.currentTimeMillis();
// 兩次檢測的時間間隔
long timeInterval=currentUpdateTime - lastUpdateTime;
// 判斷是否達到了檢測時間間隔
// if (timeInterval < UPTATE_INTERVAL_TIME)
// return;
// 現在的時間變成last時間
lastUpdateTime= currentUpdateTime;
// 獲得x,y,z坐標
float x=event.values[0];
float y=event.values[1];
float z=event.values[2];
// if (Math.abs(x) > 9.0 || Math.abs(y) > 9.0) {
// Log.e("onSensorChanged", Math.abs(x)+"::"+Math.abs(y));
// Log.e("onSensorChanged", isAdmin+"");
// if (!isAdmin) {
// Log.e("onSensorChanged", isAdmin+"");
// deviceManager.lockNow();
// // deviceManager.resetPassword("", 0);
// }
// }
//
// 獲得x,y,z的變化值
float deltaX=x - lastX;
float deltaY=y - lastY;
float deltaZ=z - lastZ;
// Log.e("onSensorChanged", deltaX+
// "::::"+deltaY+"::::::"+deltaZ+"::::"+timeInterval);
// 將現在的坐標變成last坐標
lastX= x;
lastY = y;
lastZ = z;
// double riderSpeed=Math.sqrt(deltaX * deltaX + deltaY * deltaY +
// deltaZ * deltaZ);
//
// if (riderSpeed > 5) {
// // 按照自己需求設置屏幕的亮度
// toggleBrightness(this);
// Log.e(TAG, brightness + ":::" +
// isAutoBrightness(this.getContentResolver()));
// Log.e("執(zhí)行車友的拿手機的動作", riderSpeed + "");
// // pm.goToSleep(SystemClock.uptimeMillis());
// new Handler().postDelayed(new Runnable() {
//
// @Override
// public void run() {
//
// wake.release();
//
// }
// }, 50);// 在釋放的時候異常了
// }
下一篇: PLC、DCS、FCS三大控
上一篇: 電氣控制線路圖控制原