I am trying to send primitive data from a class extending IntentService to a class extending a BroadcastReceiver, however, data received in the BroadcastReceiver is null.
In my IntentService class, I have the following code
Intent smsListener = new Intent(ScheduledRepliesService.this, SmsListener.class);
smsListener.putExtra("reply", reply);
smsListener.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(smsListener);
I can guarantee that reply is not null in IntentService class.
I retrieve it in onReceive method in BroadcastReceiver class with
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
String replyMessage = intent.getStringExtra("reply");
...
}
...
}
I have declared the receiver in Manifest.xml file
<receiver android:name=".utilities.sms.SmsListener">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
and can confirm that if replyMessage string is not null, code produces no errors.
I would appreciate your help, thank you.
Although I was not able to find an answer to my problem, I did a bit of a workaround, and it does solve my problem. I hope this answer will be useful for somebody else as well.
I have a foreground service responsible for creating notifications when SMS message is received and a broadcast receiver whose task is to return said message body and sender number.
I was thinking in the wrong direction, sending data from service to receiver, but it should be the other way around.
Create an interface
public interface SmsListener {
public void messageReceived(String messageSender, String messageText);
}
and use this code in broadcast receiver
public class SmsBroadcastReceiver extends BroadcastReceiver {
//Interface
private static SmsListener smsListener;
#Override
public void onReceive(Context context, Intent intent) {
//Get data to a Bundle
Bundle data = intent.getExtras();
//PDUs represent received SMS message(s)
assert data != null;
Object[] pdus = (Object[]) data.get("pdus");
for (int i = 0; i < pdus.length; i++) {
//Reconstruct SMS message from pdus object
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
String sender = smsMessage.getDisplayOriginatingAddress();
String messageBody = smsMessage.getDisplayMessageBody();
//SmsListener's callback method
smsListener.messageReceived(sender, messageBody);
}
}
//Bind listener for the use in ScheduledRepliesService
public static void bindListener(SmsListener listener) { smsListener = listener; }
}
and lastly, retrieve the SMS message contents and sender number (as string) in service
//Get SMS message data from SmsBroadcastReceiver
SmsBroadcastReceiver.bindListener(new SmsListener() {
#Override
public void messageReceived(String messageSender, String messageText) {
//TODO: do something in here
....
});
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.
Hi I make Android application for Xamarin. I have created a simple application in the Android studio. so any answer welcome either Java or C#
I have a service(GPS service) and 2 Activities.
MainActivity - GPS service are well connected with the broadcast. I hope MainActivity -> Another activity real time GPS point.(It is also okay to send from the GPS service to another activity.) but it is fail...app is dead..
MainActivity code
private void RegisterService()
{
_gpsServiceConnection = new GPSServiceConnection(_binder);
_gpsServiceIntent = new Intent(Android.App.Application.Context, typeof(GPS.GPSService));
BindService(_gpsServiceIntent, _gpsServiceConnection, Bind.AutoCreate);
}
private void RegisterBroadcastReceiver()
{
IntentFilter filter = new IntentFilter(GPSServiceReciever.LOCATION_UPDATED);
filter.AddCategory(Intent.CategoryDefault);
_receiver = new GPSServiceReciever();
RegisterReceiver(_receiver, filter);
}
private void UnRegisterBroadcastReceiver()
{
UnregisterReceiver(_receiver);
}
public void UpdateUI(Intent intent)
{
LatLng_txt.Text = intent.GetStringExtra("Location");
Lat = intent.GetDoubleExtra("Lat", 0.0);
Lng = intent.GetDoubleExtra("Lng", 0.0);
}
protected override void OnResume()
{
base.OnResume();
RegisterBroadcastReceiver();
}
protected override void OnPause()
{
base.OnPause();
UnRegisterBroadcastReceiver();
}
[BroadcastReceiver]
internal class GPSServiceReciever : BroadcastReceiver
{
public static readonly string LOCATION_UPDATED = "LOCATION_UPDATED";
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action.Equals(LOCATION_UPDATED))
{
Instance.UpdateUI(intent);
}
}
}
GPS Service code
public void OnLocationChanged(Location location)
{
try
{
_currentLocation = location;
if (_currentLocation == null)
{
_location = "Unable to determine your location.";
}
else
{
_location = String.Format("{0}, {1}", _currentLocation.Latitude, _currentLocation.Longitude);
Geocoder geocoder = new Geocoder(this);
IList<Address> addressList = geocoder.GetFromLocation(_currentLocation.Latitude,
_currentLocation.Longitude, 10);
Address addressCurrent = addressList.FirstOrDefault();
if (addressCurrent != null)
{
StringBuilder deviceAddress = new StringBuilder();
for (int i = 0; i < addressCurrent.MaxAddressLineIndex; i++)
{
deviceAddress.Append(addressCurrent.GetAddressLine(i)).AppendLine(",");
}
_address = deviceAddress.ToString();
}
else
{
_address = "Unable to determine the address.";
}
IList<Address> source = geocoder.GetFromLocationName(_sourceAddress, 1);
Address addressOrigin = source.FirstOrDefault();
var coord1 = new LatLng(addressOrigin.Latitude, addressOrigin.Longitude);
var coord2 = new LatLng(addressCurrent.Latitude, addressCurrent.Longitude);
var distanceInRadius = Utils.HaversineDistance(coord1, coord2, Utils.DistanceUnit.Miles);
_remarks = string.Format("Your are {0} miles away from your original location.", distanceInRadius);
Intent intent = new Intent(this, typeof(MainActivity.GPSServiceReciever));
intent.SetAction(MainActivity.GPSServiceReciever.LOCATION_UPDATED);
intent.AddCategory(Intent.CategoryDefault);
intent.PutExtra("Location", _location);
intent.PutExtra("Lat", _currentLocation.Latitude);
intent.PutExtra("Lng", _currentLocation.Longitude);
SendBroadcast(intent);
}
}
catch
{
_address = "Unable to determine the address.";
}
}
Is not there a good way?
I understood your problem.But dont know more about GPS etc.I have faced the same problem when I was creating Music App.
Two activities were there and one service.And successfully got real time song position and song data from both activities.
My MainActivity has
ServiceConnection sc=null;
public static PlayerService ps;
And gets its value in onCreate of MainActivity
sc=new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName p1, IBinder p2)
{
PlayerService.Getters getters=(PlayerService.Getters) p2;
ps=getters.getService();
}
#Override
public void onServiceDisconnected(ComponentName p1)
{
// TODO: Implement this method
}
};
Then PlayerService.Getters class is
public class Getters extends Binder
{
public PlayerService getService()
{
return PlayerService.this;
}
}
PlayerService has
#Override
public IBinder onBind(Intent p1)
{
return new Getters();
}
getService of Getters gives the object of PlayerService to my MainActivity.
Now I can get real time values of service variables and methods using static ps from multiple activities.
In order to send data or information from Service to Activity, you'll need to use Messenger API. This API will allow you to create an inter process communication (IPC) i.e. a communication link between two or more processes. In Android, Activity and Service are two separate processes, so you can use the IPC technique to establish a communication link in between them.
In the IPC technique, there are two ends, the Server end and the Client end. The Service acts as the Server and Activity acts as the Client.
Note: Service will only be able to communicate with one Activity at a time.
Messenger allows for the implementation of message-based communication across processes by help of Handlers.
Handler is a that allows you to send and process these messages.
Steps for implementing a Messenger:
Step 1. Service implements a Handler which receives the callbacks from the Activity
Step 2. The Handler then creates a Messenger object which further on creates an IBinder that the Service returns to the Activity.
Step 3. Activity then uses the IBinder to instantiate the Messenger, which the Activity uses to send messages to the Service.
Step 4. The Service receives the messages in the Handler created in the 1st step.
Lets now understand it with an example:
Create a Handler in the Service like this:
class ServiceHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
default:
super.handleMessage(msg);
}
}
}
Now, add the Messenger object along with onBind() method to the Service as mentioned in 2nd step above:
final Messenger messenger = new Messenger(new ServiceHandler());
#Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
In the Activity, we will create a ServiceConnection to fetch the iBinder from the Service to instantiate the Messenger object as mentioned in the 3rd step above.
Messenger messenger;
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder iBinder) {
messenger = new Messenger(iBinder);
}
public void onServiceDisconnected(ComponentName className) {
}
};
Bind the Service to the Activity by help of the ServiceConnection created above:
bindService(new Intent(this, MessengerService.class), serviceConnection,
Context.BIND_AUTO_CREATE);
To send messages to the Service from the Activity, use the send() method of the Messenger object.
If you want to receive messages from the Service in the Activity, you need to create a Messenger in the Activity along with a Handler and use the replyTo parameter of the Messenger to receive messages to the respective Handler.
I know this question has been asked before, but I've been over all of the answers I could find and still haven't been able to solve the problem.
The issue is that when by BroadcastReceiver starts the IntentService onHandleIntent() isn't called. Weirdly enough the constructor does run (as I can see by the Log output).
This is my code:
NoLiSeA.class
(This class contains the BroadcastReceiver that starts my service)
public void toProcess(StatusBarNotification sbn) {
LocalBroadcastManager.getInstance(this).registerReceiver(notificationForwarder, new IntentFilter("to_forward"));
Intent intent = new Intent("to_forward");
intent.putExtra("sbn", sbn);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Log.i("NoLiSe.TAG", "toProcess");
}
private BroadcastReceiver notificationForwarder = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("NoLiSe.TAG", "BroadCastReceiver.onReceive");
Intent i = new Intent(context, CoreTwoA.class);
i.putExtras(intent);
startService(i);
}
}
};
CoreTwoA.class
(This is the IntentService. onHandleIntent() is not called as I can see due to no log text in the console.)
public class CoreTwoA extends IntentService {
private TextToSpeech mtts;
public CoreTwoA() {
super("TheCoreWorker");
Log.d("source", "exception", new Exception());
Log.i("CoreTwoA.TAG", "Constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i("CoreTwoA.TAG", "onHandleIntent");
}
}
AndroidManifest.xml
<service
android:name=".CoreTwoA"
android:label="#string/service_name"
android:exported="false">
</service>
UPDATE
So based on discussions below, I was able to narrow down the problem to the following line of code in the BroadCastReceiver:
i.putExtra("sbn", sbn)
If I remove it, i.e. add no extras to the intent, then my the onHandleIntent() method in my IntentService does run.
If it is included, onHandleIntent() doesn't run and the following is written to logcat by the Log.d() in the Constructor of my IntentService
06-10 19:40:35.355 25094-25094/com.dezainapps.myapp D/source: exception
java.lang.Exception
at com.dezainapps.myapp.CoreTwoA.<init>(CoreTwoA.java:20)
at java.lang.Class.newInstance(Native Method)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2859)
at android.app.ActivityThread.-wrap4(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
06-10 19:40:35.355 25094-25094/com.dezainapps.myapp I/CoreTwoA.TAG: Constructor
Any ideas why passing a StatusBarNotification object, that implements Parcelable, to a IntentService via an Intent doesn't work?
Oddly enough broadcasting the same StatusBarNotfication sbn object from my toProcess() method via an intent (see code) does work.
I had the same problem as the OP.
The issue turned out to be that I was passing a huge ArrayList (of about 4000 Parcelable elements) via the Intent that starts the IntentService. It was extremely difficult to diagnose as it failing 'silently' on a customer's device, so I got no ACRA error report.
Anyway, I used a quick and dirty trick to fix the problem - basically to store the ArrayList in a static element that I used to set/get the ArrayList rather than try to pass it through the Intent.
On further investigation (doing some tests other devices) I found a TransactionTooLargeException being thrown. More discussion on that here.
Here's my test code if anyone would like to replicate:
Test code
If you want to make it work, change the 4000 value to something much smaller.
ArrayList<ParcelableNameValuePair> testArrayList = new ArrayList<>();
for (int i = 0; i < 4000; i++) {
testArrayList.add(new ParcelableNameValuePair("name" + i, "value" + i));
}
TestIntentService.startAction(AdminHomeActivity.this, testArrayList);
TestIntentService.java
public class TestIntentService extends IntentService {
private static final String LOG_TAG = TestIntentService.class.getSimpleName();
private static final String TEST_ACTION = "com.example.action.FOO";
private static final String EXTRA_PARAM1 = "com.example.extra.PARAM1";
private static final String EXTRA_PARAM2 = "com.example.extra.PARAM2";
public TestIntentService() {
super("TestIntentService");
}
public static void startAction(Context context, ArrayList<ParcelableNameValuePair> testArrayList) {
Log.d(LOG_TAG, "1. startAction()");
Utilities.makeToast(context, "1. startAction()");
try {
int arrayListSize = (testArrayList == null) ? -1 : testArrayList.size();
Log.d(LOG_TAG, "2. arrayListSize: " + arrayListSize);
Utilities.makeToast(context, "2. arrayListSize: " + arrayListSize);
Intent intent = new Intent(context, TestIntentService.class);
intent.setAction(TEST_ACTION);
//intent.putExtra(EXTRA_PARAM1, testArrayList);
intent.putParcelableArrayListExtra(EXTRA_PARAM2, testArrayList);
/**
* This line should result in a call to onHandleIntent() but, if we're sending a huge ArrayList, it doesn't...
*/
context.startService(intent);
}
catch(Exception e) {
Log.e(LOG_TAG, "Exception starting service", e);
Utilities.makeToast(context, "Exception starting service: " + e);
}
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d(LOG_TAG, "3. onHandleIntent()");
Utilities.makeToast(getApplicationContext(), "3. onHandleIntent()");
try {
if (intent != null) {
final String action = intent.getAction();
if (TEST_ACTION.equals(action)) {
ArrayList<ParcelableNameValuePair> testArrayList = intent.getParcelableArrayListExtra(EXTRA_PARAM1);
int testArrayListSize = (testArrayList == null) ? -1 : testArrayList.size();
Log.d(LOG_TAG, "4. testArrayListSize: " + testArrayListSize);
Utilities.makeToast(getApplicationContext(), "4. testArrayListSize: " + testArrayListSize);
ArrayList<ParcelableNameValuePair> testArrayList2 = intent.getParcelableArrayListExtra(EXTRA_PARAM2);
int testArrayList2Size = (testArrayList2 == null) ? -1 : testArrayList2.size();
Log.d(LOG_TAG, "5. testArrayList2Size: " + testArrayList2Size);
Utilities.makeToast(getApplicationContext(), "5. testArrayList2Size: " + testArrayList2Size);
}
}
}
catch(Exception e) {
Log.e(LOG_TAG, "Exception handling service intent", e);
Utilities.makeToast(getApplicationContext(), "Exception handling service intent: " + e);
}
}
}
ParcelableNameValuePair.java
public class ParcelableNameValuePair implements Parcelable {
private String name, value;
public ParcelableNameValuePair(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeString(value);
}
public static final Parcelable.Creator<ParcelableNameValuePair> CREATOR = new Parcelable.Creator<ParcelableNameValuePair>() {
public ParcelableNameValuePair createFromParcel(Parcel in) {
return new ParcelableNameValuePair(in);
}
public ParcelableNameValuePair[] newArray(int size) {
return new ParcelableNameValuePair[size];
}
};
private ParcelableNameValuePair(Parcel in) {
name = in.readString();
value = in.readString();
}
}
Like I say, I used a quick and dirty solution to get around this problem. I think a better solution would be for the app to write the ArrayList to the file system, then pass a reference to that file (e.g., filename/path) via the Intent to the IntentService and then let the IntentService retrieve the file contents and convert it back to an ArrayList.
When the IntentService has done with the file, it should either delete it or pass the instruction back to the app via a Local Broadcast to delete the file that it created (passing back the same file reference that was supplied to it).
Have you tried using context.startService(intent);?
When you're in a broadcast receiver like this you don't have a context of your own to reference I believe so you need to use the one passed to the onRecieve method.
Use this:
private BroadcastReceiver notificationForwarder = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("NoLiSe.TAG", "BroadCastReceiver.onReceive");
Intent i = new Intent(context, CoreTwoA.class);
i.putExtra("intent",intent);
context.startService(i);
}
}
};
Use below code:
private BroadcastReceiver notificationForwarder = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("NoLiSe.TAG", "BroadCastReceiver.onReceive");
Intent i = new Intent(context, CoreTwoA.class);
i.putExtra("sbn",intent.getParcelableExtra("sbn"));
startService(i);
}
};
You are incorrectly using i.putExtras(intent); which I removed and added other way of sending the StatusBarNotification object through putExtra.
I tested this code and does call onHandleIntent
I am new to android and I am receiving the message in Android using jms topic now I want to generate notification of that message, For example as message is received notification appears. For generating notification, I need context of the application I tried different ways but not working can anybody now how to get the context in message listener. My listener implementation and notification function
Message Listener
private class DestinationMessageListener implements MessageListener {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
System.out.println(message);
Context ctx = getApplicationContext();
sendNotification("Helo","title",1,"test");
} catch (Exception ex) {
ex.printStackTrace();
//logMessage("EXCEPTION: " + ex.getMessage());
}
}
Notification Class
public void sendNotification(String title, String msg, int msgId, String badge,Context ctx) {
mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
Intent tabsIntent = new Intent(ctx, TabsFragmentActivity_.class);
tabsIntent.putExtra("GO_TO_MESSAGES", true);
tabsIntent.putExtra("MSG_ID", msgId);
tabsIntent.putExtra(TabsFragmentActivity.BADGE_KEY, badge);
PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0, tabsIntent, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx);
mNotificationManager.notify(msgId, mBuilder.build());
}
Note:
I am not receiving context in message listener implementation and this class is a non-activity class.I am using android annotation framework.
#Awais: you have to pass context to non activity/receiver/service class. You can also have other option like defining an application class in your application package and through this application package you can have appliction context.
In Android Manifest file declare following
<application android:name="com.xyz.MyApplication">
</application>
then write the class
public class MyApplication extends Application{
private static Context context;
public void onCreate(){
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
private class DestinationMessageListener implements MessageListener {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
System.out.println(message);
Context ctx = MyApplication.getAppContext();
sendNotification("Helo","title",1,"test",ctx);
} catch (Exception ex) {
ex.printStackTrace();
//logMessage("EXCEPTION: " + ex.getMessage());
}
}
So I need to send a push notification to a user's device. Then when the user clicks on the notification, I need my app to take a specific action. I want to include the parameter for the action in the notification. But I don't want the user to see the parameter; they should just see the message. Doing some research, I found the following on the urban airship website
{
"audience": "all",
"notification": {
"alert": "Extras example",
"android": {
"extra": {
"url": "http://example.com",
"story_id": "1234",
"moar": "{\"key\": \"value\"}"
}
}
},
"device_types": ["android"]
}
So I am supposing that the alert portion is what a user sees. And that the portion under android could be the parameters. So my question is, in Java, how do I read those extra portions? such as story_id, or moar?
Since there is no answer accepted here I thought I'll show how I am doing it.
I have a class that extends BroadcastReceiver like you should if you are following UrbanAirship. In this class I recieve the notifications:
public class IntentReceiver extends BroadcastReceiver {
private static final String logTag = "PushUA";
private String pushToken;
public static String APID_UPDATED_ACTION_SUFFIX = ".apid.updated";
#Override
public void onReceive(Context context, Intent intent) {
Log.i(logTag, "Received intent: " + intent.toString());
String action = intent.getAction();
if (action.equals(PushManager.ACTION_PUSH_RECEIVED)) {
int id = intent.getIntExtra(PushManager.EXTRA_NOTIFICATION_ID, 0);
logPushExtras(intent);
} else if (action.equals(PushManager.ACTION_NOTIFICATION_OPENED)) {
Log.i(logTag, "User clicked notification. Message: " + intent.getStringExtra(PushManager.EXTRA_ALERT));
logPushExtras(intent);
String url= intent.getStringExtra("url"); //Here you get your extras
...
Might help someone:)
You can extend BasicPushNotificationBuilder and override buildNotification. That method gets the extra parameters in extras.
#Override
public Notification buildNotification(String alert, Map<String, String> extras) {
// Only build inbox style notification for rich push messages
if (extras != null && RichPushManager.isRichPushMessage(extras)) {
return createRichNotification(alert);
} else {
return super.buildNotification(alert, extras);
}
}
See docs here.
Eran had the right idea, but you actually want to implement PushNotificationBuilder and then override buildNotification().
Something like this:
/**
* This class encapsulates notifications (those that appear in the notification shade).
*
* #author Karim Varela
*/
public class ManagerNotifications implements PushNotificationBuilder
{
#Override
public Notification buildNotification(String alert, Map<String, String> extras)
{
return null;
}
#Override
public int getNextId(String alert, Map<String, String> extras)
{
return 0;
}
}