【android4.3】记一次完整的android源码截屏事件的捕获(不同于

1.背景

我们知道android提供了一个系统截屏功能,就是按住电源键和音量减的按键0.5秒,系统将执行截屏功能。所以要实现系统截屏的功能,,就是要捕获系统的这两个组合键下面的函数,然后一层一层的向下挖掘。现在网上找到的版本是在Surface.java文件下存在ScreenShot()函数,是@hide的。但是这是之前版本的办法,在android4.3之后已经是不适用的,因为在/frameworks/base/core/java/android/view/的Surface.java下并没有ScreenShot()函数,我猜google不会这么绝情,一定会在framework层给开发者留了接口,只不过写到了别的地方。所以博主按照前任的思路自行挖掘,最后找到了新版本的ScreenShot函数,在这与大家分享。

2.挖掘过程 (1)Android源码中对按键的捕获位于文件PhoneWindowManager.java(\frameworks\base\policy\src\com\android\internal\policy\impl)中 ase 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();}

我们看到了,如果同时按下电源键与音量减会启动函数,

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());}}}

我们看到handle中的这个函数,应该就是我们要执行的截屏功能

mScreenshotChordLongPress

我们进入这个函数

private final Runnable mScreenshotChordLongPress = new Runnable() {public void run() {takeScreenshot();}};

终于让我们找到了takeScreenShot,不过别急,我们转到这个函数

private void takeScreenshot() {synchronized (mScreenshotLock) {if (mScreenshotConnection != null) {return;}ComponentName cn = new ComponentName("com.android.systemui","com.android.systemui.screenshot.TakeScreenshotService");Intent intent = new Intent();intent.setComponent(cn);ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {synchronized (mScreenshotLock) {if (mScreenshotConnection != this) {return;}Messenger messenger = new Messenger(service);Message msg = Message.obtain(null, 1);final ServiceConnection myConn = this;Handler h = new Handler(mHandler.getLooper()) {@Overridepublic void handleMessage(Message msg) {synchronized (mScreenshotLock) {if (mScreenshotConnection == myConn) {mContext.unbindService(mScreenshotConnection);mScreenshotConnection = null;mHandler.removeCallbacks(mScreenshotTimeout);}}}};msg.replyTo = new Messenger(h);msg.arg1 = msg.arg2 = 0;if (mStatusBar != null && mStatusBar.isVisibleLw())msg.arg1 = 1;if (mNavigationBar != null && mNavigationBar.isVisibleLw())msg.arg2 = 1;try {messenger.send(msg);} catch (RemoteException e) {}}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {mScreenshotConnection = conn;mHandler.postDelayed(mScreenshotTimeout, 10000);}}}我们看到它启动了一个截图的service( "com.android.systemui.screenshot.TakeScreenshotService")。(这也印证了我一个猜想,android的一切功能都是handle通过sendmessage然后通过service实现的)

(2)找到那个servicepublic class TakeScreenshotService extends Service {private static final String TAG = "TakeScreenshotService";private static GlobalScreenshot mScreenshot;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:final Messenger callback = msg.replyTo;if (mScreenshot == null) {mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);}mScreenshot.takeScreenshot(new Runnable() {@Override public void run() {Message reply = Message.obtain(null, 1);try {callback.send(reply);} catch (RemoteException e) {}}}, msg.arg1 > 0, msg.arg2 > 0);}}};@Overridepublic IBinder onBind(Intent intent) {return new Messenger(mHandler).getBinder();}}我们看到类GlobalScreenshot的对象执行了截图的功能。

(3)打开GlobalScreenshot /*** Takes a screenshot of the current display and shows an animation.*/void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {// We need to orient the screenshot correctly (and the Surface api seems to take screenshots// only in the natural orientation of the device :!)mDisplay.getRealMetrics(mDisplayMetrics);float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};float degrees = getDegreesForRotation(mDisplay.getRotation());boolean requiresRotation = (degrees > 0);if (requiresRotation) {// Get the dimensions of the device in its native orientationmDisplayMatrix.reset();mDisplayMatrix.preRotate(-degrees);mDisplayMatrix.mapPoints(dims);dims[0] = Math.abs(dims[0]);dims[1] = Math.abs(dims[1]);}// Take the screenshotmScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);if (mScreenBitmap == null) {notifyScreenshotError(mContext, mNotificationManager);finisher.run();return;}if (requiresRotation) {// Rotate the screenshot to the current orientationBitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(ss);c.translate(ss.getWidth() / 2, ss.getHeight() / 2);c.rotate(degrees);c.translate(-dims[0] / 2, -dims[1] / 2);c.drawBitmap(mScreenBitmap, 0, 0, null);c.setBitmap(null);// Recycle the previous bitmapmScreenBitmap.recycle();mScreenBitmap = ss;}// OptimizationsmScreenBitmap.setHasAlpha(false);mScreenBitmap.prepareToDraw();// Start the post-screenshot animationstartAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,statusBarVisible, navBarVisible);}经验是由痛苦中粹取出来的

【android4.3】记一次完整的android源码截屏事件的捕获(不同于

相关文章:

你感兴趣的文章:

标签云: