Inform service about finished job so he could be stopped - java

I have Service, that in onStart I'm calling to existing AsyncTask <> from new Thread.
public void onStart(Intent intent, int startId) {
Thread thread = new Thread() {
public void run() {
try {
//code that fetch data
}
myTask taskA = new myTask ();
taskA.execute();
}
};
thread.start();
}
After the work in taskA is finished, how he can inform Service so he can be stopped.

Firstly you should override both onStart and onStartCommand in a service to accommodate for all range of Android devices.
Regarding stopping a service from within you can you stopSelf() to do that.

Related

Accurate android Thread Handler postDelayed

I have been trying to write a metronome in Android however I am finding really hard to sync the beats accurately using the Handler postdelayed method. I manage to achieve an accurate timing using a ScheduledThreadPoolExecutor, but the issue is that with the ScheduledThreadPoolExecutor I can't control the timing from within the run method and therefore I am forced to stop and start the scheduled job, which is not ideal. Is there a way to make the Handler postdelayed more accurate? or a way to reschedule the ScheduledThreadPoolExecutor without having to stop and start the thread?
My current code is as below:
public class Metronome extends Service implements Runnable
{
private Handler handler = new Handler();
private SoundPool soundPool;
private long interval;
private void initSoundPool()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
soundPool = new SoundPool.Builder()
.setMaxStreams(1)
.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.build();
} else
{
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
}
soundId = soundPool.load(context, R.raw.secondary_clave, 1);
}
#Override
public void run()
{
handler.postDelayed(this, interval);
soundPool.play(soundId, 1, 1, 0, 0, 1);
}
public void start()
{
handler.post(this);
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
With the ScheduledThreadPoolExecutor it's super accurate, however, I don't have control via the "interval" flag inside the run loop, so if I change the interval I have to terminate the executor and start a new one everytime I need to rechedule which is horrible.
public class Metronome extends Service implements Runnable
{
private SoundPool soundPool;
private long interval;
private ScheduledThreadPoolExecutor beatsPerBarExec;
private ScheduledFuture<?> futureThread;
private void initSoundPool()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
soundPool = new SoundPool.Builder()
.setMaxStreams(1)
.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.build();
} else
{
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
}
soundId = soundPool.load(context, R.raw.secondary_clave, 1);
}
#Override
public void run()
{
soundPool.play(soundId, 1, 1, 0, 0, 1);
}
public void start()
{
beatsPerBarExec = new ScheduledThreadPoolExecutor(1);
futureThread = beatsPerBarExec.scheduleAtFixedRate(this, 0, interval, TimeUnit.MILLISECONDS);
}
public void pause()
{
futureThread.cancel(false);
beatsPerBarExec.purge();
beatsPerBarExec = null;
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
You may be seeing the effects of drift.
Example: you want your Runnable to run every 200msec. You reschedule your Runnable in the run() method using postDelayed() and pass it 200msec as the delay. When the run() method is called the next time, it may not be exactly 200msec since the previous time. Perhaps it is 210msec. Now you reschedule your Runnable to run in another 200msec. This time the run() method may be called again after 210 msec, which means your sound plays 420msec since the first one, etc.
To eliminate drift, you need to determine the exact clock time you want the Runnable to run at, subtract the current time and use that in the call to postDelayed(). This will take into account any potential variance in the thread timing.
Be aware that when you call postDelayed() you are posting a Runnable to run on the main (UI) thread. This is the thread that handles all the UI updates and when your Runnable is ready to run it will just be queued to the main (UI) thread handler, and may not run immediately.
You can mitigate this problem by scheduling your Runnable to run on a background thread instead of the main (UI) thread. However this means your Runnable will be called on the background thread and I don't know if your other code (that plays the sound) needs to run on the main (UI) thread or not.

Service get stopped on app closing

I am working on a wallpaper application in which i am setting a gallery of images on wallpaper with shuffle effect for 5 min, 10 min etc. I am using service for this task. My service works well when app remains in background, but service get stopped when app get stopped.This is my code for service class:
public class WallpaperService extends Service {
ArrayList<String> arrayList;int counter = 0;
boolean serviceStopped;
private IBinder binder = new WallpaperServiceBinder();
public WallpaperService() {
}
private Handler mHandler;
private Runnable updateRunnable = new Runnable() {
#Override
public void run() {
if (serviceStopped == false)
{
createNotificationIcon();
}
queueRunnable();
}
};
public class WallpaperServiceBinder extends Binder {
public WallpaperService getService() {
return WallpaperService.this;
}
}
private void queueRunnable() {
// 600000 : cada 10 minutos, comprueba si hay nuevas notificaciones y actualiza la
// notification BAR
mHandler.postDelayed(updateRunnable, 5000);
}
#Override
public int onStartCommand(Intent intent,int flag, int start_id){
super.onStartCommand(intent,flag,start_id);
arrayList = intent.getStringArrayListExtra("image_url");
return START_STICKY;
}
#Override
public void onRebind(Intent intent) {
Log.v("Service","in onRebind");
super.onRebind(intent);
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public void onCreate() {
serviceStopped = false;
mHandler = new Handler();
queueRunnable();
}
#Override
public void onStart(Intent intent, int startid) {
}
public void createNotificationIcon()
{
counter += 1;
Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
Picasso.with(getApplicationContext()).load(arrayList.get(counter)).into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
try {
final WallpaperManager wallpaperManager =
WallpaperManager.getInstance(getApplicationContext());
wallpaperManager.setBitmap(bitmap);
wallpaperManager.suggestDesiredDimensions(1080, 1920);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
//Here you should place a loading gif in the ImageView to
//while image is being obtained.
}
});
}}
This is the code i am using to start service:
Intent intent = new Intent(CategoryActivity.this,WallpaperService.class);
intent.putExtra("image_url",img_urls);
intent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
startService(intent);
bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
Have you added these lines in manifest file
<application> <service android:name=".ExampleService" /></application>
 
Important Fact about the bindService
If a component calls bindService() to create the service and onStartCommand() is not called, the service runs only as long as the component is bound to it. After the service is unbound from all of its clients, the system destroys it.
Try using Started Service
A started service is one that another component starts by calling
startService(), which results in a call to the service's
onStartCommand() method.
When a service is started, it has a lifecycle that's independent of
the component that started it. The service can run in the background
indefinitely, even if the component that started it is destroyed. As
such, the service should stop itself when its job is complete by
calling stopSelf(), or another component can stop it by calling
stopService().
An application component such as an activity can start the service by
calling startService() and passing an Intent that specifies the
service and includes any data for the service to use. The service
receives this Intent in the onStartCommand() method.
Handling onStartCommand
Notice that the onStartCommand() method must return an integer. The
integer is a value that describes how the system should continue the
service in the event that the system kills it. The default
implementation for IntentService handles this for you, but you are
able to modify it. The return value from onStartCommand() must be one
of the following constants:
START_NOT_STICKY If the system kills the service after onStartCommand() returns, do not recreate the service unless there are
pending intents to deliver. This is the safest option to avoid running
your service when not necessary and when your application can simply
restart any unfinished jobs.
START_STICKY If the system kills the service after onStartCommand() returns, recreate the service and call
onStartCommand(), but do not redeliver the last intent. Instead, the
system calls onStartCommand() with a null intent unless there are
pending intents to start the service. In that case, those intents are
delivered. This is suitable for media players (or similar services)
that are not executing commands but are running indefinitely and
waiting for a job.
START_REDELIVER_INTENT If the system kills the service after onStartCommand() returns, recreate the service and call
onStartCommand() with the last intent that was delivered to the
service. Any pending intents are delivered in turn. This is suitable
for services that are actively performing a job that should be
immediately resumed, such as downloading a file.
Note: In your case you should use Started Service and return START_STICKY or START_REDELIVER_INTENT (based on your requirement) in onStartCommand()
Check Official Documentation for detailed description of the Services.

Can I Make A Background Service That runs at certain intervals of time even after closing the Application

I am Referring this Application https://github.com/hzitoun/android-camera2-secret-picture-taker.
In this Application there are two classes(APictureCapturingService.java & PictureCapturingServiceImpl.java) that takes pictures without preview can these two classes be converted to Background Service that runs always never dies.
Is this possible to have camera capturing process as a background service if yes how to proceed?
I don't know how you are taking picture in your activity but i can guide you to run your app in background even your close your app and you can able to run your camera code in every second..
public class SampleService extends Service {
public SampleService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
#Override
public int onStartCommand(final Intent inten, int flags, int startId) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
// Make use your method here..
}
});
}
}, 5000, 2000);
return super.onStartCommand(inten, flags, startId);
}
#Override
public void onCreate() {
super.onCreate();
}}
And you need to start the service form activity..
startService(new Intent(this, SampleService.class));
I have used this for monitoring the foreground app it will work..

Service code stops when application is stopped

I have an app that gets users messages from database and if there is a new message it pushes a notification i use a service for that.. The service works fine when the app is opened or in the foreground.. But when I close it it doesn't work.. It is not destroyed or stopped it's just doesn't work :S I don't know why.. This is my service code :
public class BGService extends Service {
ArrayList<Message> messages = new ArrayList<Message>();
ArrayList<String> requests = new ArrayList<String>();
Timer timer = new Timer();
Timer timer2 = new Timer();
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onDestroy() {
Log.d("Chat", "BGService Destroyed");
timer.cancel();
timer.purge();
timer2.cancel();
timer2.purge();
}
#SuppressWarnings("unchecked")
#Override
public void onStart(Intent intent, int startId) {
Log.d("Chat", "BGService Started");
messages = (ArrayList<Message>) intent.getExtras().get("messages");
requests = (ArrayList<String>) intent.getExtras().get("requests");
Log.d("Button Clicked", "Messages: " + messages);
new Timer().scheduleAtFixedRate(new TimerTask() {
public void run() {
Log.d("Service", "Running");
}
}, 2000, 2000);
}
}
Your telling the service to stop in your code. Because your using onBind() it appears that your not starting the service and instead binging to it. If you bind to a service the service automatically ends when your activity ends.
If you want to keep your service running.
Start the service to keep service running
Bind to the service to have a service while app is running
Set a notification so the service is in the foreground
Change your manifest so the service runs in a separate process
Start the service so you can return startsticky in the onStartCommand() to tell the os you want this to stick on
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
Start Service. Bind to the service only if you have a connection to communicate with the service back and forth with. This is returned from onBind()
startService(new Intent(context, ServiceLocationRecorder.class));
// bind to the service
if (!mIsBound) {
// Bind to the service
bindService(new Intent(context,
ServiceLocationRecorder.class), mConnection,
Context.BIND_AUTO_CREATE);
mIsBound = true;
}
Binding to the service is used to setup a binder handler that you can communicate to and from the service with. Returning null in onBind() defeats the purpose of the onBind() event so you could skip this code.
/**
* When binding to the service, we return an interface to our messenger for
* sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
set the service to the foreground you do this and the os will be less likely to end your service to get memory for another app.
//this is done inside the service
startForeground(R.id.action_record, getMyCustomNotification());
Run the service in its own process then GC will be able to collect your activity and keep the service running.
<service
android:name="com.example.service"
android:process=":myseparateprocess" >s -->
</service>

Destroy foreground notification when the service get killed

I have a foreground service to download some contents form the web.
Unfortunately, due to bug which reported here my service being killed when a receiver received a broadcast within my service but it's notification won't be killed and I saw the following log in my logcat:
I/ActivityManager(449): Killing 11073:my-package-name/u0a102 (adj 0): remove task
Is there anyway to to destroy foreground notification when it's parent service get killed by OS?
Service ending remove the foreground notification.
#Override
public void onDestroy() {
// mycleanup first
stopForeground(true);
super.onDestroy();
}
Use stopForeground:
#Override
public void onDestroy() {
// as the bug seems to exists for android 4.4
if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.KITKAT)
{
stopForeground(true);
}
super.onDestroy();
}
or
public void onTaskRemoved (Intent rootIntent)
{
// as the bug seems to exists for android 4.4
if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.KITKAT)
{
stopForeground(true);
}
}
Inside the notification check periodically (e.g. Handler) if service is still running using code below:
public static boolean isMyServiceRunning(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MyService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
And if not just shut down notification.
private static final int DEALY = 10000;
private Handler handler = new Handler();
...
handler.postDelayed(ensureSericeIsRunning, DELAY);
...
private Runnable ensureSericeIsRunning = new Runnable() {
#Override
public void run() {
if (!isMyServiceRunning(getActivity())){
//shut down notification
} else {
handler.postDelayed(ensureSericeIsRunning, DELAY);
}
}
};
use onDestroy(). Even tho the documentation says it is not guaranteed to be called, I have tested multiple times and the only cases it doesn't get called is when the system kills the process, but to that point your notification will be killed to, so it's ok.
You could remove the notification from onStop or onDestroy inside of your service class.
P.S there no guarantee this would work, in api 14 and above the service can still be running even without foreground notification, you could return START_STICKY that will bring back your service even if the system destroyed it, system will restart it.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
use the service onTaskRemoved() method.

Categories

Resources