1

I have the following flow:
ActivityA/FragmentA passes to ActivityB/FragmentB via intents a custom somewhat large object.
Among the attributes of the object is a List<CustomObject> items
User presses a widget in FragmentB and then FragmentB starts ActivityC/FragmentC passing also that custom object (Parcelable) that is supposed to show details in its UI and also starts a Service to fetch the list that populates the items of that specific object.
When Service fetches the result from a background HTTP call I need to update the list in the UI that is expected to display these items fetched. This list is in FragmentC which has a copy of the custom object with the items null.
The Service has another copy of the custom object and the items just fetched but can not update the list of the fragment.
Making a static variable of the fragment and assigning this and then exposing a public method in the fragment that the Service can call to pass the items works but is very dirty.
I was wondering what is a clean/standard design for this?

Jim
  • 18,826
  • 34
  • 135
  • 254
  • Use an event bus, as I described [here](https://stackoverflow.com/questions/29183445/update-running-activity-using-intentservice/29183544?s=1|0.0000#29183544) and [here](https://stackoverflow.com/questions/28907289/how-should-the-data-from-long-running-operations-be-passed-to-the-fragment-to-be/28907447?s=4|0.0000#28907447) and [here](https://stackoverflow.com/questions/27582335/i-have-a-service-gcm-in-the-background-how-do-i-know-if-the-app-is-open-or-not/27582492?s=12|0.0000#27582492), among other answers. – CommonsWare Mar 21 '15 at 20:03
  • @CommonsWare:I am using LocalBroadcastManager but the list is fairly large and I am not sure if passing it via the intent is a good idea – Jim Mar 21 '15 at 20:06

1 Answers1

1

I am using LocalBroadcastManager but the list is fairly large and I am not sure if passing it via the intent is a good idea

Normally, when we use an Intent, it is to cross process boundaries, and so the Intent has to be converted into a byte array (by means of a Parcel), and that gets to be a problem with large data. LocalBroadcastManager does not do that -- it just passes the Intent object around as is.

The downside of LocalBroadcastManager is that the message is an Intent, and because an Intent is usually used for IPC, it has limitations on data types. Personally, I recommend greenrobot's EventBus, or even Square's Otto, over LocalBroadcastManager, for just this reason. That being said, if it is easy enough for you to get your data into an Intent, size should not be an issue.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I am not clear on what you mean by `cross process boundaries`. Each fragment is a separate process? They are are not just classes *within* the same process space? – Jim Mar 21 '15 at 20:19
  • @Jim: "Each fragment is a separate process?" -- no, but you don't normally use `Intent` objects with fragments. You use `Intent` objects with methods like `startActivity()`, `startService()`, and `sendBroadcast()`. Those cross process boundaries, even if the activity/service/receiver in question happens to be in your own process. That is why **normally** size is an issue with `Intent`. `LocalBroadcastManager` is **local** and is not subject to the same limitations. – CommonsWare Mar 21 '15 at 20:22
  • So the mechanism by default in Android offered is via an infrastructure best suited for IPC calls? – Jim Mar 21 '15 at 20:29
  • @Jim: I have no idea what you think "the mechanism by default" is. – CommonsWare Mar 21 '15 at 20:31
  • LocalBroadcastManager is an Android component that uses intents that you said are best suited/oriented for IPC. While the rest event buses (greenrobots, Square Otto) are third party dependencies. Only the LocalBroadcastManager is part of the distribution, hence default – Jim Mar 21 '15 at 20:35
  • @Jim: "LocalBroadcastManager is an Android component that uses intents that you said are best suited/oriented for IPC" -- an `Intent` is a data structure. An `Intent` is normally used in cases that involve IPC, but it does not have to be, just as a string beginning with `http://` is normally used in network I/O, but it does not have to be. "Only the LocalBroadcastManager is part of the distribution, hence default" -- since it's part of the Android Support package, it is only incrementally more "default" than a third-party dependency IMHO. – CommonsWare Mar 21 '15 at 20:38
  • @Jim: Beyond that, you stated "via an infrastructure best suited for IPC calls". An `Intent` is not an "infrastructure" any more than a `boolean` is, IMHO. – CommonsWare Mar 21 '15 at 20:39
  • Thanks for clearing this up for me. So Intents pass copies of the data. Using eventbuses are copies passed between activities/fragments or references to the same objects? – Jim Mar 21 '15 at 20:53
  • @Jim: "So Intents pass copies of the data" -- no, IPC passes copies of the data. "Using eventbuses are copies passed between activities/fragments or references to the same objects?" -- all three major event bus implementations (`LocalBroadcastManager`, Otto, EventBus) pass references to the event objects, not copies. – CommonsWare Mar 21 '15 at 20:59
  • CommonsWare:That is strange because the reason in my OP was that when I updated the items of the object I passed in the Intent this update was not visible to the other fragment. (Was still null) – Jim Mar 21 '15 at 21:05
  • @Jim: Then somewhere you wound up with a copy. I do not know your code and cannot surmise what you are doing. I inspected [the source code to `LocalBroadcastManager`](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v4/java/android/support/v4/content/LocalBroadcastManager.java) a while back to confirm that it does not make copies of the data; you are welcome to take a peek at it yourself. – CommonsWare Mar 21 '15 at 21:14
  • Even for Parcelable?Because I seen from the debugger that the constructor using Parcel in is called – Jim Mar 21 '15 at 21:20
  • @Jim: You would need to examine the call stack at the point of that breakpoint to determine who is using that constructor and why. – CommonsWare Mar 21 '15 at 21:43
  • The stack trace is: `getIntent().getParcelableExtra(PARAM)` --> `getParcelableExtra(String name)` --> `Bundle#getParcelable(String name)` --> `BaseBundle#unparcel()` --> `Parcel#readArrayMapInternal()` --> `Parcel#readValue()` --> `Parcel#readParcelable()` --> the CREATOR I wrote in my Parcelable for `createFromParcel(Parcel in)` – Jim Mar 21 '15 at 21:51
  • There is no `getIntent()` involved with `LocalBroadcastManager`. If you are calling `getIntent()` on your `Activity` and retrieving your `Parcelable` from there, then that is not `LocalBroadcastManager`. – CommonsWare Mar 21 '15 at 21:53
  • It is `Fragment A` passes object via intent to `ActivityB` (and starting it). ActivityB gets object from intent and passes it to LocalBroadcast and starts an IntentService and then creates FragementB and passes to FragmentB the object from Intent to its arguments. The instance that FragmentB has and the instance the IntentService has are different. Am I doing it wrong? Is there a way the copy to be shared? – Jim Mar 21 '15 at 22:01
  • @Jim: "The instance that FragmentB has and the instance the IntentService has are different" -- assuming that by "the instance the `IntentService` has" you mean the one it gets in `onStartCommand()`, then yes. I have no idea why you are passing it to `LocalBroadcastManager`, as you appear to have nothing listening for such a broadcast. Your problem isn't `LocalBroadcastManager`; your problem is `startService()`, as that makes a copy of the `Intent`, because `startService()` always involves IPC, even though your service is part of your process. – CommonsWare Mar 21 '15 at 22:06
  • @Jim: "Is there a way the copy to be shared?" -- switch to greenrobot's EventBus and use a sticky event. Or, switch to Otto and use a `@Producer`. Or go with a shared data model and singleton manager. – CommonsWare Mar 21 '15 at 22:08
  • One last question: Using a shared data model and singleton manager, can cause data to be lost e.g. on pause. Does this happen with the other approaches? – Jim Mar 21 '15 at 22:26
  • 1
    @Jim: "Using a shared data model and singleton manager, can cause data to be lost e.g. on pause" -- that would depend upon how you implement it. "Does this happen with the other approaches?" -- that too would depend upon how you implement it. And this is getting rather far afield from your original question. – CommonsWare Mar 21 '15 at 22:55