截屏 多难未遂
看到未遂,就知道这只是一篇分享感受的文章,但是,或多或少,有过同样过程的人至少会有一点同感。。或有一点思路以及灵感。
其实网上关于截屏的demo很多,以及思路也很多。我也不知道自己是哪里出了问题,总是未遂。
但是还是有几个值得注意的:
不管什么FrameBuffer呀,还是Android源码里面截屏的ScreenShot。。还是ScreenControll。。。and。。。这里要去掏Android更深,更底层或是最原始的都是需要root的。。。但是,本人root真不是很在行。
开始,我问了很多大神,他们都说Android屏幕绝对来截到。。但是,,,等他们看到我应用的界面的时候,然后我将我的应用代码与他们所说的截屏代码放在一起的时候,oho。。。没戏了。不能,所谓的截屏也只能截到此界面的背影。要不,代码上起,堪忧大神能解不:
if (null == mContext) { mContext = getApplicationContext(); } MyApplication.getInstance().addActivity(this); webView = new WebView(mContext); android.view.ViewGroup.LayoutParams params = new LayoutParams( android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.MATCH_PARENT); webView.setLayoutParams(params); windowsMan = (WindowManager) getApplicationContext().getSystemService( Context.WINDOW_SERVICE); WindowManager.LayoutParams vmParams = new WindowManager.LayoutParams(); // TYPE_PHONE TYPE_SYSTEM_ERROR vmParams.type = LayoutParams.TYPE_SYSTEM_ERROR; vmParams.format = PixelFormat.RGBX_8888; vmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE; vmParams.gravity = Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL; vmParams.x = 0; vmParams.y = 0; vmParams.width = WindowManager.LayoutParams.MATCH_PARENT; vmParams.height = WindowManager.LayoutParams.MATCH_PARENT; if (webView.getParent() == null) { windowsMan.addView(webView, vmParams); }
当当。。能知道是什么原因阻碍了我们的截屏吗?bingo,就是WindowManager,我用的是系统谈错误消息的那一层,如果我没有记错的话,这应该是窗口的第一层,而且,我还用了not touch这个属性,也就是这个界面只要我没让他死亡,你是拿他无可奈何的,他会一直浮在界面的上层。。遮盖了其他的界面。所以,若果大神不小心看见了,然后会解的话。。。。希望不吝赐教哦!
当然,我们往往截屏会使用一种很简单的方法,如果截屏在此次任务中没有显得那么重要的话。
private void CutImage() { cur_Time = System.currentTimeMillis() / 1000; if (null == isSdcard()) { Toast.makeText(this, "内存卡不存在", Toast.LENGTH_SHORT).show(); } else { Path = isSdcard() + fileName; String SavePath = Path + mId + "_" + cur_Time + ".jpg"; bmp = shot(); File pathFile = new File(Path); if (!pathFile.exists()) { System.out.println("mkdir"); pathFile.mkdir(); } File file = null; try { file = new File(SavePath); if (!file.exists()) { file.createNewFile(); System.out.println("createNewFile"); } } catch (Exception e) { e.printStackTrace(); } FileOutputStream fos = null; try { fos = new FileOutputStream(file); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } if (null != fos) { bmp.compress(Bitmap.CompressFormat.PNG, 90, fos); System.out.println("compress"); try { fos.flush(); } catch (IOException e) { e.printStackTrace(); } try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { } } } /** * 截屏方法 * * @return */ private Bitmap shot() { View view = getWindow().getDecorView(); Display display = this.getWindowManager().getDefaultDisplay(); view.layout(0, 0, display.getWidth(), display.getHeight()); view.setDrawingCacheEnabled(true);// 允许当前窗口保存缓存信息,这样getDrawingCache()方法才会返回一个Bitmap Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache()); return bmp; } /** * 先判断是否有SD卡 * */ private String isSdcard() { File sdcardDir = null; boolean isSDExist = Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED); if (isSDExist) { // 如果存在SDcard 就找到跟目录 sdcardDir = Environment.getExternalStorageDirectory(); return sdcardDir.toString(); } else { return null; } }
上面的就是一个得到截屏,然后保存的写法,下面这个可以选择去掉标题栏。
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Rect; import android.view.View; public class ScreenShot { private static Bitmap takeScreenShot(Activity activity) { // View是你需要截图的View View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap b1 = view.getDrawingCache(); // 获取状态栏高度 Rect frame = new Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top; // 获取屏幕长和高 int width = activity.getWindowManager().getDefaultDisplay().getWidth(); int height = activity.getWindowManager().getDefaultDisplay() .getHeight(); // 去掉标题栏 Bitmap b = Bitmap.createBitmap(b1, 0, statusBarHeight, width, height - statusBarHeight); view.destroyDrawingCache(); return b; } private static void savePic(Bitmap b, File filePath) { FileOutputStream fos = null; try { fos = new FileOutputStream(filePath); if (null != fos) { b.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); } } catch (FileNotFoundException e) { // e.printStackTrace(); } catch (IOException e) { // e.printStackTrace(); } } public static void shoot(Activity a, File filePath) { if (filePath == null) { return; } if (!filePath.getParentFile().exists()) { filePath.getParentFile().mkdirs(); } ScreenShot.savePic(ScreenShot.takeScreenShot(a), filePath); } }
以上两个代码都差不多而已。
所以,当我选择想要放弃截取当前这个应用的时候,想要自己打开一个服务,然后再后台定时扑捉界面,然后保存到一个我给定的目录下面,以便我能确认。所以,就动手在网上搜了一大堆关于标签是“android中实现后台截屏”,结果,得知的基本上是手机需要root或者这个程序需要root。我是哭的心都有了。本人自知自己水平连菜鸟都还不如,怎么会很快的吧程序给root了呢?说道root,曾经我也是试过很多方法。什么为了编译源码,使用NDK,什么在注册文件里面添加哈桑UId属性啦,或者在Android.mk里面添加
LOCAL_CERTIFICATE := platform
这样一句话呀,我算是折腾呀。但是,程序员就这宿命。
Notice:以前我一直认为,Android.mk需要每次把自己root权限的项目亲自给生成。但是,后来,我请教了一个我认为是大神的人,他告诉我,Android.mk可以直接从其他项目里面给copy过来。只要改几个地方就okay。嘿嘿
只要将:
LOCAL_PACKAGE_NAME
给一个给自己本项目的name即可,没什么特殊要求。不知道我理解错没。
后来知道原来,Android‘一直默默在底层安装了一个缓存帧的小把戏。。呵呵,那就是FrameBuffer,但是,知道了又怎么样呢。因为这个还是需要一个root了的手机。而且,现在手机的多样化以及很多喜欢自己改装,所以导致FrameBuffer花屏呀,或者是他缓存的路径不对("/dev/graphics/fb0")都是可能的。
但是,还是为了能方便起见,还是把代码给出来吧。在我的文件里面的“FrameBuffer截屏.jar”里面也可以直接给看到:
当然,这里会有更直接的链接,我觉得够料哦!
http://pan.baidu.com/s/1jG43QfW
然后哦还有基于源码的截屏,我也直接给链接了吧:
http://blog.csdn.net/buptgshengod/article/details/21958183
当然,这里还有SurfaceControl.java的链接:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/view/SurfaceControl.java/
有时候下载一个关于什么的资料或者Demo真够费我的小币的。所以,这里有一些关于截屏的demo:我给出了链接:
http://pan.baidu.com/s/1o6HSXOQ
。。。先就将这些外界来的信息写到这里吧。
毕竟我想要的是一个可以直接启动一个服务,然后来在后台打开这个程序的,然后运行截屏的。然后就考虑了一个不用root权限的。也就是DDMS原理的。但是也跟网上有朋友交流过,他们说这个方法行不通。但是,还是写了,但是。。。。在导入jar包的时候,总是报一个让我无法理解的错误。
[2014-11-19 01:08:39 - TakeSrenn] Conversion to Dalvik format failed with error 1
这个错误真的是让我无法理解。网上也查了很多,除了我没哟升级SDK外,其余的都检查了。而且,我觉得我的SDK已经可以不用升级了。
还有这里需要的dt.jar已经rt.jar都在jdk以及jre下面就可以找到。
但是就是这个dt.jar和rt.jar导入就报这样的错误。ddmlib.jar都不会引起。
这里还是把这个给贴出来。后台开启一个程序,然后定时去执行截屏。但是添加三面提到的三个包就okay。小人算是未遂了·。希望能有大神指点迷津。。
现有一个主类,用来启动服务:
package com.lhl.startservice; import java.io.File; import java.io.FileOutputStream; import com.lhl.service.StartService; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Display; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.Toast; /** * * 主界面 * @author Catherine * */ public class MainActivity extends Activity { private Handler handler = new Myhandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { Intent in = new Intent(MainActivity.this, StartService.class); startService(in); finish(); } private class Myhandler extends Handler { @Override public void handleMessage(Message msg) { Toast.makeText(MainActivity.this, "接受了 消息", 0).show(); super.handleMessage(msg); } } public void GetandSaveCurrentImage() { WindowManager widowManager = getWindowManager(); Display display = widowManager.getDefaultDisplay(); int w = display.getWidth(); int h = display.getHeight(); Bitmap Bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888); View decorview = this.getWindow().getDecorView(); decorview.setDrawingCacheEnabled(true); Bmp = decorview.getDrawingCache(); try { String SavePath = getSDCardPath() + "/ScreenImage"; File path = new File(SavePath); String filepath = SavePath + "/Screen_1.jpg"; File file = new File(filepath); if (!path.exists()) { path.mkdirs(); } if (!file.exists()) { file.createNewFile(); } FileOutputStream fos = null; fos = new FileOutputStream(file); if (null != fos) { Bmp.compress(Bitmap.CompressFormat.PNG, 90, fos); fos.flush(); fos.close(); Log.i("LW", "截屏文件已保存至SDCard/ScreenImage/下"); Message m = handler.obtainMessage(); handler.sendMessage(m); } } catch (Exception e) { e.printStackTrace(); } } private String getSDCardPath() { File sdCardDir = null; boolean sdcardExit = Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED); if (sdcardExit) { sdCardDir = Environment.getExternalStorageDirectory(); } return sdCardDir.toString(); } }
然后一个服务打开:
package com.lhl.service; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Timer; import java.util.TimerTask; import com.android.ddmlib.AdbCommandRejectedException; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.IDevice; import com.android.ddmlib.RawImage; import com.android.ddmlib.TimeoutException; import com.lhl.application.GetApplication; import android.app.Service; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.os.Environment; import android.os.IBinder; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; /** * * 开启一个服务 * @author Catherine * */ public class StartService extends Service { @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override @Deprecated public void onStart(Intent intent, int startId) { // TODO Auto-generated method stub super.onStart(intent, startId); } @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { new Timer().schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub System.out.println("123"); /** * 被注释的这个方法 可以截屏 指定的 某个界面 */ // GetandSaveCurrentImage() ; CunCrruntPage(); } private void CunCrruntPage() { // try { IDevice device; AndroidDebugBridge bridge = AndroidDebugBridge .createBridge(); waitDeviceList(bridge); // IDevice devices[] = bridge.getDevices(); device = devices[0]; // RawImage rawScreen = device.getScreenshot(); // if (rawScreen != null) { // BufferedImage image = null; // boolean landscape = false; // int width2 = landscape ? rawScreen.height // : rawScreen.width; // int height2 = landscape ? rawScreen.width // : rawScreen.height; // if (image == null) { // image = new BufferedImage(width2, height2, // BufferedImage.TYPE_INT_RGB); // } else { // if (image.getHeight() != height2 // || image.getWidth() != width2) { // image = new BufferedImage(width2, height2, // BufferedImage.TYPE_INT_RGB); // } // } // int index = 0; // int indexInc = rawScreen.bpp >> 3; // for (int y = 0; y < rawScreen.height; y++) { // for (int x = 0; x < rawScreen.width; x++, index += indexInc) { // int value = rawScreen.getARGB(index); // if (landscape) // image.setRGB(y, rawScreen.width - x - 1, // value); // else // image.setRGB(x, y, value); // } // } // // ImageIO.write((RenderedImage)image,"PNG",new // // File("D:/temp.jpg")); // // String SavePath = getSDCardPath() + "/ScreenImage"; // File path = new File(SavePath); // String filepath = SavePath + "/Screen_1.jpg"; // File file = new File(filepath); // if (!path.exists()) { // path.mkdirs(); // } // if (!file.exists()) { // file.createNewFile(); // } // ImageIO.write((RenderedImage) image, "PNG", file); // // Log.i("LW", "截屏文件已保存至SDCard/ScreenImage/下"); // } // // } catch (TimeoutException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } catch (AdbCommandRejectedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } }, 5000); return super.onStartCommand(intent, flags, startId); } private static void waitDeviceList(AndroidDebugBridge bridge) { int count = 0; while (bridge.hasInitialDeviceList() == false) { try { Thread.sleep(100); // 如果没有获得设备列表,则等待 count++; } catch (InterruptedException e) { } if (count > 300) { // 设定时间超过300×100 ms的时候为连接超时 System.err.print("Time out"); break; } } } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); } public void GetandSaveCurrentImage() { WindowManager widowManager = (WindowManager) getSystemService(getApplicationContext().WINDOW_SERVICE); Display display = widowManager.getDefaultDisplay(); int w = display.getWidth(); int h = display.getHeight(); Bitmap Bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888); View decorview =GetApplication.getMain().getWindow().getDecorView(); decorview.setDrawingCacheEnabled(true); Bmp = decorview.getDrawingCache(); try { String SavePath = getSDCardPath() + "/ScreenImage"; File path = new File(SavePath); String filepath = SavePath + "/Screen_1.jpg"; File file = new File(filepath); if (!path.exists()) { path.mkdirs(); } if (!file.exists()) { file.createNewFile(); } FileOutputStream fos = null; fos = new FileOutputStream(file); if (null != fos) { Bmp.compress(Bitmap.CompressFormat.PNG, 90, fos); fos.flush(); fos.close(); Log.i("LW", "截屏文件已保存至SDCard/ScreenImage/下"); } } catch (Exception e) { e.printStackTrace(); } } private String getSDCardPath() { File sdCardDir = null; boolean sdcardExit = Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED); if (sdcardExit) { sdCardDir = Environment.getExternalStorageDirectory(); } return sdCardDir.toString(); } }
最后这个只是aoolication:
package com.lhl.application; import com.lhl.startservice.MainActivity; import android.app.Application; /** * * 用来锁定当前的application * * @author Catherine * */ public class GetApplication extends Application { private static MainActivity main; public static MainActivity getMain() { return main; } public static void setMain(MainActivity main) { GetApplication.main = main; } }
这里给出链接,也可以直接下载源码,或者在我的文件里面也有下载。
http://pan.baidu.com/s/1pJ7H1Nl