I'm trying to understand the IntentService class so that I can build my own extension in which I can block the queue the handles all the threads whenever I want. The code below has been copied from here: http://www.google.com/codesearch#cZwlSNS7aEw/frameworks/base/core/java/android/app/IntentService.java&q=IntentService&exact_package=android&type=cs
In my main Activity I have the following code:
Intent intent = new Intent(this,DownloadService.class);
for(int i=0;i<filesArray.length;i++){
startService(intent);
}
The code basically starts an IntentService for every file in the array. Now I want to know how does the IntentService class queue up this threads or services and in particular I want to know how I can modify the IntentService class to stop 'end' the service and clear the queue from all the threads which are waiting to be processed. Really appreciate any help o this. It has been bothering me for quite some time now!!!!
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {#link #onStartCommand(Intent, int, int)} will return
* {#link Service#START_REDELIVER_INTENT}, so if this process dies before
* {#link #onHandleIntent(Intent)} returns, the process will be restarted
* and the intent redelivered. If multiple Intents have been sent, only
* the most recent one is guaranteed to be redelivered.
*
* <p>If enabled is false (the default),
* {#link #onStartCommand(Intent, int, int)} will return
* {#link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
#Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
#Override
public void onDestroy() {
mServiceLooper.quit();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 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.
*
* #param intent The value passed to {#link
* android.content.Context#startService(Intent)}.
*/
protected abstract void onHandleIntent(Intent intent);
}
EDIT:
I'm trying to Download multiples files one by one using an IntentService. However, when the Internet Connection is down the IntentService gets stuck on the current thread that it is processing or it just continues it's lifecycle. I want to end the service's lifecycle when the Internet connection is down.
EDIT:
I have copied the code for the IntentService and created a class MyIntentService using the same code. I have made the following changes to the code:
public volatile Looper mServiceLooper;
public volatile ServiceHandler mServiceHandler;
public String mName;
public boolean mRedelivery;
final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
the vars above have been made public and the inside class has been changed to final class. Now in my DownloadService which extends the MyIntentService I have a code like the following:
if(!isOnline()){
mServiceHandler.removeMessages(0);
mServiceLooper.quit();
stopSelf();
}
I'm checking first if we have Internet connection, if not then I'm removing the messages from the service handler's queue (assuming i'm doing it correctly), I'm stoping the loop, and i'm stoping the service which i'm not sure which service it is stopping. HEELP!!
An intent service will hold a queue of intents that are automatically run on a separate thread. The service lives for as long as there are messages to be processed. The messages are guaranteed to be run sequentially.
You have no control over when the service finishes it's lifecycle but that shouldn't matter to you. It is all transparent from your point of view.
EDIT: you will need to handle the queue manually and expose a method to clear it when needed.
I believe the Handler.removeMessages method will do the trick.
Also take a look at this: Drop intents while IntentService is processing
EDIT:
I have never done this myself and can't be sure if it will work. What you want is to set the "what" of the message before it is send so you can remove it after. In the onStart method a Message is obtained and two parameters are set. Also try setting the 'what' parameter.
The reason removeMessages wasn't working for you is because the what field of the Message is never set, you need to set that and then clear accordingly.
Related
I have a BroadcastReceiver here:
NotificationServiceReceiver:
public class NotificationServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(RestService.ACTION_PENDING_REMINDERS_UPDATED)) {
//Reminders updated
NotificationServer.startNotificationWorkRequest(context);
}
}
A Notification Server:
public class NotificationServer extends IntentService {
private static final String LOG_TAG = "NotificationService";
public static final String ACTION_SHOW_NOTIFICATION = "com.android.actions.SHOW_NOTIFICATION";
// this is a bypass used for unit testing - we don't want to trigger this service when the calendar updates during
// the intergration tests
public static boolean sIgnoreIntents = false;
private WorkManager mWorkManager;
private LiveData<List<WorkStatus>> mSavedWorkStatus;
public NotificationServer() {
super(NotificationServer.class.getName());
mWorkManager = WorkManager.getInstance();
}
/**
* Handles all intents for the update services. Intents are available to display a particular notification, clear all
* notifications, refresh the data backing the notification service and initializing our timer. The latter is safe to
* call always, it will check the current state of on-device notifications and update its timers appropriately.
*
* #param intent - the intent to handle. One of ACTION_SHOW_NOTIFICATION,
* ACTION_REFRESH_DATA or ACTION_INIT_TIMER.
*/
#Override
protected void onHandleIntent(Intent intent) {
startNotificationWorkRequest(this);
}
public void startNotificationWorkRequest(Context context) {
WorkContinuation continuation = mWorkManager
.beginUniqueWork(IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker.class));
}
}
I want to start a WorkManager task onReceive of the Broadcast Receiver. The problem is I can't do this statically as I need access to the current WorkManager object. The example code that Google provides here: https://github.com/googlecodelabs/android-workmanager/blob/master/app/src/main/java/com/example/background/BlurActivity.java
Grabs the ViewModel like this: ViewModelProviders.of(this).get(BlurViewModel.class);
I can't do this obviously because my notification server class is not a view model. How should I approach this problem?
For anyone that sees this, you can use WorkManager.getInstance() to get the WorkManager object statically. There is only one instance of WorkManager, just make sure you initialize it like this on the start of your application: WorkManager.initialize(this, new Configuration.Builder().build());
Android Custom Work Manager Config official documentation
I'm writing an app which should collect some sensor data every 10 seconds or so and write them to disk.
So far, I have an Activity which starts a service. The service has a runnable, which uses a handler to run my task periodically via handler.postDelayed(). See below for the (shortened) code.
So far, this works fine as long as the device is active. As soon as the device goes into idle, it doesn't run my task until it wakes up again.
So, my question is how to run my task ALWAYS.
With setExactAndAllowWhileIdle(), the AlarmManager seems to offer exactly what I need, but...
To reduce abuse, there are restrictions on how frequently these alarms will go off for a particular application. Under normal system operation, it will not dispatch these alarms more than about every minute (at which point every such pending alarm is dispatched); when in low-power idle modes this duration may be significantly longer, such as 15 minutes.
Battery life has just a minor priority, though not being awake the entire time would be fine. (Not sure if android can be awake for just a second or so)
MyActivity
...
public void onStartService(View view) {
Intent i= new Intent(getBaseContext(), MyAppService.class);
getBaseContext().startService(i);
}
public void onStopService(View view) {
stopService(new Intent(getBaseContext(), MyAppService.class));
}
....
MyService
public class MyAppService extends Service {
MyRunnable mr;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mr= new MyRunnable(getApplicationContext() );
mr.Start();
return Service.START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mr.Stop();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
MyRunnable
public class MyRunnable implements Runnable{
// context is needed for sensorListener (?)
private Context myContext;
private Handler handler;
public MyRunnable(Context c){
myContext=c;
handler= new Handler();
}
public void Start(){
run();
}
public void Stop(){
handler.removeCallbacks(this);
// some clean-up
}
#Override
public void run() {
//acquire and write to file some sensor data
handler.postDelayed(this, 10000);
}
}
i think what you are looking for is STICKY SERVICE.
Officail Docs: 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 were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.
you just need to pass a flag at the time of calling the service.
To know the difference between IntentService and Service in Android, I created the below posted small test of an IntentService class. The IntentService class can be started using
startService(intent); which will result in a call to nStartCommand(Intent intent, int flags, int startId). Also to send values from the IntentService class to the MainActivity
for an example, we should send it via sendBroadcast(intent); and the MainActivity should register a broadcastReceiver for that action so it can receive the values sent via
sendBroadcast(intent);
so far I cant see any difference between Service and IntentService!! Since they are similar in the way of starting them and the way they broadcast data,can you please tell me in
which context they differ?
please tell me why i am receiving those errors and how to solve it
MainActivity
public class MainActivity extends AppCompatActivity {
private final String TAG = this.getClass().getSimpleName();
private Button mbtnSend = null;
private int i = 0;
private BroadcastReceiver mBCR_VALUE_SENT = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MyIntentService.INTENT_ACTION)) {
int intnetValue = intent.getIntExtra(MyIntentService.INTENT_KEY, -1);
Log.i(TAG, SubTag.bullet("mBCR_VALUE_SENT", "intnetValue: " + intnetValue));
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver(this.mBCR_VALUE_SENT, new IntentFilter(MyIntentService.INTENT_ACTION));
this.mbtnSend = (Button) findViewById(R.id.btn_send);
this.mbtnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), MyIntentService.class);
intent.putExtra("intent_key", ++i);
startService(intent);
}
});
}
}
MyIntentService:
public class MyIntentService extends IntentService {
private final String TAG = this.getClass().getSimpleName();
public final static String INTENT_ACTION = "ACTION_VALUE_SENT";
public final static String INTENT_KEY = "INTENT_KEY";
public MyIntentService() {
super(null);
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
setIntentRedelivery(true);
}
#Override
public void onCreate() {
super.onCreate();
Log.w(TAG, SubTag.msg("onCreate"));
}
#Override
protected void onHandleIntent(Intent intent) {
Log.w(TAG, SubTag.msg("onHandleIntent"));
int intent_value = intent.getIntExtra("intent_key", -1);
Log.i(TAG, SubTag.bullet("", "intent_value: " + intent_value));
Intent intent2 = new Intent();
intent2.setAction(MyIntentService.INTENT_ACTION);
intent2.putExtra(MyIntentService.INTENT_KEY, intent_value);
sendBroadcast(intent2);
SystemClock.sleep(3000);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, SubTag.msg("onStartCommand"));
return super.onStartCommand(intent, flags, startId);
}
In short, a Service is a broader implementation for the developer to set up background operations, while an IntentService is useful for "fire and forget" operations, taking care of background Thread creation and cleanup.
From the docs:
Service A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.
IntentService Service is a base class for IntentService Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Refer this doc - http://developer.android.com/reference/android/app/IntentService.html
Service
This is the base class for all services. When you extend this class, it’s important that you create a new thread in which to do all the service’s work, because the service uses your application’s main thread, by default, which could slow the performance of any activity your application is running.
IntentService
This is a subclass of Service that uses a worker thread to handle all start requests, one at a time. This is the best option if you don’t require that your service handle multiple requests simultaneously. All you need to do is implement onHandleIntent(), which receives the intent for each start request so you can do the background work.
Below are some key differences between Service and IntentService in Android.
1) When to use?
The Service can be used in tasks with no UI, but shouldn’t be too long. If you need to perform long tasks, you must use threads within Service.
The IntentService can be used in long tasks usually with no communication to Main Thread. If communication is required, can use Main Thread handler or broadcast intents. Another case of use is when callbacks are needed (Intent triggered tasks).
2) How to trigger?
The Service is triggered calling to method onStartService().
The IntentService is triggered using an Intent, it spawns a new worker thread and the method onHandleIntent() is called on this thread.
for more clarity refer this
http://www.onsandroid.com/2011/12/difference-between-android.html
I have a background service that should always run.
It should wait for in-coming e-mails, when an e-mail arrives(to my old phone), it will trigger an action. (to send an sms to my new phone). Not a clever user-case but just learning android.
However, All the above works fine if I keep the app in 'Recent' apps. The moment I swipe it off, it doesn't work.
Any idea on how can I achieve this?
I have seen in some apps like Whatsapp, Facebook etc.. that even after we swipe off, there is a background service running to listen new notification etc.
How do I achieve with my app??
For that you need to make service sticky return START_STICKY onStartCommand of the service. and in the doc its mentioed if the process is killed system will recreate again
if this service's process is killed while it is started (after
returning from onStartCommand(Intent, int, int)), then leave it in the
started state but don't retain this delivered intent. Later the system
will try to re-create the service. Because it is in the started state,
it will guarantee to call onStartCommand(Intent, int, int) after
creating the new service instance; if there are not any pending start
commands to be delivered to the service, it will be called with a null
intent object, so you must take care to check for this.
public class MyService extends Service {
/*
* (non-Javadoc)
*
* #see android.app.Service#onBind(android.content.Intent)
*/
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handler.postDelayed(run, 1000);
return Service.START_STICKY;
}
private Runnable run = new Runnable() {
#Override
public void run() {
handler.removeCallbacks(run);
handler.sendEmptyMessage(0);
}
};
private Handler handler = new Handler() {
/*
* (non-Javadoc)
*
* #see android.os.Handler#handleMessage(android.os.Message)
*/
#Override
public void handleMessage(Message msg) {
Log.e("handleMessage", "" + System.currentTimeMillis());
handler.postAtTime(run, 1000);
}
};
}
Create sticky service for that:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
START_STICKY if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Later the system will try to re-create the service. Because it is in the started state, it will guarantee to call onStartCommand(Intent, int, int) after creating the new service instance; if there are not any pending start commands to be delivered to the service, it will be called with a null intent object.
I am used to developing standalone applications, ones that you click on, it runs, and when you are done, you exit.
I am now interested in tackling a new type (not sure if that's the right word) of app, and was wondering how I should go about it. I am not sure what to research, and would appreciate your advice to help me get the ball rolling. I'll give you an idea about what I have in mind.
My app would need to perform a special action in the dialer. When the user dials a number and is in the middle of a call, I would like the user to be able to press the Menu key, and find an option to scroll through all their contacts (either the stock app, or my own list which I grab from the contacts stored in the phone), and select one. Upon selection, that contact's number is pasted into the dialer (keep in mind, in the middle of a call).
I certainly don't expect an answer telling me how to do this exactly, I just need some guidance as I have never written an app of this nature before. On top of that, is it even possible to do what I want to do?
Thank you.
You need to go through Android Service or IntentService. A Service is an application component that can perform long-running operations in the background and does not provide a user interface(UI).
The following example is taken from android blog which is an implementation of the Service class
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
#Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
On the other hand, The same thing can be achieved using IntentService, which is a base class for Services that handle asynchronous requests on demand.
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
You can also go through SO post https://stackoverflow.com/a/4353653/432903
If your app isn't mainly written in javascript/webview/phonegap, then all you have to do is look at the Service class. That class and the linked documents tell you everything you need to know.
maybe you can use an IntentFilter so you can get a system notify when the user uses a dialer.
and you should learn the Service component which can work in background in android.