What are the appropriate places to call different scheduling android components - android

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.

Related

CountDownTimer : In Activity, ViewModel or separate class?

I would like to create a CountdownTimer which will trigger events that will update the UI (trigger popup, start an animation, etc.).
I wonder how to do this clean, here are my hypothesis and why :
A separate component EventCountdownTimer. I could then benefit the use of LifecycleObserver, but I wonder how to communicate the information back to the activity (I tried extending CountdownTimer and using it in the activity but I have an error and can't get it to compile)
In the Activity itself, it's the simplest but I'm not sure it belongs there as it isn't a UI component and I can't benefit the LifecycleObserver
In the ViewModel. I thought as it's activity related and the CountdownTimer is kinda logic data, it should go in here, but that means also watching the lifecycle of the activity, and holding any Activity related field within ViewModel is bad practice.
What's the best option according to you? And why?
In a MVVM pattern you could have a LiveData observable in your ViewModel which will be observed by the UI and upon value change you update the UI accordingly. How that observable changes value, that is your business logic and all of it should be in your ViewModel or in separate components that will be used by the ViewModel to update the observable state.
This will allow you to separate the UI from the business logic being your observable the bridge of communication between both, without the ViewModel having any knowledge of whats going on in the UI. In simple words it only executes what it is told to execute and updates a variable that is being observed, what then happens in the UI is the UI responsibility and with this you have reached a clear separation of concerns.
A separate component "EventCountdownTimer"
In my opinion, this is the best implementation that you might have in your case. For communicating information back to your activity, you might consider having an interface like the following.
public interface TimerListener {
void onTimerResponse(String response);
}
Modify your EventCountdownTimer to have a constructor which takes TimerListener as a parameter and override the onTimerResponse method in your activity. Now from your EventCountdownTimer, when you are trying to communicate with your activity along with a message, for example, you might just call the function onTimerResponse(msgToDeliver).
Hence your EventCountdownTimer should look something like this.
public class EventCountdownTimer {
public static Context context;
public static TimerListener listener;
public EventCountdownTimer(Context context, TimerListener listener) {
this.context = context;
this.listener = listener;
}
public startCountdown() {
// Start the count down here
// ... Other code
// When its time to post some update to your activity
listener.onTimerResponse(msgToDeliver);
}
}
And from your activity, initialize the EventCountdownTimer like the following.
EventCountdownTimer timer = new EventCountdownTimer(this, new TimerListener() {
#Override
public void onTimerResponse(String message) {
// Do something with the message data
// Update your UI maybe
}
});
I think you have provided good reasons already for not going for other options that you have mentioned.
Google solution : see it on github
/**
* A ViewModel used for the {#link ChronoActivity3}.
*/
public class LiveDataTimerViewModel extends ViewModel {
private static final int ONE_SECOND = 1000;
private MutableLiveData<Long> mElapsedTime = new MutableLiveData<>();
private long mInitialTime;
private final Timer timer;
public LiveDataTimerViewModel() {
mInitialTime = SystemClock.elapsedRealtime();
timer = new Timer();
// Update the elapsed time every second.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final long newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000;
// setValue() cannot be called from a background thread so post to main thread.
mElapsedTime.postValue(newValue);
}
}, ONE_SECOND, ONE_SECOND);
}
public LiveData<Long> getElapsedTime() {
return mElapsedTime;
}
#Override
protected void onCleared() {
super.onCleared();
timer.cancel();
}
}

Android MVVM: Observing database changes from broadcast receiver

In my app, I need to add/remove/update data in my db from a BroadcastReceiver. I am wondering what are the best practices regarding this. Since the onReceive is called on the main thread, I need a way to run the queries on a worker thread and on completion I need the response in the onReceive method.
For this, I used a simple Observer pattern like this.
public class NetworkChangeReceiver extends BroadcastReceiver implements IDbUpdateListener{
private MyRepository repo;
private Application application;
#Override
public void onReceive(Context context, Intent intent) {
//Some conditions
//Initializing and setting listener for repo
respo = new MyRepository(this); //this is the listener interface
repo.getAllContents();
}
}
}
//Interface method implemented
#Override
public void onDbUpdate(Content content) {
//Do something with the data
}
}
I passed the listener to the repo where I call the onDbUpdate() method on the listener and thereby get the response in the receiver.
If it was an activity/fragment instead of a broadcast receiver, I would have simply used a viewModel with live data as the observable and in my activity, I would observe the viewmodel for changes like this
mViewModel.getAllContent().observe(this, new Observer<List<Content>>() {
#Override
public void onChanged(#Nullable final List<Content> contents) {
// Do something
}
});
Is my approach ok or is there an obvious better way of achieving this in BroadcastReceiver? Thanks!!
I believe you should use some sort of manager that can handle task for you.
Android currently have a library Work Manager that handles this nicely.
With WorkManager you can schedule a OneTimeWorkRequest or a PeriodicWorkRequest.
Another benefit is that you necessarily don't have to listen for connectivity state yourself as you can specify/configure this and a lot others in the constraints passed to WorkManager.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.build()
And Yes, it can also handle retries if the network is pretty bad by simply specifying a backOffCriteria.
val workRequest = OneTimeWorkRequest.Builder(RequestWorker::class.java)
.setInputData(mapOf("record_id" to recordId).toWorkData())
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES)
.build()
If you are also interested in the status of the task/work, you can observe LiveData<WorkStatus> by calling getStatusById(workId)

Does Android Job library DailyJob start an Application class onCreate?

I use Android Job library from Evernote. Version 1.2.0.
For daily jobs I use DailyJob (https://evernote.github.io/android-job/javadoc/com/evernote/android/job/DailyJob.html) like that:
public class DailySyncJob extends DailyJob {
public static final String TAG = "DailySyncJob";
public static void schedule() {
if (!JobManager.instance().getAllJobRequestsForTag(TAG).isEmpty()) {
// job already scheduled, nothing to do
return;
}
JobRequest.Builder builder = new JobRequest.Builder(TAG).setRequiredNetworkType(JobRequest.NetworkType.UNMETERED);
// run job between 11pm and 6am
DailyJob.schedule(builder, TimeUnit.HOURS.toMillis(23), TimeUnit.HOURS.toMillis(6));
}
#NonNull
#Override
protected DailyJobResult onRunDailyJob(Params params) {
// do something
return DailyJobResult.SUCCESS;
}
}
Does onRunDailyJob start an Application class onCreate()?
I'm the main dev on the library. If your process isn't running, then yes your Appliction#onCreate() is called first. That's nothing specific to the library. Android works this way, if your process already died.

GSMTaskManager onRunTask calls MainActivity method?

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?

Android: get reference to started Service in instrumentation test

I'm trying to write instrumentation test for my NetworkMonitorService as described in the official "testing your service" documentation.
Currently I'm stuck because I can't figure out how can I grab a reference to the started service in order to inject mocks into it and assert behavior.
My code:
#RunWith(AndroidJUnit4.class)
#SmallTest
public class NetworkMonitorServiceTest {
#Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule();
#Test
public void serviceStarted_someEventHappenedInOnStartCommand() {
try {
mServiceTestRule.startService(new Intent(
InstrumentationRegistry.getTargetContext(),
NetworkMonitorService.class));
} catch (TimeoutException e) {
throw new RuntimeException("timed out");
}
// I need a reference to the started service in order to assert that some event happened
// in onStartCommand()...
}
}
The service in question doesn't support binding. I think that if I'd implement support for binding and then use this in test in order to get a reference to the service it could work. However, I don't like writing production code just for sake of supporting test cases...
So, how can I test (instrumentation test) a Service that doesn't support binding?
Replace your application with special version "for tests". Do it by providing custom instrumentation test runner. Mock your dependencies it this "app for tests". See for details
Here is a simplified example how "app for test" can be used. Let's assume you want to mock network layer (eg. Api) during tests.
public class App extends Application {
public Api getApi() {
return realApi;
}
}
public class MySerice extends Service {
private Api api;
#Override public void onCreate() {
super.onCreate();
api = ((App) getApplication()).getApi();
}
}
public class TestApp extends App {
private Api mockApi;
#Override public Api getApi() {
return mockApi;
}
public void setMockApi(Api api) {
mockApi = api;
}
}
public class MyTest {
#Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule();
#Before public setUp() {
myMockApi = ... // init mock Api
((TestApp)InstrumentationRegistry.getTargetContext()).setMockApi(myMockApi);
}
#Test public test() {
//start service
//use mockApi for assertions
}
}
In the example dependency injection is done via application's method getApi. But you can use Dagger or any others approaches in the same way.
I found a very simple way for doing this. You can just perform a binding and you'll get the reference to the already running service, there are no conflicts with service creation because you already started it with onStartCommand, if you check you will see onCreate is called only once so you can be sure it is the same service. Just add the following after your sample:
Intent serviceIntent =
new Intent(InstrumentationRegistry.getTargetContext(),
NetworkMonitorService.class);
// Bind the service and grab a reference to the binder.
IBinder binder = mServiceRule.bindService(serviceIntent);
// Get the reference to the service
NetworkMonitorService service =
((NetworkMonitorService.LocalBinder) binder).getService();
// Verify that the service is working correctly however you need
assertThat(service, is(any(Object.class)));
I hope it helps.
this works at least for bound services:
#Test
public void testNetworkMonitorService() throws TimeoutException {
Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), NetworkMonitorService.class);
mServiceRule.startService(intent);
IBinder binder = mServiceRule.bindService(intent);
NetworkMonitorService service = ((NetworkMonitorService.LocalBinder) binder).getService();
mServiceRule.unbindService();
}
to access fields, annotate with #VisibleForTesting(otherwise = VisibleForTesting.NONE)

Categories

Resources