0

So i have the following code below which basically takes the initial battery level, waits a certain amount of time, and takes the ending battery level inside of calculateHelper which then finds the difference and prints it.

    // Get the initial battery level
    IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    Intent batteryStatus = this.registerReceiver(null, ifilter);
    int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
    System.out.println("Initial battery level is: " + level);
    int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
    final float batteryPctTemp0 = level / (float) scale;
    final float batteryPct0 = batteryPctTemp0 * 100;

    int waitTime = 60000 * interval; // 1 minute is 60000 miliseconds
    System.out.println("Wait time is " + waitTime);
    Runnable r = new Runnable() {
        @Override
        public void run(){
            calculateHelper(batteryPct0,startButton);
        }
    };
    Handler h = new Handler();
    h.postDelayed(r, waitTime);

I want to infinitely loop (until program exit) this entire process so that after each successive thread finishes, the next one begins, taking a new initial battery level each time and passing it into the calculateHelper function for calculation of a new difference. I do NOT want threads to stack up. I want one thread at a time. In other words, the loop needs to wait for the thread to finish before starting another one.

I can't for the life of me figure out how to do this! If i put the entire thing into a while, it will just repeatedly open up threads crashing the phone.

If anyone can point me in the right direction on the matter I would be greatly appreciative. Also, if any more code is needed to solve the problem, simply comment and I will reply as soon as I have added it to my question.

Thank you.


Thanks to Whooper, I've added in this method of regulating execution order in a loop. However, for some reason my postExecute() method is never being executed and nothing is happening.

    private class BatteryLifeTask extends AsyncTask<Void, Void, Void> {

    // Member variables
    Context appContext;
    float batteryPct0;
    Button startButton;

    public BatteryLifeTask(Context context, Button start) {
        super();
        appContext = context;
        startButton = start;
    }

    protected Void doInBackground(Void... params) {
        // Get the initial battery level
        IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        Intent batteryStatus = appContext.registerReceiver(null, ifilter);
        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        System.out.println("Initial battery level is: " + level);
        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        final float batteryPctTemp0 = level / (float) scale;
        batteryPct0 = batteryPctTemp0 * 100;
        return null;
    }

    protected void onPostExecute() {
        int waitTime = 60000 * interval; // 1 minute is 60000 miliseconds
        System.out.println("In postExecute. waitTime is" + waitTime);
        Runnable r = new Runnable() {
            @Override
            public void run(){
                System.out.println("An interval has passed.");
                calculateHelper(batteryPct0,startButton);
                new BatteryLifeTask(appContext,startButton).execute();
            }
        };
        Handler h = new Handler();
        h.postDelayed(r, waitTime);
    }
}

and my call to the execute method:

    // Start the task loop
    new BatteryLifeTask(getApplicationContext(), startButton).execute();

I've found the problem:

I forgot to set the @Override annotation, and this answer : https://stackoverflow.com/a/11127996/2247192 states:

"If your params of onPostExecute(Param param) don't match the one you defined with extends AsyncTask<...,...,Param> and you didn't use the @Override annotation, it will never be executed and you don't get a warning from Eclipse."

So I've corrected my postExecute method to:

        @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        int waitTime = 60000 * interval; // 1 minute is 60000 miliseconds
        System.out.println("In postExecute. waitTime is " + waitTime);
        Runnable r = new Runnable() {
            @Override
            public void run(){
                System.out.println("An interval has passed.");
                calculateHelper(batteryPct0,startButton);
                new BatteryLifeTask(appContext,startButton).execute();
            }
        };
        Handler h = new Handler();
        h.postDelayed(r, waitTime);
    }

All issues are now resolved.

Community
  • 1
  • 1
Riptyde4
  • 5,134
  • 8
  • 30
  • 57
  • Use Service - So that even if you close the APP the thread will still continue Running. and If you want to wake-up a certain command during an expected time trigger it by AlarmManager, AlarmManager allows you to trigger a "code" in a given SET-time even if the Application is Close. – neferpitou Jan 07 '15 at 14:23

1 Answers1

1

Try using an AsyncTask. http://developer.android.com/reference/android/os/AsyncTask.html

This way you can execute the task again when onPostExecute() is called.

Something like this:

private class BatteryLifeTask extends AsyncTask<Void, Void, Void> {
         protected void doInBackground(Void... params) {
             // Get the initial battery level
            IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
            Intent batteryStatus = this.registerReceiver(null, ifilter);
            int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
            System.out.println("Initial battery level is: " + level);
            int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
            final float batteryPctTemp0 = level / (float) scale;
            final float batteryPct0 = batteryPctTemp0 * 100;
         }

         protected void onPostExecute() {
            int waitTime = 60000 * interval; // 1 minute is 60000 miliseconds
            Runnable r = new Runnable() {
                @Override
                public void run(){
                   new BatteryLifeTask.execute();
                }
            };
            Handler h = new Handler();
            h.postDelayed(r, waitTime);
         }
 }

Be aware that this code is untested. But I hope it gives you an idea :-)

dthomasen
  • 826
  • 15
  • 32
  • A couple of words of caution on the above: 1)`AsyncTask` is not lifecycle aware, so if you exit your `Activity` this could result in a crash or other strange behavior. 2) The creation of a new `Handler` in the `doInBackground()` method is most likely not what you want. When a `Handler` is created, it is bound to the `Looper` of the thread in which it is created: your background thread. Since the background thread is managed by `AsyncTask`, this will likely fail and your `Runnable` will just run on the bg thread. – Larry Schiefer Jan 07 '15 at 13:54
  • @LarrySchiefer: I Agree with you on 1) but not on 2). He creates a Handler in onPostExecute which runs in the UI Thread. – Carnal Jan 07 '15 at 14:04
  • @LarrySchiefer My app is meant to be run in the background and does not close. So #1 should not be a problem. Unless by exit your activity you mean switch apps and not just closing it... – Riptyde4 Jan 07 '15 at 14:07
  • When I looked at the answer before the handler was being created in the `doInBackground` and all the `onPostExecute` was doing was kicking off another instance of the `AsyncTask`. None of that time delay code was there, either. – Larry Schiefer Jan 07 '15 at 14:09
  • @LarrySchiefer You're right. I edited that in, after a closer look at his code. – dthomasen Jan 07 '15 at 14:09
  • So let me get this straight. The background task completes and then the onPostExecute() runs. This is exactly what I need. But if I create a loop while(true){new BatteryLifeTask();} won't this stack up the runnable threads inside postExecute? – Riptyde4 Jan 07 '15 at 14:15
  • @Riptyde4 why do you need a loop? Just call: new BatteryLifeTask.execute(); Somewhere in your service, and then it will run in a loop. – dthomasen Jan 07 '15 at 14:16
  • @Whooper it needs to run continuously for the purpose of my program – Riptyde4 Jan 07 '15 at 14:16
  • @Whooper Ah, so it automatically keeps running? – Riptyde4 Jan 07 '15 at 14:17
  • @Riptyde4 It will. Just start the task, and then it will start itself each time the task is complete. Just like a loop. – dthomasen Jan 07 '15 at 14:17
  • @Whooper I'm just confused where i should place my call to calculateHelper. Above or below the line in run() ? – Riptyde4 Jan 07 '15 at 14:27
  • @Riptyde4 If I understand your code correctly, it should be in the end of the doInBackground() method. This way it will be called each time new data has been gathered. – dthomasen Jan 07 '15 at 14:29
  • @Whooper No, because the time interval specified by waitTime needs to pass between the initial battery level calculation and the call to calculateHelper. – Riptyde4 Jan 07 '15 at 14:30
  • @Riptyde4 Ahh okay.. In that case place it just before the new BatteryLifeTask.execute(); in the onPostExecute() method. Then the task will run, wait, call calculateHelper() and then start the whole thing over again. – dthomasen Jan 07 '15 at 14:32
  • @Whooper ok. I've got everything to compile, but I'm having an issue. postExecute is not being called at all... I've updated my question with the current code so you can take a look – Riptyde4 Jan 07 '15 at 14:55