Java tutorial
/* Jiffy Resource Monitor Copyright (C) 2012 Tom Dignan <tom@tomdignan.com> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.tomdignan.jiffymonitor; import java.io.FileNotFoundException; import java.util.ArrayList; //import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.view.View; import android.widget.Button; import com.tomdignan.analytics.AnalyticsSession; import com.tomdignan.analytics.AnalyticsSessionFactory; import com.tomdignan.analytics.Trackable; /** * Main Activity of the application. Responsible for displaying four meters * or three depending on whether the user has a dual core processor. * * CPU0, CPU1, RAM, and Battery. * * @author Tom Dignan */ public class MonitorActivity extends FragmentActivity implements ResourceMonitorFragment.OnResourcesReceivedListener, Trackable { private static final String FRAGMENT_MONITOR = "MONITOR"; private static final String TAG = "MonitorActivity"; private ResourceMonitorFragment resourceMonitor; // currently unused but will use for memory // private ActivityManager activityManager; private FragmentManager fragmentManager; private UpdateResourcesRunnable updateResourcesRunnable; /** One session to rule them all. */ private AnalyticsSession analytics = AnalyticsSessionFactory.makeSession(); /** List of available resource meters */ private ArrayList<Fragment> resourceMeters = new ArrayList<Fragment>(); /** Click handler */ private MonitorClickHandler monitorClickHandler; /** Whether the activity has been paused. */ private boolean isPaused = false; /** {@inheritDoc} */ @Override public void onCreate(Bundle savedInstanceState) { this.analytics.create(this, TAG); super.onCreate(savedInstanceState); setContentView(R.layout.activity_resource_monitor_layout); this.fragmentManager = getSupportFragmentManager(); this.updateResourcesRunnable = new UpdateResourcesRunnable(); this.resourceMonitor = (ResourceMonitorFragment) this.fragmentManager.findFragmentByTag(FRAGMENT_MONITOR); if (this.resourceMonitor == null) { try { this.resourceMonitor = new ResourceMonitorFragment(); } catch (FileNotFoundException e) { Intent intent = new Intent(this, ErrorHandlerActivity.class); intent.putExtra(ErrorHandlerActivity.EXTRA_MESSAGE, "This app requires a system file that was not found on your device. " + "For the technical, that file is the Linux /proc/stat file."); intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); startActivity(intent); return; } } this.resourceMonitor.setRetainInstance(true); this.fragmentManager.beginTransaction().add(this.resourceMonitor, FRAGMENT_MONITOR).commit(); this.resourceMonitor.setOnResourcesReceivedListener(this); } @Override public void onResume() { this.analytics.resume(); super.onResume(); if (this.resourceMonitor != null) { this.isPaused = false; // set up click handler this.monitorClickHandler = new MonitorClickHandler(this, this.resourceMonitor); ((Button) findViewById(R.id.buttonCrank)).setOnClickListener(this.monitorClickHandler); Button toggle = (Button) findViewById(R.id.buttonToggle); toggle.setOnClickListener(this.monitorClickHandler); toggle.setText(getString(R.string.Stop)); this.resourceMonitor.start(); } } /** {@inheritDoc} */ public void onPause() { this.analytics.pause(); super.onPause(); if (this.resourceMonitor != null) { this.resourceMonitor.stop(); ((Button) findViewById(R.id.buttonToggle)).setText(getString(R.string.Start)); this.isPaused = true; } } private static class MonitorClickHandler implements View.OnClickListener { private String labelStop; private String labelStart; private ResourceMonitorFragment monitor; public MonitorClickHandler(Context context, ResourceMonitorFragment monitor) { this.labelStop = context.getString(R.string.Stop); this.labelStart = context.getString(R.string.Start); this.monitor = monitor; } private void toggle(Button button) { if (this.monitor.isStarted()) { this.monitor.stop(); button.setText(this.labelStart); } else { this.monitor.start(); button.setText(this.labelStop); } } /** Creates a tiny load on the CPU in a separate thread. */ private static void crank() { for (int i = 0; i < 4; i++) { new Thread(new Runnable() { public void run() { for (int rev = 0; rev < 10; rev++) { // compute a very high fibonacci number // Leaving off one order of magnitude deliberately (one // missing f) for (long i = 1, j = 1; i + j < 0x7ffffffffffffffl;) { long temp = i + j; j = i; i = temp; Loggy.d(TAG, "Logs are so slow! %l %l\n", i, j); } } } }).start(); } } public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.buttonToggle: toggle((Button) view); break; case R.id.buttonCrank: crank(); break; } } } /** * Receives results from the resource monitor. * * @param long[] cpuUsages -- First result is the number of CPUs * available in the array (It is always 8 longs, but doesn't use * all of the available space. This is so it can be used when phones * with more cores come out.), second result is the aggregate of all cores, * all subsequent results are the cpu0->cpuN. Don't read past cpuUsages[0] * elements! */ private class UpdateResourcesRunnable implements Runnable { public ArrayList<Fragment> resourceMeters; public float[] cpuUsages; int fragmentSeq = 0; public synchronized void run() { if (isPaused) { Loggy.v(TAG, "UpdateResourcesRunnable::run(): Activity is paused, returning."); return; } // Update memory // Update CPU monitors synchronized (updateResourcesRunnable) { // add any missing meters boolean isDirty = false; int totalCpus = (int) cpuUsages[0]; Loggy.d(TAG, "totalCpus=%d", totalCpus); while (resourceMeters.size() < totalCpus) { isDirty = true; String tag = String.valueOf(fragmentSeq); Fragment chart = MonitorActivity.this.fragmentManager.findFragmentByTag(tag); if (chart == null) { chart = ChartFragmentFactory.makeDefault(); //chart.setRetainInstance(true); } Loggy.d(TAG, "UpdateResourcesRunnable::run(): adding chart %d", chart.getId()); // Using commitAllowingStateLoss so that if this happens after // onSaveInstanceState nothing blows up MonitorActivity.this.fragmentManager.beginTransaction().add(R.id.vgCpuMonitors, chart, tag) .commit(); resourceMeters.add(chart); fragmentSeq++; } if (isDirty) { // Ensure that views are created for any new fragments before attempting // to call setNextValue. MonitorActivity.this.fragmentManager.executePendingTransactions(); } int i; for (i = 0; i < totalCpus; i++) { Updatable meter = (Updatable) resourceMeters.get(i); float nextVal = cpuUsages[i + 1]; if (meter != null && nextVal >= 0) { meter.setEnabled(); meter.setName("cpu" + (i == 0 ? " " : (i - 1) + " ")); meter.setNextValue(nextVal); } else if (meter != null) { meter.reset(); meter.setName("cpu" + (i == 0 ? " " : (i - 1) + " ")); meter.setDisabled(); } } } } } @Override public void onResourcesReceived(final float[] cpuUsages) { synchronized (updateResourcesRunnable) { updateResourcesRunnable.cpuUsages = cpuUsages; updateResourcesRunnable.resourceMeters = resourceMeters; } runOnUiThread(updateResourcesRunnable); } @Override public AnalyticsSession getAnalyticsSession() { return this.analytics; } }