<div ></div>
今天看源码的时候看到一个使用power键和音量下键来组合实现屏幕截图的功能,还挺有趣的,之前一直都不知道。。。
废话不多说,直接看过程吧
在android中由WindowManagerService这个系统服务来循环读取窗口获取的消息(包括按下,弹起,双击,单击等)然后分发到各个类接收,在这个过程中有一个类会进行消息过滤处理,就是PhoneWindowManager了,PhoneWindowManager中有两个方法interceptKeyBeforeDispatching和interceptKeyBeforeQueueing,其中包括了几乎所有按键的处理,interceptKeyBeforeDispatching主要处理Home键、Menu键、Search键等,
interceptKeyBeforeQueueing主要处理音量键、电源键、耳机键等。
截屏功能的代码就是在interceptKeyBeforeQueueing方法中,看两段代码
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
if (isScreenOn && !mVolumeDownKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mVolumeDownKeyTriggered = true;
mVolumeDownKeyTime = event.getDownTime();
mVolumeDownKeyConsumedByScreenshotChord = false;
cancelPendingPowerKeyAction();
interceptScreenshotChord();
}
} else {
mVolumeDownKeyTriggered = false;
cancelPendingScreenshotChordAction();
}
......
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
if (isScreenOn && !mPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
mPowerKeyTriggered = true;
mPowerKeyTime = event.getDownTime();
interceptScreenshotChord();
}
......
可以看到正是这里(响应down事件)捕获是否按了音量下键和电源键,而且两个地方都会进入函数interceptScreenshotChord()中,接下来看看这个函数做了什么操作:
private void interceptScreenshotChord() {
if (mVolumeDownKeyTriggered && mPowerKeyTriggered
&& !mVolumeUpKeyTriggered) {
final long now = SystemClock.uptimeMillis();
if (now <= mVolumeDownKeyTime +
SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
&& now <= mPowerKeyTime +
SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
mVolumeDownKeyConsumedByScreenshotChord = true;
cancelPendingPowerKeyAction();
mHandler.postDelayed(mScreenshotChordLongPress,
ViewConfiguration.getGlobalActionKeyTimeout());
}
}
}在这个函数中,用两个布尔变量判断是否同时按了音量下键和电源键后,再计算两个按键响应Down事件之间的时间差不超过150毫秒,也就认为是同时按了这两个键后,算是真正的捕获到屏幕截屏的组合键。
调用函数interceptScreenshotChord,
private void interceptScreenshotChord() {
if (mScreenshotChordEnabled
&& mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
final long now = SystemClock.uptimeMillis();
if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
&& now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
mVolumeDownKeyConsumedByScreenshotChord = true;
cancelPendingPowerKeyAction();
mHandler.postDelayed(mScreenshotChordLongPress, getScreenshotChordLongPressDelay());
}
}
}发送消息mScreenshotChordLongPress到消息队列里面,点进去可以看到
private final Runnable mScreenshotChordLongPress = new Runnable() {
public void run() {
takeScreenshot();
}
};这个takeScreenshot一看就知道是实现截图功能的函数了,在takeScreenshot里面主要是绑定一个TakeScreenshotService的服务,还有一些返回的消息处理,再往下就是涉及到JNI调用底层服务的东西,在这就不作介绍了,也没仔细研究过
BroadcastReceiver
作用:接收传来的特定类型的intent,然后执行相应操作
使用方法:
1.新建一个类MyBroadcastReceiver,extends BroadcastReceiver
2.override 其中的onReceiver(...)方法,一旦激活一个 MyBroadcastReceiver 对象,该对象马上调用该方法,该方法结束时,该MyBroadcastReceiver对象被清理。
3.(1)在AndroidManifest.xml中注册,在application中添加一个<Receiver android:name="MyBroadcastReceiver"></Receiver>标签
该标签内含有<intent-filter>,此标签用于过滤得到该MyBroadcastReceiver想要处理的intent对象
过滤可以基于intent的action/data/catagory三个标准,即在<intent-filter>插入<action>或者<data>或者<catagory>,和这三个表情的android:name相同的intent会得到MyBroadcastReceiver的处理
(2)在代码中注册,代码如下
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_EDIT);
MainActivity.this.registerReceiver(receiver, filter);
PS:intent被发送后,会被插入到某个不可知的队列当中,一个BroadcastReceiver被程序注册激活以后,会从队列中并行取出满足条件的intent,多线程并发执行。
在代码中解除注册用的是:MainActivity.this.unregisterReceiver(receiver);
4.在某activity中,首先new intent(),初始化intent,然后使用
Activity.this.sendBroadcast(intent);
将该intent广播出去,如果和哪个BroadcastReceiver的intent-filter匹配了,就可以激活该BroadcastReceiver。
5.使用难点:intent的action和data和catagory的理解?