實現Android下的FPS即時顯示工具

來源:互聯網
上載者:User

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);      }  };   

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.