Does Android Job library DailyJob start an Application class onCreate? - android

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.

Related

What are the appropriate places to call different scheduling android components

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.

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)

Android Espresso: Wait for Activity to finish/start

Is there a canonical solution using Espresso to wait for a specific Activity to finish or start?
I have a SplashActivity that appears for a few seconds, then a MainActivity. I want Espresso to interact with the MainActivity, not the SplashActivity, but I can't seem to find any information about waiting for such a condition.
The closest thing I can find is a mention of idle resources but its not clear to me how I would use that here to wait for the Activity.
I guess your splash activity is performing some initialization.
If this is the case, my suggestion is to define some sort of listener pattern in order to be able to get a callback when the initialization is done. Then, you can make Espresso wait for the initialization with an IdlingResource.
NB: The following is NOT complete code, but it is meant to give you a hint in how to do so:
public class SplashIdlingResource implements IdlingResource, YourApplicationInitListener {
// volatile because can be set by a different
// thread than the test runner: the one calling back
private volatile boolean mIsInitialized;
private ResourceCallback mCallback;
public SplashIdlingResource() {
YourApplication application = // retrieve your Application object
mIsInitialized = application.isInitialized();
if (!mIsInitialized) {
application.addInitListener(this);
}
}
#Override
public String getName() {
return SplashIdlingResource.class.getName();
}
#Override
public boolean isIdleNow() {
return mIsInitialized;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mCallback = callback;
}
#Override
public void onApplicationInitCompleted() {
m_isInitialized = true;
if (m_callback != null) {
m_callback.onTransitionToIdle();
}
}
}
Where onApplicationInitCompleted() is the callback you defined and which must be called when the Splash Activity, and so the initialization, is done.
Finally, register this new IdlingResource with Espresso by calling Espresso.registerIdlingResource in test setup.

How to determine if Android Application is started with JUnit testing instrumentation?

I need to determine in runtime from code if the application is run under TestInstrumentation.
I could initialize the test environment with some env/system variable, but Eclipse ADK launch configuration would not allow me to do that.
Default Android system properties and environment do not to have any data about it. Moreover, they are identically same, whether the application is started regularly or under test.
This one could be a solution: Is it possible to find out if an Android application runs as part of an instrumentation test but since I do not test activities, all proposed methods there won't work. The ActivityManager.isRunningInTestHarness() method uses this under the hood:
SystemProperties.getBoolean("ro.test_harness")
which always returns false in my case. (To work with the hidden android.os.SystemProperties class I use reflection).
What else can I do to try to determine from inside the application if it's under test?
I have found one hacky solution: out of the application one can try to load a class from the testing package. The appication classloader surprisingly can load classes by name from the testing project if it was run under test. In other case the class is not found.
private static boolean isTestMode() {
boolean result;
try {
application.getClassLoader().loadClass("foo.bar.test.SomeTest");
// alternatively (see the comment below):
// Class.forName("foo.bar.test.SomeTest");
result = true;
} catch (final Exception e) {
result = false;
}
return result;
}
I admit this is not elegant but it works. Will be grateful for the proper solution.
The isTestMode() solution did not work for me on Android Studio 1.2.1.1. Almighty Krzysztof from our company tweaked your method by using:
Class.forName("foo.bar.test.SomeTest");
instead of getClassLoader(). Thanks for Krzysztof!
We created a solution to pass parameters to the MainActivity and use it inside the onCreate method, enabling you to define how the Activity will be created.
In MainActivity class, we created some constants, which could also be an enum. We created a static attribute too.
public class MainActivity {
public static final int APPLICATION_MODE = 5;
public static final int UNIT_TEST_MODE = 10;
public static final int OTHER_MODE = 15;
public static int activityMode = APPLICATION_MODE;
(...)
#Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
switch (activityMode) {
case OTHER_MODE:
(...)
break;
case UNIT_TEST_MODE:
Log.d(TAG, "Is in Test Mode!");
break;
case APPLICATION_MODE:
(...)
break;
}
(...)
}
(...)
}
We made MainActivityTest class abstract, created a setApplicationMode and called this method inside the setUp() method, before calling the super.setUp() method.
public abstract class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
protected void setUp() throws Exception {
setApplicationMode(); // <=====
super.setUp();
getActivity();
(...)
}
(...)
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.UNIT_TEST_MODE;
}
}
All other test classes inherit from MainActivityTest, if we want it to have another behaviour, we can simply override the setApplicationMode method.
public class OtherMainActivityTest extends MainActivityTest {
(...)
#Override
public void setApplicationMode() {
MainActivity.activityMode = MainActivity.OTHER_MODE;
}
}
The user nathan-almeida is the friend that is co-author of this solution.

Properties on Application not persisting between Service and Activity

The problem is that when accessing TaskTimerApplication.TEST from the activity, it is still "Creation", but in the service it is "Modification" like it should be. I have already made 100% sure that my service is executing its code before the activity is accessing the data. Below is a simple test scenario that presents the problem in a more obvious way.
Application code:
public class TaskTimerApplication extends Application {
// Static properties
private static final String TAG = "Application";
public static final boolean DEBUG = true;
public static String TEST = "Declaration";
#Override
public void onCreate() {
super.onCreate();
TEST = "Creation";
Log.v(TAG, "Created");
}
}
Service code:
public class TaskService extends Service {
public static final String TAG = "TaskService";
#Override
public void onCreate() {
TaskTimerApplication.TEST = "Modification";
Log.d(TAG, TaskTimerApplication.TEST);
}
}
My full, non-test code can be seen at my GitHub project. In the full code, the TASKS ArrayList stays empty in the activity, but is properly filled in the service.
Your problem lies in your manifest:
<service android:description="#string/service_description" android:name=".TaskService" android:label="#string/service_label" android:process=":TaskService"></service>
There is usually no good reason to waste RAM and CPU by putting your service in a separate process. Moreover, if you do put the service in a separate process, then your activity and your service will not be in the same process and therefore will not share a common Application object instance.
My recommendation is to drop the android:process attribute.

Categories

Resources