tl;dr: my app doesn't update my SharedPreferences with the last value from the server because sometimes the phone is sleeping.
Explanation:
I'm new in this android world, and I've been working on my first "real" application but I'm having a few issues right know...
Here's how I need my application to work:
Check every 30 minutes for new data (data is pulled from a json file on my server)
If the data is different from the stored on the phone, then store the new data and send a notification to the user. (I'm using SharedPreferences for the "storage")
Here's what I used to try to achieve that:
MainActivity.java / Activity (my main activity, duh')
cargarJson.java / AsyncTask (a class that pulls json data and returns a jsonobject). Cargar = Load (in spanish)
DolarService.java / IntentService (The service creates a loadJson object and execute it, then sends the notification).
AlarmReceiver.java / BroadcastReceiver (It's a receiver that has an AlarmManager that executes my DolarService every 30 minutes and runs everytime the phone boot up and when the user runs the app for the first time).
I kind of managed to do it, but the problem is that I don't want to use wakelocks because it drains the battery really fast.
My AlarmManager doesn't use wakelocks so when it sends the notification every 30 minutes it contains the last value stored in the SharedPreferences when the phone was "awake" and not the new data from the server.
Here's my source:
https://gist.github.com/anonymous/d5cccf5ba331f041630e
My question: how do I keep my up-to-date data in my SharedPreferences (updating it every 30 minutes) without using wakelocks. Also, is something wrong with my approach to do this app ?
Thank you in advance and sorry for my poor english, I tried my best to explain my situation!
It doesn't look like you're using wake locks. You can make sure of this, however, by removing your <uses-permission android:name="android.permission.WAKE_LOCK" /> from your manifest, and then you'll know for certain that your application isn't acquiring a wakelock, because it doesn't have permission.
Edit:
You're using your IntentService incorrectly. See the documentation on how to implement an IntentService. An IntentService already creates it's own worker thread, similar to an AsyncTask, so executing an AsyncTask within an intent service is unnecessary in your case. Instead, you should pull out everything in your AsyncTask and implement it in your IntentService's onHandleIntent(), rather than it's onCreate() and onDestroy() methods.
The other option would be to use a normal Service, and stop it in your AsyncTask's onPostExecute() method.
Also, this is a little off topic, but when passing a context to another object, you should use a WeakReference<T>:
WeakReference<Context> context;
public cargarJson(String jsonUrl, Context context) {
this.jsonUrl = jsonUrl;
this.context = new WeakReference<Context>(context);
}
All things are fine. But its good if you use a backgorund service for your server operations and start this service through the AlarmManager followed by PendingIntent in every 30 minutes.
Related
I am using oreo 8.1.0.
I am facing a weird problem whose solution I can't find on stackoverflow, hence I am writing this question. I know one solution that through foreground service, I can implement it but I don't find notification user friendly in my application context.
I will describe my problem using two cases.
Case 1:
When user opens my app by clicking on the icon and removes it from recent apps, then service automatically restarts. This is fine.
Case 2:
Here my app is closed and is not in recent apps.
When user copies a text, then my service starts one of the activity of my app but when he removes it from the recent apps, then my service gets stopped permanently.
So my problem lies in second case,I don't want my service to get killed. Even if it gets killed I want it to restart.
I tried all the methods mentioned on the stackoverflow like using START_STICKY and onTaskRemoved but I am not able to make it work.
Even I tried killing my activity whenever user clicks on on recent app button and remove it from the recent apps programmatically but this also did not work.
Though this restarts the service even in second case when user kills my app using the back button.
This part of the code is from the activity which opens when user copies some text.
#Override
public void finish() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
super.finishAndRemoveTask();
} else {
super.finish();
}
}
This part of the code is from the service that starts the activity.
#Override
public void onTaskRemoved(Intent rootIntent){
Log.d("testing 12","onTaskRemoved");
Intent restartServiceTask = new Intent(getApplicationContext(),CBWatcherService.class);
restartServiceTask.setPackage(getPackageName());
PendingIntent restartPendingIntent =PendingIntent.getService(getApplicationContext(), 1,restartServiceTask, PendingIntent.FLAG_ONE_SHOT);
AlarmManager myAlarmService = (AlarmManager) getApplicationContext().getSystemService(this.ALARM_SERVICE);
myAlarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartPendingIntent);
super.onTaskRemoved(rootIntent);
}
#Override
public int onStartCommand(Intent intent,int flag,int startId){
super.onStartCommand(intent, flag, startId);
return START_STICKY;
}
EDIT 1
I just need the service to remain alive.Here are the things that will not hinder the user experience in my app case
1. Killing the activity by yourself programitically when onPause is called so that service does not get killed is acceptable if you make it happen
2. Showing the notification for few second is acceptable
3. Restarting the service is acceptable
From Android Oreo there is a paradigm shift to limit background service execution. It was a frequent criticism affecting battery life, performance and security. As posted in the comments above there are alternative approaches such as JobScheduler.
Refactoring services to the JobScheduler pattern can be quite involved. I advise you look into changing your IntentService to a JobintentService from the Android support v4 library. It uses JobScheduler on Oreo and later targets but reverts to the older IntentService design on older devices. You just need to replace your override of onHandleIntent with onHandleWork in your Service implementation.
Add
android:permission="android.permission.BIND_JOB_SERVICE"
to the service declaration in your AndroidManifest.xml. It can also be useful to add
<uses-permission android:name=”android.permission.WAKE_LOCK” />
But, saying all that, reading through your question, it seems to me your basic complaint is with the cosmetic issue of foreground services requiring a notification. In my view, your solution is either to simply use Context.startForegroundService, or make sure your service is bound. Either way there is visible content to the user that the service is running. You will find it an uphill struggle to try to subvert Android design decisions; better to embrace it - they are wanting you to show these notifications from now on.
Anyway updating your code for newer Android targets is still good practice even if you can't completely avoid those notification icons.
This was the problem
Whenever my service used to start the activity, my service used to get destroyed after the activity used to get started.
Why onTaskRemoved was not working?
The thing I was doing was not working because my service used to get destroyed and hence it was not active to listen to onTaskRemoved.
How I solved it?
The solution to this problem was putting a check in the activity(started by service) to check whether the service is alive or not. As in my case service was getting destroyed. Hence I need to start the service again if the service is not alive.
It even fits the OREO design pattern as we can start the background service when the app is in foreground and the service will stay alive even if the activity gets destroyed.
On Oreo though after sometime service gets destroyed but this is a different problem.
More Info
I am trying to implement a background service that makes an HTTP request to an API every 15mins for the whole day, starting when a certain activity is started. I need the service to be started every 15 minutes even if my application is not running or I am in another activity of that application. I've searched for an example for how to proceed an have looked at some Stackoverflow questions and answers for example: Start Android Service after every 5 minutes. which linked to this page : http://code4reference.com/2012/07/tutorial-on-android-alarmmanager/
From the discussion I know that I need to use an alarm manager, however all the implementations do not have this being done in the same activity they have broadcast receiver. The reason I would want to have the implementation in the same activity is because the results of the request is what I want to display on the UI.
Is there a way of implementing an alarm manager in this type of situation
however all the implementations do not have this being done in the same activity they have broadcast receiver
That is because that is your only viable option, and even that will not work well on Android 6.0+. The recipe for using AlarmManager for this sort of scenario is to have it start a WakefulBroadcastReceiver, which in turn will work with an IntentService to do the work and go away when the work is completed.
On Android 6.0+, courtesy of "Doze mode", your AlarmManager events will not fire every 15 minutes, if the device is not charging and not moving. Also, courtesy of "app standby" on Android 6.0+, your AlarmManager events will not fire every 15 minutes, if the user has not been in your application's UI for some time and the device is not charging.
The reason I would want to have the implementation in the same activity is because the results of the request is what I want to display on the UI.
This runs counter to an earlier statement that you made:
I need the service to be started every 15 minutes even if my application is not running
If your application is not running, then you do not have an activity.
You are welcome to have your IntentService post a message on an event bus, such as greenrobot's EventBus, to let your activity know about the results of the work... if the activity happens to be around (otherwise, the message will be ignored).
I have used the Alarm Manager to allow the user to schedule a certain task to repeat at a certain amount of time. In the context of the application, I have an Android game where people are able to schedule when to send their ships. The Alarm Manager is working fine, alarms get kicked off at the right time etc.
What happens when an alarm is fired off (usually every hour and a bit), is that my IntentService will start communicating with the server to send the ships. This action may take 1 minute, but it can last up to 10 minutes. Even this all works fine. Alarms get fired, ships get sent, everyone happy. The problem arises at night, I go to sleep and expect when waking up that my ships have been sent all night long. Nope. Logging & notifications show that the Alarms are fired correctly, but it appears that the IntentService is killed when it's communicating with the server.
Possible cause for this is that I'm not looking at the game every once in a while like I do when I'm awake, and thus keep some form of process running which prevents the IntentService from being garbage collected.
I've already tried a LOT of things to fix this. I used to work with a BroadCastReceiver, I used to spawn ASyncTasks in the IntentService, but I've since refactored my code to not use those things anymore as they're bad practice for Services.
I have also checked this resource: http://techtej.blogspot.com.es/2011/03/android-thread-constructspart-4.html but I'm still not sure if what I'm doing is the correct thing to handle this situation. I have placed some extensive logging for the next night to review in the morning but I'd like you guys' opinion over this.
Oh I'm also requesting a WakeLock for the complete duration of the onHandleIntent function using:
PowerManager pm = (PowerManager) c.getSystemService(Context.POWER_SERVICE);
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "MyApplicationWakeLock");
My IntentService:
public class ScheduledRequestService extends IntentService {
public ScheduledRequestService() {
super("ScheduledRequestService");
}
#Override
public int onStartCommand(Intent intent, int flags, int startID){
Logger.d("OnStartCommand!");
return super.onStartCommand(intent, flags, startID);
}
#Override
protected void onHandleIntent(Intent intent) {
ScheduledRequest request = (ScheduledRequest) intent.getExtras().getSerializable("request");
// This function will start a lot of client <-> server communication
request.onExecute(this, intent);
}
#Override
public void onDestroy(){
Logger.d("OnDestroy!");
super.onDestroy();
}
}
So again, my question is; am I structuring this correctly? Should I use a normal Service instead of an IntentService ? Can my IntentService get garbage collected while it is handling an Intent (I think I read that this is possible)?
Can my IntentService get garbage collected while it is handling an Intent (I think I read that this is possible)?
No, but your process can be terminated.
am I structuring this correctly?
Not if you are trying to use a _WAKEUP alarm. You need to set things up more carefully in that case, and I would recommend either WakefulBroadcastReceiver or my WakefulIntentService to handle that pattern.
Should I use a normal Service instead of an IntentService ?
No, an IntentService will be fine. You may need to consider making it a foreground service using startForeground().
Turns out the error was somewhere else than the garbage collector of Android. I was using cache which eventually led to me 'running out of ships' because on send time, it deducted the sent amount of ships from the pool. When they returned however, they were never added back to the cache. During the day I probably manually refreshed the cache or forced Android to clear it by using my phone otherwise which didn't cause the problem to arise (as much).
I am writing my first android app so I will try my best to be clear and precise. I can see some similar questions to this but none that seems to answer it exactly so perhaps it can't be done but here goes:
I have got my main activity to the point where it has data stored which includes a list of apps on the device that the user has selected to launch. On clicking a button on the main activity screen I would like the device to launch each of these selected apps in turn and then (ideally) return the user to the main activity that I have written. I would also like to define some restrictions on running each app - for example, each app runs for 30 seconds or until the app stops using the internet whichever comes earliest.
I don't believe I have any issue with linking all of this to the button click, nor is there any issue cycling through all of the selected apps. What I really need is the code to launch each app and then recall from it/move to the next app (ideally after the 30 seconds or when the app stops using the internet). Hopefully the below code makes clear where I am looking for help with the TODO. Does anyone know whether this is possible and if it is how can I get it done?
public class MainActivity extends Activity {
... //some code up here
//when the Run Apps button is clicked the onClick will run all of the selected apps using this Method:
public void RunApps(View view) {
//run through the list of Apps and run the ones that are selected
for (App application : list) {
if (application.isSelected()) {
/* TODO code that is meant to run the selected app and return to the main
* activity after say 30 seconds or when the app is done using the internet.
* As a starter I have the below but this is crashing and even if it did run
* I believe that it would not return me to the original main activity:
*/
Intent i = new Intent(Intent.ACTION_MAIN);
PackageManager manager = getPackageManager();
i = manager.getLaunchIntentForPackage(application.getPackageName());
i.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(i);
}
}
};
...//some more code here
}
just a couple of notes - the App class is defined elsewhere and includes the package name and whether the app has been selected by the user. list is a List of Apps.
I believe that the ACTION_MAIN and CATEGORY_LAUNCHER values may not be the best to use and perhaps startActivity(i) is not the right method for what I want but am not sure how that needs to be changed or whether there a more fundamental changes needed.
Many thanks for any help.
You should run each app from your top-level MainActivity sequentially by invoking them one-at-a-time.
Here's how:
Keep a counter in your MainActivity to indicate which app you are
currently invoking.
Use startActivityForResult() instead of startActivity() to start
your applications. This will cause execution to return to
MainActivity.onActivityResult() when each of the apps is finished.
The requestCode of startActivityForResult() will be returned to
onActivityResult(), so you will know which application completed.
Therefore, MainActivity can increment the counter and start the
next application in onActivityResult().
Restriction:
One of your requirements is to return to MainActivity after each
app completes. These steps satisfy that requirement.
Another requirement is to return to `MainActivity after all of the
apps are finished. These steps also satisfy that requirement.
You will know when you have finished
the final app because of the value of your counter.
The final requirement is to limit the duration of each app to 30
seconds. This is a more difficult problem. You will use a Timer in
your MainActivity as a watchdog to monitor the spawned apps. Use methods
described here to stop the spawned app when time runs out:
Finish an activity from another activity.
Warning: get everything else working first, before you try to externally stop an app.
That's all. Good luck!
I have a class that extends AsyncTask, which fetches the gps cordinates from the device.
I would like to keep the data updated, so my initial though was to call the class from a timer or a handler. Is this a smart way to implement it, or am i better off listening to the onLocationChanged and do my updates in there?
Hope you get the idea, otherwise ill elaborate.
Thanks!
An alarmManager will be a good solution here.
These allow you to schedule your application to be run at some point in the future.
When an alarm goes off, the Intent that had been registered for it is broadcast by the system, automatically starting the target application if it is not already running.
So when alarm gets triggered, call your execute() method of Async task.
For more info see this: http://developer.android.com/reference/android/app/AlarmManager.html
I also want to implement the same in my app in near future. If you get the solution, don't forget to update the post about how you implemented it.
Thank you.