I have an array of type named ItinerarySegment, and this type has subclasses: WalkSegment, BusSegment etc.
public interface ItinerarySegment
{
}
public class WalkSegment implements ItinerarySegment
{
}
public class BusSegment implements ItinerarySegment
{
}
What sort of strategy should I follow when making the array of ItinerarySegment parcelable? The main concern here is how it will be used later when re-constructing the array via the createTypedArray method (prepared by writeTypedArray method).
In which the createTypedArray method take a Creator field parameter. The problem lies here...Where should the Creator field be defined? (in ItinerarySegment, WalkSegment, or BusSegment?).
public static final Creator<Typename> CREATOR = new Parcelable.Creator<Typename>()
{
public Typename createFromParcel(Parcel in)
{
return new Typename(in);
}
public Typename[] newArray(int size)
{
return new Typename[size];
}
};
If I make ItinerarySegment an abstract class and define the Creator field's method, then the subsequent subclasses's data will be lost since neither of their constructors are called with the Parcel parameter, instead ItinerarySegment's constructor will be called.
constructor(Parcel in);
If I make WalkSegment define Creator field, then BusSegment would have a problem.
Any clarification needed?
In order to do this using writeTypedArray() and createTypedArray() you would need to make ItinerarySegment an abstract class, not an interface. ItinerarySegment would need to implement Parcelable and it would need to have a CREATOR defined that would be called to unmarshall the Parcel and create new objects. Derived classes would also need to implement the methods writeToParcel(), describeContents() and the CREATOR.
NOTE: Because writeTypedArray() does NOT write the type of the object into the Parcel, you will need to do this yourself. Here's 2 possible ways:
The writeParcel() method of each derived class would have to write something to the Parcel at the very beginning that identifies its type (a String or int value).
The writeParcel() method of each derived class would have to call super.writeToParcel() before writing anything to the Parcel. In the ItinerarySegment.writeToParcel() method you could figure out what type of derived class it is and write something to the Parcel that identifies its type (a String or int value).
The CREATOR in ItinerarySegment would first read the identifier from the Parcel and then using that, determine which type of object to instantiate. It would then call the corresponding object's CREATOR to actually instantiate the object and return that to its caller.
This basically works like an object factory, in which the base class knows how to instantiate different types of its own derived classes.
The downside of all this is that the abstract base class would have to know about all of its derived classes. I suppose you could also do this dynamically, by have all derived classes call a static method in the base class passing its "type" and CREATOR, which the base class would then store in an array to be used as needed.
All doable, but pretty complicated.
As an alternative you can use writeParcelableArray() and readParcelableArray(), where the class name of each object is written to the Parcel so that it knows which CREATOR to call when unmarshalling.
IMHO the only time to use writeTypedArray() and createTypedArray() is when all the objects in the array are instances of the same class, and you are writing more than a handful of them into the Parcel. In that case, you save the overhead of writing the class name into the Parcel for each object, as you know they are all the same. In this case you don't need to go through all the agony I described above because you know the type of all objects beforehand (no polymorphic types).
I realize this answer is probably more than a year late, but what the heck. Maybe it will help someone else ;-)
Related
In Kotlin, you can create a singleton using a companion object:
class MyClass {
companion object {
fun doSomething() {
}
}
}
According to the Kotlin docs, it states:
Note that, even though the members of companion objects look like
static members in other languages, at runtime those are still instance
members of real objects...
https://kotlinlang.org/docs/reference/object-declarations.html
Does this mean that after using a function in the companion object, the instance of the class (MyClass) remains in memory for the entire lifecycle of the app? Is there a way in Android Studio to check to see if this is the case?
instance of the class (MyClass) remains in memory for the entire lifecycle of the app?
Companion object in JVM
in kotlin
class MyClass {
companion object {
fun doSomething() {
}
}
}
MyClass(Kotlin) converted in JVM
public final class MyClass {
public static final MyClass.Companion Companion = new MyClass.Companion(null);
public static final class Companion {
public final void doSomething() {
}
private Companion() {
}
public Companion() {
this();
}
}
}
As above code, companion object is declared as Companion class in JVM, and it's created as static field inside MyClass class. Thus, isn't collected by gc. So, the memory of object(Companion) is remained during the ProcessLifecycle. static final object isn't released in normal case.
In conclusion, if referred to MyClass.Companion instance in application, that instance will not be garbage collected. (on general Classloaders).
*If not referred to MyClass.Companion instance in application, it may be removed by code shrinking feature.
Is there a way in Android Studio to check to see if this is the case?
You can see through android studio > profiler > Heap dump.
Reference
https://developer.android.com/topic/performance/memory-overview
https://developer.android.com/studio/build/shrink-code
As you seem to know and the above answer also makes clear that companion objects are translated to classes, and the class which declares them holds a static reference to the object of companion class, something as following:
public static final MyClass.Companion Companion = new MyClass.Companion(null);
Now the question
Do companion objects remain in memory for app's lifecycle
because the declaring class holds a static reference to companion class, the question reduces to the life time of static fields in jvm class and the answer lies in the JVM spec, but the spec is bit dry on the explanation so I am adding some snippets from the book Inside the Java Virtual Machine.
As in your example let say we have a class with nothing but single companion object.
First question is when an object of companion class will be created ? or
when static fields are initialized ?
relevant text from the book. (for context the book is talking about class loading procedure)
Initialization
The final step required to
ready a class or interface for its first active use is initialization,
the process of setting class variables to their proper initial values.
As used here, a "proper" initial value is the programmerís desired
starting value for a class variable. A proper initial value contrasts
with the default initial value given to class variables during
preparation. As described above, the virtual machine assigns default
values based only on each variableís type. Proper initial values, by
contrast, are based on some master plan known only to the programmer.
In Java code, a proper initial value is specified via a class variable
initializer or static initializer.
So we know that once MyClass is loaded and initialized, the object of companion class will be created.
but what would cause JVM to load MyClass ?
The Java Virtual Machine specification gives implementations
flexibility in the timing of class and interface loading and linking,
but strictly defines the timing of initialization. All implementations
must initialize each class and interface on its first active use. An
active use of a class is:
The invocation of a constructor on a new
instance of the class
The creation of an array that has the class as
its an element type
The invocation of a method declared by the class
(not inherited from a superclass)
4 The use or assignment of a field
declared by the class (not inherited from a superclass or
super interface), except for fields that are both static and final, and
are initialized by a compile-time constant expression
So as per 4th point when you do MyClass.foo() from kotlin or MyClass.Companion.foo() at this point MyClass will be loaded and ready. (Probably a lot early)
Please note that at this point no object of MyClass exist, that is we haven't used expression MyClass().
Does this mean static fields will remain in memory as long as the application is running ?
They can be garbage collected if the declaring type gets unloaded, in our case if JVM or ART (on android) unloads the MyClass then there is a possibility that companion object will be Garbage collected.
JVM Spec has following to say about class unloading
An implementation of the Java programming language may unload classes.
A class or interface may be unloaded if and only if its defining class
loader may be reclaimed by the garbage collector as discussed in
§12.6.
Classes and interfaces loaded by the bootstrap loader may not be
unloaded.
In practicality class unloading almost(I said almost) never happens, so yes companion objects will remain in memory for app's life cycle.
I need to send an object ModuleDetection from an activity to another. I've found that using the Parcel class to turn my object into a Parcelable object would help me. However, in every example I could find, the parcelable object's attributes had default types of Android. But the ModuleDetection object has an attribute of ImageInterface type, another class that I made.
When writing the method :
#Override
public void writeToParcel(Parcel dest, int flags) {
}
I can't use methods like dest.writeInt() or dest.writeString() , because my attribute hasn't a default type of android. How can I do this?
You should make both classes implement Parcelable. When you pass your ModuleDetection object as a parcelable, the ImageInterface field will get handled as well.
You should make both classes implement Parcelable. When you pass your ModuleDetection object as a parcelable, the ImageInterface field will get handled as well.
You can make your ImageInterface extend Parcelable so each ImageInterface implementation will have to implement also parcel
Here is an example
I'm trying to implement the parcelable interface using Gson. The idea is to create a json string (using gson) and write it to the parcel object.
Could it be a correct way to implement the interface?
The only problem I've encountered is when I deserialize the object. Actually, I use gson to recreate the object.
private MyClass(Parcel in) {
String json = in.readString();
Gson gson = new Gson();
gson.fromJson(json, getClass());
}
The problem is that the fromJson function returns an object that is the object the constructor should create.
The last line of code should be something like
this=gson.fromJson(json, getClass());
So, if this is a correct way to implement Parcelable, how could I solve this problem?
You should read more carefully the Parcelable javadoc. It contains everything you need.
As quoted in the docs :
Interface for classes whose instances can be written to and restored
from a Parcel. Classes implementing the Parcelable interface must also
have a static field called CREATOR, which is an object implementing
the Parcelable.Creator interface.
So you should have the writeToParcel method declared and also use a creator that will produce instances of your class from a Parcel.
The private constructor is an additional helper that you can use to set the value of the fields of an object given a parcel, but not the object itself. In Java, this is a right value, and can't be assigned.
BTW, the goal of parcelisation is to be provide a short term fast serialization process. You should, generally speaking, use a fast and compact data format when you use parcelisation. JSON is not a candidate of choice, but it will work.
I'm trying to have a common object shared for several services in different packages. Each service must call the same object.
For example, Service A (from APK A) instantiates a custom object and I want that Service B and C (from APK B and C) retrieves the reference of this object and call some method of it.
I found in the Android reference that it should be possible by using Parcel :
Active Objects
An unusual feature of Parcel is the ability to read and write active
objects. For these objects the actual contents of the object is not
written, rather a special token referencing the object is written.
When reading the object back from the Parcel, you do not get a new
instance of the object, but rather a handle that operates on the exact
same object that was originally written. There are two forms of active
objects available.
Binder objects are a core facility of Android's general cross-process
communication system. The IBinder interface describes an abstract
protocol with a Binder object. Any such interface can be written in to
a Parcel, and upon reading you will receive either the original object
implementing that interface or a special proxy implementation that
communicates calls back to the original object. The methods to use are
writeStrongBinder(IBinder), writeStrongInterface(IInterface),
readStrongBinder(), writeBinderArray(IBinder[]),
readBinderArray(IBinder[]), createBinderArray(),
writeBinderList(List), readBinderList(List), createBinderArrayList().
I tried to do this by passing my object (who extends binder) through AIDL but nothing works, I always gets a ClassCastException when I'm trying to retrieve the reference from the method createFromParcel(Parcel in).
An example of my code :
public class CustomObject extends Binder implements Parcelable {
public CustomObject() {
super();
}
public static final Parcelable.Creator<CustomObject> CREATOR = new Parcelable.Creator<CustomObject>() {
public CustomObject createFromParcel(Parcel in) {
IBinder i = in.readStrongBinder();
// HOW TO RETRIEVE THE REFERENCE ??
return null;
}
#Override
public CustomObject[] newArray(int size) {
return null;
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongBinder(this);
}
}
Is anyone has already done that ?
Thanks in advance !
Here are two approaches.
Simple: use aidl for the object itself
It seems you have an existing AIDL interface through which you pass this 'custom object' as a parcel. Don't do that. Instead:
The object which you pass through should be itself described by AIDL. Say, for example, you call it ICustomObject.aidl.
In this case you do not need to make the object Parcelable. You probably don't even need to write the above code; just use one AIDL-described type in another. For example add a line like this to the main AIDL for service A:
ICustomObject getCustomObject();
In service A, within the Stub class you've already got, you'll need to simply return something inheriting from ICustomObject.
In services B and C, you can simply call that method to get hold of an ICustomObject. Simple! No parcels, no readStrongBinder(), nothing.
Harder
If you do the above, the Android toolchain generates Java code which marshalls and unmarshalls the object. You could instead write the code yourself.
ICustomObject myObjectWhichActuallyLivesInAnotherProcess = ICustomObject.Stub.asInterface(parcel.readStrongBinder())
or even
ICustomObject myObjectWhichActuallyLivesInAnotherProcess = (ICustomObject)parcel.readStrongBinder().queryLocalInterface("com.your.custom.object");
However I think your life will be more sane if you make everything aidl.
A note on class sharing
You'll probably want to create an Android "library project" which has ICustomObject.aidl within it, such that you can share the resulting classes between the projects which build A, B and C.
have you looked into content providers?
After looking into this rather extensively myself, I don't think it can actually be done. The ClassCastException you're getting is a result of casting a BinderProxy (which is a private class that extends IBinder) to your actual class (CustomObject). A BinderProxy always seems to be passed when referring to Binders across other processes and this is the object they refer to when they state "either the original object implementing that interface or a special proxy implementation". The BinderProxy does allow you to call the onTransact() method of IBinder but nothing else.
I honestly think the documentation is incorrect when they state that the "original object" is passed across processes as there are several other instances of the documentation for that class being both blatantly copy-pasted and wrong.
I have a class that extends Parcelable, we'll call it Class A.
I have another class that extends Parcelable, we'll call it Class B.
One of the member variables of A is an ArrayList of Class B objects.
I'm trying to write the Parcelable overrides, but can't figure out how to save and read the ArrayList of Class B.
There are methods to read and write ArrayLists, but they want a parameter which is a ClassLoader and I am unfamiliar with it. I could also copy the ArrayList into an array and user the methods to read and write a Parcelable array, but this also requires a ClassLoader parameter.
Update:
It looks like
in.readTypedList(mList, ClassB.CREATOR);
and
out.writeTypedList(mList);
are what I'm looking for?
You can pass null as ClassLoader to use the default one.
Parcel.readTypedList() and Parcel.writeTypedList() seem to be OK. Or you can use Parcel.readParcelableArray() and Parcel.writeParcelableArray() and convert the list to array.