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
Related
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...
I got a service listening GCM messages and parsing JSONs around. Eventually (depending on the message content), it writes to SharedPreferences, so it needs getApplicationContext().
If I do a test on the service "as a regular object" (create Service, then execute the method) the getApplicationContext() ends up in null when running the test
If I use ServiceTestCase
public void testHandleAuth() {
// MyClass is tested
Intent intent=new Intent("com.duniap.ptp.GcmIntentService");
this.startService(intent);
GcmIntentService tester = new GcmIntentService();
// a dummy json encoded in base64
dummyAuth64="eyJkZXZpY2UiOnsiaWQiOjU2LCJzZWNyZXQiOiJGc3VCcWZsdVFcL2l5bit5TlZadW5uZz09In0sImFjdGlvbiI6ImF1dGhvcml6ZSJ9";
byte[] bytes = Base64.decode(dummyAuth64, Base64.DEFAULT);
String dummyAuth = new String(bytes);
tester.handleGCMmessage(dummyAuth);
//null pointer, if I debug I trace it to getApplicationContext() within this method
}
I still get the same null exception
A common way to deal with situations like this is to use a mocking framework in which you effectively simulate the context that your method-under-test interacts with. So you might mock the getApplicationContext() to return a mock context object that is sufficient to use with SharedContext.
General references:
Unit tests with Mockito - Tutorial - Vogella
Mockito on Android step-by-step
Mocking library/framework that works best in Android?
I'd like to create a Service that's capable of creating a number of objects, where each object connects to the internet and downloads some JSON data and a photo. Most of this is pretty straight forward, but I want it to happen one at a time, i.e. a new object cannot be created until the first object has completed its actions.
What's the best way for my Service to know when an object has performed its actions?
Here's a very rough illustration of what I'm looking for:
Use an IntentService. In normal usage, each Intent it receives is processed sequentially and is already in a background thread, so you don't even need to do your network activity in an AsyncTask. Easy.
Alternately, put all your things in a Queue and have the "do next thing" call happen as a result of the onPostExecute() call in AsyncTasks.
Queue<YourObject> unfinished = getQueue();
List<YourObject> finished = new LinkedList<YourObject>();
Handler handler = new Handler(){
handleMessage(Message m){
// Object populated! Start next? Blink lights? Whatever.
}
}
AsyncTask t = new AsyncTask<...>(handler) {
Handler h;
public AsyncTask<...>(Handler h) {
this.h = h;
}
protected V doInBackground(YourObject o) {
// Network stuff, populate the thing
return popualtedThing();
}
protected V onPostExecute(YourObject o) {
h.sendMessage(Message.obtain(0,o);
}
}.execute();
How about this? When the object creation is complete, broadcast an intent (you could use LocalBroadcast for example, if you want it to be private).
In your Service, register for the above mentioned broadcast, and when it is received, perform the next task.
Use listeners. Have the background service register as a listener to the object instance and when the object instance is does it just calls back to all the listeners.
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.
I am just getting started with OSGI and Declarative Services (DS) using Equinox and Eclipse PDE.
I have 2 Bundles, A and B.
Bundle A exposes a component which is consumed by Bundle B. Both bundles also expose this service to the OSGI Service registry again.
Everything works fine so far and Equinox is wireing the components together, which means the Bundle A and Bundle B are instanciated by Equinox (by calling the default constructor) and then the wireing happens using the bind / unbind methods.
Now, as Equinox is creating the instances of those components / services I would like to know what is the best way of getting this instance?
So assume there is third class class which is NOT instantiated by OSGI:
Class WantsToUseComponentB{
public void doSomethingWithComponentB(){
// how do I get componentB??? Something like this maybe?
ComponentB component = (ComponentB)someComponentRegistry.getComponent(ComponentB.class.getName());
}
I see the following options right now:
1. Use a ServiceTracker in the Activator to get the Service of ComponentBundleA.class.getName() (I have tried that already and it works, but it seems to much overhead to me) and make it available via a static factory methods
public class Activator{
private static ServiceTracker componentBServiceTracker;
public void start(BundleContext context){
componentBServiceTracker = new ServiceTracker(context, ComponentB.class.getName(),null);
}
public static ComponentB getComponentB(){
return (ComponentB)componentBServiceTracker.getService();
};
}
2. Create some kind of Registry where each component registers as soon as the activate() method is called.
public ComponentB{
public void bind(ComponentA componentA){
someRegistry.registerComponent(this);
}
or
public ComponentB{
public void activate(ComponentContext context){
someRegistry.registerComponent(this);
}
}
}
3. Use an existing registry inside osgi / equinox which has those instances? I mean OSGI is already creating instances and wires them together, so it has the objects already somewhere. But where? How can I get them?
Conclusion
Where does the class WantsToUseComponentB (which is NOT a Component and NOT instantiated by OSGI) get an instance of ComponentB from? Are there any patterns or best practises? As I said I managed to use a ServiceTracker in the Activator, but I thought that would be possible without it.
What I am looking for is actually something like the BeanContainer of Springframework, where I can just say something like Container.getBean(ComponentA.BEAN_NAME). But I don't want to use Spring DS.
I hope that was clear enough. Otherwise I can also post some source code to explain in more detail.
Thanks
Christoph
UPDATED:
Answer to Neil's comment:
Thanks for clarifying this question against the original version, but I think you still need to state why the third class cannot be created via something like DS.
Hmm don't know. Maybe there is a way but I would need to refactor my whole framework to be based on DS, so that there are no "new MyThirdClass(arg1, arg2)" statements anymore.
Don't really know how to do that, but I read something about ComponentFactories in DS. So instead of doing a
MyThirdClass object = new MyThirdClass(arg1, arg2);
I might do a
ComponentFactory myThirdClassFactory = myThirdClassServiceTracker.getService(); // returns a
if (myThirdClassFactory != null){
MyThirdClass object = objectFactory.newInstance();
object.setArg1("arg1");
object.setArg2("arg2");
}
else{
// here I can assume that some service of ComponentA or B went away so MyThirdClass Componenent cannot be created as there are missing dependencies?
}
At the time of writing I don't know exactly how to use the ComponentFactories but this is supposed to be some kind of pseudo code :)
Thanks
Christoph
Christoph,
Thanks for clarifying this question against the original version, but I think you still need to state why the third class cannot be created via something like DS.
DS causes components to be published as services, therefore the only way to "get" any component from DS is to access it via the service registry. Unfortunately the service registry can be hard to use correctly using the lower level APIs because it is dynamic, so you have to cope with the possibility of services going away or not being available at precisely the moment you want them to be available, and so on. This is why DS exists: it gives you an abstraction for depending on services and managing the lifecycle of your components based on the availability of services that they reference.
If you really need to access a service without using DS or something like it (and there is quite a choice of "things like it" e.g. Spring-DM, iPOJO, Guice/Peaberry, etc) then you should use ServiceTracker. I agree there is a lot of overhead -- again, this is why DS exists instead.
To answer your suggestion no (2), no you should not create your own registry of services because the service registry already exists. If you created a separate parallel registry then you would still have to handle all the dynamics, but you would have to handle it in two places instead of one. The same applies to suggestion (3).
I hope this helps.
Regards,
Neil
UPDATED: Incidentally, although Spring has the Container.getBean() backdoor, you notice that in all Spring documentation it is strongly recommended not to use that backdoor: to get hold of a Spring bean, just create another bean that references it. The same applies to DS, i.e. the best way to get hold of a DS component is to create another DS component.
Also note that in the OSGi world, even if you're using Spring-DM there is no easy way to just call getBean() because you need to get hold of the Spring ApplicationContext first. That is itself an OSGi service, so how to you get that service?
christoph,
dont know if I really understand your problem.
per ex.
Bundle A is providing a service using DS component:
<service>
<provide interface="org.redview.lnf.services.IRedviewLnfSelectedService"/>
Bundle B requires this service using DS component:
<implementation class="ekke.xyz.rcp.application.internal.XyzApplicationLnfComponent"/>
as soon as Bundle A provides the Service, Bundle B "gets" it through the bind() methode of the implementation class:
public class XyzApplicationLnfComponent {
public void bind(IRedviewLnfSelectedService lnfSelectedService) {
// here it is
}
hope this helps
ekke
Easy way: Inject the DS component into your Activator class with Riena:
http://wiki.eclipse.org/Riena_Getting_Started_with_injecting_services_and_extensions
Then you can call it from everywhere: Activator.getDefault().getWhateverService()