I have a thread, and I would like to let the user kill that thread when they click on the progress notification.
My notification works fine, but in my BroadcastReciever I can't manage to kill the thread.
I can't use thread.interupt(), because I don't have access to the thread object. This also means I can't use a public boolean to terminate the thread.
I also tried passing the process id as an extra, and killing it using Process.killProcess(pid), but this has the rather unfortunate side effect of killing my UI thread and any other threads I might have running.
Finally, I tried using the thread id, but the documentation doesn't mention using this for killing the thread.
In short, how can I kill my thread using the primitive data types that can be passed as extras?
You're looking at it with the wrong approach.
If you have a thread running and a notification on the notification bar I'll assume that you have a service running, and this service know the thread and does have a reference to it. That way, the PendinIntent for your notification, should not simply trigger a BroadcastReceiver, but trigger the same Service with an Intent extra indicating that the service should cancel the thread.
so assuming your service:
public MyService extend Service{
private Runnable myThread = new Runnable{
#Override
public void run(){
// ... here you're doing the job on the different thread
}
};
}
something like that:
Intent i = new Intent(this, MyService.class);
i.putExtra("cancel", true);
PendingIntent pi = PendingIntent.getService(// put here the intent)
then, whenever the notification is clicked, your service that have the thread will be executed from onStartCommand and all you need to do is:
cancel = getIntent.getBooleanExtra("cancel", false);
then inside the thread you must check for the boolean cancel from the service like this:
public MyService extend Service{
boolean cancel = false;
private Runnable myThread = new Runnable{
#Override
public void run(){
// ... here you're doing the job on the different thread
if(cancel)
return;
}
};
}
The correct way is stopping it from the inside. That means that you define inside a controller (for example, a boolean) that checks some additional event within your app that determines if your Thread has to still execute or not. This implies that you'll have to put your code within a loop with a Thead.sleep() object inside to not overkill your thread, or use a Handler inside with a postDelay() action inside, but that's the way.
If you don't want to use it, you can also declare a BroadcastReceiver inside. You register your receiver, and wait for some action you define (I recommend an own action so you don't interfere with Android's) and once your receive event fires, just use return in your Thread so it exits.
Related
How do I run a parallel action (process) to the main app in Android?
I know that, there are a lot of ways to do it: Threads, Tasks, Handlers and etc'...
This is the way I chose. But I think it takes a lot of memory and doesn't closes in the interrupt call.
checkReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO: Check is this a good way to handle threads
Thread t = new Thread() {
#Override
public void run() {
internetConnectionManager.TryConnect();
this.interrupt();
}
};
t.start();
}
}
};
Two things wrong with your arroach:
You should not start a thread in onRecieve method. The reason is explained here :
This has important repercussions to what you can do in an
onReceive(Context, Intent) implementation: anything that requires
asynchronous operation is not available, because you will need to
return from the function to handle the asynchronous operation, but at
that point the BroadcastReceiver is no longer active and thus the
system is free to kill its process before the asynchronous operation
completes
Second, calling Thread.currentThread().interrupt() does not make any sense in your example since your thread is already done by that line and will finish, and also because you don not check interrupted flag anyway.
The better way, in my opinion, would be to start a simple IntentService from your onReceive code. Here is a simple tutorial.
Important edit based on FunkTheMonk's comment:
If the broadcast comes from an alarm or external event, it is possible that your device will go to sleep shortly after onReceive returns (even if you create a service). If that is the case, instead of using regular BroadCastReceiver you should extend WakefulBroadcastReceiver from support library.
Use handler
if you want to stop handler then fire an intent with some value eg.("quit handler")to receiver
and call remove call back and inside handler you can handle the rest using ACTION switch
you can also use intentservice
I have a android Service, in the onStart method i get many Strings from the Intent activity, and then execute a AsynTask to download files from internet.
When the activity is running this works ok, but when i stop the activity, this relaunch the onStart method, but obiusly the intent is null causing me nullPointerException.
What can i do the service dont entry on onStart, and continues the first asyntask to download all the files?
This is my code
#Override
public void onStart(Intent intent, int startId) {
desde = intent.getIntExtra("desde", 0);
hasta = intent.getIntExtra("hasta", 1);
email = intent.getStringExtra("email");
password = intent.getStringExtra("password");
new DescargaFotos().execute();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
and in Inicio.java (The UI activity)
Intent iService = new Intent(contexto,
ServiceDownloader.class);
iService.putExtra("desde", 0);
iService.putExtra("hasta", 5);
iService.putExtra("email", email);
iService.putExtra("password", password);
startService(iService);
EDIT:
New question:
I am using IntentService and i bind the service like this:
Intent iService = new Intent(contexto,
ServiceDownloader.class);
ServiceConnection serviceConector = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
Log.i("INFO", "Service bound ");
}
#Override
public void onServiceConnected(ComponentName name,
IBinder service) {
Log.i("INFO", "Service Unbound ");
}
};
iService.putExtra("desde", 0);
iService.putExtra("hasta", 50);
iService.putExtra("email", email);
iService.putExtra("password", password);
startService(iService);
bindService(iService, serviceConector,
Context.BIND_AUTO_CREATE);
And now my problem is, in my galaxy nexus, if i enter in app runtime list, and destroy my app, the service stopped, and stop donwload, stop send notification etc? How must i bind the service to solve this?
Use an IntentService. IntentService is specifically designed to do what you want. You don't have to implement onStart, onStartCommand, etc. The work runs on a background thread. Once the work finishes, the Thread is destroyed.
IntentService will continue to run regardless of the state of the Activity.
One problem you may have is that you're sending out your Intent in the wrong place in your Activity, or you're not checking to see if the operation is complete. Before you send the Intent, check a flag in SharedPreferences (if the flag doesn't exist, it means you're starting for the first time). When you send the Intent, store a flag in SharedPreferences to indicate that you sent it. When your IntentService receives the Intent, have it update the flag to say it received it. Before the IntentService finishes, have it update the flag again. And so forth.
I do not have reputation to comment your question, so I will write a answer :)
First off all, in my opinion, if your Service do just this, get some string and download files from the Internet, you do not need the service. The AsyncTaks is enough to solve your problem and it is more simple to implement. Doing that, you avoid the problem with your intent.
From Android API:
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
However, if you decide to continue using a service, lt me know exactly when startService is called in your activity.
If you have some doubt about when use AsyncTask, Service, IntentService and Thread, this link can help you.
This would be my suggestion...
Use a thread in the service rather then AsyncTask since AsynTask should only be used small task. Check following statement from AsyncTask javadoc:
AsyncTask is designed to be a helper class around Thread and Handler
and does not constitute a generic threading framework. AsyncTasks
should ideally be used for short operations (a few seconds at the
most.) If you need to keep threads running for long periods of time,
it is highly recommended you use the various APIs provided by the
java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and
FutureTask.
So from your on start check the status of your thread, if it is running then don't do anything, if it is not running then start the thread that would download your images...
I want to start a Service from an Activity.
First I tried it with a LocalBinder. This works, but the service was bound to the activity. I don't want to stop the service when the activity is gone. I found no solution with the LocalBinder so I removed it and tried this:
use a singleton instance in the service
call the startService methode in a new thread and waits until the instance is available:
final Intent recordService = new Intent(RecordActivity.this, RecordService.class);
Runnable r = new Runnable() {
#Override
public void run() {
startService(recordService);
}
};
new Thread(r).start();
Log.i(MMLF.T, "service instance: "+serviceInstance);
final ProgressDialog mProgressDialog = ProgressDialog.show(
RecordActivity.this, "Waiting", "wait until record service is loaded",
true);
while (serviceInstance == null) {
serviceInstance = RecordService.get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.e(MMLF.T, "could not sleep", e);
}
}
mProgressDialog.dismiss();
But this doesn't work, too. It stucked in the waiting loop. If I remove this waiting stuff and the new new Thread(r).start() line is the last, the activity and service start fine.
How to start a service independent from an activity? I also let them to communicate with each other. The activity should call two methods (start and stop recording) and the service should send messages. For the second I can use LocalBroadcast.
Your question is a little confusing, because Services already live independently of Activities. Note, however, that Services run in the main thread by default. If you want to run the Service in a different thread (and in this case it looks like you do), you will have to set up a Messenger object and send messages between your worker thread and your UI thread. You can also look into using AIDL (on top of which Messenger is really built anyway). Your communication, if you don't use a Messenger, could use intents. If this is the case, you should look into IntentService. However, this only works when you are sending messages to the Service, not back and forth. If you want back and forth communication, you will have to use some sort of Messenger or similar pattern.
By the way, using an IntentService for things like 'stop' and 'start' is pretty common. Typically there is also a background thread, which communicates with the Service using a Messenger or something similar, and then sends / receives messages to instruct the worker thread as to what should be done.
You might also look into AsyncTask, as it makes this kind of thing much simpler.
One of my activity periodically updates nearby friends, which location is obtained from rest service
Currently I use postDelay:
private Runnable updateNearbyFriendsTask = new Runnable() {
#Override
public void run() {
list = api.getNearby(.....)
handler.postDelayed(this, UPDATE_RATE);
}
};
The problem is that postDelayed executed on UI thread, so this runnable task block ui with poor internet connection.
What is the right way to make periodic background rest requests from activity? I don't want to create service for that, because this rest method is used only in this activity.
EDIT
Currently switched to using ScheduledExecutor
this.scheduledExecutor.scheduleWithFixedDelay(new UpdateNearbyFriendsTask(), 0, UPDATE_RATE, TimeUnit.MILLISECONDS);
private class UpdateNearbyFriendsTask implements Runnable() {
#Override
public void run() {
list = api.getNearby(.....)
runOnUiThread(.....)
}
};
I don't see what the problem is with creating a Service, even if it is only used for this activity.
That being said, have a look at the TimerTask. It seems to do what you want.
How about BroadCast receiver using Alarm manager.. http://developer.android.com/reference/android/app/AlarmManager.html
Since its a long running and on going task, would you want to write a Service or an Intent service which does the background job for you.
You can just ping the service whenever your time ticks and let the service do the network activity, freeing up the UI thread for something else. you can always query the service to know the status, or the service itself can respond back to your UI thread.
For ticking the timer, you can use the alarm manager, or perhaps something else (I am not good at any :P )
I'm trying to create a service that runs in a given interval. The service's purpose is to update a database and when done notify an Activity with an Intent.
The service should also be callable from the activity when the user chooses to 'refresh'.
I have accomplished this, but I can't get it to run in a detached thread.
The service executes an update method in a Runnable:
private Runnable refresh = new Runnable() {
public void run() {
update(); //Runs updates
didUpdate(); //Sends broadcast
handler.postDelayed(this, 50000); // 50 seconds, calls itself in 50 secs
}
};
I have another runnable called ManualRefresh that is called via a broadcast from the activity.
However these runnables seem to be blocking the UI.
Need advice! :)
When you run a Runnable by calling it's run method, it runs on the current thread. To run on a background thread, you need to use new Thread(refresh).start(); (if the Runnable you want run is refresh).
You can also make use of AsyncTask for this, but that's more appropriate for an activity than for a Service. Information about using AsyncTask can be found in the API docs and in the article Painless Threading.
I suggest to write the service using the AlarmManager. The service will receive an Intent to tell it to periodically to update the database.
Once updated, you can notify the Activity with an Intent (as you mentioned).
When the user wants to manually refresh, have the Application sent an Intent to you service. Receiving the Intent from the AlarmManager or from the Activity would perform the same code.
You may also want to reschedule the alarm after a request to manually refresh.