Android传感器-开发指南
大部分Android平臺的設備都帶有多個傳感器,使你能監視其方位和運動狀態的變化。很多設備還有其它類型的傳感器,讓你能感知周圍的環境條件,比如溫度、壓力、濕度和光線。你可以利用Android的傳感器框架訪問這些傳感器,并獲取原始的傳感器數據。
傳感器框架提供了豐富的類和接口,能幫助你完成很多與傳感器有關的工作。比如,你可以用傳感器框架來進行:
- 確定設備上可用的傳感器
- 確定某個傳感器的性能,比如量程、制造商、能耗、分辨率等。
- 讀取傳感器的原始數據并指定最小讀取頻率
- 注冊及注銷監聽傳感器變化的傳感器事件偵聽器
本文概述了Android平臺可用的傳感器,并簡要介紹了傳感器框架。
傳感器介紹
Android傳感器框架能讓你訪問很多類型的傳感器。有些是基于硬件的,有些則是基于軟件的。基于硬件的傳感器是內置于手持或桌面設備中的物理部件。它們直接測量環境參數并發送數據,比如加速度、地磁強度、角速度等。基于軟件的傳感器則沒有物理設備,雖然它們也是模仿硬件傳感器。基于軟件的傳感器數據是來自一個或多個硬件傳感器,有時也被稱為虛擬傳感器或合成傳感器。直線加速度傳感器和重力傳感器就是基于軟件的傳感器。表1中列出了Android平臺支持的全部傳感器。
能夠擁有全部類型傳感器的Android設備非常少見。比如,大部分手持設備和桌面設備都帶有加速度和磁力傳感器,但帶氣壓計和溫度計的設備就少得多了。而且,在一臺設備上某類傳感器的數量也可以有多個。比如,一臺設備可以帶有兩個重力傳感器,每個傳感器的量程各不相同。
表1.?Android框架支持的傳感器類型
| 傳感器 | 類型 | 說明 | 常見用途 |
| TYPE_ACCELEROMETER | 硬件 | 測量施于設備的物理三維方向上(x、y和z軸)的加速度,包括重力,單位為m/s2。 | 運動檢測(晃動、傾斜等) |
| TYPE_AMBIENT_TEMPERATURE | 硬件 | 測量周圍環境的溫度,單位為攝氏度(°C)。參見下文。 | 監測氣溫 |
| TYPE_GRAVITY | 軟件或硬件 | 測量施于設備的物理三維方向上(x、y和z軸)的重力加速度,單位為m/s2?。 | 運動檢測(晃動、傾斜等) |
| TYPE_GYROSCOPE | 硬件 | 測量設備圍繞每個物理三維方向(x、y和z軸)的轉動角速度,單位為rad/s 。 | 轉動檢測(旋轉、轉動等) |
| TYPE_LIGHT | 硬件 | 測量周圍環境的光照強度(照度),單位為lx。 | 控制屏幕亮度 |
| TYPE_LINEAR_ACCELERATION | 軟件或硬件 | 測量施于設備的物理三維方向上(x、y和z軸)的加速度,但不包括重力,單位為m/s2。 | 監測某一維軸線上的加速度 |
| TYPE_MAGNETIC_FIELD | 硬件 | 測量周圍物理三維方向(x、y和z軸)的地球磁場,單位為μT。 | 創建指南針 |
| TYPE_ORIENTATION | 軟件 | 測量圍繞物理三維方向(x、y和z軸)的旋轉角度。自API level 3開始,利用重力傳感器和地磁傳感器,你可以用getRotationMatrix()?方法讀取傾角矩陣和旋轉矩陣。 | 檢測設備的方位 |
| TYPE_PRESSURE | 硬件 | 測量周圍大氣壓力,單位為hPa或mbar。 | 監測氣壓的變化 |
| TYPE_PROXIMITY | 硬件 | 測量附近的物體與設備屏幕間的距離,單位為cm。此傳感器的典型應用,是可以檢測手持設備是否被人拿起來并靠近耳朵。 | 通話時確定電話的位置 |
| TYPE_RELATIVE_HUMIDITY | 硬件 | 測量周圍環境的相對濕度,單位為百分比(%)。 | 監測結露點、絕對濕度和相對濕度。 |
| TYPE_ROTATION_VECTOR | 軟件或硬件 | 根據設備旋轉向量的三個參數測量設備的方向。 | 運動檢測和轉動檢測 |
| TYPE_TEMPERATURE | 硬件 | 測量設備的溫度,單位是攝氏度(°C)。這個傳感器的實現因設備的差異而各不相同,并自API Level 14開始由TYPE_AMBIENT_TEMPERATURE?代替。 | 監測溫度 |
傳感器框架
通過使用Android傳感器框架,你可以訪問這些傳感器并讀取傳感器的原始數據。Android傳感器框架是android.hardware?包的一部分,包含了以下類和接口:
SensorManager
你可以用這個類來創建傳感器設備的一個實例。這個類提供了多個方法,用于訪問及獲取傳感器列表、注冊及注銷傳感器事件偵聽器、讀取方位信息等。該類還提供了眾多的傳感器常量,用于報告傳感器精度、設置數據采樣率和校準傳感器。
Sensor
你可以用這個類來創建某個傳感器的實例。該類提供了很多方法,使你能確定傳感器的性能。
SensorEvent
系統用該類來創建一個傳感器事件對象,用于提供傳感器事件的相關信息。這些信息包括:傳感器原始數據、生成本事件的傳感器類型、數據的精度、事件的時間戳。
SensorEventListener
你可以用該類來創建兩個回調方法,用于傳感器數值改變時或傳感器精度變化時接收通知(傳感器事件)。
在典型的應用中,你一般會用這些傳感器API完成兩個基本的任務:
- 識別傳感器及其性能
如果你的應用有部分功能依賴于某些傳感器,則在運行時識別傳感器的類型和性能是非常有用的。比如,你也許需要識別出當前設備上所有的傳感器,并把那些所需傳感器不可用的功能禁用掉。同理,你可能還想識別出所有的某類型傳感器,以便應用程序能夠選用其中性能最佳的那個。
- 監聽傳感器事件
監聽傳感器事件是獲取傳感器原始數據的途徑。每當傳感器檢測到其測量的參數發生變化時,傳感器事件就會被觸發。傳感器事件會向你提供四塊信息:觸發事件的傳感器名稱、事件的時間戳、事件的精度、觸發事件的傳感器原始數據。
傳感器的可用性
傳感器的可用性不僅會因設備不同而各不相同,也會因Android的版本而不一樣。這是因為Android傳感器的引入跨越了多個版本。比如,很多傳感器是自Android 1.5 (API Level 3) 開始引入的,但其中有些并未實現而直至Android 2.3 (API Level 9) 才可用。同樣,一部分傳感器是自Android 2.3 (API Level 9) 和Android 4.0 (API Level 14) 才引入的。有兩個傳感器已過時,被更新更好的傳感器所替代。
表 2 匯總了每個版本可用的傳感器。由于涉及傳感器變動的版本只有四個,這里只列出了這四個版本。標明過時的傳感器在后續的版本中仍是可用的(當然設備要提供此傳感器),這也符合Android向下兼容性原則。
表2.?各版本可用的傳感器
| 傳感器 | Android 4.0 | Android 2.3 | Android 2.2 | Android 1.5 |
| TYPE_ACCELEROMETER | 是 | 是 | 是 | 是 |
| TYPE_AMBIENT_TEMPERATURE | 是 | n/a | n/a | n/a |
| TYPE_GRAVITY | 是 | 是 | n/a | n/a |
| TYPE_GYROSCOPE | 是 | 是 | n/a1 | n/a1 |
| TYPE_LIGHT | 是 | 是 | 是 | 是 |
| TYPE_LINEAR_ACCELERATION | 是 | 是 | n/a | n/a |
| TYPE_MAGNETIC_FIELD | 是 | 是 | 是 | 是 |
| TYPE_ORIENTATION | 是2 | 是2 | 是2 | 是 |
| TYPE_PRESSURE | 是 | 是 | n/a1 | n/a1 |
| TYPE_PROXIMITY | 是 | 是 | 是 | 是 |
| TYPE_RELATIVE_HUMIDITY | 是 | n/a | n/a | n/a |
| TYPE_ROTATION_VECTOR | 是 | 是 | n/a | n/a |
| TYPE_TEMPERATURE | 是2 | 是 | 是 | 是 |
1?此傳感器類型自 Android 1.5 (API Level 3) 起引入,但直至 Android 2.3 (API Level 9) 才可用。
2?此傳感器可用,但已過時。
識別傳感器及其性能
Android的傳感器框架提供了眾多的方法,使你很容易就能在運行時檢測出當前設備可用的傳感器。API也提供了很多檢測每個傳感器性能的方法,比如量程、分辨率、能耗。
要識別設備上的傳感器,你首先需要獲取一個傳感器設備的引用。你可以通過調用?getSystemService()?,并傳入?SENSOR_SERVICE?參數,來創建一個?SensorManager?。例如:
?private SensorManager mSensorManager; ... mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);然后,你可以用?TYPE_ALL?參數調用?getSensorList()?方法,以獲取一個包含設備上所有傳感器的列表。例如:
?List<Sensor> deviceSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);如果你需要列出某指定類型的所有傳感器,你可以用其它常量來代替?TYPE_ALL?,比如?TYPE_GYROSCOPE?、TYPE_LINEAR_ACCELERATION?、?TYPE_GRAVITY?。
你還可以用某類型常量作為參數的?getDefaultSensor()?來檢測設備上是否存在該類型的傳感器。如果設備上給定類型的傳感器不止一個,則其中一個傳感器必須指定為缺省傳感器。如果給定類型的缺省傳感器不存在,則該方法返回null,表示設備沒有該類型的傳感器。例如,以下代碼檢查設備上是否存在磁力計:
?private SensorManager mSensorManager; ... mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); if (mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){ ? // 成功!磁力計存在。 ? } else { ? // 失敗|沒有磁力計。 ? }注意:?Android并未要求制造商在其Android平臺的設備上安裝任何類型的傳感器,因此設備上可能會存在各種各樣的傳感器。
除了列出設備上的傳感器清單外,你還可以用?Sensor?類的公共方法來確定每個傳感器的性能和參數。如果你的應用需要根據傳感器及其性能的差異作出不同的表現,這就非常有用。比如,你可以用?getResolution()?和getMaximumRange()?方法來獲取傳感器的分辨率及最大量程。你還可以用?getPower()?方法來獲取傳感器的能耗。
如果你需要根據傳感器的制造商或版本來對應用進行優化,有兩個公共方法特別有用。比如,如果你的應用需要監控傾斜或者晃動之類的用戶手勢,則針對較新的帶某廠商重力傳感器的設備,你可以建立一套數據過濾規則和優化措施,而針對沒有重力傳感器只有加速度計的設備你可以建立另一套數據過濾器和優化措施。以下例程展示了如何用?getVendor()?和?getVersion()?來完成這類工作的。在此例中,我們將先查找Google公司出品的版本為3的重力傳感器。如果設備上不存在這類傳感器,我們再嘗試使用加速計。
?private SensorManager mSensorManager; private Sensor mSensor; ... mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); if (mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY) != null){ ? List<Sensor> gravSensors = mSensorManager.getSensorList(Sensor.TYPE_GRAVITY); ? for(int i=0; i<gravSensors.size(); i++) { ? ? if ((gravSensors.get(i).getVendor().contains("Google Inc.")) && ? ? ? ?(gravSensors.get(i).getVersion() == 3)){ ? ? ? // 使用版本3的重力傳感器 ? ? ? mSensor = gravSensors.get(i); ? ? } ? } } else{ ? // 使用加速計 ? if (mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){ ? ? mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); ? } ? else{ ? ? // 抱歉,設備上不存在加速計,你無法玩這個游戲。 ? } }其它較有用的是?getMinDelay()?方法,用于返回傳感器采集數據的最小時間間隔(微秒)。任何getMinDelay()?返回非零值的傳感器都是流式傳感器(streamingsensor)。流式傳感器以一定的時間間隔有規律地測量數據,自Android 2.3 (API Level 9) 開始引入。如果調用?getMinDelay()?時返回零,這就表示該傳感器不是流式傳感器,只有所監測的參數發生變化時它才會報送數據。
getMinDelay()?方法能讓你確定傳感器的最大采樣頻率,因此它是非常有用的。如果你的應用中某項功能需要很高的數據采樣率或者要用到流式傳感器,你就可以用此方法先確認傳感器是否符合要求,然后再據此來啟用或禁用相關的功能。
警告:?傳感器框架并不一定按照傳感器的最大數據采樣率來向你的應用發送數據。傳感器框架是通過傳感器事件來報送數據的,而很多因素會影響到應用程序對傳感器事件的接收頻率。詳情請參閱?監聽傳感器事件?。
監聽傳感器事件
要監控傳感器的原始數據,你需要實現?SensorEventListener?接口的?onAccuracyChanged()?和onSensorChanged()?回調方法。只要發生以下事件,Android系統就會調用這兩個方法:
- 傳感器精度發生變化
在這種情況下,系統會調用?onAccuracyChanged()?方法,并傳給你一個發生變化的?Sensor?對象的引用和新的傳感器精度值。精度用以下四種狀態常量之一來表示:?SENSOR_STATUS_ACCURACY_LOW、SENSOR_STATUS_ACCURACY_MEDIUM、?SENSOR_STATUS_ACCURACY_HIGH、和SENSOR_STATUS_UNRELIABLE。
- 傳感器報送一個新數據
這種情況下,系統會調用?onSensorChanged()?方法,并傳給你一個?SensorEvent?對象。?SensorEvent對象中包含了新數據的相關信息,包括:數據精度、生成數據的傳感器、生成數據的時間戳、傳感器采到的新數據。
以下代碼展示了如何用?onSensorChanged()?方法來監控光線傳感器傳回的數據,并把原始數據顯示在一個由main.xml文件定義為sensor_data的?TextView?中。
?public class SensorActivity extends Activity implements SensorEventListener { ? private SensorManager mSensorManager; ? private Sensor mLight; ? @Override ? public final void onCreate(Bundle savedInstanceState) { ? ? super.onCreate(savedInstanceState); ? ? setContentView(R.layout.main); ? ? mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); ? ? mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); ? } ? @Override ? public final void onAccuracyChanged(Sensor sensor, int accuracy) { ? ? // 如果傳感器精度發生變化,可以在這里完成些工作。 ? } ? @Override ? public final void onSensorChanged(SensorEvent event) { ? ? // 光線傳感器返回單個值。 ? ? // 很多傳感器會返回3個值,代表每個坐標軸上的數值。 ? ? float lux = event.values[0]; ? ? // 利用此值完成一些工作 ? } ? @Override ? protected void onResume() { ? ? super.onResume(); ? ? mSensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL); ? } ? @Override ? protected void onPause() { ? ? super.onPause(); ? ? mSensorManager.unregisterListener(this); ? } }在此例中,調用?registerListener()?時指定了缺省的數據延時 (SENSOR_DELAY_NORMAL)。數據延時(或采樣率)控制著由?onSensorChanged()?發送給應用的傳感器事件的觸發間隔。缺省的數據延遲是200,000微秒,適于監測典型的屏幕方向變動。你可以把數據延時指定為其它值,比如?SENSOR_DELAY_GAME?(20,000微秒)、SENSOR_DELAY_UI?(60,000微秒)或?SENSOR_DELAY_FASTEST?(0微秒)。Android3.0 (API Level 11) 開始,你還可以直接指定延時值(微秒數)。
你指定的延時只是一個建議值。Android系統和其它應用可以修改這個值。最佳方案是,你應該指定你能承受的最大延時,因為系統一般會采用一個比設定稍小一點的值(也就是說,你應該選擇應用所需的最慢采樣率)。采用更大的延時能夠降低處理器的負載并減少耗電量。
傳感器框架發送傳感器事件的實際頻率,是沒有現成的公共方法來判斷的,不過,你可以根據多個傳感器事件的時間戳來計算出采樣率。一旦采樣率(延時)設置完成,你就不應該改變它。如果由于某種原因需要修改延時,那你就必須注銷并重新注冊傳感器偵聽器。
還有一點非常重要,請注意上例中使用了?onResume()?和?onPause()?回調方法來注冊和注銷傳感器事件偵聽器。最佳方案就是,你應該保證在不用時及時關閉傳感器,特別當你的activity被暫停時。不然,因為某些傳感器的能耗很大,會快速消耗電池電量,可能會在幾個小時內將電池耗盡。當屏幕關閉時,系統會自動禁用所有傳感器。
處理各種傳感器配置
Android并沒有設定標準的傳感器配置,這意味著設備制造商可能會把所有要裝入設備的傳感器配置都放進Android平臺的設備中。這樣,設備就可能包括了各種傳感器的大量配置信息。比如,MotorolaXoom帶有壓力傳感器,而SamsungNexus S就沒有。同理,Xoom 和 NexusS 都帶有陀螺儀,但是HTC Nexus One 卻沒有。如果你的應用依賴于特定類型的傳感器,你不得不確認設備是否提供了該傳感器,以保證你的應用能成功運行。你有兩種方式來確認傳感器的存在:
- 在運行時檢測傳感器并酌情啟用或禁用應用程序的相應功能
- 使用Android Market過濾器來限定目標設備必須帶有特定傳感器
這兩種方式將在下節介紹。
在運行時檢測傳感器
如果你的應用程序用到了特定類型的傳感器,不過并不是必須使用它,那么你可以在運行時利用傳感器框架來檢測它,并酌情啟用或禁用相應功能。比如,一個導航應用也許會用到溫度、壓力、GPS和地磁傳感器來顯示溫度、氣壓、位置和南北方位。如果設備不提供壓力傳感器,你可以在運行時用傳感器框架來檢測壓力傳感器是否存在,然后在應用界面上關閉氣壓的顯示。例如,以下就是檢測設備是否提供壓力傳感器的代碼:
?? private SensorManager mSensorManager; ? ... ? mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); ? if (mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE) != null){ ? // Success! There's a pressure sensor. ? } ? else { ? // 失敗!傳感器不存在。 ? }用Android Market 過濾器來限定目標設備必須帶有指定的傳感器配置
如果你要在 Android Market 上發布應用,你可以用manifest 文件中的?<uses-feature>?元素把不提供所需傳感器的設備過濾掉。manifest 文件中的<uses-feature>元素有很多硬件描述符,利用它們你可以根據傳感器存在與否來對應用進行過濾。可列出的傳感器包括:加速計、氣壓計、羅盤(地磁)、陀螺儀、光線和鄰近距離。以下是濾除無加速計的manifest 樣例:
?<uses-feature android:name="android.hardware.sensor.accelerometer" ? ? ? ? ? ? ? android:required="true" />如果你把這個元素和描述符加入你的 manifest 中,則只有設備上帶有加速計的用戶才能在Android Market 上看到你的應用。
僅當應用程序完全依賴于某指定傳感器時,你才能把描述符設置?android:required="true"。如果你的應用中只有某些功能用到了傳感器,而沒有傳感器的話仍然能正常運行,那么你可以把傳感器列在?<uses-feature>中,但應設置?android:required="false"?。這樣可以確保沒有此傳感器的設備也能安裝你的應用。這也是項目管理實踐中的最佳方案,有助于你時刻了解應用所需要的硬件特性。請記住,如果你的應用用到了某個傳感器,但沒有此傳感器也能運行,那你就必須在運行時檢測傳感器,并酌情禁用或啟用相應功能。
傳感器的坐標系
通常,傳感器框架使用標準的三維坐標系來表示數據。對大多數傳感器而言,該坐標系是以設備保持默認方向時的屏幕為參照物來定義的(參見圖1)。當設備保持默認方向時,X軸表示從左到右的水平方向,Y軸表示自下而上的垂直方向,Z軸表示相對屏幕表面由內而外的方向。在這一坐標系中,屏幕背后的坐標用Z軸的負值表示。以下傳感器會用到該坐標系:
圖1.?傳感器API使用的坐標系(相對設備而言)
- 加速度傳感器
- 重力傳感器
- 陀螺儀
- 線性加速度傳感器
- 地磁傳感器
要理解這個坐標系,最重要的一點就是,屏幕方向變化時坐標軸并不移動——也就是說,設備移動時傳感器的坐標系永不改變。這與 OpenGL坐標系類似。
理解坐標系的另一個要點,你的應用不得假定設備的初始(默認)方向是豎直的。很多桌面設備的初始方向是橫向放置的。傳感器的坐標系總是以設備的初始方向為基準的。
最后,如果你的應用需要把傳感器數據與屏幕顯示關聯,則你要用?getRotation()?方法來確定屏幕的轉動方向,然后用remapCoordinateSystem()?方法把傳感器坐標映射為屏幕坐標。即使你的manifest文件已經指定為僅支持縱向顯示,你仍需要這么做。
關于傳感器坐標系的更多信息,包括如何處理屏幕旋轉的相關信息,請參閱?One Screen Turn Deserves Another
注意:?某些傳感器和方法的坐標系使用了地球參照系(而非設備參照系)。這些傳感器和方法返回的數據表示了相對大地坐標而言的設備運動和地理位置。詳情請參閱getOrientation()?方法、?getRotationMatrix()?方法、方向傳感器?和旋轉矢量傳感器。
訪問和使用傳感器的最佳實現方案
當你設計傳感器相關的代碼時,請確保遵守本節所列出的規范。這些規范作為最佳實現方案進行推薦,適用于需要使用傳感器框架來訪問傳感器和讀取傳感器數據的任何人員。
注銷傳感器偵聽器
當不再使用傳感器或相關activity暫停時,確保及時注銷傳感器偵聽器。如果傳感器偵聽器已注冊而相關activity被暫停,傳感器仍會繼續測量數據并消耗電池資源,除非你注銷了傳感器。以下代碼展示了如何利用onPause()?方法來注銷偵聽器:
?private SensorManager mSensorManager; ? ... @Override protected void onPause() { ? super.onPause(); ? mSensorManager.unregisterListener(this); }詳情請參閱unregisterListener(SensorEventListener)。
不要在模擬器上測試你的代碼
目前無法在模擬器上測試傳感器相關的代碼,因為模擬器不能模擬傳感器。你必須在物理設備上測試傳感器相關代碼。不過,你可以利用傳感器的模擬器來模擬傳感器的輸出。
不要阻塞onSensorChanged() 方法
傳感器數據以很高的頻率在發生變化,這意味著系統可能會非常頻繁地調用?onSensorChanged(SensorEvent)方法。最佳實現方案是,在?onSensorChanged(SensorEvent)?方法中你應該盡可能少干些事情,以防止阻塞。如果你的應用需要對傳感器數據進行過濾或剔除操作,則應該在?onSensorChanged(SensorEvent)?方法之外進行。
避免使用過時的方法或傳感器類型
有幾個方法和常量已經過時了。特別是?TYPE_ORIENTATION?傳感器類型已經過時。要獲取方位數據,你應該換用?getOrientation()?方法。同樣,?TYPE_TEMPERATURE?傳感器類型也已過時。在Android 4.0 的設備上,你應該用?TYPE_AMBIENT_TEMPERATURE?傳感器類型來代替。
在使用前先驗證傳感器
在試圖讀取數據前,請確保先驗證一下傳感器是否存在。不要因為傳感器很常用,就簡單地假定它會存在。制造商并不需要在他們的設備上提供任何傳感器。
謹慎選擇傳感器延時
當利用?registerListener()?方法注冊傳感器時,請確保為你的應用或使用場景選擇了合適的發送頻率。傳感器能夠以很高的頻率發送數據。?請保證系統有能力發送其它數據,不要無謂浪費系統資源和消耗電池電量。
參考:http://developer.android.com/guide/topics/sensors/sensors_overview.html
總結
以上是生活随笔為你收集整理的Android传感器-开发指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android中11种常见传感器的使用方
- 下一篇: Android系统自带样式(@andro