I have implemented Evernote Android Job in my android application through
implementation 'com.evernote:android-job:1.2.6'
And I have define as signleton to get instance I have initiated it in my Application class through
JobManager.create(this).addJobCreator(new CreatingJob());
And I have two classes which are
JOB CREATING CLASS
public class CreatingJob implements JobCreator {
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case SyncMasterDataJOB.TAG:
return new SyncMasterDataJOB();
}
return null;
}
}
JOB CLASS
public class SyncMasterDataJOB extends Job {
public static final String TAG = "job_note_sync";
#NonNull
#Override
protected Result onRunJob(#NonNull Params params) {
//Doing my Task HERE
MyLog.ShowELog("JOB STARTED", "Job Has been Started");
MyToast.Lmsg(getContext(), "Job Has been Started");
return Result.SUCCESS;
}
public static void scheduleJob() {
Set<JobRequest> jobRequests = JobManager.instance().getAllJobRequestsForTag(SyncMasterDataJOB.TAG);
if (!jobRequests.isEmpty()) {
return;
}
new JobRequest.Builder(SyncMasterDataJOB.TAG)
.setPeriodic(MIN_INTERVAL, MIN_FLEX)
.build()
.schedule();
}
}
But the Problem is My onRunJob() method is never called. I am new to Android JOBS. Can anyone tell me where i am doing wrong?
I am Taking reference from here
Job creator class ->
public class CreateJob implements JobCreator {
private Context context;
public CreateJob(Context context){
this.context = context;
}
// Here we have to register each of the jobs...
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case CurrentWeatherUpdateJob.TAG:
return new CurrentWeatherUpdateJob();
default:
return null;
}
}
}
this is where i am registering my JobCreator.
// To use StartingPoint Anywhere in our app
// else you have to Instantiate StartingPoint inside every Activities on create...
public class StartingPoint extends Application {
#Override
public void onCreate() {
super.onCreate();
// Create Job is a class that registers all the Jobs...
JobManager.create(this).addJobCreator(new CreateJob(getApplicationContext()));
}
}
This is your Jobs Subclass ->
public class CurrentWeatherUpdateJob extends Job {
public static final String TAG = "CurrentWeatherUpdateJob";
// Update Weather Data every 15 Minutes...
private static final int CURRENTWEATHERUPDATE_TIMEINTERVAL = 15 * 60 * 1000;
// Interface that provides Data...
private ApiInterface service;
// For Celcius - metric / Kelvin - imperial
private String UnitType = "metric";
public CurrentWeatherUpdateJob() {
service = APIClient.getRetrofit_Weather().create(ApiInterface.class);
}
private static void ScheduleJobEvery15Minutes() {
// Scheduling Job After every 15 minutes...
new JobRequest.Builder(TAG)
.setPeriodic(CURRENTWEATHERUPDATE_TIMEINTERVAL)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
.build()
.schedule();
}
// implement your onRunJob method here
}
Call your ScheduleJobEvery15Minutes() method from your activity you want.
The Problem is not in your code, but in the Setting up of the Period of the Job. evernote only works with periodic Job >=15 Min, while you are using 1 minute as period for running the Job. see the Documentation of evenote-job as it's based on Job-scheduler, which has the same constraint for running periodic job.
private void schedulePeriodicJob() {
int jobId = new JobRequest.Builder(DemoSyncJob.TAG)
.setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5))
.build()
.schedule();
}
this is the code that they put on their Library Documentation. Please check this out. ->
Why can't an interval be smaller than 15 minutes for periodic jobs? This library is a subset of 3 different APIs. Since Android Nougat the minimum interval of periodic jobs is 15 minutes. Although pre Nougat devices support smaller intervals, the least common was chosen as minimum for this library so that periodic jobs run with the same frequency on all devices.
The JobScheduler with Android Nougat allows setting a smaller interval, but the value is silently adjusted and a warning is being logged. This library throws an exception instead, so that misbehaving jobs are caught early. You can read more about it here.
this is the code that works for me ->
where // Update Weather Data every 15 Minutes...
private static final int CURRENTWEATHERUPDATE_TIMEINTERVAL = 15 * 60 * 1000;
private static void ScheduleJobEvery15Minutes() {
// Scheduling Job After every 15 minutes...
new JobRequest.Builder(TAG)
.setPeriodic(CURRENTWEATHERUPDATE_TIMEINTERVAL)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
.build()
.schedule();
}
EDIT -> Also check your Jobcreator class, you are returning null value like this,
public class CreatingJob implements JobCreator {
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case SyncMasterDataJOB.TAG:
return new SyncMasterDataJOB();
}
return null;
}
change your code to this ->
public class CreatingJob implements JobCreator {
#Nullable
#Override
public Job create(#NonNull String tag) {
switch (tag) {
case SyncMasterDataJOB.TAG:
return new SyncMasterDataJOB();
case default:
return null;
}
}
Related
I did not find anything about this topic, however I am curious what your recommendations or "best practices" are regarding when to set the rules to schedule the task? For example if I have to schedule a sync job, which should always be there as long as the app runs, where would the
new JobRequest.Builder("...")..
.build()
.schedule()
be called?
Thank you
You should create JobCreator which will instantiate your Job class like this:
public class MyJobCreator implements JobCreator {
#Override
public Job create(String tag) {
if (MyJob.TAG.equals(tag)) {
return new MyJob();
}
return null;
}
}
And initialize it in Application.onCreate():
JobManager.create(this).addJobCreator(new MyJobCreator());
MyJob.scheduleJob();
MyJob may look like this:
public class MyJob extends Job {
public static final String TAG = "my_job_tag";
#Override
#NonNull
protected Result onRunJob(Params params) {
Intent i = new Intent(getContext(), MyService.class);
getContext().startService(i);
return Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(MyJob.TAG)
.setPeriodic(60_000L) // 1 minute
.setRequiredNetworkType(JobRequest.NetworkType.ANY)
.setPersisted(true)
.setUpdateCurrent(true)
.setRequirementsEnforced(true)
.build()
.schedule();
}
}
Extending shmakova's answer, you may need to add a condition if your sync job is already scheduled like this:
public static void scheduleJob() {
if (JobManager.instance().getAllJobRequestsForTag(MyJob.TAG).isEmpty()) {
new JobRequest.Builder(MyJob.TAG)
.setPeriodic(900_000L)
.setRequiredNetworkType(JobRequest.NetworkType.ANY)
.setPersisted(true)
.setUpdateCurrent(true)
.setRequirementsEnforced(true)
.build()
.schedule();
}
}
this will prevent scheduling multiple jobs
I need to collect some data on my current app in order to analyse performance speed by checking the average ellapsed time during Activity start up. I would like to run a test battery where the activity is started 10, 100, 1000 and 5000 times. For each test, it should remain open for at least 10 seconds (time needed to collect all data that happens asynchronously). What I want is exactly this behaviour without having to write these many methods:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class TestStreamLoadingPerformance {
private static final long TIME_OUT = 2;
private static final long WAITING_TIME = 10000;
#Rule
public ActivityTestRule mActivityRule = new ActivityTestRule(HomepageActivity.class);
private ElapsedTimeIdlingResource mIdleRes = new ElapsedTimeIdlingResource(WAITING_TIME);
#Before
public void setUp() {
IdlingPolicies.setMasterPolicyTimeout(TIME_OUT, TimeUnit.HOURS);
IdlingPolicies.setIdlingResourceTimeout(TIME_OUT, TimeUnit.HOURS);
Espresso.registerIdlingResources(mIdleRes);
}
#After
public void tearDown() {
Espresso.unregisterIdlingResources(mIdleRes);
}
#Test
public void test01() {
}
#Test
public void test02() {
}
#Test
public void test03() {
}
#Test
public void test04() {
}
#Test
public void test05() {
}
#Test
public void test06() {
}
#Test
public void test07() {
}
#Test
public void test08() {
}
#Test
public void test09() {
}
}
With the help of #Be_negative comments, this blog post and this answer, I was able to solve the problem with the code below:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class TestStreamLoadingPerformance {
#Rule
public ActivityTestRule mActivityRule = new ActivityTestRule(HomepageActivity.class, false, false);
#Rule
public RepeatRule mRepeatRule = new RepeatRule();
#After
public void tearDown() {
closeActivity();
}
private void closeActivity() {
final int N = 10;
try {
for (int i = 0; i < N; i++) {
Espresso.pressBack();
}
} catch (NoActivityResumedException e) {
Log.e(TestStreamLoadingPerformance.class.getSimpleName(), "Unable to close activities", e);
}
}
#Test
#RepeatRule.Repeat(times = 10)
public void collectData() {
mActivityRule.launchActivity(null);
}
}
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class RepeatRule implements TestRule {
#Retention(RetentionPolicy.RUNTIME)
#Target({
java.lang.annotation.ElementType.METHOD
})
public #interface Repeat {
public abstract int times();
}
private static class RepeatStatement extends Statement {
private final int times;
private final Statement statement;
private RepeatStatement(int times, Statement statement) {
this.times = times;
this.statement = statement;
}
#Override
public void evaluate() throws Throwable {
for (int i = 0; i < times; i++) {
statement.evaluate();
}
}
}
#Override
public Statement apply(Statement statement, Description description) {
Statement result = statement;
Repeat repeat = description.getAnnotation(Repeat.class);
if (repeat != null) {
int times = repeat.times();
result = new RepeatStatement(times, statement);
}
return result;
}
}
The easiest (as in least amount of new code required) way to do this is to run the test as a parametrized test (annotate with an #RunWith(Parameterized.class) and add a method to provide 10 empty parameters). That way the framework will run the test 10 times.
This test would need to be the only test in the class, or better put all test methods should need to be run 10 times in the class.
Here is an example:
#RunWith(Parameterized.class)
public class RunTenTimes {
#Parameterized.Parameters
public static List<Object[]> data() {
return Arrays.asList(new Object[10][0]);
}
public RunTenTimes() {
}
#Test
public void runsTenTimes() {
System.out.println("run");
}
}
With the above, it is possible to even do it with a parameter-less constructor, but I'm not sure if the framework authors intended that, or if that will break in the future.
If you are implementing your own runner, then you could have the runner run the test 10 times. If you are using a third party runner, then with 4.7, you can use the new #Rule annotation and implement the MethodRule interface so that it takes the statement and executes it 10 times in a for loop. The current disadvantage of this approach is that #Before and #After get run only once. This will likely change in the next version of JUnit (the #Before will run after the #Rule), but regardless you will be acting on the same instance of the object (something that isn't true of the Parameterized runner). This assumes that whatever runner you are running the class with correctly recognizes the #Rule annotations. That is only the case if it is delegating to the JUnit runners.
If you are running with a custom runner that does not recognize the #Rule annotation, then you are really stuck with having to write your own runner that delegates appropriately to that Runner and runs it 10 times.
Note that there are other ways to potentially solve this (such as the Theories runner) but they all require a runner. Unfortunately JUnit does not currently support layers of runners. That is a runner that chains other runners.
I had a very similar issue and as a result I've created a library to run Android UI tests multiple times. Might be useful in your case: https://github.com/stepstone-tech/AndroidTestXRunner
I am using Otto's event bus in my application. In one of my classes I am posting the event.
MyEvent myevent = new MyEvent();
uiBus.post(myEvent);
I am able to test the post method.
Now there is another class which is receiving the event.
//ReceiverClass.java
#Subscribe
public void onEventReceived(MyEvent myevent) {
callAMethod();
}
How do I unit test that this method was invoked. I tried with the following test code
#Mock
Bus uiBus;
#Test
public void testBusReceviedEvent() {
ReceiverClass instance = new ReceiverClass();
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myEvent);
//Test
verify(instance, times(1)).callAMethod();
}
But this code doesn't work.
I'm a little late to the party but here is an example of a class which works and accounts for async calls. Instead of Mocking EventBus we simply let it do it's thing and register it in the TestDriver class below.
The thing that makes this work is the CountDownLatch which, with the help of the abstract DataTransferCallback class, waits for latch.countDown() to be called or 5 seconds to go by.
Just register your test class and in the #Subscribe method, pass it back to the method that created the DataTransferCallback and do your assertions there.
#RunWith(AndroidJUnit4.class)
public class TestDriver {
private final CountDownLatch latch = new CountDownLatch(1);
private EventBus eventBus;
private DataTransferCallback transferCallback;
public abstract class DataTransferCallback {
abstract void onSuccess(DataTransfer event);
}
#Before
public void setUp() {
EventBus.getDefault().register(this);
eventBus = spy(EventBus.getDefault());
}
#SuppressWarnings("unchecked")
#Test
public void test200Resposne() throws InterruptedException {
// Get known good JSON
final String json = TestJSON.get200Response();
// Class under test
final Driver driver = new Driver(InstrumentationRegistry.getTargetContext());
final JsonParser jsonParser = new JsonParser();
//boolean to hold our test result
final boolean[] testPassed = new boolean[]{false};
transferCallback = new DataTransferCallback() {
#Override
public void onSuccess(DataTransfer event) {
assertNotNull(event);
verify(eventBus).post(event);
assertThat(event.getStatus(), is("OK"));
assertTrue(event.getData() != null);
testPassed[0] = true;
}
};
//Set our test EventBus object
driver.setEventBus(eventBus);
// The actual method under test
driver.parseData(jsonParser.parse(json));
// Set a countdown latch to wait for the result (5s)
latch.await(5000, TimeUnit.MILLISECONDS);
// will wait here until 5s or the #Subscrube method is hit
assertTrue(testPassed[0]);
}
//Because we want to examine EventBus Output, register it
//to this class and pass the event back through our custom abstract class
#Subscribe
public void onReceiveEventBusEvent(DataTransfer event) {
assertNotNull(transferCallback);
transferCallback.onSuccess(event);
//notify latch so that we can proceed
latch.countDown();
}
}
It does not work because instance is not a mock. You will have to verify the effects of callAMethod or put that method in another class and inject a mock of this new class into your ReceiverClass class.
For example...
private class ReceiverClass {
private MyNewClass theNewClassIWasTalkingAbout;
// Stick in a setter for that ^
#Subscribe
public void onEventReceived(MyEvent myevent) {
theNewClassIWasTalkingAbout.callAMethod();
}
}
Then your test will have to change slightly...
#Mock
private MyNewClass mockNewClass;
#InjectMocks // This will be the "solid" implementation of the thing you are trying to test, it is not a mock...
private ReceiverClass instance;
#Test
public void testBusReceivedEvent() {
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myevent);
verify(mockNewClass, times(1)).callAMethod();
}
Hope this helps.
On my RxJava code, I create an Observable with an interval. The code look like this :
public class GetProducts implements RxRequest<Observable<ProductsResult>> {
private LatLng markerLocationToReport;
private static int TIMER = 5;
private static GetProducts INSTANCE = new GetProducts();
public static GetProducts getInstance() {
return INSTANCE;
}
private Observable obs;
private GetProducts() {
}
#Override
public Observable<ProductsResult> getObservable() {
if (obs == null) {
obs = Observable.interval(0, TIMER, TimeUnit.SECONDS, Schedulers.io())
.flatMap((tick) -> Observable.just(GetProductsRequest.getProducts(markerLocationToReport)))
.retry()
.observeOn(AndroidSchedulers.mainThread()).publish();
((ConnectableObservable) obs).connect();
}
return obs;
}
public void setMarkerLocationToReport(LatLng markerLocationToReport) {
this.markerLocationToReport = markerLocationToReport;
}
}
My question is, how can I use the current value of markerLocationToReport in my flatMap function ?
Because in the current code when the request is made by GetProductsRequest.getProducts(markerLocationToReport) always use the initial value of markerLocationToReport when the Observable have been created and I need to update it during my app lifecycle.
Thank's
First of all, I see a couple of races: 1) markerLocationToReport should be volatile so the function of flatMap can observe it properly; 2) unless you call getObservable from a single thread, it may create multiple instances of the timed action.
In addition, you can use convenience .publish().autoConnect(0) which will connect immediately and you don't have to cast to ConnectableObservable.
I have this class that creates threads, but I want to convert that class into Service hoping it wont be destroyed on orientation change.
Here is the class:
public class Getter {
private final String ip;
private final int amount, poolSize;
private Vector<Integer> results = new Vector<Integer>();
private final ExecutorService es;
private Collection<Future<?>> futures = new LinkedList<Future<?>>();
public Getter(String ip, int amount, int poolSize) {
this.ip = ip;
this.amount = amount;
this.poolSize = poolSize;
es = Executors.newFixedThreadPool(this.poolSize);
}
public boolean working() {
boolean work = false;
for (Future<?> future : futures) {
if (!future.isDone()) {
work = true;
}
}
return work;
}
public Vector<Integer> getResults() {
Collections.sort(results);
return results;
}
public int threads(){
return poolSize;
}
public void start() {
for (int i = 0; i <= amount; i++) {
futures.add(es.submit(new Get(ip)));
}
es.shutdown();
}
public void stop(){
for (Future<?> future : futures) {
future.cancel(true);
}
}
private class Get implements Runnable {
private String ip;
private Get(String ip) {
this.ip = ip;
}
public void run() {
try {
// network stuff
// adds result to results Vector.
} catch (Exception ex) {
}
}
}
}
so is this class possible to convert into Service so it would run on background no matter what, once its started?
you can add this to a manifest file that stops your app getting destroyed on orientation change:
android:configChanges="orientation"
but if you want to make a service then just copy this example:
http://developer.android.com/reference/android/app/Service.html
you could add the class to the service and then just add an accessor to your service connection.
You can also add something to the manifest file to let your service start when the device is turned on
see here
Trying to start a service on boot on Android
If you want a Service which runs "no matter what", you might want to set it as foreground.
Yes. I think this would make a good IntentService. Your next-best choice is probably AsyncTask.
More:
Generally, I would say, if the behavior of the background task is closely tied to whatever starts it (e.g., an Activity, Fragment, Service, whatever), then make it an AsyncTask. If it is self-contained and meant to serve several different components in a modular fashion, make it a Service. In either case, an AsyncTask or IntentService is very easy to make.