It seems that Bundle has deprecated putIBinder and getIBinder which I thought was very useful for passing binders (through Bundles) over to my service. Since these are deprecated, is there an alternative to this?
I really need to pass an IBinder object over to my service, and I thought the Bundle approach was the easiest (best) solution for this.
Thanks,
J
Posting this which may help someone who deals with binders. I use this approach, still works with Bundle
// write
bundle.putParcelable(key, new ParcelableBinder(binder));
// read
ParcelableBinder value = bundle.getParcelable(key);
IBinder binder = value == null ? null : value.getBinder();
// or with possible NPE
IBinder binder = bundle.<ParcelableBinder>getParcelable(key).getBinder()
public static class ParcelableBinder implements Parcelable {
IBinder mBinder;
public ParcelableBinder(IBinder binder) {
mBinder = binder;
}
private ParcelableBinder(Parcel in) {
mBinder = in.readStrongBinder();
}
public IBinder getBinder() {
return mBinder;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStrongBinder(mBinder);
}
public static final Creator<ParcelableBinder> CREATOR = new Creator<ParcelableBinder>() {
#Override
public ParcelableBinder createFromParcel(Parcel in) {
return new ParcelableBinder(in);
}
#Override
public ParcelableBinder[] newArray(int size) {
return new ParcelableBinder[size];
}
};
}
No need to use Bundles to pass IBinders across services (I can't even see the Bundle.{put,get}IBinder methods you mention in the javadoc).
You can use IBinder objects directly in AIDL, after you import them. For example, if you want to pass a reference to an IOtherService into a method on IService, then IService.aidl could look like this:
package com.yourpackage;
import com.yourpackage.IOtherService;
interface IService {
void doSomething(IOtherService service);
}
Would using implementation of Parcelable instead be applicable in your case (Bundle is just an implementation of Parcelable anyways)?
Check out this post on Bundle v Parcelable:
Passing a custom Object from one Activity to another Parcelable vs Bundle
Related
I am using the GCM network manager and I want to pass the service (specifically to the onRunTask(TaskParams taskParams) some objects. From the documentation taskParams are simply a string and a bundle but I want to pass more complex objects.
How can this be done?
Thank you!
One way is to have your custom object implement the Parcelable interface and use Bundle.putParcelable/Bundle.getParcelable.
It requires a little more effort to use than using Java's native serialization, but it's way faster (and I mean way, WAY faster).
For example:
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
Also you can read Parcelable vs Serializable
An Activity contains a Fragment which in turn contains a child Fragment, which requests a Service. The app tries to implement dobjanschi rest architecture.
When the Service is done working it has to propagate operation result. I tried using a PendingIntent but it seems to only be caught by the activity, while I need the child fragment to get notified. Could you suggest anything? Binder? greenRobot Eventbus? RxJava (which I already have in the project)?
Thanks.
RxJava
A simple way con be to use a Singleton to wrap a synchronized ´PublishSubject´
* Singleton
*
* to send an event use EventBusRx.getInstance().topic1.onNext("completed");
*/
public class EventBusRx {
private static EventBusRx ourInstance = new EventBusRx();
public static EventBusRx getInstance() {
return ourInstance;
}
private EventBusRx() {}
/**
* Use of multiple topics can be usefull
* SerializedSubject avoid concurrency issues
*/
public final Subject<String, String> topic1 = new SerializedSubject<>(PublishSubject.create());
public final Subject<Integer, Integer> topic2 = new SerializedSubject<>(PublishSubject.create());
}
And You can send events from service
EventBusRx.getInstance().topic1.onNext("completed");
and respond to event in fragments or whenever you want
public class MyFragment extends Fragment {
// [...]
Subscription subscription_topic1;
#Override
public void onResume() {
super.onResume();
subscription_topic1 = EventBusRx.getInstance().topic2
.subscribeOn(AndroidSchedulers.mainThread()) // or on other sheduler
.subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
// update ui
}
});
}
#Override
public void onPause() {
// important to avoid memory leaks
subscription_topic1.unsubscribe();
super.onPause();
}
}
do not forget to unsubcribe the Subscription
The idea is similar to Roger'one use a singleton but enforce ThreadSafety wrapping PublishSubject.
there is no need for Observable.switchOnNext(subject)
EventBus Libraries
greenRobot Eventbus and Otto are nice and has the same functionality, but the disadvantage is that they make the connection more smoky (expecialy EventBus) . If you already use rx i think is better to stay with it
Here is an insipring article about the topic
Implementing an Event Bus With RxJava
LocalBroadcast
The classic way to do this is to use LocalBroadcastManager but in my aopinion they are a pain
I would suggest using an Event Bus for this sort of thing. It will allow you to send messages to components within your system, without requiring creating special handlers.
Otto is a popular open source library for this, and there are others. http://square.github.io/otto/
Try this way hope it help you.
For Example:
YourService
public class MyService extends Service{
public static MyServiceListener getMyServiceListener() {
return MyService.myServiceListener;
}
public static void setMyServiceListener(MyServiceListener myServiceListener) {
MyService.myServiceListener = myServiceListener;
}
private static MyServiceListener myServiceListener;
public interface MyServiceListener{
void onResult(String response);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
executeYourTask();
}
private void executeYourTask(){
String result = "SomeResultMaybeFromServer";
if(getMyServiceListener()!=null){
getMyServiceListener().onResult(result);
}
}
}
YourFragment
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = null; // some view
// Start service
MyService.setMyServiceListener(new MyService.MyServiceListener() {
#Override
public void onResult(String response) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// To handle memory/window leaks
}
});
}
});
return v;
}
}
I'm currently developing a Bus based solely on RxJava. Since you already have RxJava on your project, you can use it for this. You should use a BehaviorSubject and Observable.switchOnNext().
For example:
private BehaviorSubject<Observable<Whatever>> subject = BehaviorSubject.create();
public void post(){
subject.onNext(...);
}
public Observable<Whatever> subscribe(){
return Observable.switchOnNext(subject);
}
You should have this as part of a Singleton so the same BehaviorSubject is used. All you have to do is post() from one fragment and subscribe() on the other one or in any other interested fragment or activity. You can have as many subscriptions as you want, plus if you implement it correctly then the last emitted Observable will survive orientation changes.
More info on BehaviorSubject can be found here: https://github.com/ReactiveX/RxJava/wiki/Subject
I'm currently using this Pub/Sub pattern with rxjava and enum class.
public enum Events {
public static PublishSubject <Object> myEvent = PublishSubject.create ();
}
//where you want to publish something
Events.myEvent.onNext(myObject);
//where you want to receive an event
Events.myEvent.subscribe (...);
I would use event bus, which is based on rx.
Make this as a sigletone and subscribe on particular class type.
public class RxBus {
private static final RxBus sBus = new RxBus();
private final Subject<Object, Object> mBus = new SerializedSubject<>(PublishSubject.create());
private RxBus() {
}
public static RxBus getInstance() {
return sBus;
}
public void send(Object o) {
mBus.onNext(o);
}
public Observable<Object> observe() {
return mBus;
}
#SuppressWarnings("unchecked")
public <T> Observable<T> observe(Class<T> c) {
return mBus.filter(o -> c.isAssignableFrom(o.getClass())).map(o -> (T) o);
}
}
usage:
class Message { public String result};
send a message:
Message m = new Message();
m.result = "Hello world";
RxBus.getInstance().send(m);
subscribe on a particular class type:
RxBus.getInstance().observe(Message.class).subscribe(msg -> Log.e(TAG, "Message was caught : " + msg));
I am implementing Parcelable in order to transmit some simple data throughout an Intent.
However, There is one method in the Parcelable interface that I don't understand at all : newArray().
It does not have any relevant documentation & is not even called in my code when I parcel/deparcel my object.
Sample Parcelable implementation :
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
So, my question is : what is this method for ? and when is it called ?
Is there any point in doing something else than return new MyParcelable[size]; in that method ?
this is a function to be called when you try to deserialize an array of Parcelable objects and for each single object createFromParcel is called.
It is there to prepare the typed array without all the generics stuff. That's it.
Returning just the standard return new MyParcelable[size]; is fine.
It is normal, that you never call it yourself. However, by calling something like Bundle.getParcelableArray() you end up in this method indirectly.
newArray is responsible to create an array of our type of the appropriate size
I'm trying to access sqlite DB (that is filled on diffrent part of the package) on AIDL stub implementation but - there is no context there. how can I get the context?
there are 2 projects (applications) - A,B.
Project A contains keeps records on sqlite DB. and contains aidl service.
Project B needs to ask project A (diffrent package, there can be many projects like B) if a record exists. the only way for project A to answer project B from the stub is to check the DB is to have a Context (the "?????" in the code below) - how can I get the project A's context from the stub?
The AIDL's implementation:
public class IRecordServiceImpl extends IRecordService.Stub{
#Override
public boolean RecordExists(String recordKey)
throws RemoteException {
boolean returnValue = false;
RecordDataSource rds = new RecordDataSource(??????);
rds.Open();
returnValue = rds.isRecordExists(recordKey);
rds.Close();
return returnValue;
}
}
The Service code:
public class IRecordService extends Service {
private IRecordServiceImpl service;
#Override
public void onCreate() {
super.onCreate();
this.service = new IRecordServiceImpl();
}
#Override
public IBinder onBind(Intent intent) {
return this.service;
}
#Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
Thanks!
Problem solved - you can pass the Service's Context via the constructor and keep it in IRecordServiceImpl as private field
into my application i use an intent:
private Handler mHandler = new Handler();
.
.
mServiceIntent = new Intent(this, ObdGatewayService.class);
mServiceConnection = new ObdGatewayServiceConnection();
mServiceConnection.setServiceListener(mListener);
// bind service
Log.d(TAG, "Binding service..");
bindService(mServiceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE);
here my activity at onCreate start a new service. this is my onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
mServiceIntent = null;
mServiceConnection = null;
mListener = null;
mHandler = null;
}
this is mServiceConnection:
public class ObdGatewayServiceConnection implements ServiceConnection{
private static final String TAG = "com.echodrive.io.ObdGatewayServiceConnection";
private IPostMonitor service = null;
private IPostListener listener = null;
public void onServiceConnected(ComponentName name, IBinder binder) {
service = (IPostMonitor) binder;
service.setListener(listener);
}
public void onServiceDisconnected(ComponentName name) {
service = null;
Log.d(TAG, "Service disconnesso.");
}
public boolean isRunning() {
if (service == null) {
return false;
}
return service.isRunning();
}
public void addJobToQueue(ObdCommandJob job) {
if (null != service)
service.addJobToQueue(job);
}
public void setServiceListener(IPostListener listener) {
this.listener = listener;
}
mListener is a listener from interface:
public interface IPostListener {
void fineTest(DatiTest risultati);
void startAcquisizione();
void aquisizioneTerminata();
void aquisizioneInterrotta(String motivo);
void connessioneCorretta();
void gpsStato(boolean stato);
}
my problem is.. how save all this code after rotation? thanks!
The recommended way to save state across rotations is to save them on the outState. This is accomplished by overriding the onSaveInstanceState method. This method gives you a Bundle outState object that you can add Parcelable and Serializable objects to. This should work fine for your Intent object since it implements Parcelable but it may not work for say Handler because it only extends Object.
Another solution is to make these members static. However, be very careful if you decide to do this. Make sure that the value of the static member never holds on to a Context or a view hierarchy, etc, or you could easily introduce memory leaks.
If neither of these is acceptable to you, there is the option suggested by Tushar. However, unless you're careful this will make your life very difficult very fast. A large reason why activities are destroyed and re-created is so that resources can be re-loaded. So if you have layouts, strings, colors, dimens, or basically any resource specifically for landscape, or tablets, or different versions, you'll have to reload the entire UI yourself.