I have a test project, my simple test case extends AndroidTestCase class :
public class MyTest extends AndroidTestCase{
private Context mContext;
public MyTest(){
super();
}
#Override
public void setUp() throws Exception {
super.setUp();
mContext = getContext();
//Start my service
mContext.startService(new Intent(mContext, MyService.class));
}
#Override
protected void runTest() {
...
}
...
}
In setUp() callback of my above test case, I started MyService.
MyService has also been declared in AndroidManifest.xml of my test project:
<service
android:name="com.my.app.services.MyService"/>
MyService.java :
public class MyService extends Service {
#Override
public void onCreate() {
super.onCreate();
Log.d("MyService", "onCreate()");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Log.d("MyService", "onStartCommand");
}
...
}
But after I run my test case, I noticed from log that neither onCreate() nor onStartCommand() callbacks of MyService have been called.
Why? Is there any special rule applied to Service usage in Android Test Framework which I missed?
The context returned by AndroidTestCase is probably a mocked context - it probably has no implementation for startService. Have you read http://developer.android.com/tools/testing/service_testing.html ?
If you want to wowk with service then use ServicetestCase.
I have been having the same problem, I think. I am trying to test code which calls startService, not test the service itself, so I created a new service for testing purposes.
It appears that test cases can start services, but the services have to be part of the main project being tested, they can't be part of the test project. I don't really like having a test only class in my project, but since it seems to be the only way ...
Related
I have a service which is running in background process.This service is going to search for bluetooth devices after the application is killed by user.
<service android:name=".BLEBackgroundService"
android:enabled="true"
android:process=":externalBLEProcess"/>
in BLEBacgroundService class I have:
public class BLEBackgroundService extends Service{
.
.
.
public BLEDetector bleDetector;
public int onStartCommand(Intent intent, int flags, int startId) {
.
.
.
Log.i(tag,"onStartCommand");
startScan();
return START_STICKY;
}
void startScan() {
bleDetector.startScan();
Log.d(tag,"StartScan");
}
#Override
public void onDestroy() {
bleDetector.stopScan();
Log.w(tag,"Service OnDestroy");
super.onDestroy();
}
}
And in my BLEDetector class I have:
public class BLEDetector{
.
.
.
BluetoothAdapter mBAdapter;
public void stopScan(){
Log.i(tag,"StopScan");
mBAdapter.stopLeScan(stopCallback);
}
}
and in my activity here is how I start this service:
public class MyActivity extends AppCompatActivity{
Intent serviceIntent;
.
.
.
//This part is inside a Retrofit Callback onResponse
serviceIntent = new Intent(ItemListActivity.this,BLEBackgroundService.class);
serviceIntent.putExtra
(BasicUtils.getStringResource(getApplicationContext()
,R.string.beaconJsonIdentifier),new Gson().toJson(beaconModel));
serviceIntent.putExtra
(BasicUtils.getStringResource
(getApplicationContext(),R.string.currssiID), prgss);
if (BleUtil.isBluetoothEnabled(getApplicationContext()))
startService(serviceIntent);
}
public void disableService(){
stopService(serviceIntent);
}
And when I try to stop this service I just call disableService().
When I start service it calls onCreate successfully and service starts wroking on a seperate process which I can see logs from Logcat.But when I try to stop service it prints
Finally after reading this part of document I realized that sometimes my services onStartCommand function is called twice (which I have no idea why) and since I created bleDetector every time onStartCommand function was being called, I lost reference to it.
So all I had to do was to check to see if bleDetector was null or not.
How ever I have no idea why onStartCommand is being called multiple times.I call startService just once in my activity when I get a successful response from server using retrofit.
EDIT:
Sometimes in my activity, onResume is called multiple times and that's the reason why my startService is called multiple times.
hi guys thank you for answering my question,i have made an android app in android studio i want to make funtion when i close the app the function start automatically in background is there any way to do it (Sorry For My Bad English)
You can use services. Here is the link for the official documentation:
https://developer.android.com/guide/components/services.html
You can use Application class.
public class App extends Application {
private static App instance;
private static Context context;
#Override
public void onCreate() {
super.onCreate();
App.context = getApplicationContext();
startService(new Intent(this, YourBackgroundService.class));
}
}
Then in BackgroundService class should be like this :
public class YourBackgroundService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
} <br>
Make sure you can declare this class in AndroidManifest.xml
<service android:name=".YourBackgroundService" />
If you declare like this the application will run always in background.
My Discovery class extends Service class. When I try to get its singletone from other class this way:
Discovery discovery = Discovery.getInstance();
I get a NullPointerException. This is the Discovery code:
public static Discovery getInstance(){
if (discovery == null){
discovery = new Discovery();
discovery.initDiscovery ();
}
Log.i(TAG, "get discovery instance");
return discovery;
}
public Discovery() {
}
private void initDiscovery(){
mDiscoveredDevices = new ArrayList<String>();
BluetoothManager bluetoothManager = (BluetoothManager) discovery.getSystemService(Context.BLUETOOTH_SERVICE);<--NullPointerException
....
}
This is not Android my friend.
To create a service you need to declare it in manifest:
<service
android:name=".DiscoveryService" />
After which you can instantiate it but never using operator new. Instead you need to call:
startService(context, new Intent(context, DiscoveryService.class);
There are other ways of firing a service intent but this will suffice for now.
The service's construction code should be placed at onCreate:
class DiscoveryService extends Service {
#Override
public void onCreate() {
service construction code here
}
}
And its request handling code in onStartCommand:
public int onStartCommand(Intent intent, int flags, int startId) {
handle incoming intent
}
Now, if you do need to access a service instance probably the simplest way
of achieving it would be to declare and maintain a static instance reference within
the service. Do it like this:
class DiscoveryService extends Service {
private static DiscoveryService inst; // <-----------------
public DiscoveryService getInstance() {
return inst;
}
#Override
public void onCreate() {
service construction code here
inst = this;
}
#Override
public void onDestroy() {
cleanup code here
inst = null;
}
}
This approach has its shortcomings, but unlike yours, it will work. Still use with care.
Finally - years of writing & reviewing Android code have led me to the conclusion
that what most novice developers want when they ask for Service, is in fact an IntentService..
Please read the docs and make sure you got your class right.
Good luck.
I've got a Service in my Android application. During onStartCommand, I pass the Service object to another class. Then, from there, there's a thread that after 30 seconds starts another Service. It is something like this:
public class FooService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MyClass mc = new MyClass(this);
mc.testMethod();
stopSelf();
return START_NOT_STICKY;
}
}
And this is MyClass:
public class MyClass {
private Service service;
public MyClass(Service service) {
this.service = service;
}
public void testMethod() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(20*1000);
Intent intent = new Intent(service, BarService.class);
service.startService(intent);
}
catch (Exception e) {
// CATCH!
}
}
}).start();
}
}
Now, as you can see, in FooService I call stopSelf() wich destroys that Service object. By the way MyClass has got a copy of that Service that was passed by value. After 20 seconds, we can start BarService from MyClass. I've tested it and it works but I can't understand why! The way I wrote the code is dirty (for me). Is it correct to start another service from one that was destroyed? Thank you for your help.
I've tested it and it works but I can't understand why
It works today on the environments you tested in. It may not work in all environments (e.g., ROM mods) and may not work tomorrow (e.g., Android OS updates). A destroyed Context, such as your stopped service, should not be used for anything. It happens that presently you can still use it to call startService() later, but that behavior is not guaranteed.
Is it correct to start another service from one that was destroyed?
No. In this case, I fail to see why you need two services in the first place.
I've got a copy of that service
No, you do not.
I'm currently writing unit tests for an android application and stumbled into the following issue:
I use the ServiceTestCase to test an IntentService like this:
#Override
public void setUp() throws Exception {
super.setUp();
}
public void testService()
{
Intent intent = new Intent(getSystemContext(), MyIntentService.class);
super.startService(intent);
assertNotNull(getService());
}
However I noticed that my IntentService is created (means that onCreate is called) but I never receive a call into onHandleIntent(Intent intent)
Has anyone already tested an IntentService with the ServiceTestCase class?
Thanks!
This is a bit late, but I just struggled with this. You could solve this by creating a class that simply overrides the onStart of you service so it calls onHandleIntent directly. So for instance, if you have a LocationUpdaterService, you could create a fake class that overrides the onStart function like this:
public class LocationUpdaterServiceFake extends LocationUpdaterService {
#Override
public void onStart(Intent intent, int startId) {
onHandleIntent(intent);
stopSelf(startId);
}
LocationUpdaterService is a subclass of IntentService, so when you write your tests, just use the LocationUpdaterServiceFake class like this
public class LocationUpdateServiceTest extends ServiceTestCase<LocationUpdaterServiceFake> {
public LocationUpdateServiceTest()
{
super(LocationUpdaterServiceFake.class);
}
public void testNewAreaNullLocation()
{
Intent intent = new Intent();
intent.setAction(LocationUpdaterService.ACTION_NEW_AREA);
startService(intent);
}
}
Now whenever you call startService, it will bypass the threading code in IntentService and just call your onHandleIntent function
I just got started into testing my own IntentService and it's proving to be a bit of a headache.
Still trying to work things out but for the scenario where it seems that you do not receive a call to your method onHandleIntent(), (I'm not very good with the technicalities behind junit so forgive my use of terminology) it should be because the test framework, based on your code, actually tears down or end the test method once your call to startService returns. There is insufficient time for onHandleIntent to be triggered.
I verified the above theory by adding an infinite loop within my test case - only then can I see my log statements in onHandleIntent logged.
You just have to add a:
Thread.sleep(XXXXXXX);
Choose the XXXX after the startService, then it will let the thread go into the onHandleIntent method.
In Android Studio 1.1, when running tests using the Run/Debug Configuration | Android Tests facility on any unit under test code (UUT) that extends IntentService, the ServiceTestCase.java (JUnit?) code does not call onHandleIntent(Intent intent) method in the UUT. ServiceTestCase only calls onCreate so the problem is in the test code.
protected void startService(Intent intent) {
if (!mServiceAttached) {
setupService();
}
assertNotNull(mService);
if (!mServiceCreated) {
mService.onCreate();
mServiceCreated = true;
}
mService.onStartCommand(intent, 0, mServiceId);
mServiceStarted = true;
}
In my file smSimulatorTest.java:
public class smSimulatorTest extends ServiceTestCase<smSimulator>
At this point, I'm looking for other solutions in the testing framework that test UUTs through Intents since this is how IntentService is instantiated.
http://developer.android.com/reference/android/app/IntentService.html - To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.
I, like others, put my code in the onHandleintent() as directed by the above documentation, however, ServiceTestCase only tests onStart and onStartCommand has shown above.
This is my approach for now:
The start Intent that invokes the service specifies the Service method to test
public void test_can_do_the_work() {
Intent startIntent = new Intent();
startIntent.putExtra("IN_TEST_MODE", "TEST_SPECIFIC_METHOD");
startIntent.setClass(getContext(), MyServiceToTest.class);
startService(startIntent);
assertNotNull(getService()); // Your assertion Service specific assertion
}
In the service onStart, we check for the specific Extra passed and call the method to test. This won't execute when Handle intent fired.
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
String in_test_mode = intent.getStringExtra("TEST_SPECIFIC_METHOD");
if(in_test_mode != null){
doServiceWork();
}
}