I've posted about this project before, I'm trying to create a service, from a Home Activity, that constantly checks if the screen is locked or not (ScreenLockService is the IntentService's name). If so, it creates another service to "listen" (Listener is the IntentService's name) for sound. If not, it stops the existing Listener service if one is running.
So to do so, I've created a Thread within the onHandleIntent method of SLS that should always be checking if the screen is locked or not. Here is how that's implemented so far:
(Sorry for the wall of code, I'll try to make it look pretty)
Thread checkScreen = new Thread(
new Runnable() {
#Override
public void run() {
Boolean lockInput = false;
while(!Thread.interrupted()) {
//The first time the screen becomes locked is special, it should start the whole process from there
KeyguardManager firstKeyManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (firstKeyManager.inKeyguardRestrictedInputMode()) {
lockInput = true;
}
//If the screen has been locked for the first time:
while(lockInput) {
//Put a timer here do slow the app down and figure out what's going wrong
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
//if the screen is locked start listener service else stop listener service (METHODS WITHIN THIS SERVICE HANDLE THIS)
KeyguardManager secondKeyManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (secondKeyManager.inKeyguardRestrictedInputMode()) {
startListener();
}
else if (!secondKeyManager.inKeyguardRestrictedInputMode()) {
stopListener();
}
}
}, 10000 //10 seconds
);
}
}
}
}
);
//Might be relevant to the problem, if the thread dies I need it to start back up again.
//This is part of the onHandleIntent method, at the very bottom, and not inside the thread.
checkScreen.setName("checkScreen");
Log.d(TAG, "Starting SLS thread");
checkScreen.start();
checkScreenAlive = true;
while(checkScreenAlive){
if(!checkScreen.isAlive()){
Log.d(TAG, "Restarting check screen!");
checkScreen.start();
}
}
Now, I have Log messages all over the place so I can see what state the app is in (starting services, stopping services, checking the screen, listening, etc). When I debug it and lock the screen for the first time, nothing will be logged until 10 seconds later it spams Listener Service already running about 20 times then the service dies.
Maybe I don't fully understand how the timer works in java, but I have no clue why the service is dying. I probably don't even need to do this in a thread, or maybe not even use an IntentService and use a regular Service instead. I've read about the differences and I think what I have is right.
If I should post more code I can, don't hesitate to ask. I'm trying to make this as straightforward as possible, this is my first app and I'm still easily confused by some of this stuff.
You have while(lockInput) { which never gets set to false and will generate a lot of Timer().schedule events.
This schedule will be kicked after 10seconds which is where you are seeing the delay.
I would start by changing
while(lockInput) {
...
}
to
if(lockInput) {
lockInput = false; //Only want this called once per check
//Put a timer here do slow the app down and figure out what's going wrong
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
//if the screen is locked start listener service else stop listener service (METHODS WITHIN THIS SERVICE HANDLE THIS)
KeyguardManager secondKeyManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (secondKeyManager.inKeyguardRestrictedInputMode()) {
startListener();
}
else if (!secondKeyManager.inKeyguardRestrictedInputMode()) {
stopListener();
}
}
}, 100 //Fire of the check almost instantly
);
}
Thread.sleep(10000); //So we are yeilding to the system, don't want an essential while(true) loop
Related
I'm doing too much work on the main thread, and so I want to learn how to run some things on other threads. But I'm having difficulties understanding how threads work.
From what I've gathered, you can no longer stop or cancel a thread, and the way that I need it to work is that, if Button A is clicked, do the function, if Button B is clicked, stop as to not use resources.
Here's where I stand:
> //Inside OnCreate
Thread newthread = new Thread(new Runnable(){
#Override
public void run(){
while(bool) {
...
}
...
}
});
newthread.start();
Button A Listener(){
bool = true;
}
Button A Listener(){
bool = false;
}
when I first start the thread it runs, but after changing bool to false, and back to true it doesn't.
putting return at the end destroys the thread, but calling start again isn't allowed.
I've tried putting 2 while loops, but it still doesn't switch between the loops.
Is there even a reason to use interrupt? and how would it be implemented?
What I need is to create another thread, have it keep doing something until I click button A, then resume when I click on Button B, that's all.
but calling start again isn't allowed.
Not sure why don't you can not call start again, but I think it's good solution. Base on your requirement tries this code put inside run
while (!Thread.currentThread().isInterrupted()) {
try {
if (loop) {
...
}
} catch (InterruptedException e) {
return;
}
}
Why we need to check Thread.currentThread().isInterrupted() because when your Activity destroy you should call newthread.interrupt() to release your Thread because it can be leak memory.
I am trying to make background service that will run 15 sec after user closes tha app, I have done service that runs 15 sec (loop with Logs), bud when I close tha app, then it stopes
and another problem is, when I try to stop it from main activity by stopService(intent); then the onDestroy method is called, but thread with loop continues
.. please can someone help me?
*sorry for my english - no native :D
public class NotificationService extends Service {
final private class MyThread implements Runnable {
int service_id;
MyThread(int service_id) {
this.service_id = service_id;
}
#Override
public void run() {
synchronized (this) {
for (int i = 0; i < 15; i++) {
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("onStartCommand", "loop:" + i);
}
stopSelf(service_id);
}
}
}
Thread thread;
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
Log.e("onStartCommand", "started");
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
thread = new Thread(new MyThread(startId));
thread.start();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Log.e("onDestroy", "onDestroy");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
super.onDestroy();
}
}
I am trying to make background service that will run 15 sec after user closes tha app, I have done service that runs 15 sec (loop with Logs), bud when I close tha app, then it stopes
Your code only starts the loop thread when startService(yourNotificationService)is called on the Activity or Broadcast Receiverthat is responsible for calling it does so. It then kills itself with stopSelf(service_id).
If, after you have returned from onStartCommand(), you immediately kill the app without calling stopSelf(service_id) (i.e. your 15 seconds is not up), then your Service will MOST LIKELY restart itself given the START_STICKY return value. However, after you call stopSelf(service_id) you are telling the Service to kill itself; after you close your app, there is nothing to tell your Service to restart through the onStartCommand() call.
and another proble is, when I try to stop it from main activity by stopService(intent); then the onDestroy method is called, but thred with loop continues
A Service is an Android component; it is not another process or thread, it runs in the same process and thread as the main UI thread unless you specify otherwise, as seen here.
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.
In your case, calling stopService(intent) tells the Service to stop itself, which it does. It does not stop the Thread you started (the MyThread instance). To do that, you must first make your Thread interruptible; see here to do that. Once you do that, you need to change your onDestroy() code to actually interrupt the MyThread instance, as here
#Override
public void onDestroy() {
Log.e("onDestroy", "onDestroy");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
thread.interrupt();
super.onDestroy();
}
So, I have an activity with a handler.
private final Runnable m_Runnable = new Runnable()
{
public void run()
{
if(LiveAPI.getStatus() == 1){
matches = LiveAPI.getMatches();
listAdapter.notifyDataSetChanged();
}
LivePage.this.mHandler.postDelayed(m_Runnable, 5000);
}
};
Here I get some data and update my list with it. It works.
When I click on an item of my list, this functon is called
private void showLiveMatch(int position) {
Intent i = new Intent(this, LiveMatch.class);
i.putExtra("match", matches.get(position));
startActivity(i);
}
My new activity appears, wich also contains another handler:
private final Runnable m_Runnable = new Runnable()
{
public void run()
{
if(LiveAPI.getStatus() == 1){
match = LiveAPI.getMatch(match.getId());
displayCommentaries();
}
LiveMatch.this.mHandler.postDelayed(m_Runnable, 5000);
}
};
Sometimes this works as I want.
But in some cases it seems like in second activity is still called LiveAPI.getMatches() from the first handler rather than LiveAPI.getMatch(match.getId());
Every function displays a console text, and that's how I figure it out what function is called.
Can someone explain me why?
Once you post either m_Runnable (from LivePage or LiveMatch), it does its stuff and then schedules itself to run in 5 seconds. Basically, each time you start one with a click, it creates an infinite loop. Enough clicks and you will have the logic for each of these running constantly. (That is, unless you have some other code that periodically calls mHandler.removeCallbacks(m_Runnable); that you haven't shown us.) Without knowing more about what you're trying to do, it's hard to recommend how to fix this, but you should somehow avoid creating these kind of infinite loops.
Be aware that all handlers you create on the UI thread simply feed Runnable objects into the (single) MessageQueue for the thread. So there's no such thing as something being called from one handler or another.
I just solved the problem myself. I had multiple calls for syncCustomers() due to a dialog closing event problem. I solved it by providing the parent JFrame in the JDialog constructor. Pretty stupid error on my side.
My application contains a task that synchronizes with a webservice and a local database. This task may take up to several minutes. Thus I want to notify the user about this time consuming process with a simple dialog (Swing). The user is not supposed to continue working while the sync process is running.
So I thought of:
open modal dialog with the notification for the user
start the sync process in a separate thread
close modal dialog after sync process is done
User clicked on the button to start sync process:
private void syncCustomers() {
if (checkWebserviceAuth()) {
SyncDialog dialog = new SyncDialog();
dialog.setLocationRelativeTo(this);
dialog.setVisible(true);
SyncCustomersTask task = new SyncCustomersTask(dialog, getCoach());
task.run(); // task.start() will result in the same problem
} else {
openAuthorizeDialog(true);
}
}
public class SyncDialog extends javax.swing.JDialog {
public SyncDialog() {
initComponents();
// I already noticed that the modal dialog won't work for me since it interrupts within syncCustomers()
//this.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
this.setTitle(Application.getApplicationTitle());
}
...
}
public class SyncCustomersTask extends Thread {
private void doWork() {
System.out.println("Start doWork() and sleep for 10 seconds...");
try {
// for testing purpose
Thread.sleep(10000);
} catch (InterruptedException ex) {
}
System.out.println("Done with doWork().");
}
#Override
public void run() {
doWork();
if (getCallback() != null) {
System.out.println("Invoke callback...");
getCallback().dispose();
System.out.println("Callback invoked.");
}
}
...
}
This will result in an infinite loop of:
Start with doWork()...
Start doWork() and sleep for 10 seconds...
Done with doWork().
Invoke callback...
Callback invoked.
If I comment out
getCallback().dispose();
, the loop will stop after the second execution:
Start with doWork()...
Start doWork() and sleep for 10 seconds...
Done with doWork().
Invoke callback...
Callback invoked.
Start with doWork()...
Start doWork() and sleep for 10 seconds...
Done with doWork().
Invoke callback...
Callback invoked.
I don't get it. What fires the thread to execute over and over again?
I guess this whole thing isn't a good idea to start with, but I wasn't able to get things like ProgressMonitor working either. :(
Call start(), not run(). The latter will simply execute the thread, but not in a separate thread! The start() method will instantiate a new thread, and only then invoke your run() method in that new thread.
This is a surprising common problem, btw.
invoking run() does not execute code in a new thread.
I know that pausing a thread can easily lockup the UI, and it is generally a bad idea. However it was my understanding that if something is running as a service that will not cause any issues, because the service will pause and the main app will continue running.
With that in mind, I am either doing something wrong, or just misunderstood the use of a service for a MediaPlayer.
I create the object
public AudioService AudioService;
public boolean AudioServiceBound = false;
and then in my SurfaceView's onStart event I bind it:
public void onStart() {
Intent intent = new Intent(gameContext, AudioService.class);
gameContext.bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
throughout the rest of the class I run methods that pause and resume the AudioService based on the onResume and onPause events.
I have tried to introduce a new ability to my service. Within my main update loop I run the function HalfOverSwitch() seen below:
public void HalfOverSwitch()
{
if (( ((float)player.getCurrentPosition()) / ((float)player.getDuration()) > 0.5) && !transitioning)
{
transitioning = true;
MediaPlayer temp = MediaPlayer.create(this, R.raw.dumped);
temp.setVolume(0, 0);
temp.setLooping(true);
temp.start();
for (int i = 0; i < 100; i++)
{
player.setVolume(100-i, 100-i);
temp.setVolume(i, i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
player = temp;
player.setVolume(100,100);
transitioning = false;
}
}
Because that function doesn't return anything and is running in a different thread, it was my understanding that the main activity would not pause. It does however. That brings up the question, what is the best way to do something like that, and what is the point of making my AudioService a service at all (and not just a class)?
Service runs in the same thread in which service is created.
http://developer.android.com/reference/android/app/Service.html
"Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. "