Android: Obtaining a class after a call to startService() - android

I am getting confused with all the different terminology when using Android: Activity, Service...
Right now I create a service:
startService(new Intent(this, RingerServer.class));
And this service starts a thread:
public class RingerServer extends Service {
public void onCreate() {
super.onCreate();
new Thread(new Ringer()).start();
}
public class Ringer implements Runnable { ... }
public void refuseConnection() { ... }
}
In this service, the RingerServer, I also have methods that I want to use. I would like to keep a reference to the RingerServer. I would basically like the Activity that created the service to be able to call refuseConnection(), but not make that method static.
startService returns a ComponentName, so I've been trying to cast it back to RingerServer but that doesn't seem to work. I see that it has getClass() and I've checked and getClassName() gives me the correct class. I haven't been able to use getClass() properly though.
Is there any way I can please keep a reference to the newly created RingerServer class? I am sure this is trivial, but I am stuck right now.
Thank you very much,
James

You have two options
1.Override onStartCommand of the service and start the server with intent using an action. that intent will be received in service, based on the intent action you can call refuseConnection()
//In Activity
...
//Start the service
Intent intent=new Intent("com.xx.xx.REFUSE_CONNECTION");
startService(this,intent);
...
//In Service
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
if(intent.getAction().equals("com.xx.xx.REFUSE_CONNECTION")){
//Refuse the connection
refuseConnection();
}else {
//Do something else
}
}
//In Manifest
<service android:name="RingerService">
<intent-filter>
<action android:name="com.xx.xx.REFUSE_CONNECTION"></action>
</intent-filter>
</service>
Implement AIDL interface and override onBind() of service , and use this interface to call refuseConnection(). Refer to this link http://developer.android.com/guide/developing/tools/aidl.html regarding AIDL.

You can use a ServiceConnection to get access to your service class. See sample code here:
Android service running after pressing Home key
That said, managing things via the service's onStart handler is much simpler.

Related

Binding to a started Service

I have a remote Service that I start with startService() then bind to with bindService(). I do this because I want the Service to run in the background until the app explicitly stops it, even if the user closes the Activity with a swipe. Here are the relevant parts of the Service:
public class TrackService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("onStartCommand","got to onStartCommand");
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Log.i("onBind", "got to onBind");
return trackServiceBinder;
}
}
Here is the activity:
public class GPSLoggerActivity extends Activity implements ServiceConnection {
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(TrackService.class.getName());
intent.setClass(getApplicationContext(), TrackService.class);
startService(intent);
Log.i("onStart", "started TrackService");
if (!getApplicationContext().bindService(intent, this, 0)) {
// close the Activity
}
Log.i("onStart", "bound to TrackService");
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("onServiceConnected", "got here");
// get and use the interface from the Binder
}
}
Here is the manifest for the Service:
<service
android:name=".TrackService"
android:process=":track_service_process"
android:stopWithTask="false"
android:label="#string/track_service_label"
android:exported="false"
android:enabled="true">
<intent-filter>
<action android:name="com.mydomain.gpslogger.TrackService" />
</intent-filter>
</service>
When I run this in the debugger step-wise through startService() and bindService() everything is fine. Within the Service, onStartCommand() is invoked followed by onBind(), and in the Activity onServiceConnected() is invoked with the Binder. However, when I'm not debugging, for some reason the Service gets its onBind() method invoked before its onStartCommand() method. Here's the output from the log:
11-28 23:17:01.805: I/onStart(1103): started TrackService
11-28 23:17:01.815: I/onStart(1103): bound to TrackService
11-28 23:17:01.985: I/onBind(1165): got to onBind
11-28 23:17:01.995: I/onStartCommand(1165): got to onStartCommand
The Activity's onServiceConnected() method is never invoked. I've tried using BIND_AUTO_CREATE in the bindService() call, to no effect. It's pretty clear I have some sort of race condition going on here, but I've run this probably 20 times and it always comes out in the same order. The only way I can think of to enforce the correct order of calls in the Service is to put a BroadcastReceiver in the Activity and send an Intent from the onStartCommand() method of the Service to let the Activity know that it's time to call bindService(). That seems pretty kludgy...is there a better way to do this? Or is there something that I'm missing that is causing the out-of-order calls?
You could call startService() and then wait some period of time before calling bindService(). How long to wait is certainly a question, but I would think if your Service doesn't do massive amounts of work when started, 500 or 1000 milliseconds delay should do it.
I would question why this is important. The Service has an onCreate() method, which is called when the Service is instantiated, regardless of whether onStartCommand() or onBind() is called first. You should be able to setup your architecture so that you do all important setup in onCreate() and then it shouldn't matter in what order onStartCommand() or onBind() is called. NOTE: You must use flag BIND_AUTO_CREATE in the call to bindService()
I also do not understand why the onServiceConnected() method is not called. Unless the call to bindService() returns false (which basically means that Android can't find the Service to bind to), you should get the onServiceConnected() callback. This sounds strange. One possibility is that the Service crashes in onCreate(), or it takes too long to execute the onCreate() method, in which case Android will declare the Service broken.

How do I cancel all pending intents that are qued for intent Service

I have an intentservice that gets qued by the user and by my app automatically. I need to be able to kill all pending intents that are qued when the user logs out of my application, but I cannot seem to get that to work. I have tried stopService() and stopself(), but the intents continue to fire off the intentservice after the user has logged out. I would try to get the id of the intent but that is difficult as everytime the intentservice starts, the variable holding the intent id's is empty. Here is my intentservice code:
public class MainUploadIntentService extends IntentService {
private final String TAG = "MAINUPLOADINTSER";
private GMLHandsetApplication app = null;
private SimpleDateFormat sdf = null;
public boolean recStops = true;
public MainUploadIntentService() {
super("Main Upload Intent Service");
GMLHandsetApplication.writeToLogs(TAG,
"GMLMainUploadIntentService Constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Started");
if (app == null) {
app = (GMLHandsetApplication) getApplication();
}
uploadData(app);
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Finished");
}
#Override
public void onDestroy() {
GMLHandsetApplication.writeToLogs(TAG, "onDestroy Started");
app = null;
stopSelf();
GMLHandsetApplication.writeToLogs(TAG, "onDestroy completed");
}
public void uploadData(GMLHandsetApplication appl) {
//All of my code that needs to be ran
}
Unfortunately, I don't think it's possible to accomplish that with the standard IntentService methods since it doesn't offer a way to interrupt it while it's already going.
There are a few options I can think of that you can try to see if they fit your need.
Copy the IntentService code to make your own modifications to it that would allow you to remove pending messages. Looks like someone had some success with that here: Android: intentservice, how abort or skip a task in the handleintent queue
Instead of copying all the IntentService code, you might also be able to Bind to it like a normal Service (since IntentService extends Service) so you can write your own function to remove pending messages. This one is also mentioned in that link.
Rewrite the IntentService as a regular Service instead. With this option, you'd have more control over adding and removing messages.
I had what sounds like a similar situation where I was using an IntentService, and I eventually just converted it to a Service instead. That let me run the tasks concurrently and also cancel them when I needed to clear them.
Here
When should I free the native (Android NDK) handles? is the HangAroundIntentService class that has the method cancelQueue().
The class also has the method
public static Intent markedAsCancelIntent(Intent intent)
that converts an intent into a cancel intent, and
public static boolean isCancelIntent(Intent intent).
The class is based on the open-sourced Google's code.
Just a thought but inside of your onhandleintent can you have an argument that checks to see if app is running if not then don't run the code? example. In the start of your app you could have a static var
boolean appRunning;
Next in your onhandle of the intent, when you set the appRunning to false, after an onPause or onDestroy of activity, you could wrap the onhandleintent code in a boolean:
protected void onHandleIntent(final Intent intent) {
if(MainActivity.appRunning){
...
}
}
Just a thought

Android ServiceTestCase for IntentService

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();
}
}

Service not getting started

I am writing my first service. My activity works well, but when I call my service, it doesn't.
It looks like it's onCreate() is not getting called.
My service code:
public class NeglectedService extends Service {
public static final String MY_SERVICE = "android.intent.action.MAIN";
public void onCreate() {
Toast.makeText(this, "Service onCreate...", Toast.LENGTH_LONG).show();
}
}
I am not even getting the Toast message.
Here is my activity
startService(new Intent(NeglectedService.MY_SERVICE));
My manifest
action android:name="android.intent.action.MAIN"
Did you enter something like
<service android:name=".subpackagename.ServiceName"/>
into your Android Manifest xml file?
Seeing as the NeglectedService.MY_SERVICE is just a string, in your startService call you're essentially calling:
startService(new Intent("android.intent.action.MAIN"));
Clearly that doesn't have any reference to your particular service and isn't what you want. Instead, either register the service for particular intent filters and include those in your intent, or call it by class:
startService(new Intent(this, NeglectedService.class));
Call your Service using an Explicit intent, instead of using an implicit action string, which should be more unique anyway. In other words, use this in your Activity code:
startService( new Intent(this, NeglectedService.class) );
Hope that helps!

how to reference object created with Intent / startService

If I create a service in my app's onCreatelike this:
Intent srv = new Intent( this, MyService.class );
startService( srv );
how do I get a reference to the service object and how does the service object reference the app which launched it?
(Yes, I have listed the service in my AndroidManifest).
There are a few ways to handle this. You can bind to the service (bindService) where you will be called back with an IBinder interface.
Another approach is to just keep calling startService() with different intent data as a way of messaging to the service, with intent extra data containing message specifics.
Finally, if you know the service is in the same process, you can share the service instance in some static memory.
Building a Service
First of all, we need to create the Service in the AndroidManifest.xml file. Remember, that every Activity, Service, Content Provider you create in the code, you need to create a reference for here, in the Manifest, if not, the application will not recognize it.
<service android:name=".subpackagename.ServiceName"/>
In the code, we need to create a class that extends from “Service”
public class ServiceName extends Service {
private Timer timer = new Timer();
protected void onCreate() {
super.onCreate();
startservice();
}
}
This is a way to create Services, there are others ways, or the way I use to work with them. Here, we create a Timer, that every X seconds, calls to a method. This is running until we stop it. This can be used, for example, to check updates in an RSS feed. The “Timer” class is used in the startservice method like this
private void startservice() {
timer.scheduleAtFixedRate( new TimerTask() {
public void run() {
//Do whatever you want to do every “INTERVAL”
}
}, 0, INTERVAL);
; }
Where INTERVAL, is the time, every time the run method is executed.
To stop the service, we can stop the timer, for example, when the application is destroyed (in onDestroy())
private void stopservice() {
if (timer != null){
timer.cancel();
}
}
So, this application will be running in the background...

Categories

Resources