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.
Related
I am looking into sharing active objects between two processes, however the documentation regarding it is often lackluster and sometimes even incorrect.
Specifically I was looking into sharing a Binder objects via parcels. The documentation states.
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().
However when receiving the parcel, the object returned is merely a BinderProxy, which cannot be cast to the original class or implement any calls beyond basic IBinder calls. While this is obviously the proxy object referred to in the documentation, I was wondering what the method was for referencing the original object that was alluded to in the same statement.
I was also wondering if there was a source where this topic was more thoroughly explained or documented.
You should be able to cast the objects to its original class, given that it implement the Parcelable interface correctly. Casting it's just like this:
// in ProcessDataActivity retrieve User
Intent intent = getIntent();
ParcelableObjectExample usr = (ParcelableObjectExample)
intent.getParcelableExtra("parcelableExample");
For this to be successful you need to correctly implement the Parcelable interface on ParcelableObjectExample, for this you must:
Implement its describeContents method
Implement its abstract method writeToParcel, which takes the current state of the object and writes it to a Parcel
Add a static field called CREATOR to our class, which is an object implementing the Parcelable.Creator interface
Add a Constructor that takes a Parcel as parameter. The CREATOR calls that constructor to rebuild our object.
I use a third-party API (JAudioTagger) and I would like to start an activity with an object of this API (AudioFile).
The problem is this object does not implement Parcelable or Serializable.
What is the best way to do this ?
EDIT
Google's answer : http://developer.android.com/guide/faq/framework.html
You have a few options, none of which are easy or perfect (depending on the object and use-case).
Create a custom object that extends the AudioFile object and implements either Serializable or Parcelable - which can be tedious, but not impossible. With custom objects like this, the documentation may be lacking for this option.
Someone mentioned static as an option. This can generally work well, except you are talking about Android. Android can destroy and re-create the JVM for your app at any time when it is not visible to the user. So, if this AudioFile class is playing in the background in your app, strange behavior could occur if Android decides to kill the process.
You can use an object in the Application class, but is potentially has the same issues as #2.
Use SharedPreferences and some kind of index system to retrieve the file.
You can create a class of your own, which would accept an object of type AudioFile, and populate fields with its values.
public class MyAudioFile implements Parcelable{
private File file;
//other fields...
public MyAudioFile(AudioFile audioFile){
this.file = audioFile.getFile();
//populate other fields
}
//parcelable stuff
}
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 ;-)
I've been stumped with this for a while now.
I'm working on an android app that stores a person's fish catches, favorite fishing locations, tackle box inventory and other data. All my classes are Serializable and can saved and loaded between activities which seems to work thus far. But I'm predicting as more and more data is stored, the app will start running slow.
What I'm basically asking is there any way to retain this data throughout the entire application, so I don't have to load it every time a new screen pops up. I've already found the following information to help but it needs to be a little more clear for me to understand:
Another forum said you could stuff it in the Application object:
[Application]
public class MyApp : Android.App.Application {
public MyApp(IntPtr handle)
: base (handle)
{
}
public FishingData Data {get; set;}
}
Then within your Activity:
((MyApp) this.ApplicationContext).Data = value;
So I've never really heard of doing this approach before and I'm not sure this will live through the entire application process (I feel like either way it's going to have to load the data via serialization. Here's what I want the app todo:
The first activity is the main menu and the following must be done when the screen loads:
If a settings file is found, use serialization to load a previous FishingData object (I know how to do this)
If not, then create a new clean FishingData object to save later (I know this as well)
So now that we have a FishingData object, how do I ensure that I don't have to repeat steps 1-2 in every activity. How can I somehow pass the FishingData object to the next activity and ensure that it lives globaly while the app is still living. I only want to load it once (via serializing) (<--Don't know how to do this) and save it only when a user adds data to this object (which I know how to do).
Any help will be appreciated. This is bugging me I cant seem to figure this out. This seems like it would be a common thing to do but I haven't had any luck finding any detailed information.
Here is how I would pass my data around the app via parcelable. Lets say you have a class named Fisherman (for a user basically)
public class Fisherman implements Parcelable {
private String name;
private Tacklebox box;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeParcelable(box, 0);
}
public static final Parcelable.Creator<Fisherman> CREATOR
= new Parcelable.Creator<Fisherman>() {
public Fisherman createFromParcel(Parcel in) {
return new Fisherman(in);
}
public Fisherman[] newArray(int size) {
return new Fisherman[size];
}
};
private Fisherman(Parcel in) {
name = in.readString();
box = in.readParcelable(com.fisher.Tacklebox);
}
}
In this example, you define parcelable for each data model you have. So say you have a fisherman object, that contains another object called tacklebox. You will also define this for tacklebox, and so on if you continue to nest models. This way, all you need to do to pass data between activities is
Intent intent = new Intent(this, Activity.class);
intent.putParcelableExtra("com.fisher.Fisherman", fisherman);
and read
Bundle b = getIntent().getExtras();
Fisherman fisher = b.getParcelable("com.fisher.Fisherman");
This unfortunetly answers only step 3 of your problem, but I suggest breaking each one of your 3 steps into its own question because what your trying to do is slightly more lengthy than one question
You can use this approach, it will live as long as your Application object is alive (Which means it will live through your entire application and activities). You can read more about using global variables stored in the Application object here. I don't think mono would make a difference which will prevent you from using this approach.
i'm developing an app that, when i press a button, downloads a XML file, put the xml data in a custom object and passes it to a second activity.
The problem is that something is wrong: when a call the startActivity() function the app crashes with a Runtime error.
My code is:
public void onClickBtn1(View view)
{
final ProgressDialog dlg = ProgressDialog.show( this, "Data wait", "Waiting data from the site ..");
// Thread to wait data
Thread th = new Thread() {
public void run() {
// Download and parse xml data
final DatiSport dati = new DatiSport();
boolean ret = dati.download();
dlg.dismiss();
// check result
if (ret==true)
{
// -- Ok
handlerUI.post( new Runnable() {
public void run() {
Intent intSec = new Intent(AICSActivity.this, SportActivity.class);
intSec.putExtra("datiSport", dati);
startActivity(intSec);
}
});
}
else
{
The app crashes on the startActivity() call. When i break on the startActivity() line i'm not able to look the variable called 'dati' and i guess this is not well defined.
If i substitute dati with 12345, there is not problem.
Which is the problem with dati ?
--- Changed here cause I'm not enabled to reply myself ---
Ok guys. Thanks for replies!
My guess is that i need to re-design the app data.
My first attempt was: download the XML text and accommodate the data into a (rather) complex object. This object contain a list of championships, each of them contains a list of categories, each of them contains a list of teams.
The problem is that, since the Serializable is not working, the implementation of Parcelable is too complex and it should generate almost the same data as the xml file.
I'm wondering if it should be easier passing directly the xml text to other activities (they have to show in turn the list of championships, then the categories of a selected championship, then the list of teams for a selected category...)
Any other idea?
Extract from this Answer :
Serializable is a standard Java interface. You simply mark a class Serializable by implenting the interface, and Java will automatically serialize it in certain situations.
Parcelable is an Android specific interface where you implement the serialization yourself. It was created to be far more efficient that Serializable, and to get around some problems with the default Java serialization scheme.
Extract from this answer :
Seeing Parcelable might have triggered the question, why is Android
not using the built-in Java serialization mechanism? It turns out that
the Android team came to the conclusion that the serialization in Java
is far too slow to satisfy Android’s interprocess-communication
requirements. So the team built the Parcelable solution. The
Parcelable approach requires that you explicitly serialize the members
of your class, but in the end, you get a much faster serialization of
your objects.
After seeing some answer on StackOverFlow, i come to conclusion that Parcelable is optimized than Serialization in android.
How to make class to Parcelable ?? (Check out this, this & this tutorials)
Use a Serializable or Parcelable when passing objects
You need a class to implement the Serializable class
//to pass :
intent.putExtra("MyClass", obj);
// to retrieve object in second Activity
getIntent().getSerializableExtra("MyClass");
Your class would look something like this;
import java.io.Serializable;
#SuppressWarnings("serial") //with this annotation we are going to hide compiler warning
public class MyClass implements Serializable {
public Deneme(Object obj){
this.obj= obj;
}
private Object obj;
}
The Intent class has a method as
putExtra(String name, int value)
thats why it works when you put 12345 at the place of "value", but there is no overloaded version of putExtra that takes "DatiSport" object.
You must ensure that "DatiSport" is Serializable or Parcelable.
See below for more info-
http://developer.android.com/reference/android/content/Intent.html#putExtra%28java.lang.String,%20java.io.Serializable%29
How to send an object from one Android Activity to another using Intents?
How to pass an object from one activity to another on Android
Make your class implement Serializable interface and then pass object instances in intent extra.
To pass data from one Activity to another :
intent.putExtra("ClassName", obj);
To retrieve data in the Second Activity from the First Activity :
getIntent().getSerializableExtra("ClassName");
I found the problem !!!
An internal class were not implementing Serializable!
In the dump window i saw the internal object 'ioe' that said that there was a NotSerializable error and the name of the class!!
Now i checked each internal class and the data is passed to the next activity.
Thanks a lot