(一) 前言
P-Sensor,距離感應器,能夠感應手機和人體距離。具體使用用途是在通話過程當中打開P-Sensor,那麼當手機屏幕貼近用戶臉部時,就會自動感應出手機和人體距離是多少。當小於某一個值時,就會熄滅屏幕,再也不接收用戶觸摸屏幕事件,從而有效的防止通話過程當中誤觸摸事件的出現。(有不少人通話過程當中臉部會觸碰到掛斷鍵,從而致使通話中斷有沒有? ^_^)。
(二) 打開P-Sensor
剛纔咱們講了,P-Sensor主要用於通話過程當中防止用戶誤操做屏幕,那麼咱們就以通話過程爲例,看看電話程序爲P-Sensor作了什麼。
a. 電話程序在啓動的時候,在PhoneApp.java裏面新建了一個P-Sensor的wackLock對象,以下:
複製內容到剪貼板
代碼:
mProximityWakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, LOG_TAG);
其實,wackLock這個東東咱們之前講過,它是用來請求控制屏幕點亮和熄滅的一個東東, 具體能夠看這個帖子:
http://bbs.51cto.com/thread-1018050-1.html
b. ok,那既然咱們擁有了這個關於P-Sensor對象,怎麼使用它呢?
在電話狀態發生改變的時候,好比,接通了電話,它就會調用PhoneApp.java的updateProximitySensorMode(Phone.State state)方法,這個方法會根據當前電話的狀態,決定要不要打開P-Sensor,
那麼若是在通話過程當中,電話也就是OFF-HOOK狀態,嗯,打開P-Sensor:
複製內容到剪貼板
代碼:
if (!mProximityWakeLock.isHeld()) {
if (DBG) Log.d(LOG_TAG, "updateProximitySensorMode: acquiring...");
mProximityWakeLock.acquire();
}
其中mProximityWakeLock.acquire();會展轉調用到PowerManagerService.java的enableProximityLockLocked()方法,顧名思義,這個方法是打開P-Sensor,是的!這個方法會去判斷當前手機有沒有P-Sensor,若是有的話,就會去向SensorManager註冊一個P-Sensor監聽器,那麼當P-Sensor檢測到手機和人體距離發生改變時,就會調用咱們PowerManagerService.java的監聽器.一樣,當電話掛斷時,電話模塊會去調用mProximityWakeLock.release(flags), 這樣就會取消P-Sensor監聽器.
ok.. 那麼接下來就是分析PowerManagerService裏面這個P-Sensor是怎麼工做的。
(三) PowerManagerService裏面P-Sensor監聽器工做原理
監聽器的代碼,當P-Sensor檢測到距離有變化時發生。
複製內容到剪貼板
代碼:
SensorEventListener mProximityListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
long milliseconds = SystemClock.elapsedRealtime();
synchronized (mLocks) {
float distance = event.values[0]; //檢測到手機和人體的距離
long timeSinceLastEvent = milliseconds - mLastProximityEventTime; //此次檢測和上次檢測的時間差
mLastProximityEventTime = milliseconds; //更新上一次檢測的時間
mHandler.removeCallbacks(mProximityTask);
boolean proximityTaskQueued = false;
// compare against getMaximumRange to support sensors that only return 0 or 1
boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD &&
distance < mProximitySensor.getMaximumRange()); //若是距離小於某一個距離閾值,默認是5.0f,說明手機和臉部距離貼近,應該要熄滅屏幕。
if (mDebugProximitySensor) {
Slog.d(TAG, "mProximityListener.onSensorChanged active: " + active);
}
if (timeSinceLastEvent < PROXIMITY_SENSOR_DELAY) {
// enforce delaying atleast PROXIMITY_SENSOR_DELAY before processing
mProximityPendingValue = (active ? 1 : 0);
mHandler.postDelayed(mProximityTask, PROXIMITY_SENSOR_DELAY - timeSinceLastEvent);
proximityTaskQueued = true;
} else {
// process the value immediately
mProximityPendingValue = -1;
proximityChangedLocked(active); //熄滅屏幕操做
}
// update mProximityPartialLock state
boolean held = mProximityPartialLock.isHeld();
if (!held && proximityTaskQueued) {
// hold wakelock until mProximityTask runs
mProximityPartialLock.acquire();
} else if (held && !proximityTaskQueued) {
mProximityPartialLock.release();
}
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// ignore
}
};
代碼裏面我已經有一些註釋,下面來用文字描述下。
a. 首先會拿到這測距離變化的距離,float distance = event.values[0];
b. 檢測此次距離變化和上次距離變化時間差,若是小於系統設置的閾值,則不會去熄滅屏幕。過於頻繁的操做系統會忽略掉。
因此,若是你感受P-Sensor不夠靈敏,就能夠修改這個系統默認值
複製內容到剪貼板
代碼:
private static final int PROXIMITY_SENSOR_DELAY = 1000;
若是你改的很小,就會發現P-Sensor會變得靈敏不少。。。
c. 嗯,若是P-Sensor檢測到此次距離變化小於系統默認值,且此次是一次正常的變化,那麼就應該去熄滅屏幕:
複製內容到剪貼板
代碼:
proximityChangedLocked(active);
這裏的active是true,一樣,在這裏它還會判斷P-Sensor是否能夠用,若是不可用,則返回。。忽略此次距離變化
複製內容到剪貼板
代碼:
if (!mProximitySensorEnabled) {
Slog.d(TAG, "Ignoring proximity change after sensor is disabled");
return;
}
若是一切都知足,則調用:
複製內容到剪貼板
代碼:
goToSleepLocked(SystemClock.uptimeMillis(), WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR);
熄滅屏幕。。