【android4.3】记一次完整的android源码截屏事件的捕获(不同于网上的老版本)
感謝網友cjd6568358的幫助,新版的Android系統截屏功能已經實現,需要的朋友請移步項目主頁:https://github.com/Android-ScreenShot/AndroidScreenShotService(別忘點個star哦)
---------------------------------------------------------------------------------------------------------------------
(轉載請注明出處:http://blog.csdn.net/buptgshengod)
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)找到那個service
public 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);}我們主要看這句,我們終于找到了screenshot(),這個地方跟低版本的android源碼是有改動的,之前的surface操作是寫到surface類里,現在增加了這個surfacecontrol類來控制surface。
// Take the screenshotmScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);(4)現在我們打開surfacecontrol類,位于/frameworks/base/core/java/android/view
代碼開頭我們可以看到,這個類是被google隱藏了,所以不能直接調用,若是想用截屏功能要在源碼中編譯才行,至于如何調用這個功能以后會講,have fun!
總結
以上是生活随笔為你收集整理的【android4.3】记一次完整的android源码截屏事件的捕获(不同于网上的老版本)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【android-tips】androi
- 下一篇: android4.3 截屏功能的尝试与失