My app has one service already that handles notifications, but I need a second one that runs persistent in the background listening for incoming data from the Pebble smartwatch.
However, for some reason, even though the service is declared in the Android manifest and will launch with the app, it closes immediately and permanently.
I don't really want to use a foreground service, because I don't feel like I should have to. There are plenty of service that run quietly in the background in a persistent fashion like Facebook and Music Boss.
The service is being started in the main activity's onCreate, so why is my service being killed immediately?
From PebbleService.java:
package net.thevgc.quotes;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import com.getpebble.android.kit.PebbleKit;
import com.getpebble.android.kit.util.PebbleDictionary;
import java.util.UUID;
public class PebbleService extends Service {
private PebbleKit.PebbleDataReceiver appMessageReciever;
private static final int KEY_AUTHOR = 1;
private static final int KEY_QUOTE = 0;
private static final UUID WATCHAPP_UUID = UUID.fromString("18451441-8451-4418-4514-418451441845");
public void onCreate() {
super.onCreate();
}
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
String[] extras = (String[]) intent.getSerializableExtra("data");
final String quote = extras[0];
final String author = extras[1];
// Define AppMessage behavior
if (appMessageReciever == null) {
appMessageReciever = new PebbleKit.PebbleDataReceiver(WATCHAPP_UUID) {
#Override
public void receiveData(Context context, int transactionId, PebbleDictionary data) {
// Always ACK
PebbleKit.sendAckToPebble(context, transactionId);
// Send KEY_QUOTE to Pebble
PebbleDictionary out = new PebbleDictionary();
out.addString(KEY_QUOTE, mainActivity.quote[0]);
out.addString(KEY_AUTHOR, mainActivity.quote[1]);
PebbleKit.sendDataToPebble(getApplicationContext(), WATCHAPP_UUID, out);
}
};
// Add AppMessage capabilities
PebbleKit.registerReceivedDataHandler(this, appMessageReciever);
}
return START_STICKY;
}
public IBinder onBind(Intent intent) {
return null;
}
}
From AndroidManifest.xml:
<service
android:enabled="true"
android:name="PebbleService" />
UPDATE: Apparently the service is running somewhere, because I fiddled with the code some and now I'm getting a null pointer, but only when I close the main activity. I'm pretty sure it's getting relaunched and can't find the extra data it needs from the main activity because that's not the intent that started it. Which means I still have to use MainActivity mainActivity = new MainActivity(); to get the string data I need.
UPDATE 2: Okay, I feel really bad for causing all this confusion. The service is running, but it's not showing up in my Settings > Apps > Running list, even under the parent activity. I know it's running, though, because it finally ticked what it was supposed to do. Guessing a weak Bluetooth connection. That being said, I'm still throwing a NullPointerException with the current code trying to receive the intent extras. I've opened a new thread for that issue, though.
Since you are returning START_NOT_STICKY, Android will stop your Service as soon as it returns from onStartCommand(). If you want your Service to stay alive then you need to return START_STICKY from onStartCommand().
Also, your Service doesn't automatically launch with the app. It needs to be started by calling startService().
Also, as someone noted in a comment, do not create Android components using new, like this:
MainActivity mainActivity = new MainActivity();
Only Android can properly instantiate components Activity, Service, BroadcastReceiver and Provider, because they also need to have their Context set up.
Related
I have a skeleton class of JobIntentService
public class BackgroundRequestService extends JobIntentService {
/**
* Unique job ID for this service.
*/
static final int JOB_ID = 1234;
public static void enqueueWork(Context context, Intent work) {
BackgroundRequestService.enqueueWork(context, BackgroundRequestService.class, JOB_ID, work);
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
if (intent.getExtras() != null){
String x = ";";
}
}
}
I have included the Service in the manifest
<service android:name=".BackgroundRequestService"
android:enabled="true"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />
And calling the proper enqueue method
Intent intent = new Intent();
intent.putExtra("hardware", hardware);
BackgroundRequestService.enqueueWork(context, intent);
But the onHandleWork is never getting called. i have looked at all the pages about setting the services correctly and making sure onBind isn't overridden, but this still isn't working, wondering if another set of eyes would spot something ive missed. Thank you
Try to start service this way:
ComponentName comp = new ComponentName(context.getPackageName(), BackgroundRequestService.class.getName());
BackgroundRequestService.enqueueWork(context, (getIntent().setComponent(comp)));
Found the issue through some proper looking into the Logcat
Turns out the "hardware" that i was putting into my Intent was missing a field to be Serialized.
This caused this message to appear in the Logcat
I/UDP: no longer listening for UDP broadcasts cause of error Parcelable encountered IOException writing serializable object (name = Project.Hardware)
After fixing this Serialization issue i was able to call the onHandleWork() method.
I have a class in a service
MyClass m = new MyClass();
and inside my class I check if I have permission to overlay the view; if so, it's ok, otherwise I must start an activity
if (Settings.canDrawOverlays(mContext)) {
// draw over app
} else {
// start the activity
Intent i = new Intent(context,Calls.class);
context.startActivity(i);
}
When I start the activity I have a problem communicating between the class and the activity. I know how to use the interface but how can I register it in activity.
Some time I want to pass an object or data from the class to the activity or from the activity to the class... how can I do that?
I saw many examples in Stack Overflow about how to communicate between service and activity; they suggest to start the class from the activity but this does not work in my app because my class must be running all the time.
Perhaps you could use an event bus like mechanism where you can send or receive events through out your app, Though there are several libraries out there, I would recommend using Otto library for android.
Usage is pretty simple just register in your activity onCreate
Bus bus = new Bus();
bus.register(this);
For sending events
// example data to post
public class TestData {
public String message;
}
// post this data
bus.post(new TestData().message="Hello from the activity");
And subscribe to events like this
#Subscribe public void getMessage(TestData data) {
// TODO: React to the event somehow!
}
More info here
If you want to implement a communication pattern between a Service and an Activity, you should use a LocalBroadcastManager.
It will turn handy because, in case your Service is still on but your Activity
has been destroyed (very common situation), then the 'messagging' between the two will simply have no effect (no NPE or whatsoever will be thrown).
Step 1
Create a BroadcastReceiver in your Activity and define an ID / Filter
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service / Background Task
}
}
public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL")
Step 2
In your Activity register the broadcast in onResume() and unregister it in onPause().
#Override
protected void onResume() {
// Listen if a Service send me some data
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}
#Override
protected void onPause() {
// I'm going to the background / or being destroyed: no need to listen to anything anymore...
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}
Your Activity is now ready to receive data from any other component in your Application.
If it's in the background, then there is no need to update the UI: in fact the Activity will not respond if in the background.
In the same way, if it's being garbage collected, the Receiver will be unregistered and the Activity will just not respond to anything.
If the Activity is resumed / restarted, onResume() will be triggered and the Receiver will be registered again.
Step 3
All you need to do right now, is send data from the Service.
Simply call
final Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER);
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
and your Activity will accordingly respond to the signal.
It's surprising how few people know about the LocalBroadcastManager and instead use some self-implemented callback / singleton pattern, which increases complexity and non-readability.
This pattern is built-in in Android, so you don't need external libraries. As for security, this ensures that your signals stay internal to your application: no data can therefore be read by other apps.
I similarly answered to another question here.
The app that I am working on has two services (Pushwoosh and Helpshift) that use GCM for push notifications. I am attempting to implement the functionality shown here, in the Pushwoosh documentation, to allow both systems to function; https://docs.pushwoosh.com/v1.0/docs/gcm-integration-legacy. However my Android knowledge is failing me for how I actually route the bundle recieved to the relevant handlers. The project is dones in Unity but this is very much Android territory.
Here is the GcmListenerService class I have created that is very similar to the example;
import android.os.Bundle;
import android.content.Intent;
import android.util.Log;
import com.google.android.gms.gcm.*;
import android.content.ComponentName;
public class GCMListenerRouterService extends GcmListenerService
{
public GCMListenerRouterService()
{
super();
Log.i("Unity", "GCMListener - Constuctor");
}
private void dispatchMessage(String component, Bundle data)
{
Log.i("Unity", "GCMListener - dispatchMessage: " + (data != null ? data.toString() : "<null>") + " component: " + component);
Intent intent = new Intent();
intent.putExtras(data);
intent.setAction("com.google.android.c2dm.intent.RECEIVE");
intent.setComponent(new ComponentName(getPackageName(), component));
GcmReceiver.startWakefulService(getApplicationContext(), intent);
}
#Override
public void onMessageReceived(String from, Bundle data)
{
Log.i("Unity", "GCMListener - onMessageReceived: " + (data != null ? data.toString() : "<null>") + " from: " + from);
// Base GCM listener service removes this extra before calling onMessageReceived
// Need to set it again to pass intent to another service
//data.putString("from", from);
//if (TextUtils.equals(from, getString(R.string.PUSHWOOSH_PROJECT_ID)))
//{
// dispatchMessage(PushGcmIntentService.class.getName(), data);
//}
//else if (TextUtils.equals(from, getString(R.string.PRIVATE_PROJECT_ID)))
//{
// dispatchMessage(PrivateGCMListenerService.class.getName(), data);
//}
}
}
I am able to confirm that the push notification came from the correct messaging service, and I can determine if a push notifications came from one plugin or another. How do I route these bundle objects to the correct handler? I do not understand the following sample code;
if (TextUtils.equals(from, getString(R.string.PUSHWOOSH_PROJECT_ID)))
{
dispatchMessage(PushGcmIntentService.class.getName(), data);
}
else if (TextUtils.equals(from, getString(R.string.PRIVATE_PROJECT_ID)))
{
dispatchMessage(PrivateGCMListenerService.class.getName(), data);
}
I appreciate that this is example code but I can't find any functions in the Android documentation with the same signature as dispatchMessage. Do I need to make an intent service for each different type of message that is needed?
I know that for Helpshift I need to call a function with the signature handlePush(Context context, Bundle data) but I'm not sure what the Context object is. For Pushwoosh, i'm not sure what the handler is. While I am talking about two particular services I am assuming that this setup is a standard method for receiving messages and handling them.
Turns out that, for that for GCM, Bundle objects are the raw push details that you need to be handled. No further processing is needed and plugins/frameworks that support GCM should have a function that handles this, e.g. For helpshift that function is in com.helpshift.Core called handlePush(Context context, Bundle data).
Note that GCM is actually deprecated and Firebase Cloud Messenger is the new system going forward. This service has a different way of dealing with multiple push handlers and you should check your plugins for documentation on this.
Into manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Into application:
<service android:name="Myservice"/>
<receiver android:name="com.myapp.Onstart"> <!-- Tested also only .Onstart -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Onstart.java:
package com.myapp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class Onstart extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")){
context.startService(new Intent(context, Myservice.class));
}
}
}
Myservice.java:
package com.myapp;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class Myservice extends IntentService
{
public Myservice() {
super("Myservice");
}
#Override
protected void onHandleIntent(Intent intent) {
int n=0;
while(true)
{
if (i==20) {
stopSelf();
}
i = i++;
Log.i("Test", "n."+n++);
try {
Thread.sleep(10000);
}
catch (InterruptedException e)
{ }
}
}
}
Launching the application manually is shown the Main.java, and I want to know there (in Main.java) if my IntentService Myservice is still running. How to do it?
While I know that your question is to whether it's running or not, I do not see why would you need to know. Since IntentService works on demand.
IntentService is a base class for Services that handle asynchronous
requests (expressed as Intents) on demand. Clients send requests
through startService(Intent) calls; the service is started as needed,
handles each Intent in turn using a worker thread, and stops itself
when it runs out of work.
Also, from Context.startService(Intent) call doc:
If the service is being started or is already running, the
ComponentName of the actual service that was started is returned; else
if the service does not exist null is returned.
If you must, you can check the startService(Intent) return parameter.
Edit: It seems that you need an started service handled solely by you, not the system. This will allow you to have your own stop condition. Please refer to Services and Services Guide on how to use them.
References:
IntentService
startService(Intent service)
Services
Services Guide
I search a lot and I tried several ways but I couldn't find anything that avoid my error.
I'm working on app for my learning, where is an String in my MainActivity and then I call it in my Service. I tried something like this:
This next one goes in my myService.class
//In my myService.class that extends Service
public class myService extends Service{
AlarmManager manager;
PendingIntent pintent;
String te;
#Override
public void onCreate()
{
manager = (AlarmManager)(this.getSystemService(Context.ALARM_SERVICE));
pintent = PendingIntent.getBroadcast( this, 0, new Intent("blahblah"), 0 );}
#Override
public int onStartCommand(Intent intent, int flags, int startid)
{
super.onStartCommand(intent, flags, startid);
te = intent.getStringExtra("tst"); //if I change this line to te = "something", everything goes fine
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive( Context context, Intent intent )
{
Toast.makeText(getApplicationContext(),te, Toast.LENGTH_SHORT).show();
}
};
this.registerReceiver(receiver, new IntentFilter("blahblah") );
// set alarm to fire 30 min and waits 2 min
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 1000*30*60, 1000*2*60, pintent);
return START_STICKY;}
public IBinder onBind(Intent p1)
{
// TODO: Implement this method
return null;
}
}
Here the code runs perfectly, but when I exit my App, it crashes. After 1 minute, my device shows again that my App crashes, confirming my app "successfully" ran into background. What is wrong with it?
I also learned I could use IntentService instead of Service, wich one is better for long tasks and what is the difference between them ?
EDIT***
I received the following error: java.lang.NullPointerExeption.
So I change this:
te = intent.getStringExtra("tst");
To this:
try
{
te = intent.getStringExtra("tst");
}
catch(NullPointerException e)
{}
When I changed it my app works with any error, but The problem is: I need to retrieve my String from my MainActivity, when I close my app my service runs without errors but my "te" String takes null valor, what can I do to "save" my String in my service, to be able to use it and keeping showing the "working" message after I close my App ? Should I use SharedPreferences ?
Hope I was clear.
IntentService is different from Service.
IntentService Self kills the service as it finishes the task. Whereas Service runs for ever unless you kill it.
For my coding experience, I would use IntentService only for small tasks that run for a couple of seconds and use Service for long running one and call StopSelf() as needed.
Kindly post the log to answer your problem