FPS is one of the main indicators of graphic performance. Some Android applications have the function of displaying FPS, such as Bsplayer, Skype, and Antutu. However, most applications do not provide the function of displaying FPS. In addition, applications often provide the update rate of applications, which is not equal to the update rate seen by end users, because more than one application is displayed on the screen. We know that every app in Android will draw its own Surface, and it will be thrown to Surfaceflinger after it is finished. Surfaceflinger will uniformly composition them and then output swap framebuffer to the screen. The so injection and hook techniques (http://blog.csdn.net/ariesjzj/article/details/9900105) in Android are introduced in the previous article, and the eglSwapBuffers function in Surfaceflinger is demonstrated in the example. Naturally, we can use it to calculate the current FPS and display it on the screen in real time. There is an FPS real-time display tool-FPS meter in ARM, but it is billed. In this article, we made a free tool with similar functions, and it is common to x86 and ARM platforms. The entire application is divided into several parts: the native program used for so injection and the dynamic link library to be injected. This part is the main content involved in the previous article (http://blog.csdn.net/ariesjzj/article/details/9900105), not to mention. The basic idea is to define your own eglSwapBuffers function in the so to be injected, and then replace it with the old eglSwapBuffers function address in the got table during initialization. In this way, when Surfaceflinger needs to refresh the screen, it will first call the eglSwapBuffers function we defined. When this function is called, it will record and count the number of calls, write a specialized pipe file and call the eglSwapBuffers of the system. On the Application end, we need the following parts: an Activity is used to display interface interaction with users, and a Service is used to read FPS information from pipe and display it on the screen in real time, finally, a native program is used to complete so injection at service startup. The general process is as follows: when the Activity starts, copy the native program used for injection and so to the private directory of the application based on the platform ABI: 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 ");} els E {Log. e (TAG, "ABI not supported \ n"); Toast. makeText (this, "ABI not supported", Toast. LENGTH_LONG ). show () ;}} else {Log. d (TAG, "Already copied \ n");} Then wait for the user to start 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 Startup The onCreate () function for initialization, including creating pipe, displaying floating text, and executing injection: // Create the pipe file for processing ing data createPipe (); // Create floating textview to display FPS createLayout (); // Inject and hook ArrayList <String> list = new ArrayList <String> (); list. add (& quot; chmod 775 & quot; + APP_PATH + & quot; inject & quot;); list. add (& quot; chmod 666 & quot; + APP_PATH + & quot; pipe & quot;); list. add ("chmod 775" + APP_PATH + "libfpsshow. so "); list. add (APP_PATH + "injec T "); // 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 ();} The execution injection and hook programs require the root permission. Therefore, use: public final boolean execute (ArrayList <String> commands ){... process suProcess = runtime.getruntime(cmd.exe c ("su"); DataOutputStream OS = new DataOutputS Tream (suProcess. getOutputStream (); for (String currCommand: commands) {OS. writeBytes (currCommand + "\ n"); OS. flush ();} OS. writeBytes ("exit \ n"); OS. flush (); int suProcessRetval = suProcess. waitFor ();...} adding floating text is actually adding Layout: windowManager = (WindowManager) getApplicationContext (). getSystemService ("window"); layoutParams = new WindowManager. layoutParams (); layoutParams. type = WindowManager. layou TParams. 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); onStartCommand () will be called when the Service is started, and the thread will be started: @ 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. postDel Ayed (myTasks, 1000); return Service. START_STICKY;} the thread executes the run () function every second. This function reads the FPS sent from Surfaceflinger from pipe and then displays it on the screen. 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 );}};