I have an app that the MainActivity has a method (called doUpdate()) that is called from a button hit. This uses the MainActivity's public variables to access a database, perform some updates, and update some records. This works well.
We now need to automate this with a PeriodicTask as well.
I created a GCMTaskManager service as follows:
public class MyPeriodicTaskService extends GcmTaskService {
public MyPeriodicTaskService() {
}
#Override
public int onRunTask(TaskParams taskParams) {
Log.i("MYLOG","Task Running...");
return 0;
}
}
In my MainActivity, onCreate(), I setup the PeriodicTask as follows:
GcmNetworkManager networkManager=GcmNetworkManager.getInstance(this);
PeriodicTask task=new PeriodicTask.Builder()
.setService(MyPeriodicTaskService.class)
.setPeriod(60)
.setFlex(30)
.setRequiresCharging(true)
.setTag("UpdateSchedule")
.build();
networkManager.schedule(task);
By watching the LOG, I know that the onRunTask() fires periodically as I hoped.
Now I need to call my MainActivity method... doUpdate(). Because this method is declared PUBLIC VOID and not STATIC, I can't call it from the services doRunTask(). If I attempt to make it a STATIC PUBLIC VOID then the MainActivity variables can't be accessed properly as needed for the internal processing steps.
How do I get around this... any recommendations?
Related
I need to know which android component I should use for scheduling a task, I need to execute a task which will update application data from server in every 3 minutes (Hence I cannot use JobScheduler or SyncAdapter both are restricted to minimum of 15 minutes poll interval).
So what are best alternative?
TimerTask
Handler
ThreadPoolExecuter
ScheduledThreadPoolExecutor
If possible kindly elaborate where should we use those components.
You can use the Android-Job library from Evernote
implementation 'com.evernote:android-job:1.2.6'
A utility library for Android to run jobs delayed in the background.
Depending on the Android version either the JobScheduler, GcmNetworkManager or AlarmManager is getting used
Usage
The class JobManager serves as entry point.
Your jobs need to extend the class Job.
Create a JobRequest with the corresponding builder class and schedule this request with the JobManager.
Before you can use the JobManager you must initialize the singleton.
You need to provide a Context and add a JobCreator implementation after that.
The JobCreator maps a job tag to a specific job class. It's recommended to initialize the JobManager in the onCreate() method of your Application object, but there is an alternative, if you don't have access to the Application class.
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
JobManager.create(this).addJobCreator(new DemoJobCreator());
}
}
public class DemoJobCreator implements JobCreator {
#Override
#Nullable
public Job create(#NonNull String tag) {
switch (tag) {
case DemoSyncJob.TAG:
return new DemoSyncJob();
default:
return null;
}
}
}
After that you can start scheduling jobs.
public class DemoSyncJob extends Job {
public static final String TAG = "job_demo_tag";
#Override
#NonNull
protected Result onRunJob(Params params) {
// run your job here
return Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(30_000L, 40_000L)
.build()
.schedule();
}
}
This is an advanced version of Default Job Scheduler in android and have great capabilities than the default one which is having a lot of limitations and back ward compatibility.
AsFirebaseMessagingService does not use the Main Thread, I am just wondering as all my code in all of my activities or fragments run in UI thread(Main Thread). Now suppose my activity's onCreate method is executing and then I receive the push notification. Will these two blocks of code run in parallel, or will the push notification code wait in the queue until onCreate() method OR Activity's last life cycle method gets executed?
Edit- As you are saying code will run parallelly then suppose I have a variable in App.java
public class App extends Application {
int ctr = 100;
}
StatusActivity.java
public class StatusActivity extends BaseActivity {
public void onCreate() {
fun();
}
public void fun() {
int d = App.ctr - 1;//Step 1 Here d = 99
int m = App.ctr - 1; // Step 3 Here m = 98
}
}
FcmListener.java
public class FcmListener extends FirebaseMessagingService {
Override
public void onMessageReceived(RemoteMessage mssg) {
App.ctr = App.ctr - 1;//STEP 2 // Now App.ctr = 99
}
}
Now as you can see in the above code there will be problems if push notif code executes in parallel with fun(). I want push_notif and fun() to run serially, where order doesn't matter but not in parallel.
As already pointed out in a parallel answer, the overriden methods of FirebaseMessagingService run in a background thread, so you should use synchronization strategies in order to access/use mutable object from different thread.
But the question I want to answer is a bit different. Let's for a moment assume, that overriden methods run on a main thread. So is there a possibility, that the order of execution will be STEP 1 then STEP 2 and then STEP 3?
Android works with a technique called MessageQueue, basically there are Messages posted on that queue, on which Looper loops and "parses/executes" them.
Now if we assume, that you are currently located on STEP 1, it means, that there was a particular Message which is currently being executed (hypothetically, let's assume that action is - perform onCreate() of this activity).
Until this message is fully executed there cannot exist another Message which might get have a chance to be executed. So if we assume, that Firebase dispatches an event on background thread but the actual overriden method is being run on main thread, then this overriden method would have chance to be executed only after current Message (activity's onCreate()) has finished. In other words, there would be posted another Message on the MessageQueue, which would perform onMessageReceived() when the Looper will give chance for this message to be executed.
So, theoretically, there is no chance that the ordering would be STEP 1 -> STEP 2 -> STEP 3.
If STEP 1 is already executed, then it will continue with STEP 3 and the STEP 2 (at some point in future, because you can't know what other Messages are already posted on MessageQueue).
See this article for more details about MessageQueue and related classes.
How about it?
class Sample {
private String message = null;
private final Object lock = new Object();
public void newMessage(String x) {
synchronized (lock) {
message = x;
}
}
public String getMessage() {
synchronized (lock) {
String temp = message;
message = null;
return temp;
}
}
}
Here is my 2 cents. You say,
Suppose my activity's onCreate method is executing and then I receive the push notification. Will these two blocks of code run parallelly or will the push notification code wait in the queue until onCreate method OR Activity's last life cycle method gets executed?
From the official documentation of FirebaseMessagingService:
Extending this class is required to be able to handle downstream messages. It also provides functionality to automatically display notifications, and has methods that are invoked to give the status of upstream messages. Override base class methods to handle any events required by the application. Methods are invoked on a background thread.
So its possible both methods execute at the same time. If you want to do the operations on a shared variable in your Application class, you can do thread safe operations using synchronize. See How to synchronize or lock upon variables in Java?. That will make sure only one thread is making changes at a time on that variable. If a new thread comes in, it waits for the lock to get free and then makes the changes on that variable. However this doesn't guarantee the order. It just means that one thread operates on it at time and is in FIFO order.
I suggest you a different approach, because using those global variables can lead to unexpected behavior.
If your ctr var is related to your activity, then keep it inside. If you need it on other activities consider passing it via the Intent as an extra.
Use LocalBroadcastManager to inform your activity that you received the push message
public class FcmListener extends FirebaseMessagingService {
public static final String ACTION_MESSAGE_RECEIVED = "ACTION_MESSAGE_RECEIVED"
#Override
public void onMessageReceived(RemoteMessage mssg) {
Intent intent = new Intent(ACTION_MESSAGE_RECEIVED) // put extra vars as needed
boolean delivered = LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// 'delivered' is true if there is at least someone listening to the broadcast, eg. your activity
// If your activity is not running, then 'delivered' is false so you can act accordingly
}
}
Then inside your activity
public class StatusActivity extends BaseActivity {
private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.equals(FcmListener.ACTION_MESSAGE_RECEIVED, action)) {
// do stuff with 'ctr'
}
}
};
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(FcmListener.ACTION_MESSAGE_RECEIVED);
LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, filter);
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver);
}
}
I have a service in Android that encapsulates a framework that has a start method. The service boils down to something like this, many things omitted:
public class MyService extends Service {
private IBinder thisBinder;
public MyService(){
thisBinder = new LocalBinder();
}
#Override
public IBinder onBind(Intent intent) {
return thisBinder;
}
public void start(Map<String, Object> options)
{
getDriverManager().start(options);
}
}
I also have a bridging class that makes calls to the service:
public class MyServiceBridge implements ServiceConnection {
private boolean started = false;
private boolean bound = false;
private MyService myService;
public MyServiceBridge(Context context){
this.context = context;
}
public void bindService(){
Intent intent = new Intent(getContext(), MyService.class);
getContext().bindService(intent, this, getContext().BIND_AUTO_CREATE);
getContext().startService(intent);
}
// Here's a sample call, and the one that is relevant
public void start(Map<String, Object> options){
setOptions(options);
if(bound == true){
getMyService().start(options);
}
else{
started = true;
}
}
}
I call the bridge's start method in order to run the service. This works fine, except in this particular situation (so far). The MyApplication class calls the bridge's start method on onCreate:
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
getServiceBridge().start(null);
}
}
This, according to the docs is "Called when the application is starting, before any activity, service, or receiver objects (excluding content providers) have been created.". Indeed it appears to be so, because the service does not start, and instead starts when I close the app (odd, at least). This works if I move the call to an activity's onCreate method, but that's not ideal because I can also stop the service, and I want the service to run for the lifetime of the app. That is, the service should start when the app starts and stop when the app terminates. Does this make sense? Is there another way to do this?
In my opinion, you did a good job when you decided to run service in Application onCreate. but it is strange to hear that service is started when you close the app.
I have done this several times, and service starts in application onCreate which must be called.
Have you checked if your application is alive and run in background? Please make sure that you killed you application before testing. Use Task Killer or something like that, to be sure that application is always freshly started.
Sadly, Android does not have appropriate mechanism to notify you when application is exited, because it is still alive until system decides to kill it and free resources.
Learning to use the BroadcastReceiver class in Android, I have written a small program to receive the battery charge state and write it to three TextView fields in an activity.
However, I have made the BroadcastReceiver as a separate class to make it more simple and separate from the activity. Therefore I have to find a method to tell my Activity class that the battery data has been updated, or, which is my solution, to pass in references to the TextView fields from the Activity to the BroadcastReceiver class.
Does anyone know whether it is possible to make a callback method from the BroadcastReceiver to start a function, f.ex. updateTextViews(); in the Activity?
Here is the source code - note there are two java files:
http://pastebin.com/qjCTsSuH
Regards, Niels.
What worked a charm for me is simply declaring the interface objects as static. Bear in mind though that statics can cause as many problems as they solve as statics persist therir values accross instances.
public class MainActivity extends AppCompatActivity implements SocketMessageReceiver.ISocketMessageReceiver {
//Declare the cb interface static in your activity
private static SocketMessageReceiver.ISocketMessageReceiver iSocketMessageReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
//Assign this
iSocketMessageReceiver = this;
socketMessageReceiver.registerCallback(iSocketMessageReceiver);
}
#Override
public void sendSocketMessage(String socketMessage) {
lblEchoMessage.setText(socketMessage);
}
}
And in your Receiver ....
public class SocketMessageReceiver extends BroadcastReceiver {
interface ISocketMessageReceiver {
void sendSocketMessage(String socketMessage);
}
//Also declare the interface in your BroadcastReceiver as static
private static ISocketMessageReceiver iSocketMessageReceiver;
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("com.WarwickWestonWright.SocketExample.RECEIVE")) {
iSocketMessageReceiver.sendSocketMessage(intent.getBundleExtra("DATA").getString("DATA"));
}
}
public void registerCallback(ISocketMessageReceiver iSocketMessageReceiver) {
this.iSocketMessageReceiver = iSocketMessageReceiver;
}
}
I have made the BroadcastReceiver as a separate class to make it more simple
IMHO, you made it more complex.
Therefore I have to find a method to tell my Activity class that the battery data has been updated, or, which is my solution, to pass in references to the TextView fields from the Activity to the BroadcastReceiver class.
Option #1: Just go back to using an inner class for the BroadcastReceiver. ACTION_BATTERY_CHANGED can only be used via registerReceiver() anyway. Just have onReceive() call some method on the activity to do the work of updating the UI.
Option #2: Pass your activity into the constructor of the BroadcastReceiver, and call the method as in option #1.
Option #3: Use an event bus, like Square's Otto or greenrobot's EventBus.
According to RoboSpice documentation https://github.com/octo-online/robospice/wiki/Design-of-RoboSpice , i can use it in any Context.
Can't find an example of using Robospice in service context.
I did some attempts but nothing happened, requests just not executes, no exceptions (Maybe some log leaking, what i need to do to enable robospice log on device?)
Where to start/stop it? (spiceManager.start(this) / spiceManager.shouldStop())
Where to create SpiceManager instance? (My service starts in application.onCreate() method, maybe i have to wait for some SpiceService initialization?)
some code
public abstract class SpicyService extends Service {
private SpiceManager spiceManager = new SpiceManager(SpiceService.class);
#Override
public void onCreate() {
super.onCreate();
spiceManager.start(this);
}
#Override
public void onDestroy() {
spiceManager.shouldStop();
super.onDestroy();
}
}
Shame on me ...
After drinking some coffee i spotted that child Service, dont have super.onCreate() call to start spiceManager.
It's working perfectly fine now!
Sorry for your time.