【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() {
                @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
 */
时间: 2024-11-02 13:09:41

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

android 4.3中一次完整的android源码截屏事件的捕获

1.背景 我们知道android提供了一个系统截屏功能,就是按住电源键和音量减的按键0.5秒,系统将执行截屏功能.所以要实现系统截屏的功能,就是要捕获系统的这两个组合键下面的函数,然后一层一层的向下挖掘.现在网上找到的版本是在Surface.java文件下存在ScreenShot()函数,是@hide的.但是这是之前版本的办法,在android4.3之后已经是不适用的,因为在/frameworks/base/core/java/android/view/的Surface.java下并没有Scre

哥哥姐姐们好,我有个关于android源码蓝牙的问题!求解。。。

问题描述 哥哥姐姐们好,我有个关于android源码蓝牙的问题!求解... 我现在有个需求就是 在android源码中修改蓝牙默认名称,在网上查了好久, " frameworksasecorejniAndroid_bluetooth_common.h 将BTMTK_ANDROID_DEFAULT_LOCAL_NAME值改掉,注意此处有引号." 网上这么说,可是我找不到 Android_bluetooth_common.h 这个文件,还有这么说 "可以通过修改alps/medi

android 4.3截屏功能的尝试与失败分析

1.背景 上一篇讲了在源码中捕获到了android手机的截屏函数(同时按下电源键与音量减,详情http://blog.csdn.net/buptgshengod/article/details/19911909),经过一周的研究还是没有在手机上实现系统截屏功能,总结下尝试的方法与失败的原因. 2.失败方法分析(1)bufferframe读取fb0 在手机的/dev/graphics目录下的fb0文件是负责屏幕渲染的帧缓存,网上有一些教程讲如何用c将手机中的fb0转换成bmp格式的图片.我在and

android源码下载方式

android源码下载方式: 方式一: 1) 创建~/bin $ mkdir ~/bin$ PATH=~/bin:$PATH 2) 下载repo $ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo$ chmod a+x ~/bin/repo 3) 创建保存Android源码文件 $ mkdir WORKING_DIRECTORY$ cd WORKING_DIRECTORY 4 ) 下载Androi

Android实现的截屏小程序示例_Android

本文实例讲述了Android实现的截屏小程序.分享给大家供大家参考,具体如下: 先看截图,不过这个截屏还不够完整,头上的statusbar没有,呈黑色. 多按了几次,就成这样了,呵呵. package com.test; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Bitmap.Config; import

Android 源码分析,FreeMind 是一件超级利器

Android 源码分析,FreeMind 是一件超级利器 思维导图软件 XMind 与 FreeMind 的对比 作者: 善用佳软 日期: 2012-04-17 分类: 1 文本办公, 1.5 思维导图 标签: mindmap 思维导图类软件中,最有影响力的开源免费软件是 FreeMind 和 XMind.FreeMind历史悠久,当属经典:XMind作为后起之秀,大有赶超之势.同作为免费.开源的思维导图解决方案,应如何选择/结合两款软件?本文试做分析,以供用户/开发者参考. 本文的分析基于W

GitHub 上有哪些完整的 iOS-App 源码值得参考?

GitHub 上有哪些完整的 iOS-App 源码值得参考? http://mobdevgroup.com/ ProducthuntOSX Mac 上开源的 Product Hunt OSX 客户端软件 GanHuoCode 干货集中营的第三方iOS客户端 ESTMusicPlayer 一款基于 DOUAudioStreamer 开发的一款优雅简洁的音乐播放器. beautifulApp 用Swift做的一个高仿最美应用 Geofancy The Geofancy iOS app. Helpin

Android源码分析-Alarm机制与Binder的交互

转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/18448997 前言 本次给大家分析的是Android中Alarm的机制以及它和Binder的交互,所用源码为最新的Android4.4.因为Alarm的功能都是通过Binder来完成的,所以,介绍Alarm之前必须要先介绍下它是如何调用Binder来完成定时功能的.由于内容较多,本文会比较长,在文章结构安排上是这样的:首先简单介绍如何使用Alarm并给出其工作原理,接着分析

《深入解析Android 虚拟机》——第1章 获取并编译Android源码 1.1获取Android源码

第1章 获取并编译Android源码 在本章中,将详细讲解获取并编译Android源码的基本知识,介绍各个目录中主要文件的功能,为读者步入本书后面知识的学习打下基础. 1.1 获取Android源码 要想研究Android系统的源码,需要先获取其源码.目前市面上主流的操作系统有Windows.Linux.Mac OS的操作系统,由于Mac OS源自于Linux系统,因此本书将讲解分别在Windows系统和Linux系统中获取Android源码的知识. 1.1.1 在Linux系统获取Androi