I'm trying to periodically check with a web service if a new item has been added to a database. If there's a new item a listview that the user sees should be updated.
I'm using PCL and I have accomplish it creating a new Task with a timer inside. But this only works if the app is open. I want to do the same when the app is closed so the user gets a notification when a new item is added remotely.
I've been doing some research and I found andoid services, the info said that the service will continue, regardless of the app state, until you tell it to stop. But i haven't found many examples in how to implement it.
Here's the code that I had that works only when the app is opened:
Task.Run(() =>
{
Device.StartTimer(TimeSpan.FromSeconds(secs), checkUpdates);
});
bool checkUpdates()
{
if (isOnline)
{
var lastUp= Application.Current.Properties["lastUpdate"] as string;
//returns true if a new item is added
if (service.newItem(DateTime.Parse(lastUp)))
{
var itm = service.getNewItem(DateTime.Parse(lastUp));
Device.BeginInvokeOnMainThread(() =>
{
notification.ShowNotification(itm.Title, 1000);
listView.Insert(0, itm);
}
});
}
App.Current.Properties["lastUpdate"] = DateTime.Now.ToString();
}
return true;
}
I'm trying to do the same with an Android service using dependency services, here's what I've got so far:
public interface IService
{
void Start();
}
[Service]
public class DependentService : Service, IService
{
public void Start()
{
var intent = new Intent(Android.App.Application.Context, typeof(DependentService));
Android.App.Application.Context.StartService(intent);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// From shared code or in your PCL
return StartCommandResult.NotSticky;
}
}
The problem is that I don't know how to implement the code that I had in the timer to the service, can someone please help?
For what you want to achieve, you should probably look at the AlarmManager
Have a look at this StackOverflow post, where there's an example for one and maybe this one as well. The second one is Java but it might give you an idea of what you might deal with
EDIT:
You might also want to have a look at these Xamarin.Android documents around Services and BroadcastReceivers, to get better understanding with what you're dealing
Related
I need to write three items of data in Firebase Realtime Database in case the user kill the app
from recent list while it's still running; I implemented a service in order to
update the database when onTaskRemoved is called.
In the manifest the service is declared with the option android:stopWithTask="false"
Here is the service
public class ServiceAppMonitoring extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
FirebaseDatabase mDatabase = FirebaseDatabase.getInstance();
SharedPreferences mSettings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
//Get some datas from Shared Preferences...
String path1 = "first/node/path";
mDatabase.getReference(path1).setValue(false);
if (condition) {
// Compose array of datas
List<Object> data2 = Arrays.asList(new Object[]{ ... });
String path2 = "second/node/path";
mDatabase.getReference(path2).setValue(data2);
// Compose array of datas
List<Object> data3 = Arrays.asList(new Object[]{ ... });
String path3 = "third/node/path";
mDatabase.getReference(path3).setValue(data3);
stopSelf();
} else {
stopSelf();
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) { return null; }
}
Strangely only the first instruction is successful, the other two seem to be ignored... at least no data is written into database.
Further I've noticed another "unusual" behaviour, if I arrange all the DatabaseReferences in the
following way:
mDatabase.getReference("first").child("node").child("path").setValue(false);
no one of the instructions end up writing into database, to get (at least) the first one working I've to arrange this way:
mDatabase.getReference("first/node/path").setValue(false);
Can anybody kindly help me to understand why this happens?
Thanks
This is almost certainly because Firebase operations are asynchronous, and return immediately before the writes are complete. onTaskRemved is going to return before either of the database writes fully finish.
I'm guessing that your app process is going to die very soon, if not immediately, after onTaskRemved returns. This means that your database writes might not finish. Android doesn't know that these writes are pending, and it's not going to wait for them.
Since you don't have way from your service to tell Android to wait for these writes, you will have to schedule them for later. I suggest looking into using WorkManager to schedule the writes to happen in the background, whenever Android allows it. It might not be immediate, but WorkManager will make sure that any scheduled tasks will eventually complete.
Hey based on your answer I updated a small thing on database from onTaskRemoved like this
I already initialized the DatabaseReference in the onCreate method
DatabaseReference temp = FirebaseDatabase.getInstance.getReference().child("temp");
and in onTaskRemoved
temp.setValue(true);
this is getting executed and I added a onchange listener in onCreate method to listen for this values change and it worked like a charm. If you don't understand anything feel free to ask and let me know how you got over this problem:)
EDIT:
This is not working in all devices...
EDIT AGAIN: I simply used on Destroy method in my service and killed the service after completing the task and is working for now...
EDIT AGAIN AND AGAIN: OnDestroy is not working in android 6 or below I guess. I tested in Android 6 and it didnt work.
I am currently resuming a project I had been working on, and starting from scratch to recreate it.
However, upon creating a Service class, I noticed something - in my old project, a method inside the Service called onStartCommand contains all of the code that needs to be fired, whereas in my new project when I create a Service class, this method is nowhere to be found.
- Do I need to manually ADD this "onStartCommand" method to contain my service code?
- If not, where exactly would my code go? It seems in my "old" project's code, I completely comment-block public TimerService, and pass null into IBinder, and create onStartCommand etc instead.. and I can't quite figure out why.
- While i'm here, can someone please double-check my CountdownTimer code below? and if it's correct, should I be putting it inside of a Thread?
When I create a new Service Class, it looks like this:
public class TimerService extends Service {
public TimerService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
However in my old Project, my Service class looks like this:
public class TimerService extends Service {
/*
public TimerService() {
}
*/
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
intent.getStringExtra("TIMER_VALUE");
String string_timerValue;
string_timerValue = intent.getStringExtra("TIMER_VALUE");
long long_timerValue;
long_timerValue = Long.parseLong(String.valueOf(string_timerValue));
// I DO NOT WANT ANY TICK VALUE, SO GIVE IT FULL TIMER VALUE
long long_tickValue;
long_tickValue = Long.parseLong(String.valueOf(string_timerValue));
new CountDownTimer(long_timerValue, long_tickValue) {
public void onTick(long millisUntilFinished) {
// DO NOTHING
}
public void onFinish() {
Toast.makeText(TimerService.this, "TIMES UP", Toast.LENGTH_LONG).show();
stopService(intent);
}
}.start();
return START_STICKY;
// END OF onStartCommand
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}
// END OF ENTIRE SERVICE CLASS
}
THANK YOU!!
Do I need to manually ADD this "onStartCommand" method to contain my service code?
Yes.
can someone please double-check my CountdownTimer code below?
Only create a service when one is absolutely necessary. It is unclear why this service is necessary.
Beyond that:
Use stopSelf(), not stopService(), to stop a service from inside that service.
Examining Intent extras and using START_STICKY is not a good combination. START_STICKY says "if you terminate my process to free up system RAM, please restart my service when possible, but pass null for the Intent". That will cause your service to crash with a NullPointerException.
I will do something when service stopped in my app
for example:
i have a countdown code in my service,and then i will when that countdown stopped random button set visible.
i don't know how i can do something by service,i can't set button in service's body so how i can set it?and where i should do it?
in this following code i wrote my countdown in //your code line but i don't know how i can set button visible when count down stopped
public class MyService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
strong text // your code
return Service.START_FLAG_REDELIVERY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
What you are trying to do is communicate between a running service and an Activity. There are multiple ways to do this. The most easiest would be to pass an event from the service and catch that event in the activity. You can implement it from scratch or use an Event Bus. I would recommend using this library EventBus. Using this library you can pass an object containing some info (about what happened in the service) to the activity and based on that you can perform the action you need. I'm not posting any code. Please try doing it yourself, it will be a good learning experience.
I think i have coded myself into a bit of a corner here. but I will try to explain my issue as best as I can. basically I am building an application on android that is supposed to be a copy of the alarm app on the phone. I have an object that represents the alarm. it includes time, and days as booleans etc. i persist these objects by serializing them and saving them to a file. then, on boot, I have a broadcastreciever which starts a boot service. basically the bootservice has a loop that iterates over my list of alarms. iam trying to call a method on each alarm object called "setSysAlarm()", which is supposed to recreate each android system alarm at the saved time. at present, the "setSystemAlarm" method is only creating a toast for testing purposes. BUT this is causing the application to crash. there is obviously a flaw in my design and I think that trying to use the app context is causing the crash. My thinking is that I should possibly create all alarms via the service through the GUI when is running, aswell as recreating the alarms on boot? therefore alarms creation can be done regardless of wheather the app is running, also the alarms will be created in one place ? below is my boot service code which is executed on boot.
public class BootService extends Service {
private Thread setallarams;
private file_acces_int fileaccess;
private ArrayList<alarm_entity> alarmlst;
private Runnable setalarmsrunnable = new Runnable() {
#Override
public void run()
{
for(alarm_entity alarm : alarmlst)
{
alarm.setSystemAlarm();
}
}
};
#Override
public void onCreate()
{
fileaccess = new file_access_model(getApplicationContext());
alarmlst = fileaccess.readFromFile();
setallarams = new Thread(setalarmsrunnable);
cont = this;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
setallarams.start();
try {
setallarams.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
stopSelf();
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
and here is the setSystemAlarm() method from my alarm_entity class. basically this is meant to create a toast on boot but at present the app is crashing
public void setSystemAlarm()
{
Toast.makeText(cont, "hi there", Toast.LENGTH_SHORT);
}
if someone could help me out here id appreciate it. the methods i was using to get the context were
1. pass it in from the bootService. doesnt seem doable to me?
2. cretaing an instance on of Applicationin the constuctor of each alarm_entity and using that as the context. this also seems pretty stupid to me. lol
anyway thanks in advance and sorry for the long winded question!
Anyway, we´ve discussed too much for how You get the Logcat output :) . The basic question is, why You get this error. Maybe this is not the full answer, but I need to show some code. You are initializing Your context inside onCreate() of the service:
#Override
public void onCreate()
{
fileaccess = new file_access_model(getApplicationContext());
alarmlst = fileaccess.readFromFile();
setallarams = new Thread(setalarmsrunnable);
cont = this;
}
But like described in the API:
Do not call this method directly.
I don´t know if this is the reason for Your crash, we have no Logcat, it could be any other reason. But it´s recommended to initialize in onStartCommand(), so put Your initializations inside onStartComand():
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
fileaccess = new file_access_model(getApplicationContext());
alarmlst = fileaccess.readFromFile();
setallarams = new Thread(setalarmsrunnable);
setallarams.start();
.
.
.
The other thing is, I can´t see initializing alarm_entity class, maybe I am blind (?), but I can´t see nowhere that You pass a context to this class. For example, if You are initializing this class, it must be something like this inside Your onStartCommand();
mAlarm_entity = new alarm_entity(this);
Because Service holds context, then simply call this. Delete the onCreate() and then You don´t need to initialize a context, because the service has it allready.
Add this permission to your manifest
> android.permission.RECEIVE_BOOT_COMPLETED
It's my first question on SO, I hope this question won't be bad.
I have a service, it starts working when user launchs an app and works until user will kill it via task killer or turn off his device.
This service has a background thread which does some work with data. I need to bind activities (from activities, not by service) and sometimes (1-2 times per 30 seconds) send data to binded activities.
Structure of my service:
public class myserv extends Service {
public static boolean started=false;
public class workwithdata extends Thread {
#Override
public synchronized void start() {
super.start();
//.. Not important.
}
#Override
public void run() {
if (running) return;
while (true) {
if(condition) mythread.sleep(30000);
else {
Object data = recieveMyData();
if (!data.isEmpty()) {
//.. Some work with recieved data, not important.
sendDataToBindedActivities(data); //This is what I need.
}
mythread.sleep(10000);
}
}
}
}
#Override
public void onCreate() {
super.onCreate();
this.started=true;
mythread = new workwithdata();
mythread.start();
}
}
Well, I found one question but my problem has a little differences: I don't need to send any data to the service, I need just send some data to all binded activities (which service doesn't know at all).
Structure for which I'm looking for:
public class myact extends Activity {
#Override
public void onCreate(Bundle bun) {
super.onCreate(bun);
if(!myserv.started) {
Intent service = new Intent(getApplicationContext(), myserv.class);
getApplicationContext().startService(service);
}
bindToService(this);
}
#Override
public void onRecievedData(Object data) {
//work with recieved data from service "myserv".
}
}
I also tried to find some solutions in android documentation but I didn't find what I need.
So, main question is: is it possible to work with communications from service to activities?. If no: What should I use for this purpose? If yes, just, sorry, can I ask for some code or class names, because I tried to find and didn't...
Thank you.
You need to use a RemoteCallbackList
When your clients bind to the service, you will need to register them using RemoteCallbackList.register().
When you want to send data to the bound clients, you do something like this:
int count = callbackList.beginBroadcast();
for (int i = 0; i < count; i++) {
try {
IMyServiceCallback client = callbackList.getBroadcastItem(i);
client.onRecievedData(theData); // Here you callback the bound client's method
// onRecievedData() and pass "theData" back
} catch (RemoteException e) {
// We can safely ignore this exception. The RemoteCallbackList will take care
// of removing the dead object for us.
} catch (Exception e) {
// Not much we can do here except log it
Log.e("while calling back remote client", e);
}
}
callbackList.finishBroadcast();
An example can be found here It is kinda complicated, but maybe you don't need everything this offers. In any case, have a look.