<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的理解?