Sending serializable object between applications with broadcast receiver - java

Im trying to send an Object that implements "Serializable" from one application to another via a Broadcast receiver.
I want to send a class like:
public class MyObject implements Serializable{
//basic properties
}
In application A i do this:
Intent i = new Intent("myApplication.string");
Bundle b = new Bundle();
b.putSerializable("myclass",obj);
i.putExtras(b);
sendBroadcast(i);
When i debug this I can verify that the object is properly stored in the bundle in the intent.
In application B i do this:
Manifest
<receiver
android:name="com.example.myapplication.myreceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="myApplication.string"/>
</intent-filter>
</receiver>
Broadcast Receiver
#Override
public void onReceive(Context context, Intent intent) {
try{
Bundle b = intent.getExtras();
MyObject s = (MyObject)b.getSerializable("myclass");
}catch (Exception e){
Log.d(TAG,e.getMessage());
}
}
In application B the intent does not hold the data that it did in application A.
When i try to cast the data it throws an:
Parcelable encountered ClassNotFoundException reading a Serializable object (name = com.example.myApplication.MyObject)
I've copied the class implementation in both application so they match.
In application A all the data is with in the mMap property of the Intent.extras - but in app B it is empty.
Can anyone help me understand this?
Thanks.

Im trying to send an Object that implements "Serializable" from one application to another via a Broadcast receiver.
That is not a good idea. It requires both apps to have the same class definition for that Java class, or at least one that is compatible. Since the apps may not be updated at the same time, you can run into cases where the sender has a newer edition of the Java class than does the recipient, which can cause a deserialization exception.
In application A i do this:
That is not a good idea. You are not sending the data to the other app. You are sending the data to any app that cares to listen for this broadcast, including any that may want to spy on your output.
In application B i do this:
That is not a good idea. Any app can send a broadcast to this app, either to spoof messages or attempt to cause the app to crash. And on Android 8.0+, you will not receive the broadcast in any case.
I've copied the class implementation in both application so they match.
Perhaps there is a problem in how you did this, as the error message would seem to disagree with your assessment.
I would start by getting rid of the Serializable. Only put things into extras that are guaranteed to be recognized correctly by all parties accessing those extras. So, use simple primitives, or Bundle, or other framework classes, not custom Serializable or Parcelable implementations. Then, see if your more ordinary extras are making it from app to app.
Then, do a better job of inter-process communication:
Use an explicit Intent (one with a ComponentName), not an implicit Intent (one with an action string), so that the "broadcast" only goes between the two parties and works around the Android 8.0+ implicit broadcast restrictions
Either implement permissions (e.g., android:permission on the <receiver>) or perform signature checks to ensure that the two parties are who you think they are

MyObject should implement Parcelable, not Serializable...

Related

Android Best way to keep and share data through the app

I am making a small Android app that run a Foreground Service which listen and notify the user whenever a new SMS message arrived. When the Service start it register the BroadcastReceiver with action android.provider.Telephony.SMS_RECEIVED in IntentFilter, and unregister it when stop. The application also get the user data along with a list of selected number from a JSON file in internal storage so that the Receiver can filter out which number to notify the user. My MainActivity has 3 buttons, Start and Stop Service, along with a Setting button that move to SettingActivity that display some user info as well as the list of selected number and allow the user to change it. My question is that: What is the best way to share the user data throughout the app so that all Activity as well as Service and Receiver can access it? I have thought of and tried a few ways:
Getting the user data from file in MainActivity where the application start up and pass it through Intent to others: this worked well when passing data from Activity to Activity as well as Service, but does not seem to work with BroadcastReceiver since it is registered to listen to SMS_RECEIVED and not other Broadcast even if I create a new Intent and broadcast it with startBroadcast
Making the user object static: I tried with making the User public static so that it can be called by other class when the MainActivity is done getting the data from the file, basically putting a public static User user in MainActivity and calling it by MainActivity.user in other class, this worked even with BroadcastReceiver but I am not sure if this the right way to share data throughout the application
Lastly is to get the data from the file when each Activity or Service is called, this mean when each Activity or Service is called, it get data again from the JSON file. I have not tried it yet but I think it might impact the performance as well as data consistency throughout the app.
So what is the right way to implement this? Please give me some suggestion. Thank you guys in advance.
There are a few suggestions for this:
1 - You could store the data you need in shared preferences, though if this is personal data, that you'd want to keep private (eg passwords, personally identifiable information) you probably don't want to do this.
2 - Similarly to what you are doing in your activity, you could create a service which is responsible for getting the user data from the file and keeps hold of it until the app is killed, for example -
public class UserDataStore()
This way you can create an instance of a UserDataStore on startup and then, through dependency injection, pass it around your app. So wherever you create your service that handles the broadcast events, you can just add a UserDataStore as a parameter, assuming you create your UserDataStore first. eg -
public class BroadcastReceiverService(UserDataStore store)
This way also means that your BroadcastReceiverService is more testable as you can mock the UserDataStore.
Example -
public class MainActivity() {
UserDataStore store = new UserDataStore()
BroadcastReceiverService broadcastService = new BroadcastReceiverService(store)
}
Then in your UserDataStore -
public class UserDataStore() {
// in here you want to get all your info about the user eg username, email etc
String name = ""
init {
User user = getUserInfo()
name = user.name
}
public String getUserName() {
return name
}
}
Then finally in your broadcast receiver
public class BroadcastReceiverService(UserDataStore store) {
public void receiveMessage(Message msg) {
if(msg.username == store.name) // continue
}
}

Problems accesing database in FirebaseMessagingService

I had an app with PushBots and it was working fine. The client asked to migrate to FCM, so i did. And its mostly working perfectly. Im receiving notifications and, when the app is in the foreground, the notifications get saved. With the app in the background the notifications are still arriving without any trouble but in this case its not saving into database.
So i started with this:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Timber.d("RECIEVED");
RemoteMessage.Notification n = remoteMessage.getNotification();
if (n != null) {
Notification notification = new Notification();
notification.setTitle(n.getTitle());
notification.setMessage(n.getBody());
notification.setDate(System.currentTimeMillis());
NotificationDAO dao = new NotificationDAO(MyApp.getInstance());
dao.create(notification);
showNotification(n.getTitle(), n.getBody());
}
}
So i thought the problem could be on the context, so i tried replacing MyApp.getInstance() with getApplicationContext(). That wasnt working, so then i changed getApplicationContext() with this. As it wasnt working neither i tried to do this access inside another thread, but none its working.
What am i doing wrong? The dao's method create is working in the rest of the application.
Regards.
If you want to use "context" in your FirebaseMesaagingService, you can use 'this'
The hierarchy of classes is as below
FirebaseMessagingService extends Service which (Service) extends ContextWrapper
which inturn (ContextWrapper) extends Context
Hence you can directly use,
'MyFirebaseMessagingService.this' or simply 'this'

Passing arbitrary object in Android Intent

I wish to pass an arbitary object via an Android intent, I have the following code:
In the activity I wish to send an intent from:
Intent intent = new Intent(containerListActivity, com.uk.jacob.containerdroid.activities.ContainerDetailsActivity.class);
intent.putExtra("container", containers.get(i));
containerListActivity.startActivity(intent);
The activity I wish to recieve an intent:
System.out.println(getIntent().getExtras("container"));
I seem to get the error "cannot resolve method putExtra.
containers is essentially a Jackson JSON mapped POJO.
Unfortunately, you can't put an arbitrary object in the Intent. It needs to be Parcelable or Serializable.
http://developer.android.com/reference/android/content/Intent.html
You can do it a few ways:
Have your custom class implement Serializable. This is quick to code, but may not be performant.
Have your custom class implement Parcelable. This could be a bit of work.
Convert your container to a string using Jackson, then convert it back after using getStringExtra.
Serialize your JSON to a string and then push it to the intent. In the receiving activity try getStringExtra() to extract it from the intent object.

How to make a callback from a Service to an Activity

Sorry for bugging you again, but I still can't find a way to make a callback from my activity to a service...
Found a similar question - How to Define Callbacks in Android?
// The callback interface
interface MyCallback {
void callbackCall();
}
// The class that takes the callback
class Worker {
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
}
// Option 1:
class Callback implements MyCallback {
void callback() {
// callback code goes here
}
}
worker.callback = new Callback();
yet not sure how to integrate that sample into my project.
Any suggestions or links to clear tutorials would be great!
That kind of callbacks (Observer pattern) that you are showing in your example won't work between a service and an activity. Use observer patter when, from class A, you created the instance of class B and want to send callbacks from B to A.
With regards to the services and activities, things are completely different. AFAICT, if you want to callback your Activity from a Service, the best method to achieve this is to use ResultReceiver. There are a lot of interesting things about ResultReceiver:
Its constructor receives a Handler (that you must create inside the activity), which will allow you to change UI from the service.
It implements Parcelable thus you can put a reference of your ResultReceiver in the Intent extras that you used to start the service.
Its onReceive method has a result code integer which allows you to generate different kind of callbacks (this is like if your callback interface had many methods). Also, it receives a Bundle which you can use to put all result data.
On the other hand, if you want to do a callback (not sure if that is correct term in this case), from your Activity to your Service, I guess you will have to send a Broadcast message or something like that.

Android: Passing a Service a Handler

So, I've read the android AIDL documentation and have a general idea of how RPC works between an Activity and a Service. However, for my application it seems overboard to implement such features: basically, I want to pass a Service a nice handler so its thread can pass data to my Activity. Currently I'm getting around this by using a static public member (a hack) but I would prefer just passing a Handler object in the Service's starting Intent. E.g. I can easily pass ints to my service upon creation:
int x = 0;
Intent svc = new Intent(this, MyService.class);
svc.putExtra("x",x);
startService(svc);
However since a Handler isn't serialize-able , I haven't found a way to pass it to the service without a simple static member hack. Any insight? Or, am I just going to have to suck it up and do a formal RPC to the service?
If your Service and Activity are in the same process, you can pass a Binder from your Service without doing the complicated RPC stuff:
public class MyEasyButNotGoodPracticesBinder {
public void gimmeHandler(Handler handler) {
// you got it!
}
}
IBinder mBinder = new MyEasyButNotGoodPracticesBinder();
public IBinder onBind(Intent intent) {
return mBinder;
}
Then in your Activity when you get the IBinder object just cast it to a MyEasyButNotGoodPracticesBinder and call the gimmeHandler(Handler) method. Now, I think this is bad practices because if you ever want to put your Service in a separate process so that it doesn't crash the whole process if it crashes, this would break. I don't think it's that future-proof either. But it does work.
An AIDL interface is not that hard - you may just want to do that instead.
Android documentation suggests to work with a messenger as client interface in their Remote Messenger Service Sample:
Remote Messenger Service Sample
Code example can be found in the link above, no need to repost here I guess...
Regards,
Michael

Categories