handler.postDelayed is not working in onHandleIntent method of IntentService
Asked Answered
F

7

11
final Handler handler = new Handler();
LOG.d("delay");
handler.postDelayed(new Runnable() {
    @Override public void run() {
        LOG.d("notify!");
        //calling some methods here
    }
}, 2000);

The "delay" does shows in the log, but not others at all. And the method called in the run() is not called at all also. Can anyone help explain why this happens, am I doing anything wrong?

The class that has this code extends IntentService, will this be a problem?

============================

UPDATE: I put this code in the class that extends IntentService. The only place I found it worked was in the constructor. But I need to put it in the onHandleIntent method. So I checked the documentation for onHandleIntent and it said:

This method is invoked on the worker thread with a request to process.Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call stopSelf.

So based on the result I get, I feel like I cannot use postDelayed in "worker thread". But can anyone explain this a bit more, like why this is not working in worker thread? Thanks in advance.

Fop answered 30/4, 2016 at 0:3 Comment(11)
is import from os.Handler ?Ardussi
@NJ yes it is. android.os.HandlerFop
Where are you calling this from? Needs context.Millet
Why is the @Override next to public and not above? Why is it "LOG.d" and not "Log.d("your tag", "notify!")" ? If you indeed imported os.Handler then it should work.Slaw
@Millet I'm calling this to delay a notification for 2 seconds before it shows up. Not sure if this is the right place to call. Can you explain where I should call the handler from?Fop
@UFCInsider it shouldn't matter if the override is next to public, it's automatically generated by IDE. Also the "LOG" is the variable name of a logger.Fop
IntentService is usually used for long tasks that don't require comminucation with the Main thread. maybe explain what you're trying to achieveSlaw
@UFCInsider this class is used to send notifications when necessary, say SCREEN_ON. but what i want to do is the delay the showing of notification for 30s. So that only after screen is on for 30s, the notification can be show.Fop
How did you resolve this? I'm running into something similar... just that I'm using the handler.sendEmptyMessageDelayed instead, in intentservice... my next try would've been where you got stuck... my screen is also on, so it should be a wakelock issue either...Linell
This might resolve the problem, yet to try... However, doesn't explain what the problem is... https://mcmap.net/q/1013631/-make-an-intentservice-not-sleep-until-it-executes-a-handler-postdelayed-runnableLinell
Tried and confirmed, using normal service instead of IntentService resolves the problemLinell
C
18

Convert

final Handler handler = new Handler();

to

final Handler handler = new Handler(Looper.getMainLooper());

This worked for me.

Cowes answered 22/1, 2018 at 16:38 Comment(2)
This wouldn't make any difference as long as you are instantiating the handler in the main thread.Leverett
@Leverett It will make a difference, IntentService's onHandleIntent runs in its own thread.Hylotheism
C
12

You are using looper of the main thread. You must create a new looper and then give it to your handler.

HandlerThread handlerThread = new HandlerThread("background-thread");
handlerThread.start();
final Handler handler = new Handler(handlerThread.getLooper());
handler.postDelayed(new Runnable() {
    @Override public void run() {
        LOG.d("notify!");
        // call some methods here

        // make sure to finish the thread to avoid leaking memory
        handlerThread.quitSafely();
    }
}, 2000);

Or you can use Thread.sleep(long millis).

try {
    Thread.sleep(2000);
    // call some methods here

} catch (InterruptedException e) {
    e.printStackTrace();
}

If you want to stop a sleeping thread, use yourThread.interrupt();

Charybdis answered 15/2, 2018 at 14:36 Comment(0)
S
1

this is how i use handler:

import android.os.Handler;

Handler handler;
//initialize handler
handler = new Handler();

//to start handler
handler.post(runnableName);

private Runnable runnableName= new Runnable() {
        @Override
        public void run() {
            //call function, do something
            handler.postDelayed(runnableName, delay);//this is the line that makes a runnable repeat itself
        }
};
Sportsmanship answered 30/4, 2016 at 3:31 Comment(6)
Thanks for answering but it's not working. I don't know if its because i cannot put this guy in some threads or something else.Fop
but I can't import android.os.Handler in my project.Fag
shouldn't runnableName be assigned before calling handler.post?Cohobate
@Cohobate no, just think of it as a method.Sportsmanship
@Fop I don't think you should combine a Thread and a Handler. They kinda do the same thing so you just need to choose which one to use.Sportsmanship
@DPrince try to remove the import statement on the top of your code, if you placed it there. Sometimes, if you got auto-import turned-on, Android Studio will import the correct one. If not, the word Handler should be in red. Place your cursor over it. Then Handler would have an underscore, press Alt+Enter. The import options will appear and choose there os.Handler.Sportsmanship
P
0

Handlers and Services will be predictable when the device screen is on. If the devices goes to sleep for example the Handler will not be a viable solution.

A much more better and reliable solution will be to use: AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

Putandtake answered 16/2, 2017 at 11:15 Comment(2)
not if you're trying to delay for say, a few secondsUrbane
Do you have a reference for this statement? I'm asking since my handler works unpredictably after approx. 15 minutes of the screen being offBearing
R
0

IntentService is not designed for such scenario. You can use a regular Service instead. You can put the handler inside the onStartCommand(). Don't forget to call stopSelf() on the Service instance to shut it down after the handler.postDelayed(){}

Reticent answered 28/8, 2017 at 16:21 Comment(0)
Z
0

The most simple is to wait before ending onHandleIntent():

SystemClock.sleep(2000);
Zsolway answered 11/11, 2021 at 19:22 Comment(0)
R
0

My answer does not answer your particular query.

Adding it for anyone who needs to schedule multiple tasks with small delays b/w them and getting unexpected behavior from handler.postDelayed.

I was trying to implement tasks with fixed delays between them. I tried the below code using for loop and it just scheduled all the tasks to run at sum(delays). Later I fixed it by using recursion.

For loop code (does NOT work)

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        val handler = Handler(Looper.getMainLooper())
        // code to fetch instructions from intent
        var cummulativeDelay = 0L
        for (instruction in instructions) {
            val handler = Handler(Looper.getMainLooper())
            handler.postDelayed({
                executeInstruction(instruction)
            }, cummulativeDelay)
            cummulativeDelay += instruction.delay
        }
    }

Recursion code (this works)

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        val handler = Handler(Looper.getMainLooper())
        // code to fetch instructions from intent
        executeInstructions(instructions, 0, handler)
    }

    private fun executeInstruction(instruction: Instruction) {
        // execute the instruction
    }

    private fun executeInstructions(instructions: List<Instruction>, i: Int, handler: Handler) {
        if (i == instructions.size) {
            stopSelf()
            return
        }
        val instruction = instructions[i]
        try {
            executeInstruction(instruction)
        } catch(e: Exception) {
            e.printStackTrace()
        }
        val delay = /* delay in milliseconds */
        handler.postDelayed({
            executeInstructions(instructions, i+1, handler)
        }, delay)
    }

Is it because of batch processing of delayed instructions with small delays?

Reprography answered 31/10, 2023 at 19:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.