├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── kotlinc.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── yang │ │ └── noobcoach │ │ ├── App.java │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── coach ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── yang │ │ └── coach │ │ ├── CoachDataListener.java │ │ ├── CoachTouchListener.java │ │ ├── CoachView.java │ │ ├── ICoach.java │ │ ├── NoobCoach.java │ │ ├── cpu │ │ ├── CpuCoach.java │ │ └── CpuUtils.java │ │ ├── fps │ │ ├── Calculation.java │ │ ├── FPSCoach.java │ │ ├── FPSConfig.java │ │ ├── FPSFrameCallback.java │ │ └── FPSResult.java │ │ ├── memory │ │ ├── MemCoach.java │ │ └── MemUtils.java │ │ └── utils │ │ ├── DoubleUtils.java │ │ └── FileUtil.java │ └── res │ ├── layout │ └── view_meter.xml │ └── values │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Groovy 46 | 47 | 48 | Java 49 | 50 | 51 | Potentially confusing code constructsGroovy 52 | 53 | 54 | Threading issuesJava 55 | 56 | 57 | 58 | 59 | Android 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 81 | 82 | 84 | 85 | 86 | 87 | 88 | 1.8 89 | 90 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NoobCoach 2 | 3 | ## 简介 4 | 轻量级的Android性能监控工具 支持fps cpu 内存监控 5 | 6 | ### 初始化 7 | ```java 8 | public class App extends Application { 9 | 10 | @Override 11 | public void onCreate() { 12 | super.onCreate(); 13 | NoobCoach.startDefaultMeter(this); 14 | } 15 | } 16 | ``` 17 | 18 | ## 备注 19 | 注意权限添加: 20 | ```xml 21 | 22 | ``` 23 | fps监控规则:
24 | 绿:掉帧率<5%
25 | 黄:掉帧率5%-20%
26 | 红:掉帧率>20% -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | defaultConfig { 7 | applicationId "yang.noobcoach" 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile project(':coach') 25 | } 26 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/yangtian/Downloads/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/yang/noobcoach/App.java: -------------------------------------------------------------------------------- 1 | package yang.noobcoach; 2 | 3 | import android.app.Application; 4 | 5 | 6 | import yang.coach.NoobCoach; 7 | 8 | /** 9 | * author: Matthew Yang on 17/8/21 10 | * e-mail: yangtian@yy.com 11 | */ 12 | 13 | public class App extends Application { 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | NoobCoach.startDefaultMeter(this); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/yang/noobcoach/MainActivity.java: -------------------------------------------------------------------------------- 1 | package yang.noobcoach; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class MainActivity extends Activity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_main); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NoobCoach 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /coach/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /coach/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(dir: 'libs', include: ['*.jar']) 26 | } 27 | -------------------------------------------------------------------------------- /coach/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/yangtian/Downloads/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /coach/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/CoachDataListener.java: -------------------------------------------------------------------------------- 1 | package yang.coach; 2 | 3 | 4 | import yang.coach.fps.FPSResult; 5 | 6 | /** 7 | * author: Matthew Yang on 17/8/16 8 | * e-mail: yangtian@yy.com 9 | */ 10 | 11 | public interface CoachDataListener { 12 | void onShowFPS(FPSResult fps); 13 | void onShowCPU(String usage); 14 | void onShowPss(long pss); 15 | void onShowPrivateDirty(long privateDirty); 16 | } 17 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/CoachTouchListener.java: -------------------------------------------------------------------------------- 1 | package yang.coach; 2 | 3 | import android.view.MotionEvent; 4 | import android.view.View; 5 | import android.view.WindowManager; 6 | 7 | class CoachTouchListener implements View.OnTouchListener { 8 | 9 | private int initialX; 10 | private int initialY; 11 | private float initialTouchX; 12 | private float initialTouchY; 13 | 14 | private WindowManager.LayoutParams paramsF; 15 | private WindowManager windowManager; 16 | 17 | CoachTouchListener(WindowManager.LayoutParams paramsF, 18 | WindowManager windowManager) { 19 | this.windowManager = windowManager; 20 | this.paramsF = paramsF; 21 | } 22 | 23 | @Override 24 | public boolean onTouch(View v, MotionEvent event) { 25 | switch (event.getAction()) { 26 | case MotionEvent.ACTION_DOWN: 27 | initialX = paramsF.x; 28 | initialY = paramsF.y; 29 | initialTouchX = event.getRawX(); 30 | initialTouchY = event.getRawY(); 31 | break; 32 | case MotionEvent.ACTION_UP: 33 | break; 34 | case MotionEvent.ACTION_MOVE: 35 | paramsF.x = initialX + (int) (event.getRawX() - initialTouchX); 36 | paramsF.y = initialY + (int) (event.getRawY() - initialTouchY); 37 | windowManager.updateViewLayout(v, paramsF); 38 | break; 39 | } 40 | return false; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/CoachView.java: -------------------------------------------------------------------------------- 1 | package yang.coach; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.graphics.PixelFormat; 7 | import android.view.Gravity; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.view.WindowManager; 12 | import android.widget.TextView; 13 | 14 | import yang.coach.fps.FPSResult; 15 | 16 | 17 | class CoachView implements CoachDataListener { 18 | 19 | private TextView fpsText, cpuText, pssText, privateDirtyText; 20 | private final WindowManager windowManager; 21 | 22 | CoachView(Context context) { 23 | View meterView = LayoutInflater.from(context).inflate(R.layout.view_meter, null); 24 | fpsText = (TextView) meterView.findViewById(R.id.fps); 25 | onShowFPS(new FPSResult(60, FPSResult.Metric.GOOD)); 26 | cpuText = (TextView) meterView.findViewById(R.id.cpu); 27 | onShowCPU("0%"); 28 | pssText = (TextView) meterView.findViewById(R.id.mem_pss); 29 | onShowPss(0); 30 | privateDirtyText = (TextView) meterView.findViewById(R.id.mem_private_dirty); 31 | onShowPrivateDirty(0); 32 | // grab window manager and add view to the window 33 | windowManager = (WindowManager) meterView.getContext().getSystemService(Service.WINDOW_SERVICE); 34 | addViewToWindow(meterView); 35 | } 36 | 37 | 38 | private void addViewToWindow(View view) { 39 | WindowManager.LayoutParams paramsF = new WindowManager.LayoutParams( 40 | ViewGroup.LayoutParams.WRAP_CONTENT, 41 | ViewGroup.LayoutParams.WRAP_CONTENT, 42 | WindowManager.LayoutParams.TYPE_PHONE, 43 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 44 | PixelFormat.TRANSLUCENT); 45 | // configure starting coordinates 46 | paramsF.x = 0; 47 | paramsF.y = 0; 48 | paramsF.gravity = Gravity.TOP | Gravity.START; 49 | // add view to the window 50 | windowManager.addView(view, paramsF); 51 | // attach touch listener 52 | view.setOnTouchListener(new CoachTouchListener(paramsF, windowManager)); 53 | // disable haptic feedback 54 | view.setHapticFeedbackEnabled(false); 55 | } 56 | 57 | @Override 58 | public void onShowFPS(FPSResult fps) { 59 | switch (fps.metric) { 60 | case GOOD: fpsText.setTextColor(Color.GREEN); break; 61 | case MEDIUM: fpsText.setTextColor(Color.YELLOW); break; 62 | case BAD: fpsText.setTextColor(Color.RED); break; 63 | default: break; 64 | } 65 | fpsText.setText(String.format("FPS : %d", fps.value)); 66 | } 67 | 68 | 69 | @Override 70 | public void onShowCPU(final String usage) { 71 | cpuText.post(new Runnable() { 72 | @Override 73 | public void run() { 74 | cpuText.setText(String.format("CPU : %s", usage)); 75 | } 76 | }); 77 | } 78 | 79 | @Override 80 | public void onShowPss(final long pss) { 81 | pssText.post(new Runnable() { 82 | @Override 83 | public void run() { 84 | pssText.setText(String.format("Pss : %dm", pss/1024)); 85 | } 86 | }); 87 | } 88 | 89 | @Override 90 | public void onShowPrivateDirty(final long privateDirty) { 91 | privateDirtyText.post(new Runnable() { 92 | @Override 93 | public void run() { 94 | privateDirtyText.setText(String.format("PrivateDirty : %dm", privateDirty/1024)); 95 | } 96 | }); 97 | } 98 | } -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/ICoach.java: -------------------------------------------------------------------------------- 1 | package yang.coach; 2 | 3 | /** 4 | * author: Matthew Yang on 17/8/18 5 | * e-mail: yangtian@yy.com 6 | */ 7 | 8 | public interface ICoach { 9 | long sampleTimeInMs = 1000; //取样间隔 单位ms 10 | void start(); 11 | } 12 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/NoobCoach.java: -------------------------------------------------------------------------------- 1 | package yang.coach; 2 | 3 | import android.content.Context; 4 | 5 | import yang.coach.cpu.CpuCoach; 6 | import yang.coach.fps.FPSCoach; 7 | import yang.coach.memory.MemCoach; 8 | 9 | 10 | /** 11 | * author: Matthew Yang on 17/8/18 12 | * e-mail: yangtian@yy.com 13 | */ 14 | 15 | public class NoobCoach { 16 | 17 | public static void startDefaultMeter(Context context) { 18 | CoachView coachView = new CoachView(context); 19 | FPSCoach fpsCoach = new FPSCoach(); 20 | CpuCoach cpuCoach = new CpuCoach(); 21 | MemCoach memCoach = new MemCoach(context); 22 | fpsCoach.setCoachDataListener(coachView); 23 | cpuCoach.setCoachListener(coachView); 24 | memCoach.setCoachListener(coachView); 25 | fpsCoach.start(); 26 | cpuCoach.start(); 27 | memCoach.start(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/cpu/CpuCoach.java: -------------------------------------------------------------------------------- 1 | package yang.coach.cpu; 2 | 3 | import android.os.Process; 4 | 5 | import yang.coach.CoachDataListener; 6 | import yang.coach.ICoach; 7 | import yang.coach.utils.DoubleUtils; 8 | 9 | 10 | /** 11 | * author: Matthew Yang on 17/8/18 12 | * e-mail: yangtian@yy.com 13 | */ 14 | 15 | public class CpuCoach implements ICoach { 16 | 17 | public static final String TAG = "CpuCoach"; 18 | 19 | private double pCpu = 0.0; 20 | private double aCpu = 0.0; 21 | private double o_pCpu = 0.0; 22 | private double o_aCpu = 0.0; 23 | private boolean enabled = false; 24 | private int currentPid; 25 | private CoachDataListener coachDataListener; 26 | 27 | Runnable cpuProcessTask = new Runnable() { 28 | @Override 29 | public void run() { 30 | while (enabled) { 31 | String usage = getProcessCpuUsage(currentPid); 32 | if (coachDataListener != null) { 33 | coachDataListener.onShowCPU(usage); 34 | } 35 | try { 36 | Thread.sleep(ICoach.sampleTimeInMs); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | }; 43 | 44 | public void setCoachListener(CoachDataListener coachListener) { 45 | this.coachDataListener = coachListener; 46 | } 47 | 48 | @Override 49 | public void start() { 50 | if (!enabled) { 51 | currentPid = Process.myPid(); 52 | new Thread(cpuProcessTask).start(); 53 | enabled = true; 54 | } 55 | } 56 | 57 | 58 | public String getProcessCpuUsage(int pid) { 59 | String result = ""; 60 | String[] result1 = null; 61 | String[] result2 = null; 62 | if (pid >= 0) { 63 | 64 | result1 = CpuUtils.getProcessCpuAction(pid); 65 | if (null != result1) { 66 | pCpu = Double.parseDouble(result1[1]) 67 | + Double.parseDouble(result1[2]); 68 | } 69 | result2 = CpuUtils.getCpuAction(); 70 | if (null != result2) { 71 | aCpu = 0.0; 72 | for (int i = 2; i < result2.length; i++) { 73 | 74 | aCpu += Double.parseDouble(result2[i]); 75 | } 76 | } 77 | double usage = 0.0; 78 | if ((aCpu - o_aCpu) != 0) { 79 | usage = DoubleUtils.div(((pCpu - o_pCpu) * 100.00), 80 | (aCpu - o_aCpu), 2); 81 | if (usage < 0) { 82 | usage = 0; 83 | } else if (usage > 100) { 84 | usage = 100; 85 | } 86 | 87 | } 88 | o_pCpu = pCpu; 89 | o_aCpu = aCpu; 90 | result = String.valueOf(usage) + "%"; 91 | } 92 | return result; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/cpu/CpuUtils.java: -------------------------------------------------------------------------------- 1 | package yang.coach.cpu; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileReader; 7 | import java.io.IOException; 8 | 9 | import yang.coach.utils.FileUtil; 10 | 11 | 12 | /** 13 | * CPU相关工具类。 14 | */ 15 | class CpuUtils { 16 | 17 | static String[] getProcessCpuAction(int pid) { 18 | String cpuPath = "/proc/" + pid + "/stat"; 19 | String cpu = ""; 20 | String[] result = new String[3]; 21 | 22 | File f = new File(cpuPath); 23 | if (!f.exists() || !f.canRead()) { 24 | /* 25 | * 进程信息可能无法读取, 26 | * 同时发现此类进程的PSS信息也是无法获取的,用PS命令会发现此类进程的PPid是1, 27 | * 即/init,而其他进程的PPid是zygote, 28 | * 说明此类进程是直接new出来的,不是Android系统维护的 29 | */ 30 | return result; 31 | } 32 | 33 | FileReader fr = null; 34 | BufferedReader localBufferedReader = null; 35 | 36 | try { 37 | fr = new FileReader(f); 38 | localBufferedReader = new BufferedReader(fr, 8192); 39 | cpu = localBufferedReader.readLine(); 40 | if (null != cpu) { 41 | String[] cpuSplit = cpu.split(" "); 42 | result[0] = cpuSplit[1]; 43 | result[1] = cpuSplit[13]; 44 | result[2] = cpuSplit[14]; 45 | } 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | FileUtil.closeReader(localBufferedReader); 50 | return result; 51 | } 52 | 53 | static String[] getCpuAction() { 54 | String cpuPath = "/proc/stat"; 55 | String cpu = ""; 56 | String[] result = new String[7]; 57 | 58 | File f = new File(cpuPath); 59 | if (!f.exists() || !f.canRead()) { 60 | return result; 61 | } 62 | 63 | FileReader fr = null; 64 | BufferedReader localBufferedReader = null; 65 | 66 | try { 67 | fr = new FileReader(f); 68 | localBufferedReader = new BufferedReader(fr, 8192); 69 | cpu = localBufferedReader.readLine(); 70 | if (null != cpu) { 71 | result = cpu.split(" "); 72 | 73 | } 74 | } catch (FileNotFoundException e) { 75 | e.printStackTrace(); 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | } 79 | FileUtil.closeReader(localBufferedReader); 80 | return result; 81 | } 82 | } -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/fps/Calculation.java: -------------------------------------------------------------------------------- 1 | package yang.coach.fps; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class Calculation { 8 | 9 | public static List getDroppedSet(FPSConfig fpsConfig, List dataSet) { 10 | List droppedSet = new ArrayList<>(); 11 | long start = -1; 12 | for (Long value : dataSet) { 13 | if (start == -1) { 14 | start = value; 15 | continue; 16 | } 17 | 18 | int droppedCount = droppedCount(start, value, fpsConfig.deviceRefreshRateInMs); 19 | if (droppedCount > 0) { 20 | droppedSet.add(droppedCount); 21 | } 22 | start = value; 23 | } 24 | return droppedSet; 25 | } 26 | 27 | /** 28 | * 计算丢帧数 29 | * 30 | * @param start 31 | * @param end 32 | * @param devRefreshRate 33 | * @return 34 | */ 35 | public static int droppedCount(long start, long end, float devRefreshRate) { 36 | int count = 0; 37 | long diffNs = end - start; 38 | 39 | long diffMs = TimeUnit.MILLISECONDS.convert(diffNs, TimeUnit.NANOSECONDS); 40 | long dev = Math.round(devRefreshRate); 41 | if (diffMs > dev) { 42 | long droppedCount = (diffMs / dev); 43 | count = (int) droppedCount; 44 | } 45 | 46 | return count; 47 | } 48 | 49 | public static FPSResult calculateMetric(FPSConfig fpsConfig, 50 | List dataSet, 51 | List droppedSet) { 52 | long timeInNS = dataSet.get(dataSet.size() - 1) - dataSet.get(0); 53 | long size = getNumberOfFramesInSet(timeInNS, fpsConfig); 54 | 55 | //metric 56 | int runningOver = 0; 57 | // total dropped 58 | int dropped = 0; 59 | 60 | for (Integer k : droppedSet) { 61 | dropped += k; 62 | if (k >= 2) { 63 | runningOver += k; 64 | } 65 | } 66 | 67 | float multiplier = fpsConfig.refreshRate / size; 68 | float answer = multiplier * (size - dropped); 69 | long realAnswer = Math.round(answer); 70 | 71 | // calculate metric 72 | float percentOver = (float) runningOver / (float) size; 73 | FPSResult.Metric metric = FPSResult.Metric.GOOD; 74 | if (percentOver >= 0.2f) { 75 | metric = FPSResult.Metric.BAD; 76 | } else if (percentOver >= 0.05f) { 77 | metric = FPSResult.Metric.MEDIUM; 78 | } 79 | 80 | return new FPSResult(realAnswer, metric); 81 | } 82 | 83 | protected static long getNumberOfFramesInSet(long realSampleLengthNs, FPSConfig fpsConfig) { 84 | float realSampleLengthMs = TimeUnit.MILLISECONDS.convert(realSampleLengthNs, TimeUnit.NANOSECONDS); 85 | float size = realSampleLengthMs / fpsConfig.deviceRefreshRateInMs; 86 | return Math.round(size); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/fps/FPSCoach.java: -------------------------------------------------------------------------------- 1 | package yang.coach.fps; 2 | 3 | 4 | import android.view.Choreographer; 5 | 6 | import yang.coach.CoachDataListener; 7 | import yang.coach.ICoach; 8 | 9 | 10 | public class FPSCoach implements ICoach { 11 | 12 | public static final String TAG = "FPSCoach"; 13 | 14 | private FPSConfig fpsConfig; 15 | private FPSFrameCallback frameCallback; 16 | 17 | public FPSCoach() { 18 | fpsConfig = new FPSConfig(); 19 | frameCallback = new FPSFrameCallback(fpsConfig); 20 | } 21 | 22 | public void setCoachDataListener(CoachDataListener coachListener) { 23 | frameCallback.setCoachDataListener(coachListener); 24 | } 25 | 26 | @Override 27 | public void start() { 28 | Choreographer.getInstance().postFrameCallback(frameCallback); 29 | } 30 | } -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/fps/FPSConfig.java: -------------------------------------------------------------------------------- 1 | package yang.coach.fps; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import yang.coach.ICoach; 6 | 7 | 8 | /** 9 | * author: Matthew Yang on 17/8/16 10 | * e-mail: yangtian@yy.com 11 | */ 12 | 13 | class FPSConfig { 14 | 15 | float refreshRate = 60; //60fps 16 | float deviceRefreshRateInMs = 16.6f; //设备刷新速率 单位ms 17 | 18 | 19 | FPSConfig() { 20 | 21 | } 22 | 23 | long getSampleTimeInNs() { 24 | return TimeUnit.NANOSECONDS.convert(ICoach.sampleTimeInMs, TimeUnit.MILLISECONDS); 25 | } 26 | 27 | long getDeviceRefreshRateInNs() { 28 | float value = deviceRefreshRateInMs * 1000000f; 29 | return (long) value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/fps/FPSFrameCallback.java: -------------------------------------------------------------------------------- 1 | package yang.coach.fps; 2 | 3 | import android.view.Choreographer; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import yang.coach.CoachDataListener; 9 | 10 | 11 | /** 12 | * author: Matthew Yang on 17/8/18 13 | * e-mail: yangtian@yy.com 14 | */ 15 | 16 | class FPSFrameCallback implements Choreographer.FrameCallback { 17 | public static final String TAG = "FPSFrameCallback"; 18 | 19 | private FPSConfig fpsConfig; 20 | private List dataSet = new ArrayList<>(); //保存 一个样例时间的 frame times 21 | private boolean enabled = true; 22 | private long startSampleTimeInNs = 0; 23 | private CoachDataListener coachDataListener; 24 | 25 | FPSFrameCallback(FPSConfig fpsConfig) { 26 | this.fpsConfig = fpsConfig; 27 | } 28 | 29 | void setCoachDataListener(CoachDataListener coachListener) { 30 | this.coachDataListener = coachListener; 31 | } 32 | 33 | void setEnabled(boolean enabled) { 34 | this.enabled = enabled; 35 | } 36 | 37 | @Override 38 | public void doFrame(long frameTimeNanos) { 39 | if (!enabled) { 40 | destroy(); 41 | return; 42 | } 43 | 44 | if (startSampleTimeInNs == 0) { 45 | startSampleTimeInNs = frameTimeNanos; 46 | } 47 | //frame time in new list 48 | if (isFinishedWithSample(frameTimeNanos)) { 49 | collectSampleAndSend(frameTimeNanos); 50 | } 51 | 52 | // add current frame time to our list 53 | dataSet.add(frameTimeNanos); 54 | 55 | //we need to register for the next frame callback 56 | Choreographer.getInstance().postFrameCallback(this); 57 | } 58 | 59 | private void collectSampleAndSend(long frameTimeNanos) { 60 | //this occurs only when we have gathered over the sample time ~700ms 61 | List dataSetCopy = new ArrayList<>(); 62 | dataSetCopy.addAll(dataSet); 63 | 64 | List droppedSet = Calculation.getDroppedSet(fpsConfig, dataSetCopy); 65 | 66 | if(coachDataListener != null) { 67 | coachDataListener.onShowFPS(Calculation.calculateMetric(fpsConfig, dataSetCopy, droppedSet)); 68 | } 69 | 70 | // clear data 71 | dataSet.clear(); 72 | 73 | //reset sample timer to last frame 74 | startSampleTimeInNs = frameTimeNanos; 75 | } 76 | 77 | /** 78 | * 完成一个样例收集 时长 > 样例设置时长 79 | * @param frameTimeNanos 80 | * @return 81 | */ 82 | private boolean isFinishedWithSample(long frameTimeNanos) { 83 | return frameTimeNanos - startSampleTimeInNs > fpsConfig.getSampleTimeInNs(); 84 | } 85 | 86 | void destroy() { 87 | dataSet.clear(); 88 | startSampleTimeInNs = 0; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/fps/FPSResult.java: -------------------------------------------------------------------------------- 1 | package yang.coach.fps; 2 | 3 | /** 4 | * author: Matthew Yang on 17/8/18 5 | * e-mail: yangtian@yy.com 6 | */ 7 | 8 | public class FPSResult { 9 | public enum Metric {GOOD, BAD, MEDIUM} 10 | 11 | public long value; 12 | public Metric metric; 13 | 14 | public FPSResult(long value, Metric metric) { 15 | this.value = value; 16 | this.metric = metric; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/memory/MemCoach.java: -------------------------------------------------------------------------------- 1 | package yang.coach.memory; 2 | 3 | import android.content.Context; 4 | import android.os.Process; 5 | 6 | import yang.coach.CoachDataListener; 7 | import yang.coach.ICoach; 8 | 9 | 10 | /** 11 | * author: Matthew Yang on 17/8/18 12 | * e-mail: yangtian@yy.com 13 | */ 14 | 15 | public class MemCoach implements ICoach { 16 | 17 | public static final String TAG = "MemCoach"; 18 | 19 | private boolean enabled = false; 20 | 21 | private CoachDataListener coachDataListener; 22 | private int currentPid; 23 | private Context context; 24 | 25 | Runnable memProcessTask = new Runnable() { 26 | @Override 27 | public void run() { 28 | while (enabled) { 29 | long totalPss = getTotalPss(); 30 | long totalPrivateDirty = getTotalPrivateDirty(); 31 | if (coachDataListener != null) { 32 | coachDataListener.onShowPss(totalPss); 33 | } 34 | if (coachDataListener != null) { 35 | coachDataListener.onShowPrivateDirty(totalPrivateDirty); 36 | } 37 | try { 38 | Thread.sleep(ICoach.sampleTimeInMs); 39 | } catch (InterruptedException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | } 44 | }; 45 | 46 | @Override 47 | public void start() { 48 | if (!enabled) { 49 | currentPid = Process.myPid(); 50 | new Thread(memProcessTask).start(); 51 | enabled = true; 52 | } 53 | } 54 | 55 | public MemCoach(Context context) { 56 | this.context = context; 57 | } 58 | 59 | public void setCoachListener(CoachDataListener coachListener) { 60 | this.coachDataListener = coachListener; 61 | } 62 | 63 | private long getTotalPss() { 64 | return MemUtils.getPSS(context, currentPid)[2]; 65 | } 66 | 67 | private long getTotalPrivateDirty() { 68 | return MemUtils.getPrivDirty(context, currentPid)[2]; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/memory/MemUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * Tencent GT (Version 2.4 and subsequent versions) available. 4 | * 5 | * Notwithstanding anything to the contrary herein, any previous version 6 | * of Tencent GT shall not be subject to the license hereunder. 7 | * All right, title, and interest, including all intellectual property rights, 8 | * in and to the previous version of Tencent GT (including any and all copies thereof) 9 | * shall be owned and retained by Tencent and subject to the license under the 10 | * Tencent GT End User License Agreement (http://gt.qq.com/wp-content/EULA_EN.html). 11 | * 12 | * Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved. 13 | * 14 | * Licensed under the MIT License (the "License"); you may not use this file 15 | * except in compliance with the License. You may obtain a copy of the License at 16 | * 17 | * http://opensource.org/licenses/MIT 18 | * 19 | * Unless required by applicable law or agreed to in writing, software distributed 20 | * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 21 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 22 | * specific language governing permissions and limitations under the License. 23 | */ 24 | package yang.coach.memory; 25 | 26 | import android.app.ActivityManager; 27 | import android.content.Context; 28 | import android.os.Debug; 29 | import android.os.Debug.MemoryInfo; 30 | 31 | import java.lang.reflect.Method; 32 | 33 | /** 34 | * 内存信息工具类。 35 | */ 36 | public class MemUtils { 37 | 38 | /** 39 | * 获取内存信息:total、free、buffers、cached,单位MB 40 | * 41 | * @return 内存信息 42 | */ 43 | public static long[] getMemInfo() { 44 | long memInfo[] = new long[4]; 45 | try { 46 | Class procClazz = Class.forName("android.os.Process"); 47 | Class paramTypes[] = new Class[] { String.class, String[].class, 48 | long[].class }; 49 | Method readProclines = procClazz.getMethod("readProcLines", 50 | paramTypes); 51 | Object args[] = new Object[3]; 52 | final String[] memInfoFields = new String[] { "MemTotal:", 53 | "MemFree:", "Buffers:", "Cached:" }; 54 | long[] memInfoSizes = new long[memInfoFields.length]; 55 | memInfoSizes[0] = 30; 56 | memInfoSizes[1] = -30; 57 | args[0] = new String("/proc/meminfo"); 58 | args[1] = memInfoFields; 59 | args[2] = memInfoSizes; 60 | if (null != readProclines) { 61 | readProclines.invoke(null, args); 62 | for (int i = 0; i < memInfoSizes.length; i++) { 63 | memInfo[i] = memInfoSizes[i] / 1024; 64 | } 65 | } 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | } 69 | 70 | return memInfo; 71 | } 72 | 73 | /** 74 | * 获取空闲内存 75 | * 76 | * @return 空闲内存 77 | */ 78 | public static String getFreeMem() { 79 | long[] memInfo = getMemInfo(); 80 | return Long.toString(memInfo[1] + memInfo[2] + memInfo[3]) + "M"; 81 | } 82 | 83 | /** 84 | * 获取总内存 85 | * 86 | * @return 总内存 87 | */ 88 | public static String getTotalMem() { 89 | long[] memInfo = getMemInfo(); 90 | return Long.toString(memInfo[0]) + "M"; 91 | } 92 | 93 | /** 94 | * 获取空闲内存和总内存拼接字符串 95 | * 96 | * @return 总内存 97 | */ 98 | public static String getFreeAndTotalMem() { 99 | long[] memInfo = getMemInfo(); 100 | return Long.toString(memInfo[1] + memInfo[2] + memInfo[3]) + "M/" 101 | + Long.toString(memInfo[0]) + "M"; 102 | } 103 | 104 | /** 105 | * 获取空闲内存和总内存拼接字符串 该方法引入的目的是一次不重复多次获取内存值,性能优化用 106 | * 107 | * @return 总内存 108 | */ 109 | public static String trans2FreeAndTotalMem(long[] memInfo) { 110 | return Long.toString(memInfo[1] + memInfo[2] + memInfo[3]) + "M/" 111 | + Long.toString(memInfo[0]) + "M"; 112 | } 113 | 114 | /** 115 | * 获取进程内存Private Dirty数据 116 | * 117 | * @param context 118 | * @param pid 119 | * 进程ID 120 | * @return nativePrivateDirty、dalvikPrivateDirty、 TotalPrivateDirty 121 | */ 122 | public static long[] getPrivDirty(Context context, int pid) { 123 | 124 | ActivityManager mAm = (ActivityManager) context 125 | .getSystemService(Context.ACTIVITY_SERVICE); 126 | int[] pids = new int[1]; 127 | pids[0] = pid; 128 | MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids); 129 | MemoryInfo pidMemoryInfo = memoryInfoArray[0]; 130 | long[] value = new long[3]; // Natvie Dalvik Total 131 | value[0] = pidMemoryInfo.nativePrivateDirty; 132 | value[1] = pidMemoryInfo.dalvikPrivateDirty; 133 | value[2] = pidMemoryInfo.getTotalPrivateDirty(); 134 | return value; 135 | } 136 | 137 | /** 138 | * 获取进程内存PSS数据 139 | * 140 | * @param context 141 | * @param pid 142 | * @return nativePss、dalvikPss、TotalPss 143 | */ 144 | public static long[] getPSS(Context context, int pid) { 145 | long[] value = new long[3]; // Natvie Dalvik Total 146 | if (pid >= 0) { 147 | int[] pids = new int[1]; 148 | pids[0] = pid; 149 | ActivityManager mAm = (ActivityManager) context 150 | .getSystemService(Context.ACTIVITY_SERVICE); 151 | MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids); 152 | MemoryInfo pidMemoryInfo = memoryInfoArray[0]; 153 | 154 | value[0] = pidMemoryInfo.nativePss; 155 | value[1] = pidMemoryInfo.dalvikPss; 156 | value[2] = pidMemoryInfo.getTotalPss(); 157 | } else { 158 | value[0] = 0; 159 | value[1] = 0; 160 | value[2] = 0; 161 | } 162 | 163 | return value; 164 | } 165 | 166 | public static long[] getHeapNative() { 167 | int Native_HeapSize = 0; 168 | int Native_HeapAlloc = 1; 169 | long[] value = new long[2]; 170 | value[Native_HeapSize] = Debug.getNativeHeapSize() >> 10; 171 | value[Native_HeapAlloc] = Debug.getNativeHeapAllocatedSize() >> 10; 172 | return value; 173 | } 174 | 175 | public static long[] getHeapDalvik() { 176 | int Total_HeapSize = 0; 177 | int Total_HeapAlloc = 1; 178 | 179 | long[] value_total = new long[2]; 180 | value_total[Total_HeapSize] = Runtime.getRuntime().totalMemory() >> 10; 181 | value_total[Total_HeapAlloc] = (Runtime.getRuntime().totalMemory() - Runtime 182 | .getRuntime().freeMemory()) >> 10; 183 | 184 | long[] value_native = getHeapNative(); 185 | 186 | int Dalvik_HeapSize = 0; 187 | int Dalvik_HeapAlloc = 1; 188 | long[] value_dalvik = new long[2]; 189 | value_dalvik[Dalvik_HeapSize] = value_total[Total_HeapSize] 190 | - value_native[0]; 191 | value_dalvik[Dalvik_HeapAlloc] = value_total[Total_HeapAlloc] 192 | - value_native[1]; 193 | 194 | return value_dalvik; 195 | } 196 | 197 | /** 198 | * 获取堆内存数据,精确到KB Get VM Heap Size by calling: 199 | * Runtime.getRuntime().totalMemory(); Get Allocated VM Memory by calling: 200 | * Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); 201 | * Get VM Heap Size Limit by calling: Runtime.getRuntime().maxMemory() Get 202 | * Native Allocated Memory by calling: Debug.getNativeHeapAllocatedSize(); 203 | */ 204 | public static long[] getVM() { 205 | long[] value = new long[5]; 206 | value[0] = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime() 207 | .freeMemory()) >> 10; 208 | value[1] = Runtime.getRuntime().totalMemory() >> 10; 209 | 210 | value[2] = Debug.getNativeHeapAllocatedSize() >> 10; 211 | value[3] = Debug.getNativeHeapSize() >> 10; 212 | value[4] = Debug.getGlobalAllocSize() >> 10; 213 | return value; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/utils/DoubleUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * Tencent GT (Version 2.4 and subsequent versions) available. 4 | * 5 | * Notwithstanding anything to the contrary herein, any previous version 6 | * of Tencent GT shall not be subject to the license hereunder. 7 | * All right, title, and interest, including all intellectual property rights, 8 | * in and to the previous version of Tencent GT (including any and all copies thereof) 9 | * shall be owned and retained by Tencent and subject to the license under the 10 | * Tencent GT End User License Agreement (http://gt.qq.com/wp-content/EULA_EN.html). 11 | * 12 | * Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved. 13 | * 14 | * Licensed under the MIT License (the "License"); you may not use this file 15 | * except in compliance with the License. You may obtain a copy of the License at 16 | * 17 | * http://opensource.org/licenses/MIT 18 | * 19 | * Unless required by applicable law or agreed to in writing, software distributed 20 | * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 21 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 22 | * specific language governing permissions and limitations under the License. 23 | */ 24 | package yang.coach.utils; 25 | 26 | import java.math.BigDecimal; 27 | 28 | /** 29 | * Double数据的操作 使用Java,double 进行运算时,经常出现精度丢失的问题,总是在一个正确的结果左右偏0.0000**1。 30 | * 特别在实际项目中,通过一个公式校验该值是否大于0,如果大于0我们会做一件事情,小于0我们又处理其他事情。 31 | * 这样的情况通过double计算出来的结果去和0比较大小,尤其是有小数点的时候,经常会因为精度丢失而导致程序处理流程出错。 32 | */ 33 | public class DoubleUtils { 34 | /** 35 | * double 乘法 36 | * 37 | * @param d1 38 | * @param d2 39 | * @return 40 | */ 41 | public static double mul(double d1, double d2) { 42 | BigDecimal bd1 = new BigDecimal(Double.toString(d1)); 43 | BigDecimal bd2 = new BigDecimal(Double.toString(d2)); 44 | 45 | try 46 | { 47 | return bd1.multiply(bd2).doubleValue(); 48 | } catch (Exception e) 49 | { 50 | // 根据bugly观测,在进入GTOpMulPerfActivity页时有极小概率crash,故加上异常保护 51 | // @see http://bugly.qq.com/detail?app=900010910&pid=1&ii=152#stack 52 | e.printStackTrace(); 53 | return 0; 54 | } 55 | } 56 | 57 | /** 58 | * double 除法 59 | * 60 | * @param d1 61 | * @param d2 62 | * @param scale 63 | * 四舍五入 小数点位数 64 | * @return 65 | */ 66 | public static double div(double d1, double d2, int scale) { 67 | // 当然在此之前,你要判断分母是否为0, 68 | // 为0你可以根据实际需求做相应的处理 69 | 70 | BigDecimal bd1 = new BigDecimal(Double.toString(d1)); 71 | BigDecimal bd2 = new BigDecimal(Double.toString(d2)); 72 | // return bd1.divide(bd2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 73 | // 直接向下取整,保持和UI展示一致 74 | try 75 | { 76 | return bd1.divide(bd2, scale, BigDecimal.ROUND_DOWN).doubleValue(); 77 | } catch (Exception e) 78 | { 79 | // 根据bugly观测,在进入GTOpMulPerfActivity页时有极小概率crash,故加上异常保护 80 | // @see http://bugly.qq.com/detail?app=900010910&pid=1&ii=46#stack 81 | e.printStackTrace(); 82 | return 0; 83 | } 84 | 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /coach/src/main/java/yang/coach/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * Tencent GT (Version 2.4 and subsequent versions) available. 4 | * 5 | * Notwithstanding anything to the contrary herein, any previous version 6 | * of Tencent GT shall not be subject to the license hereunder. 7 | * All right, title, and interest, including all intellectual property rights, 8 | * in and to the previous version of Tencent GT (including any and all copies thereof) 9 | * shall be owned and retained by Tencent and subject to the license under the 10 | * Tencent GT End User License Agreement (http://gt.qq.com/wp-content/EULA_EN.html). 11 | * 12 | * Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved. 13 | * 14 | * Licensed under the MIT License (the "License"); you may not use this file 15 | * except in compliance with the License. You may obtain a copy of the License at 16 | * 17 | * http://opensource.org/licenses/MIT 18 | * 19 | * Unless required by applicable law or agreed to in writing, software distributed 20 | * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 21 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 22 | * specific language governing permissions and limitations under the License. 23 | */ 24 | package yang.coach.utils; 25 | 26 | import android.net.LocalSocket; 27 | import android.util.Log; 28 | 29 | import java.io.BufferedInputStream; 30 | import java.io.File; 31 | import java.io.FileInputStream; 32 | import java.io.FileOutputStream; 33 | import java.io.IOException; 34 | import java.io.InputStream; 35 | import java.io.OutputStream; 36 | import java.io.RandomAccessFile; 37 | import java.io.Reader; 38 | import java.io.Writer; 39 | import java.net.Socket; 40 | import java.nio.channels.FileChannel; 41 | 42 | public class FileUtil { 43 | private static final String TAG = "--FileUtil--"; 44 | public static final String separator = "/"; 45 | 46 | // ==================================================关于文件处理==================================================== 47 | public static boolean isPathStringValid(String path) { 48 | if (null == path || path.length() == 0) { 49 | return false; 50 | } 51 | 52 | if (path.contains(":") || path.contains("*") || path.contains("?") 53 | || path.contains("\"") || path.contains("<") 54 | || path.contains(">") || path.contains("|")) { 55 | Log.w(TAG, "filename can not contains:*:?\"<>|"); 56 | 57 | return false; 58 | } 59 | 60 | return true; 61 | } 62 | 63 | public static boolean isPath(String path) { 64 | if (path.contains(separator) || path.contains("\\")) { 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | public static String getPath(String path) { 71 | int la = path.lastIndexOf(separator); 72 | String subPath = path.substring(0, la); 73 | return subPath; 74 | } 75 | 76 | /** 77 | * @param path 需要转换的路径或文件名 78 | * @param defPosfix 默认后缀名,当path不带后缀名时,则自动将其加上 79 | * @return 80 | */ 81 | public static String convertValidFilePath(String path, String defPosfix) { 82 | String filePath = path; 83 | if (path.contains(separator) || path.contains("\\")) { 84 | int la = filePath.lastIndexOf("."); 85 | if (la < 0) { 86 | filePath = path + defPosfix; 87 | } else { 88 | String temp = filePath.substring(la); 89 | if (temp.contains(separator) || temp.contains("\\")) { 90 | // "."是目录名的一部分而不是后缀名的情况 91 | filePath = path + defPosfix; 92 | } 93 | // else fileName = fileName 94 | } 95 | } else { 96 | if (!path.contains(".")) // 没有有后缀 97 | { 98 | filePath = filePath + defPosfix; 99 | } 100 | } 101 | 102 | return filePath; 103 | } 104 | 105 | public static boolean isFileExists(String file) { 106 | try { 107 | File f = new File(file); 108 | if (!f.exists()) { 109 | return false; 110 | } 111 | } catch (Exception e) { 112 | e.printStackTrace(); 113 | return false; 114 | } 115 | return true; 116 | } 117 | 118 | public static boolean isFileValid(File f) { 119 | if (!f.exists()) { 120 | try { 121 | f.createNewFile(); 122 | } catch (IOException e) { 123 | 124 | return false; 125 | } 126 | f.delete(); 127 | } 128 | return true; 129 | } 130 | 131 | public static boolean isFileValid(File parent, String name) { 132 | File f = new File(parent, name); 133 | return isFileValid(f); 134 | } 135 | 136 | /** 137 | * 删除存在的文件 138 | * 139 | * @param filePath 140 | */ 141 | public static void delExistFile(String filePath) { 142 | File f = new File(filePath); 143 | if (f.exists()) 144 | f.delete(); 145 | } 146 | 147 | /** 148 | * BH中的日志保存 149 | * 150 | * @param path 151 | */ 152 | public static boolean createDir(String path) { 153 | 154 | if (isSDCardExist()) { 155 | return false; 156 | } 157 | 158 | File f = new File(path); 159 | if (!f.exists()) { 160 | return f.mkdirs(); 161 | } 162 | return true; 163 | } 164 | 165 | public static void createDir(File f) { 166 | 167 | if (isSDCardExist()) { 168 | return; 169 | } 170 | 171 | if (!f.exists()) { 172 | try { 173 | f.mkdirs(); 174 | } catch (Exception e) { 175 | e.printStackTrace(); 176 | } 177 | } 178 | } 179 | 180 | /** 181 | * 关闭bufferReader 182 | * 183 | * @param br 184 | */ 185 | public static void closeReader(Reader br) { 186 | if (br != null) { 187 | try { 188 | br.close(); 189 | } catch (IOException e) { 190 | e.printStackTrace(); 191 | } 192 | } 193 | } 194 | 195 | /** 196 | * 关闭Writer 197 | */ 198 | public static void closeWriter(Writer wr) { 199 | if (wr != null) { 200 | try { 201 | wr.close(); 202 | } catch (IOException e) { 203 | e.printStackTrace(); 204 | } 205 | } 206 | } 207 | 208 | /** 209 | * flush Writer 210 | */ 211 | public static void flushWriter(Writer wr) { 212 | if (wr != null) { 213 | try { 214 | wr.flush(); 215 | } catch (IOException e) { 216 | e.printStackTrace(); 217 | } 218 | } 219 | } 220 | 221 | /** 222 | * 输入流的关闭 223 | * 224 | * @param in 225 | */ 226 | public static void closeInputStream(InputStream in) { 227 | if (in != null) { 228 | try { 229 | in.close(); 230 | } catch (IOException e) { 231 | e.printStackTrace(); 232 | } 233 | } 234 | } 235 | 236 | /** 237 | * 输出流的关闭 238 | * 239 | * @param out 240 | */ 241 | public static void closeOutputStream(OutputStream out) { 242 | if (out != null) { 243 | try { 244 | out.close(); 245 | } catch (IOException e) { 246 | e.printStackTrace(); 247 | } 248 | } 249 | } 250 | 251 | /** 252 | * 文件管道的关闭 253 | */ 254 | public static void closeFileChannel(FileChannel chl) { 255 | if (chl != null) { 256 | try { 257 | chl.close(); 258 | } catch (IOException e) { 259 | e.printStackTrace(); 260 | } 261 | } 262 | } 263 | 264 | /** 265 | * RandomAccessFile的关闭 266 | * 267 | * @param f RandomAccessFile对象 268 | */ 269 | public static void closeRandomAccessFile(RandomAccessFile f) { 270 | if (f != null) { 271 | try { 272 | f.close(); 273 | } catch (IOException e) { 274 | e.printStackTrace(); 275 | } 276 | } 277 | } 278 | 279 | /** 280 | * Socket的关闭 281 | * 282 | * @param s Socket对象 283 | */ 284 | public static void colseSocket(Socket s) { 285 | if (s != null) { 286 | try { 287 | s.close(); 288 | } catch (IOException e) { 289 | e.printStackTrace(); 290 | } 291 | } 292 | } 293 | 294 | /** 295 | * LocalSocket的关闭 296 | * 297 | * @param s Socket对象 298 | */ 299 | public static void colseLocalSocket(LocalSocket s) { 300 | if (s != null) { 301 | try { 302 | s.close(); 303 | } catch (IOException e) { 304 | e.printStackTrace(); 305 | } 306 | } 307 | } 308 | 309 | 310 | public static void deleteFile(File file) { 311 | if (file.exists()) { // 判断文件是否存在 312 | if (file.isFile()) { // 判断是否是文件 313 | file.delete(); // delete()方法 你应该知道 是删除的意思; 314 | } else if (file.isDirectory()) { // 否则如果它是一个目录 315 | File files[] = file.listFiles(); // 声明目录下所有的文件 files[]; 316 | for (int i = 0; i < files.length; i++) { // 遍历目录下所有的文件 317 | deleteFile(files[i]); // 把每个文件 用这个方法进行迭代 318 | } 319 | } 320 | file.delete(); 321 | } else { 322 | Log.e(TAG, "文件不存在!"); 323 | } 324 | } 325 | 326 | /** 327 | * 拷贝文件 328 | * 329 | * @param s 源文件 330 | * @param t 目标文件 331 | */ 332 | public static void copyFile(File s, File t) { 333 | FileInputStream fi = null; 334 | FileOutputStream fo = null; 335 | FileChannel in = null; 336 | FileChannel out = null; 337 | 338 | try { 339 | if (!t.exists()) { 340 | t.createNewFile(); 341 | } 342 | 343 | fi = new FileInputStream(s); 344 | fo = new FileOutputStream(t); 345 | in = fi.getChannel(); 346 | out = fo.getChannel(); 347 | // 连接两个通道,并且从in通道读取,然后写入out通道 348 | in.transferTo(0, in.size(), out); 349 | } catch (IOException e) { 350 | e.printStackTrace(); 351 | } finally { 352 | closeOutputStream(fo); 353 | closeInputStream(fi); 354 | closeFileChannel(in); 355 | closeFileChannel(out); 356 | } 357 | } 358 | 359 | public static void copyInputToFile(InputStream in, String path) { 360 | BufferedInputStream bis = null; 361 | FileOutputStream fos = null; 362 | try { 363 | byte[] buffer = new byte[10 * 1024]; 364 | bis = new BufferedInputStream(in); 365 | fos = new FileOutputStream(path); 366 | int a = bis.read(buffer, 0, buffer.length); 367 | while (a != -1) { 368 | fos.write(buffer, 0, a); 369 | fos.flush(); 370 | a = bis.read(buffer, 0, buffer.length); 371 | } 372 | } catch (Exception e) { 373 | e.printStackTrace(); 374 | } finally { 375 | closeOutputStream(fos); 376 | closeInputStream(bis); 377 | closeInputStream(in); 378 | } 379 | } 380 | 381 | /** 382 | * 是否存在SD卡 383 | */ 384 | public static boolean isSDCardExist() { 385 | return android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED); 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /coach/src/main/res/layout/view_meter.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | -------------------------------------------------------------------------------- /coach/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | coach 3 | 4 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matthewYang92/NoobCoach/06574a9ca7294a7db17813dc7d28f81e5bb4576e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 21 17:23:33 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':coach' 2 | --------------------------------------------------------------------------------