I've been looking around some anwers given here but i don't get exactly the solution to my problem: I don't want to create a new class and extends runnable or thread.
I have a service that when created must check every 10 secs some stuff and the calls needed can't be done from the main thread, so onStartCommand() method I do the following:
mThread = new Thread() {
public void run() {
while(true) {
// some code
try {
Thread.sleep(10000);
}
catch (Exception e){
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
Log.i("Exception", errors.toString());
}
}
Now, when i call onStopService() i want to stop this thread. The method stop() is deprecated so I'm using interrupt():
#Override
public void onDestroy(){
mDelete.interrupt();
mDownload.interrupt();
super.onDestroy();
}
As I expected, it throws InterruptedException because the thread is sleeping when calling interrupt.
Is there any way to stop the thread without creating a new class and extend from runnable or thread?
Thanks in advance
As you can see here, you can use this code inside your thread:
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// We've been interrupted, and returning from the run method will stop your thread.
return;
}
However, in general it is better avoid infinite loop service. You should consider to stop your service when the work is done, and restart it (using an AlarmManager) when a new work is required. I will not repeat the code for use IntentService and AlarmManager, since Eugen Pechanec has given you a good explanation.
1) Extend your service from IntentService.
This type of service is used for short granular operations and it runs on a background thread so you can access network. Do your work in method onHandleIntent(Intent) instead of onStartCommand(Intent, int, int).
2) Schedule this service using AlarmManager (the following code will work in an activity).
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, Service.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
long nowElapsed = SystemClock.elapsedRealtime();
long tenMinutes = 10 * 60 * 1000;
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, nowElapsed, tenMinutes, pi);
Notice the word "inexact". That means that the interval won't be exactly 600000 milliseconds. It is more energy effective.
The PendingIntent.FLAG_CANCEL_CURRENT flag is for proper rescheduling.
3) Cancel the PendingIntent when you don't need it anymore. After this your service will not run automatically until you enable it again.
Intent i = new Intent(this, Service.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_NO_CREATE);
if (pi != null) pi.cancel;
More about using alarms: https://developer.android.com/training/scheduling/alarms.html
More about IntentService: http://developer.android.com/reference/android/app/IntentService.html
Related
I'd like to run a service in background but after I read the docs I'm a bit confused.
(I'm targeting SdkVersion 27, which means I can't start a BroadcastReceiver from the AndroidManifest.xml and need to do these tasks within the Application class, afaik.)
Before I was starting the IntentService within a BroadcastReceiver, which I started by using a PendingIntent, which was triggered by an AlarmManager. It felt a bit too much, so I started the service directly on the onCreate() of the Application.
It's working, but I'm not sure if that's a good practice.
The service is supposed to run forever and fire it's own threads for operations that can take up to one minute and run again as soon as they are finished (+ 5 seconds).
Pseudocode of the services purpose
MyService // starts on Application creation an runs "forever"
threads = []
itemIds = []
async loop manageThreads // start / kill
itemIds = getItemIdsFromDatabase()
loop itemIds vs threads
if noThreadRunningForCurrentItemId
threads.push(new ItemThread(itemId).start())
loop threads vs itemIds
if threadRunsForNoneExistingItemId
threads[currentItemId].kill()
sleep(20000) // manage threads every 20 seconds
ItemThread(int itemId)
doSomething()
sleep(5000)
ItemThread(itemId) // restart thread every 5 seconds
I'd like to avoid that the service get's killed by Android, blocks other Threads or leads to memory leaks.
What's the best practise for this use-case, any idea?
I don't think my question is opinion-based, because I guess that a pattern exists, which I'm not aware of yet.
App.java
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
// Start MyService to run in the background
Intent service = new Intent(this, MyService.class);
this.startService(service);
}
}
MyService.java
public class MyService extends IntentService {
public MyService() {
super("MyService")
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
try {
int i = 0;
while(true) {
Log.d("MyService", "i = " + String.valueOf(i));
i++;
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
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();
}
I have this inside a Timer:
if (wManager.startScan()) {
try {
//Following method is one I call to process the results
acquireCurrentZoneFromServer.run(client, wManager.getScanResults());
} catch (Exception e) {
e.printStackTrace();
}
}
This works fine. However, when I set the Timer to run in small intervals, such as 1 second, the results I get are repeated in groups of 2.
Any workaround on this?
I think, your solution is wrong. You don't need to check for the scan results every 1 second or any other interval. You should create BroadcastReceiver. BroadcastReceiver will notify your app when it gets the scan results. Method named startScan() does not guarantee the time of delivery of the results. When you try to read scan results every 1 second, it's not deterministic. You may receive something, but you also may not. Nevertheless, the timer is going, what reduces performance and drains the battery, so this solution is not so efficient.
Here's exemplary code snippet presenting the idea:
final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.startScan(); // without starting scan, we may never receive any scan results
final IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.RSSI_CHANGED_ACTION); // you can keep this filter if you want to get fresh results when singnal stregth of the APs was changed
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
wifiManager.startScan();
final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override public void onReceive(Context context, Intent intent) {
wifiManager.startScan(); // start scan again to get fresh results ASAP
wifiManager.getScanResults();
}
};
context.registerReceiver(receiver, filter);
// don't forget to unregister receiver when appropriate
// context.unregisterReceiver(receiver);
You can adjust this snippet to your needs.
If you want to, you can also use my library called ReactiveWiFi available at https://github.com/pwittchen/ReactiveWiFi. It allows you to monitor change of the WiFi access points with RxJava Observables as follows:
new ReactiveWifi().observeWifiAccessPoints(context)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<ScanResult>>() {
#Override public void call(List<ScanResult> scanResults) {
// do something with scanResults
}
});
This solution also uses BroadcastReceiver under the hood similar to the first code snippet, but it's wrapped with observable, so usage is simpler.
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. "
I have a service that needs to compare previous and current html code.
I have configured it to do its work every 1 hour, but it actually does it in weird unlinear intervals. I have added to the code a command to write a log file in order to see whenever the work is done. The result is weird: sometimes the interval is less than an hour, sometimes much more (2-3 hours), and sometimes really 1 hour... edit: when the interval is shorter (1 minute) it operates normally here's my code:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(new Runnable() {
public void run() {
try {
if (isNetworkAvailable()) {
doServiceWork();
check("SLeEPING!!!", c);
}
else {
check ("NO INTERNET", c);
}
}
catch (Exception e) {check("78", c);}
}
}, 1, 3600, TimeUnit.SECONDS);
}
Use AlarmManager. You are making the thread sleep which I am not sure Android guarantees it will work that way. Something like this should help you. This will start the service every hour:
PendingIntent serviceIntent= PendingIntent.getService(context,
0, new Intent(context, MyService.class), 0);
long firstTime = SystemClock.elapsedRealtime();
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
long currenciesIntervalInSec = 3600;
if (automaticCurrencies)
am.setRepeating(AlarmManager.ELAPSED_REALTIME, firstTime, currenciesIntervalInSec*1000, serviceIntent);
Edit:
You should extend IntentService. It takes care of the threads and everything is done in the background (Its particulary useful if you need to do things in the background and you don't need several threads at the same time). Whatever you put in HandleIntent will be executed when you start the service. Internally it will keep a list of the queue.
For example this is a simple service class(You NEED to create both constructors):
public class MyService extends IntentService {
public MyService() {
super("MyService");
}
public MyService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent arg0) {
if (isNetworkAvailable()) {
doServiceWork();
check("SLeEPING!!!", c);
}
else {
check ("NO INTERNET", c);
}
}
}
Also, you need to add this in your manifest (Of course with your own package):
<service android:name="com.services.MyService"/>
Maybe you activity is destroyed by the Android task Manager and it is not shutdown by the timer. This is why you are getting shorter values. And this is why, when setting a very low value, you don't get that.
You should read about how Android handles the Processes and applications here.