关闭

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

标签: 源码framework截屏
11606人阅读 评论(18) 收藏 举报
分类:

感谢网友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() {
                @Override
                public 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()) {
                            @Override
                            public 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) {
                        }
                    }
                }
                @Override
                public 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() {
        @Override
        public 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);
            }
        }
    };

    @Override
    public 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 orientation
            mDisplayMatrix.reset();
            mDisplayMatrix.preRotate(-degrees);
            mDisplayMatrix.mapPoints(dims);
            dims[0] = Math.abs(dims[0]);
            dims[1] = Math.abs(dims[1]);
        }

        // Take the screenshot
        mScreenBitmap = 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 orientation
            Bitmap 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 bitmap
            mScreenBitmap.recycle();
            mScreenBitmap = ss;
        }

        // Optimizations
        mScreenBitmap.setHasAlpha(false);
        mScreenBitmap.prepareToDraw();

        // Start the post-screenshot animation
        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
                statusBarVisible, navBarVisible);
    }

我们主要看这句,我们终于找到了screenshot(),这个地方跟低版本的android源码是有改动的,之前的surface操作是写到surface类里,现在增加了这个surfacecontrol类来控制surface。

// Take the screenshot
        mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);

(4)现在我们打开surfacecontrol类,位于/frameworks/base/core/java/android/view

代码开头我们可以看到,这个类是被google隐藏了,所以不能直接调用,若是想用截屏功能要在源码中编译才行,至于如何调用这个功能以后会讲,have fun!

/**
 * SurfaceControl
 *  @hide
 */


12
1
查看评论

Android截屏事件监听

Android系统没有直接对截屏事件监听的接口,也没有广播,只能自动动手丰衣足食!
  • xiaohanluo
  • xiaohanluo
  • 2016-12-23 11:52
  • 6267

android 应用内页面,截屏监听

公司的项目由于安全需要,对某一特定的页面需要监听是否被用户截屏了。 简单搜了一下,很少有这方面的问题,没办法,只能自己折腾了。 目前想到两种思路: 1、监听广播 当然,前提是系统在截屏的时候发送某一广播,然而并没有。 2、监听按键 android手机按下“电源键+音量减”会进行截屏,此外大部...
  • sollian
  • sollian
  • 2016-05-20 19:03
  • 5339

用C#来捕获屏幕

 用C#来捕获屏幕(1) [ 作者: 王天 添加时间: 2002-3-29 14:37:15 ] 来源: 赛迪网 www.ccidnet.com 其实用C#可以完成其他程序设计语言的几乎全部功能,当然C#自身的许多独到的功能,是其他程序语言所无法实现的,这就是...
  • dql1982
  • dql1982
  • 2007-12-20 13:51
  • 284

c#捕获窗口最小化和还原的事件

<br />下面的代码演示了当前窗口最小化、还原的时候另一个窗口f2也最小化、还原的功能,参考了http://topic.csdn.net/t/20020717/09/879937.html 中wistaria的回答<br /> [DllImport(...
  • assuper
  • assuper
  • 2011-02-20 14:59
  • 1924

Android源码解析(二十六)-->截屏事件流程

今天这篇文章我们主要讲一下Android系统中的截屏事件处理流程。用过android系统手机的同学应该都知道,一般的android手机按下音量减少键和电源按键就会触发截屏事件(国内定制机做个修改的这里就不做考虑了)。那么这里的截屏事件是如何触发的呢?触发之后android系统是如何实现截屏操作的呢?...
  • qq_23547831
  • qq_23547831
  • 2016-05-22 16:59
  • 9789

Android系统 截屏监听 的 原理与实现

Android系统并没有提供截屏通知相关的API,需要我们自己利用系统能提供的相关特性变通实现。Android系统有一个媒体数据库,每拍一张照片,或使用系统截屏截取一张图片,都会把这张图片的详细信息加入到这个媒体数据库,并发出内容改变通知,我们可以利用内容观察者(ContentObserver)监听...
  • xietansheng
  • xietansheng
  • 2016-10-16 18:35
  • 10080

android实现截屏操作

最近开发了一些Android小游戏,想在游戏结束或者完成之后把整个屏幕截取下来并分享到社交平台上。先上效果吧。 网上一搜,截屏的方法很多。这里只贴出了一种,将截取到Bitmap赋给Dialog上的ImageView并弹出对话框。对对话框加了弹出和收起的动画。看起来就有截屏的感觉了。...
  • Tom_xiaoxie
  • Tom_xiaoxie
  • 2016-06-02 11:41
  • 1075

Android系统 截屏监听 的 原理与实现

本文链接: http://blog.csdn.net/xietansheng/article/details/52692163 1. 原理 Android系统并没有提供截屏通知相关的API,需要我们自己利用系统能提供的相关特性变通实现。Android系统有一个媒体数据库,每拍一...
  • laowu119119
  • laowu119119
  • 2017-03-01 18:29
  • 476

Android 监听截屏获取图像

最近有人问我zen
  • liuhanhan512
  • liuhanhan512
  • 2014-07-03 16:03
  • 3283

Android系统截屏功能提取Demo

  • 2014-09-15 21:34
  • 59KB
  • 下载
    我的微信公众号

    作者公众号:凡人机器学习

    凡人机器学习

    机器学习微信交流群
    为了方便大家学习与交流,凡人云近日已开通机器学习社群! 分享“凡人机器学习”公众号名片到40人以上的大群并截图给小助手,小助手就会拉你入群 在这里你可以得到: 1.各种学术讨论 2.最新的资料分享 3.不定期的征文以及联谊活动! 小助手微信号:meiwznn
    作者新书《机器学习实践应用》

    主要讲述算法和业务的结合,适合初学者

    机器学习实践应用

    京东地址

    个人资料
    • 访问:842109次
    • 积分:11247
    • 等级:
    • 排名:第1683名
    • 原创:236篇
    • 转载:40篇
    • 译文:0篇
    • 评论:460条
    博客专栏
    统计