实现Android下的FPS实时显示工具

标签: android fps 实时 | 发表时间:2013-08-30 12:16 | 作者:ariesjzj
出处:http://blog.csdn.net

原文地址:http://blog.csdn.net/ariesjzj/article/details/10428435

FPS是图形性能的主要指标之一,Android中的一些应用有显示FPS的功能,如Bsplayer,Skype,Antutu等,但绝大多数应用并不提供显示FPS的功能。而且应用提供的往往是应用本身的刷新率,并不等于最终用户所看到的刷新率,因为屏幕上往往不止一个应用参与显示。我们知道Android中每个应用都会绘制自己的Surface,完了都丢给Surfaceflinger,Surfaceflinger统一对它们进行composition,然后swap framebuffer输出到屏幕。前文介绍了Android中的so注入和hook技巧(http://blog.csdn.net/ariesjzj/article/details/9900105),示例了如何动态hook系统中的Surfaceflinger中的eglSwapBuffers函数。那么很自然的,我们就可以通过它来计算当前的FPS,并实时显示在屏幕上。


ARM下有FPS实时显示工具-FPS meter,不过要收费。本文中我们自己做一个功能类似的免费工具,而且x86和ARM平台通用。整个应用分为几个部分,首先是用于so注入的native程序和要注入的动态链接库。这部分是前文(http://blog.csdn.net/ariesjzj/article/details/9900105)中主要涉及的内容,不再累述。基本思想是在要注入的so中定义自己的eglSwapBuffers函数,然后在初始化时将之替换got表中老的eglSwapBuffers函数地址。这样,当Surfaceflinger需要刷新屏幕时,就会先调用我们定义的eglSwapBuffers函数,在这个函数被调用时,它会记录和统计调用次数,并写入一个专用的pipe文件,然后调用系统自己的eglSwapBuffers。在应用端我们需要以下几个部分:一个Activity用于显示界面与用户交互,一个Service用于主要工作,即从pipe读取FPS信息并且实时显示在屏幕上,最后是一个native的程序,用于在service启动时完成so的注入。


大体流程如下:

Activity启动时根据平台ABI将相应版本的用于注入的native程序和要注入的so拷贝到应用私有目录:

File file_inject = new File(APP_PATH + "inject");
File file_lib = new File(APP_PATH + "libfpsshow.so");
if (!file_inject.exists() || !file_lib.exists()) {
	String sysabi = getSystemProp("ro.product.cpu.abi");
	Log.e(TAG, "System ABI is: " + sysabi + "\n");
	if (sysabi.startsWith("armeabi")) {
		copyFile("inject_arm", APP_PATH + "inject");
		copyFile("libfpsshow_arm.so", APP_PATH + "libfpsshow.so");
	} else if (sysabi.startsWith("x86")) {
		copyFile("inject", APP_PATH + "inject");
		copyFile("libfpsshow.so", APP_PATH + "libfpsshow.so");
	} else {
		Log.e(TAG, "ABI not supported\n");
		Toast.makeText(this, "ABI not supported", Toast.LENGTH_LONG).show();
	}
} else {
	Log.d(TAG, "Already copied\n");
}
然后等待用户启动service:
		case R.id.buttonStart:
			Log.d(TAG, "starting service");
			startService(new Intent(this, FPSService.class));
			break;
		case R.id.buttonStop:
			Log.d(TAG, "stopping service");
			stopService(new Intent(this, FPSService.class));
			break;
Service启动时的onCreate()函数,做一坨初始化工作,包括创建pipe,显示悬浮文字,执行注入等:
// Create the pipe file for receiving data
createPipe();
// Create floating textview to display FPS
createLayout();

// Inject and hook
ArrayList<String> list = new ArrayList<String>();
list.add("chmod 775 " + APP_PATH + "inject");
list.add("chmod 666 " + APP_PATH + "pipe");
list.add("chmod 775 " + APP_PATH + "libfpsshow.so");
list.add(APP_PATH + "inject");

// Execute as root
if (execute(list)) {
	Log.e(TAG, "OK\n");
} else {
	Toast.makeText(this, "Execute abnormally, please make sure it's rooted.", Toast.LENGTH_LONG).show();
	Log.e(TAG, "Error\n");
	this.stopSelf();
}
其中的执行注入和hook程序是要root权限的,所以要通过:
public final boolean execute(ArrayList<String> commands) {
	...
	Process suProcess = Runtime.getRuntime().exec("su");
	DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());
	for (String currCommand : commands) {
		os.writeBytes(currCommand + "\n");
		os.flush();
	}
	os.writeBytes("exit\n");
	os.flush();
	int suProcessRetval = suProcess.waitFor();
	...
}
加悬浮文字,其实就是加个Layout:
windowManager = (WindowManager) getApplicationContext().getSystemService("window");  
layoutParams = new WindowManager.LayoutParams();  
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;  
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;  
layoutParams.format = PixelFormat.RGBA_8888;  
layoutParams.gravity = Gravity.TOP | Gravity.CENTER;  
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;  
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;  
layoutParams.x = 0;  
layoutParams.y = 0;  
// myLayout is the customized layout which contains textview
myLayout = new MyLayout(this);
windowManager.addView(myLayout, layoutParams);  

Service启动时onStartCommand()会被调用,其中会启动线程:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	Log.v(TAG, "onStartCommand");
	Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
	handleCommand(intent);
	myhandler.postDelayed(myTasks, 1000);
	return Service.START_STICKY;
}
线程每一秒执行一次run()函数,该函数从pipe读Surfaceflinger传来的FPS,然后显示在屏幕上。
private Runnable myTasks = new Runnable() {

    @Override
	public void run() {	
		int fps = readFps();
		Log.e(TAG, "Service FPS = " + fps + "\n");
	
		myLayout.setFPS(fps);
		
		// Do other customized computation.
		...
		
		myLayout.setFPSAvg(fps_avg);
		myhandler.postDelayed(myTasks, 1000);
	}
};

实现的时候还有些细节需要考虑,比如当启动多个应用后,后台非关键Service会被Android杀掉。为了解决这个问题,可以仿照Android官方例子中ApiDemo中的ForegroundService例子将Service设为前台进程,这样就不容易被杀了。另外在屏幕显示FPS的话,如果一秒更新一次,那显示FPS本身也会影响FPS(尽管只有一帧),如果觉得有影响可以把它关掉,让FPS只输出到log。

效果截图:



作者:ariesjzj 发表于2013-8-30 9:22:14 原文链接
阅读:56 评论:0 查看评论

相关 [android fps 实时] 推荐:

实现Android下的FPS实时显示工具

- - CSDN博客移动开发推荐文章
原文地址:http://blog.csdn.net/ariesjzj/article/details/10428435. FPS是图形性能的主要指标之一,Android中的一些应用有显示FPS的功能,如Bsplayer,Skype,Antutu等,但绝大多数应用并不提供显示FPS的功能. 而且应用提供的往往是应用本身的刷新率,并不等于最终用户所看到的刷新率,因为屏幕上往往不止一个应用参与显示.

开源FPS游戏Xonotic发布0.5版

- SotongDJ - Solidot
去年,基于Quake引擎开发的FPS游戏Nexuiz的创始人将名称、域名和引擎都授权给了一家商业公司开发闭源游戏,此举导致了社区分裂,社区开发者创建了Nexuiz分支Xonotic,开发新的开源射击游戏. 现在Xonotic开发者团队宣布发布了0.5版. 主要新特性包括:支持多种语言,加入7张新地图,支持车辆,重新考虑武器平衡,优化后处理特效,新的材质包,改进机器人的智能,增加新游戏模式和修改旧模式,修正bug,等等.

UberStrike HD – 画面精致 FPS [Mac] | 小众软件 > Mac

- Guan - 小众软件
刘傅二狗一直对 FPS 游戏有生理上的反感,每次见到画面晃来晃去忍不住头晕眼花. UberStrike HD 是 Mac 上为数不多让人头晕烟花来得更猛烈的射击游戏. 装备丰富,只不过好装备是要花钱的. 下载: MAS | 来自小众软件. ©2011 Thruth for 小众软件 | 原文链接 | 5 留言 | 加入我们 | 投稿 | 订阅指南.

kkrieger – 世界上最小的单人 FPS 游戏 | 小众软件 > 游戏

- Esperisto - 小众软件
你觉得一个最小的 3D 单人游戏 FPS 会有多大. 告诉你,通通都错了,答案是:95 K. 游戏操作很简单,也是标准的 FPS 的键位设置,W、A、S、D控制方向,空格跳跃,鼠标左键射击,1-5切换武器. 这个游戏是由德国一家名为 “.the .produkkt ” 的小组完成的,采用了“过程生成”技术,因此别看他小,对电脑硬件配置要求可很高,甚至用来作为进行性能测试的工具也是可以的.

Google Sky 开源项目:不使用 JAVA 开发 120 FPS 的安卓应用

- - SegmentFault 最新的文章
作为移动操作系统市场占有率最高的系统,Android 经常因不流畅和卡顿而遭人诟病. 甚至很多Android 应用都达不到称得上“流畅”的 60 FPS. 不过,好消息是,最近 Google 内部正在悄悄地测试另一种高性能的编程方式,那就 Google 的 Sky 项目. 视频 Sky: An Experiment Writing Dart for Mobile 便是谷歌官方的演示视频.

军团要塞2 – 卡通版 FPS 对战大作宣布免费 | 小众软件 > 游戏

- 飞花涟翩 - 小众软件
又到了周五游戏推荐,这次介绍一款免费 FPS 大作《军团要塞2》,免费联网对战,消磨周末强烈推荐. 提到第一人称射击游戏,CS/半条命人尽皆知,而师出同门 Valve 公司的军团要塞2 (Team Fortress 2)在国内则鲜为人知. 军团要塞2 是卡通版改良版的 CS,开发过程曲折,历经 9 年完成,但确实属于里程碑式的精品.

实时监控Android设备网络封包

- - UC技术博客
对Android网络抓包分析,一般是使用tcpdump抓个文件,再到PC用Wireshark打开分析. 能不能达到直接使用Wireshark的效果. 答案是可以的,至少已经非常接近了. 实现起来很简单,原理就是将tcpdump的数据重定向到网络端口,再通过管道(pipe)转到wireshark就可以了.

Flickr 推出 Android App,头条新功能是「实时照片共同欣赏」

- Digitalboy(张扬) - Engadget 中国版
Flickr 新发表的第一个 Android app,基本上从拍照到后制到上传到分享,所有的功能都包办了. 在拍照的部份,它可以控制手机的闪光灯、快门、对焦、长宽比等,拍完后可以从十种滤镜中选择使用. 之后你可以将照片上传到 Facebook、Twitter 或(废话)Flickr,但真正有趣的功能是在分享的部份 -- 这个名为 Photo Session 的功能,让你抓一把在在线的好友,然后可以实时地播放你的照片给他们看.

Canon 极限新旗舰 EOS-1D X 现身!全幅 1,800 万像素、14 fps、高感光达 ISO 204,800,售价 US$6,800

- Marvin - Engadget 中国版
众所猜测已久的 Canon EOS-1D 专业机种终于发表. 这台众所期待的高端机种外观依旧与历代高端机种几乎完全相同,但相信在转开这台最新旗舰机种的电源旋钮时,应该可以确切地感受到这台最新旗舰机种的全新内在. 它具备 1,810 万像素的全幅感光组件,暂时停止全幅等级的像素竞争,期望用更低的像素密度搭配全新的双 Digic 5+ 图像处理器,达到更高水平的画质表现与噪声抑制能力.

Canon 極限新旗艦 EOS-1D X 現身!全幅 1,800 萬畫素、14 fps、高感光達 ISO 204,800,售價 US$6,800

- 小趴 八足趴 八足 ramener - Engadget 中文版
眾所猜測已久的 Canon EOS-1D 專業機種終於發表. 這台眾所期待的高階機種外觀依舊與歷代高階機種幾乎完全相同,但相信在轉開這台最新旗艦機種的電源旋鈕時,應該可以確切地感受到這台最新旗艦機種的全新內在. 它具備 1,810 萬畫素的全幅感光元件,暫時停止全幅等級的畫素競爭,期望用更低的像素密度搭配全新的雙 Digic 5+ 影像處理器,達到更高水準的畫質表現與雜訊抑制能力.