Android data storage performance - android

I m actually developping an Android application with data storage, and I proceed like this :
Activity -> Business services -> Repo (with Spring REST fw). Using this, I m forced to let the activity finish his storing job before closing it (thread treatment, progress dialog ...).
Is this a bad way of coding to use an android service to store data ?
With this, users can continue to navigate, and have the impression to work with a very fluid application. Is this a good solution ?
Thanks

There is no need to keep your activity in the foreground waiting for a background logic to complete.
What you should do instead is to execute this background logic in a way which is 'detached' from your activity.
There are two ways of solving this problem: risky and safe.
The Risky Way
class MyActivity extends Activity {
void calledWhenActivityNeedsToBeClosed() {
// start a thread to do background work
new Thread() {
public void run() {
perform long running logic here
}
}.start();
// and clos the activity without waiting for the thread to complete
this.finish();
}
}
You can use an AsyncTask or any of the java.concurrent constructs instead of the thread. They will all do the work.
I have used this way for years. It mostly works fine. But.. it is inherently flawed.
Why? Because once activity is finish()-ed, Android can at any time reclaim it together with all its resources and including halting of all worker threads.
If your long running work does not exceed several seconds, and I assume your repo update is such, the risk here
is minimal. But why take it?
The Safe Way
Declare a Service and before activity going down activate it to perform the long running action:
class MyActivity extends Activity {
void calledWhenActivityNeedsToBeClosed() {
// delegate long running work to service
startService(this, new Intent(this, MyWorkerService.class));
// and close the activity without waiting for the thread to complete
this.finish();
}
}
This is much safer. Android can, and often does, also kill running services, but is does so much more reluctantly than killing background activities.
Note that if you can see a scenario in which your UI is visible while the worker service is still running,
you would probably want to use an IntentService instead.
Finally - if you want to be absolutely assured that background logic will not be cleared by Android, you
should use a foreground service. Below is how to do it, but please be warned - in cases like you have described, a foreground service is probably over engineering:
static final int NOTIF_ID = 100;
// Create the FG service intent
Intent intent = new Intent(getApplicationContext(), MyActivity.class); // set notification activity
showTaskIntent.setAction(Intent.ACTION_MAIN);
showTaskIntent.addCategory(Intent.CATEGORY_LAUNCHER);
showTaskIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pIntent = PendingIntent.getActivity(
getApplicationContext(),
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notif = new Notification.Builder(getApplicationContext())
.setContentTitle(getString(R.string.app_name))
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(pIntent)
.build();
startForeground(NOTIF_ID, notif);

Related

Android Service running on separate Process gets killed when I swipe out my App (running in other process)

I have an Android Service (implementation of Servcie interface) which is running on a separate process compared to my real app. Unfortunately when I leave my real App (in which I clicked the Button to start my Service) and swipe it out from Task Manager, my Service gets killed as well.
I know there are a lot of questions like this here, but somehow none are targeting the Problem in my concrete constellation or they are vaguely answered.
So in my manifest I have something like:
<application ...>
<activity .../>
<service Android:name="MyService"
Android:label="MyLabel"
Android:export="false"
Android:process=":MyRemoteProcessName" />
</application>
I first have played around with an IntentService, but also switched to an own implementation of the Service Interface (eliminating the IntentService to be the point of failure) which looks something like:
public class MyService extends Service {
private ScheduledExecutorService scheduledWorker = Executors.newSingleThreadScheduledExecutor();
#Override
public void onStart() {
// Init components
startForeground(this, MyNotification);
}
#Override
public int onStartCommand(Intent i, int startId) {
// Execute Work on Threadpool here
scheduledWorker.execute(new ScheduledStopRequest(this, startId), 5, TimeUnit.Minutes);
return START_REDILIVER_INTENT;
}
// Overwritten onDestroy-Method
#Override
public void onLowMemory() {
Log.e(LOG_TAG, "On Low Memory called!");
}
#Override
public IBind onBind() {
// Dont't want to let anyone bind here
return null;
}
// Simply tries to stop the service after e.g. 5 Minutes after a call
private static class MyRunnable implements Runnable {
// Constructor with params used in run method..
#Override
public void run() {
mReferenceToMyService.stopSelfResult(startId);
}
}
}
I'm starting my Service in an onClick-Listener on a special button, with an explicit Intent, which kinda looks like the following:
#Override
public void onClick(View v) {
Intent i = new Intent(this, MyService.class);
startService(i);
}
My Intention is to keep the Service running when the user leaves the app, so that the Service can finish downloading and storing some important data. When the user comes back to my app again, he can view the data (That's why I'm executing it in a separate process). So is this possible?
My assumption right now is, that Android somehow notices my Service is just being used by my App (due to missing IntentFilters in Manifest or explicit call rather than by filter?!) and thus kills it immediately when my App is closed (even when running as ForegroundService as you can see above).
Does it seem possible to you and might some changes in the call of the service fix this problem or am I getting the concept of a service wrong?
(One last note: onLowMemory-Method doesn't get called -> No log entries.)
So, according to your hints (and so new keywords for me to look for) and after some additional research by myself, I think I have solved my problem. During my research I have found an very interisting blog post on this topic, maybe also for you, which is why I would like to share it with you: http://workshop.alea.net/post/2016/06/android-service-kill/ .
After verifying and going through the steps in this article everything seems to work fine (so startForeground seems to solve the problem). I want to point out here, that I have only tested it, with my service instance still running in separate process, so manifest entries as is above.
The actual thing which really confused me at the beginning was my android studio debug session being killed everytime, just after swiping out my app from recent apps (menu). This made me think my service being killed by the system as well. But according to the article (I have added some logs to the callback methods provided) when
Opening my app
starting service
swiping out app
starting app again and finally
calling service again,
I only received callbacks to the methods as if my service would still be running. Having an explicit look at DDMS (tool) also prooved my 2nd process, and thus my service, being still alive. Having verified this, I then cleared all my app data and repeated the steps above (excluding step no. 5). Having had a look in the database afterwards, prooved the data having been downloaded by the service.
For the curious of you:
The process of swiping out my app from recent apps (and thus having the onTaskRemoved callback method being called) lead to another problem. It somehow increases the startId parameter of onStartCommand by 1 so that my DelayedStopRequest malfunctiones and doesn't stop my service anymore.
This means: Repeating above steps 1 - 3 makes me receive startId = 1 in onStartCommand. By calling stopSelfResult(1) later on (which was the latest startId) it returnes false and the service keeps running. Continuing to follow step 4 + 5 then, makes onStartCommand being called with startId = 3 (but should actually be 2! which is skipped somehow). Calling stopSelfResult(3) with parameter 3 later on is then going to stop the service again (also visible in screenshots).
I hope my answer is correct so far (, understandable) and also helpful for you. Thank you for all of your answers which provided beneficial input and also pointed me to the solution. The android version I have been working with is:
4.1.2 - Jelly Bean | API Level : 16
I also added screenshots of the log entries from DDMS (imgur is rejecting my uploads so you'll temporarily have a link to my dropbox):
screenshots from logs from DDMS
Unfortunately running service in a separate process does not help you. I think there is no way you can prevent your service from being closed if user removes its task. You can however restart your service overriding onTaskRemoved. See this answer.
If you want to run this service class indefinitely after closing the app also.. you should use Alaram Manager class ..
public void scheduleAlarm() {
// Construct an intent that will execute the AlarmReceiver
Intent intent = new Intent(this, LocationListnerServiec.class);
// Create a PendingIntent to be triggered when the alarm goes off
final PendingIntent pIntent = PendingIntent.getBroadcast(this, MyAlarmReceiver.REQUEST_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Setup periodic alarm every 5 seconds
long firstMillis = System.currentTimeMillis(); // alarm is set right away
AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
// First parameter is the type: ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC_WAKEUP
// Interval can be INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_DAY
alarm.setRepeating(AlarmManager.RTC_WAKEUP, firstMillis,
60000, pIntent);
}
Use this method for keep checking the service class is on or off.. By using this method Your service class will keep working after destroying of you application.

Correct way to do polling in Android app

I have to develop a simple application that every 5 seconds fetch a json from a server. I know that this will drain the battery but since I don't have control on the server, this is the only solution I have at the moment, so stay with me. (It's a demo app, the final release will be totally different)
What I did:
I created an IntentService and in his onHandleIntent I do the http request and manage the response asynchronously. Reading the docs, the IntentService is preferred to the Service. At the end, as seen in other topics, I use the AlarmManager class to recreate the intentService. That's the method:
private void repeat() {
lastTrigger = System.currentTimeMillis();
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent serviceIntent = new Intent(this, StatusPollingService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, serviceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, lastTrigger + UPDATE_INTERVAL, pendingIntent);
}
In my onCreate in my first Activity, I simply start the service:
serviceIntent = new Intent(BaseActivity.this, StatusPollingService.class);
startService(serviceIntent);
My issues:
When I close my application (using back button), the intentService continues to do the requests and recreates itself (as expected), so I have to kill it manually
I'd like to stop to do polling every time my app goes in background and restart when goes in foreground, to limit the battery drain
My other issues:
I implemented a class that triggers a listener when the app goes in background/foreground. I implemented the interface in my BaseActivity and in my IntentService. When the app goes in background, a boolean became false and I don't execute the repeat() method. When I go in foreground, in my method onBecameForeground I simply create the service:
serviceIntent = new Intent(BaseActivity.this, StatusPollingService.class);
#Override
public void onBecameForeground() {
startService(serviceIntent);
}
Going in background there's no problem, but going in foreground multiple intentService are created, one intentService per every Activity I have in that moment (I see it in the log).
My questions:
Is this the best way to do this job? How could I resolve these problems?
If this is the best (or the less bad) way, how can I create a single instance of the IntentService?
Thank you so much for your help
If you only need it in the foreground, then I'd not bother with the alarms. Just post intents every 5 seconds to your intent service using Handler.
Supposedly there should be only one instance of the intent service, so if you post multiple intents for processing, they will get queued and handleIntent will be called in your intent service for each intent. Note that, however, that if your service is processing intents fast enough, then it may finish (and destroyed) before you post another intent - so you'll see multiple instances of the service being created. But there will only be one instance at a time.
Edit: to expand a bit, you will need the alarms when (and if) you'll be polling the server in background, so don't through that code away ;) Handler will keep "freezing up" in background as the OS doesn't count the time while the device was sleeping for the Handler postponed execution.

Android Service that constantly updates Activity

I have built an app for running. It runs an Activity with a timer shown in the user interface, a gps listener that collects coordinates and a lot of other things (the activity does a lot of work).
Now the request of my client is to move all the activity logic in a Service. In this way, when you start a running session, the Service would start and the notification (very simple, just with a static text) would appear. The activity should keep track of the work made in the Service (timer should go on, speed should be shown, ecc...). Tapping on the notification should bring up the activity. If the activity is closed or crashes the Service should keep going on and when you tap on the notification a new Activity should be brought up without the user noticing any difference (the timer should keep showing the right time, the average speed should comprehend the speeds relevated before the activity crash, ecc...).
I know there are a lot of ways to do that.
What I am asking is: what is the best way? Are there examples of such behavior from where to start? What are the common errors I should avoid? Are there best practices to follow?
Thank you
I developed an app with similar service behaviour. It also requires a service which collects data and some activities for showing the data.
For these kind of applications you want to keep the service alive until the user stopps it manualy but it is still possible for android that it kills the service if the device is low on memory.
For the service - activity interaction you need to bind to a service. A good documentation is available here: http://developer.android.com/guide/components/bound-services.html
Be sure to return START_STICKY in the onStartCommand function of the service. This will make sure the intent will be null when the service was restored by the system and tell android that you start and stop your service explicit.
When binding to the service from the activity you need to check if the service is ready (was not restored by the system). This can be done by adding a "ready" field inside the service that is false by default and is set to true if the onStartCommand intent is not null. Therefore you can react properly to a restored service and start the app from the beginning.
To keep the service alive with a high priority you need to call startForeground inside the service. This also requires to show a notification so the users knows a service is running in the background.
Inside service you can use local broadcastmanager.
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
broadcaster = LocalBroadcastManager.getInstance(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 1000);
return START_STICKY;
}
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
DisplayLoggingInfo(); //do watever you want to push
handler.postDelayed(this, 1000); // 10 seconds
}
};

Service being re-Created by AlarmManager

I have a fairly standard Service which I wish to trigger using an alarm. Here's the initiation section of the service:
class MyService extends Service {
private Context context;
private AlarmManager alarmManager = null;
private final String startReason = "com.stuff.myreason";
private final int REASON_NO_INTENT = 0;
private final int REASON_ALARM = 1;
private final int REASON_X = 2; // and so on.
#Override
void onCreate() {
super.onCreate();
context = getApplicationContext();
alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
// do onCreate stuff
}
#Override
int onStartCommand (Intent intent, int flags, int startId) {
int reason = REASON_NO_INTENT;
if (intent != null) {
reason = intent.getExtra(startReason, REASON_NO_INTENT);
}
switch(reason) {
// handle the different reasons we may have been "started"
}
return START_STICKY;
}
}
When I trigger it using context.startService from an activity, it starts absolutely normally. In particular, if it is already running it doesn't (re)start from scratch but simply enters the existing instantiation via onStartCommand(). This is the expected behaviour. However, when I trigger it using the AlarmManager:
Intent intent = new Intent(context, MyService.class);
intent.putExtra(purposeOfStartCode, REASON_ALARM);
PendingIntent pi = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, /* A time in the future */, pi);
When the alarm is due it seems to restart the service from scratch: it starts a new instantiation, calls onCreate() and then onStartCommand() rather than just calling onStartCommand() in the already running instantiation.
I have already tried changing the PendingIntent flag to FLAG_ONE_SHOT and replacing context with MyService.this with no improvement.
I am rather bemused by this - can anyone explain this behaviour and suggest ways to get it to behave as expected?
EDIT - The collection of actions that resulted in a solution are in my answer below.
After some investigation and work, I've discovered a number of things. Having done all of them, this problem looks like it's disappeared:
If you override onStart and onStartCommand in a service (to allow for older devices) and you put super.onStartCommand in the latter, it will call onStart, meaning you get every intent coming in twice!
As per one of the other answers (and comments on it), the AlarmManager is designed and specified to deliver Broadcast intents, not other types. However, in practice, it isn't picky and seems to honour other forms. I think that this was one of the keys in resolving the issue.
If the service is in the same process as other activites etc, the service sometimes seems to "just get restarted". This may be the actual cause of the issue noted in this question. See Android service onCreate is called multiple times without calling onDestroy.
Things seem to be more stable when solely using intents to communicate with the Service rather than binding and using a Messenger or binding and accessing methods. Whilst both of these are correct, they are quite complex to manage (although you could use this approach: What is the preferred way to call an Android Activity back from a Service thread and Using the Android Application class to persist data). Whilst I fully appreciate that the android docs disagree with me, in my observation moving to broadcast intent only communication seemed key. If you go for the separate process approach you'll have to do this anyway.
It pays to be consistent in how you declare and address your classes. It's a bit messy, but, because it sometimes seems to pay to use full names ("com.company.superapp.CleverService") rather than short ("CleverService" or ".CleverService"). So, it's probably better to always use full names.
The rule of thumb floating around out there about contexts ("use getApplicationContext") isn't really the right way to do it. See When to call activity context OR application context?; in essence use this, unless you really need to use a broader context, and manage your variables well.
It's possible for the garbage collector to clear up something still in use if it was created in an Activity, Service, Thread, AsyncTask, etc that is no longer around. If the application is based around a service, it may be wise to make a copy of classes coming in so that they don't get cleared up later.
A neater way to start a service than is often suggested is to give the service an intentFilter with it's full name as the action. You can then create the intent to start it with just the class name as a string. This means you don't have to worry about context. See Issue in Calling Service.
Well, I'm actually surprised that it runs your Service at all! The PendingIntent that you pass to the AlarmManager needs to be a broadcast Intent. So you need to rearchitect your code a bit. The AlarmManager will trigger a BroadcastReceiver and the BroadcastReciever can then call startService().
See the description of AlarmManager.set()
I got this to work by using the following code:
AlarmManager almgr = (AlarmManager)MyContext.getSystemService(Context.ALARM_SERVICE);
Intent timerIntent = new Intent(MyUniqueLabel);
timerIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent pendingOffLoadIntent = PendingIntent.getBroadcast(MyContext, 1, timerIntent, 0);
you MUST do these things for it to work.
1.) Call addFlags and the intent and pass it in FLAG_RECEIVER_FORGROUND
2.) Use a non-zero request code in PendingIntent.getBroadcast
If you leave any of those steps out it will not work.

Using startForeground() with an Intent Service

I am trying to keep alive a service that reacts to screen on/off changes. The service would work perfectly for awhile, but then eventually it would be killed. I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying. I understand that there is no way to keep a process alive forever, without error, but I feel like I must be doing something wrong, as adding startForeground() added no extra life to the process. Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user).. however, due to the nature of what I am trying to accomplish, the receiver needs to run until explicitly told to stop.
Any suggestions?
Relevant Code:
public class UpdateService extends IntentService {
public UpdateService() {
super(null);
}
#Override
protected void onHandleIntent(Intent intent) {
final int myID = 1234;
Intent notificationintent = new Intent(this, Main.class);
notificationintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendIntent = PendingIntent.getActivity(this, 0, notificationintent, 0);
Notification notice = new Notification(R.drawable.icon_image, "***********", System.currentTimeMillis());
notice.setLatestEventInfo(this, "*************", "***********", pendIntent);
notice.flags |= Notification.FLAG_NO_CLEAR;
startForeground(myID, notice);
boolean screenOn = intent.getBooleanExtra("screen_state", false);
// Blah Blah Blah......
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
(Updated) I suppose there are the following possible cases:
1) documentation for IntentService states:
the service is started as needed, handles each Intent in turn using a
worker thread, and stops itself when it runs out of work.
So, it might be that your service is normally stopped after onHandleIntent() is finished (especially, as you mentioned that startForeground() added no extra life to the process).
2) You might try to check if it's somehow can be related to device going to sleep (or maybe you are starting your service by schedule and awkening device - in this case you might need to acquire WakeLock)
3) In the very rare cases, the system still can kill foreground process - so if you do a lot of allocations (really lot) and some other work in onHandleIntent() (instead of "Blah Blah Blah" at your code) - you might run into it - but I suppose it's not the case.
As question's title is "Using startForeground() with an IntentService" - would like to clarify that too:
I believe nothing (architecture, best practices, android framework, java docs for IntentService) prevents you from running your intent service as a foreground. Of course you need to thought out carefully its usage and whether you actually need a foreground service. Some ideas are available here. For sample code see below. (Sample code can end up showing multiple notifications if you queued multiple jobs/intents into IntentService, so there might be better solution depending on your need.)
public class ForegroundService extends IntentService {
private static final String TAG = "FrgrndSrv";
public ForegroundService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
Notification.Builder builder = new Notification.Builder(getBaseContext())
.setSmallIcon(R.drawable.ic_foreground_service)
.setTicker("Your Ticker") // use something from something from R.string
.setContentTitle("Your content title") // use something from something from
.setContentText("Your content text") // use something from something from
.setProgress(0, 0, true); // display indeterminate progress
startForeground(1, builder.build());
try {
doIntesiveWork();
} finally {
stopForeground(true);
}
}
protected void doIntesiveWork() {
// Below should be your logic that takes lots of time
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
IntentService automatically shuts down when onHandleIntent() completes. It is to perform a brief bit of work when something occurs. It is not supposed to live for more than a few seconds, typically.
I am going to assume that this is tied to what I wrote in your last question in this area.
Something in the rest of your app will be registering and unregistering the BroadcastReceiver for the screen on/off events -- apparently, from your comments, it is an activity. If what you want to do when those things occur is very very quick (on the order of a few milliseconds), just do the work in onReceive(), and be done with it.
If, on the other hand, you have more work than a few milliseconds' worth, you will need to have that work be done by something else that can do the work on a background thread. For example, if the "something in the rest of your app" that registered the BroadcastReceiver is indeed an activity, the activity might just spawn an AsyncTask to do the work.
Another possibility is to use an IntentService. You elected to go down this path in your work prior to that last question. I do not know why. Regardless, an IntentService, like an AsyncTask, is supposed to be a short-lived component -- you send it a command via startService(), it does its work in onHandleIntent(), and it goes away.
With all that in mind, let's talk about your specific points.
The service would work perfectly for awhile, but then eventually it would be killed.
It is unclear what you think "killed" means. An IntentService automatically goes away once onHandleIntent() returns, and that ideally should occur within a handful of seconds.
I am now attempting to use startForeground() to keep the process alive, but it still seems to be dying.
Again, it is unclear what you think "dying" means. Bear in mind that the mere existence of an IntentService does not stop the CPU from shutting down once the screen turns off, and startForeground() has nothing to do with that.
Also, as a side note, Logcat complains about a leak, as unregisterReceiver() is not called (except manually by a button press from the user)..
You also need to unregister the receiver before the user exits the activity. It is usually a good idea to call registerReceiver() in onResume() and unregisterReceiver() in onPause().
If memory is low and your consuming too much memory and your sitting in the background for too long then you WILL be killed by the ActivityManager.

Categories

Resources