Saving sender reference in Actor field - java

I am novice to Akka and I will be very glad is someone with Akka experience could help me. I read following article http://doc.akka.io/docs/akka/2.4.1/java/untyped-actors.html and part with title "Lifecycle Monitoring aka DeathWatch" contains following example:
public class WatchActor extends UntypedActor {
final ActorRef child = this.getContext().actorOf(Props.empty(), "child");
{
this.getContext().watch(child); // <-- the only call needed for registration
}
ActorRef lastSender = getContext().system().deadLetters();
#Override
public void onReceive(Object message) {
if (message.equals("kill")) {
getContext().stop(child);
lastSender = getSender();
} else if (message instanceof Terminated) {
final Terminated t = (Terminated) message;
if (t.getActor() == child) {
lastSender.tell("finished", getSelf());
}
} else {
unhandled(message);
}
}
}
For me is not clear why we could save sender ref in actor field lastSender. For example we could have following situation: actor A send kill message to WatchActor, we save in WatchActor lastSender field reference of actor A, then actor B send kill message to WatchActor, we save in WatchActor lastSender field reference of actor B, Watch actor receiver Terminated message from child actor and answer to actor B, but actor A will not receive answer. Is this code incorrect?

What you describe could happen is correct, so the example is not entirely safe and could have unexpected outcome if multiple actors would send the "kill" message to WatchActor. The example in the docs could probably be improved in this regard.
It is annoying but maybe not super relevant to the section of the docs, which essentially describes how an actor will receive Terminated once another actor it watches has been stopped and that it happens asynchronously.
Please file a ticket here if you think it is important enough: https://github.com/akka/akka/issues

Related

Difference between DJI onProductChange and onProductConnect

Context
I'm building a Flutter Plugin above the DJK SDK. For that, I have to implement the communication with the aircraft on the native side, and I'm doing it with Java. I'm also doing it only for Android.
One of the methods of the API is boolean connectToAircraft(), which must return if the connection with the aircraft succeeded.
Expected/current behavior
After I call connectToAircraft() - which invokes the DJISDKManager.getInstance().startConnectionToProduct() method, I expected to be able to use anything related to aircraft immediately, but this doesn't happen. I have to wait a few seconds before I can retrieve data from the aircraft.
Some code
public class UavApi implements IUavApi, DJISDKManager.SDKManagerCallback {
...
private final CountDownLatch onConnectToUavFinishedSignal = new CountDownLatch(1);
...
public boolean connectToUav() throws InterruptedException {
Logger.v("connectToUav()");
DJISDKManager.getInstance().startConnectionToProduct();
synchronized (onConnectToUavFinishedSignal) {
onConnectToUavFinishedSignal.await();
}
return DJISDKManager.getInstance().getProduct() instanceof Aircraft;
}
...
#Override
public void onProductConnect(#Nullable final BaseProduct baseProduct) {
Logger.v(MessageFormat.format("onProductConnect(product: {0})", baseProduct));
if (baseProduct != null) {
handleProductConnected(baseProduct);
}
}
#Override
public void onProductChanged(#Nullable final BaseProduct baseProduct) {
Logger.v(MessageFormat.format("onProductChanged(product: {0})", baseProduct));
if (baseProduct != null) {
handleProductConnected(baseProduct);
}
}
...
private void handleProductConnected(#NonNull final BaseProduct baseProduct) {
Logger.d(MessageFormat.format("Is null? {0}", baseProduct == null ? "Yes" : "No"));
Logger.d(MessageFormat.format("Type: {0}", baseProduct.getClass().getSimpleName()));
onConnectToUavFinishedSignal.countDown();
}
...
}
Problem
The code above is what I tried to do, but it's not working and guess it's because I'm misunderstanding the use of the onProductChange() and onProductConnect() methods.
The DJISDKManager.getInstance().getProduct() is always returning null.
OBS: It's always returning null immediately after the onConnectToUavFinishedSignal.await() call finishes. After a few seconds, I get a valid instance of the aircraft.
Something I've also noticed is that sometimes the onProductChange() is called with some value that the log outputs as Unknwoun and None. What are those and how can I test for them? Like if (baseProduct == ???) doSomething()
Environment
Android 9
MSDK 4.13.1
Phantom 4 Pro
Difference
According to the SDK Docs onProductChanged is primarily used to detect when the connection status changes from only remote controller connected to a full connection between the aircraft and the SDK running on your device.
Keep in mind that when the aircraft is disconnected, this method will be called with an instance of an aircraft, but this instance will come with property isConnected as false. If you print the aircraft object to the console you will notice that if isConnected is true, it will print the aircraft name, otherwise, it will print "None".
As long for the onProductConnect, it will be called always after DJISDKManager.getInstance().registerApp() succeeded or after you manually connect to the aircraft with success using DJISDKManager.getInstance().startConnectionToProduct(). In my tests, even though the app registration succeeds, the method will return false, so you might need to check if the SDKManagerCallback::onRegister results in DJISDKError.REGISTRATION_SUCCESS.
Solution
You need to listen to component change events. Unfortunately just because the product is connected it does not mean that the individual components, such as the flight controller, camera etc are connected. You will need to implement onComponentChange and add a listener to detect when a component is connected. These don't always connect in the same order and may start to connect before or after the product is connected.
#Override
public void onComponentChange(
BaseProduct.ComponentKey componentKey,
BaseComponent oldBaseComponent,
BaseComponent newBaseComponent
) {
newBaseComponent.setComponentListener(isConnected -> {
// check if component connected and access data
if (isConnected) {
if(componentKey == ComponentKey.FLIGHT_CONTROLLER) {
// DJISDKManager.getInstance().getProduct() should no longer be null
DJISDKManager.getInstance().getProduct().getModel();
}
}
})
}

Sending message to child actor on parent and wait result

I am trying to send message to child actor from parent
but the message doesnot arrive until parent actor completes the onReceive block.
Is the any way to send message to child and wait result on the parent actor.
Parent:
onReveive(message) {
if(message instanceof ChildOrder) {
onChildOrder(message);
}
}
onChildOrder(message) {
Future<Object> future = Patterns.ask(childActorRef, order, timeout);
Object result = Await.result(future, timeout.duration()); /* this always times out */
}
Child:
onReveive(message) {
do stuff
}
As mentioned above
Object result = Await.result(future, timeout.duration());
line on onChildOrder method always times out and when the onReceive method of parent completes
the message ChildOrder arrives at the child's onReceive method.
It seems child cannot process any message without parent completes first.
Is there any way to send message to child and wait result. ?
You need the child to send the result back to the parent, otherwise the parent keeps waiting for an answer forever.
In Child:
#Override
public void onReceive(Object message) throws Exception {
MyResult res = doStuff(message);
// send result back
getSender().tell(res, getSelf());
}
It is indicated in the documentation of Patterns.ask:
[...] this means that the target actor needs to send the result to the sender reference provided.
Edit based on the comment:
Forward message from grandfather to child:
If you need the child to send back to the grand-parent, the parent may forward the message to the child so that to the child the sender is the grand-parent, therefore bypassing the parent on the way back:
In parent:
#Override
public void onReceive(Object message) throws Exception {
if(message instance of MessageFromGrandParent) {
child.forward(message, getContext());
}
// ...
}

JADE ContractNet and GUI problems

I have some problems with use of ContractNet (Interaction Protocol) and GUI with the use of JADE multiagent framework.
In particular, in the override of handlePropose method.
I know that my problem comes from the use of a GUI. Let me explain:
My agent (Initiator) uses a first GUI and, after a click, the
conversation begins with a second agent (Responder). According to the
Protocol, the Initiator has thus sent a CFP to Responder. The agent
Responder responds with a PROPOSE that contains different data.
Since here, everything ok. Now...
I wish that the agent Initiator, BEFORE returning a reply, may examine
the data ... ie publish them on a JTable, for the user! The user will
examine the proposal via GUI and will choose if to accept or not, by
click on a button.
If accept, the Initiator send ACCEPT_PROPOSAL.
If not accept, the Initiator send REJECT_PROPOSAL.
This should be done in the method handleProposal. This is my code:
#Override
protected void handlePropose(final ACLMessage propose, final Vector acceptances) {
try {
System.out.println("Agent "+getLocalName()
+": receive PROPOSE from "+propose.getSender().getLocalName());
final ACLMessage reply = propose.createReply();
Vector<Goods> goods = (Vector<Goods>) propose.getContentObject();
// the JTable's GUI for visualize the list of data:
final GoodsChoiceBox gcb = new GoodsChoiceBox(propose.getSender().getName(), goods);
// the problem:
gcb.getExecuteJButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
System.out.println("Agent "+getLocalName()+": send ACCEPT PROPOSAL ");
acceptances.addElement(reply);
}
});
// similar case, but for REJECT:
// gcb.getAbortJButton().addActionListener(... bla bla
gcb.setVisible(true);
} catch (UnreadableException e){
e.printStackTrace();
}
}
..... But, obviously, does not work.
In the Initiator agent, the ContractNet behaviour is aborted... so also handleInform, handleRefuse and handleFailure (for handle the answers) do not work.
The Initiator's principal GUI is blocked. And other problems...
Instead, if I do this (WITHOUT JButton, another GUI and ActionListener):
#Override
protected void handlePropose(final ACLMessage propose, final Vector acceptances) {
try {
System.out.println("Agent "+getLocalName()
+": received PROPOSE from "+propose.getSender().getLocalName());
final ACLMessage reply = propose.createReply();
Vector<Goods> goods = (Vector<Goods>) propose.getContentObject();
// the JTable's GUI for visualize the list of data:
final GoodsChoiceBox gcb = new GoodsChoiceBox(propose.getSender().getName(), goods);
reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
System.out.println("Agente "+getLocalName()+": ACCEPT PROPOSAL di "+propose.getSender().getLocalName());
acceptances.addElement(reply);
} catch (UnreadableException e){
e.printStackTrace();
}
}
.... works.
I know that the problem is the ActionListener and its multithread nature.
But I need the GUI there.
How can I fix?
I try to answer myself. I'm not sure it's the best solution, but certainly works.
Note that before coming to this solution I am well documented with the guides and tutorials found (on http://jade.tilab.com/), and confronting myself with other
JADE developers (in mailing lists http://jade.tilab.com/pipermail/jade-develop/)
The answer is complicated, so I'll try to be exhaustive.
In my project I have to deal with two different types of agents.
The ShipperAgent, which represents one shipper: it keeps track of the vehicles owned by the shipper, those available, and the goods "reserved" from it.
The BuyerAgent, which represents customers (or buyers): each customer has a list of goods that want to move from point A to point B.
The two agents are registered to the yellow pages service.
In ShipperAgent, clicking on the "SEARCH" button you start a search: start a Contract Net Interaction Protocol.
Explain the Contract Net Interaction Protocol and my case
In the standard FIPA: http://www.fipa.org/specs/fipa00029/SC00029H.html
In JADE guide can be found here: http://jade.tilab.com/doc/programmersguide.pdf (p. 35)
Further on you will notice the changes that I had to take.
The ShipperAgent sends CFP each BuyerAgent.
Each BuyerAgent:
2.1 if he has goods, send a PROPOSE to ShipperAgent.
2.2 if does not have the goods, send a REFUSE to ShipperAgent. And for buyer, the protocol ends.
Since here is easy. With the sniffer, we can observe:
Now:
The ShipperAgent:
3.1 receives one or more PROPOSE by buyers, and displays (see image below).
3.2 if it receives the REFUSE (or does not receive anything after a certain time), ending communication with those buyer.
Here's how the Shipper graphically displays the proposals:
Now it's up to the user to choose which goods wants and what not.
To achieve this, I had to create some kind of "internal communication" to the agent himself: the GUI (in 3.1), once clicked Execute, sends a message to the agent. It may seem inelegant, but it seems to be the only way to not crash the protocol side ShipperAgent.
The ShipperAgent:
 4.1 if the user has selected one or more goods proposals (and click Execute), sends to the corresponding BuyerAgent an ACCEPT_PROPOSAL, where specific goods that want to (a subset of the previous proposal).
 4.2 if the user does not select any good (or click on Cancel), sends to the corresponding BuyerAgent an REJECT_PROPOSAL. Ends communication to that buyer.
The BuyerAgent:
 5.1 if receives an ACCEPT_PROPOSAL, check that the goods are still available (any other shippers could have them "reserved" in the meanwhile) and, if so, sends an INFORM.
5.2 if receives an ACCEPT_PROPOSAL ma one or more goods are no longer available, sends FAILURE.
 5.3 if it receives an REJECT_PROPOSAL, ends communication with the ShipperAgent.
In brief this (for example):
The code
BuyerAgent.java
I create a dispatcher who is always ready to receive the CFP. As soon as it receives and start protocol, buyer-side: start SearchJobResponder.
/*
* ...
*/
final MessageTemplate template = MessageTemplate.and(
MessageTemplate.MatchProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET),
MessageTemplate.MatchPerformative(ACLMessage.CFP) );
// SSResponderDispatcher:
SSResponderDispatcher dispatcher = new SSResponderDispatcher(this, template) {
BuyerAgent b = (BuyerAgent) this.myAgent;
protected Behaviour createResponder(ACLMessage initiationMsg) {
// SearchJobResponder for single cfp:
return new SearchJobResponder(b, initiationMsg);
}
};
addBehaviour(dispatcher);
/*
* ...
*/
ShipperAgent.java
Search all buyer, creates a CFP and start the protocol, shipper-side: start SearchJobInitiator.
/*
* ...
*/
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
AID[] buyerAgents = searchBuyers(); // search all buyerAgents
for (AID buyer : buyerAgents)
cfp.addReceiver(buyer);
addBehaviour(new SearchJobInitiator(this, cfp));
/*
* ...
*/
SearchJobInitiator.java
This was the hard part...
/*
* ...
*/
public class SearchJobInitiator extends ContractNetInitiator {
ShipperAgent shipperAgent;
public SearchJobInitiator(ShipperAgent a, ACLMessage cfp) {
super(a, cfp);
shipperAgent=a;
// Very important:
registerHandleAllResponses(new HandleProposes());
}
#Override
protected Vector<?> prepareCfps(ACLMessage cfp) {
long now = System.currentTimeMillis();
cfp.setConversationId("contractNet-by-"
+shipperAgent.getAID().getLocalName()+now);
cfp.setContent("Fammi delle proposte di lavoro");
/*
* filtering...
*/
cfp.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
cfp.setReplyByDate(new Date(now+10000));
//cfp.setReplyWith("cfp"+System.currentTimeMillis()) //useless, is overwrited at the end
return super.prepareCfps(cfp);
}
//inner class for handling a single proposal
public class HandleProposes extends Behaviour {
private static final long serialVersionUID = 1L;
private Vector<ACLMessage> proposes;
private Vector<ACLMessage> acceptances;
private int numberOfProposes;
public void onStart() {
proposes = (Vector<ACLMessage>) getDataStore().get(ALL_RESPONSES_KEY);
acceptances = (Vector<ACLMessage>) getDataStore().get(ALL_ACCEPTANCES_KEY);
numberOfProposes=proposes.size();
for (Iterator I=proposes.iterator(); I.hasNext();) {
ACLMessage propose = (ACLMessage) I.next();
// Very important:
if (propose.getPerformative()==ACLMessage.PROPOSE)
myAgent.addBehaviour(new HandleSinglePropose(propose, acceptances));
else
numberOfProposes--;
}
}
public void action() {
if (!done())
block();
}
public boolean done() {
return (acceptances.size()==numberOfProposes);
}
/*
* Inner class for handle a single proposal and display it:
*/
public class HandleSinglePropose extends Behaviour {
private ACLMessage propose;
private Vector<ACLMessage> acceptances;
private boolean finish=false;
public HandleSinglePropose (ACLMessage propose, Vector<ACLMessage> acceptances) {
this.propose=propose;
this.acceptances=acceptances;
// This is GUI in 3.1 point
GoodsChoiceBox gcb = new GoodsChoiceBox(shipperAgent, this, propose); // fill the JTable
gcb.setVisible(true);
}
#Override
public void action() {
MessageTemplate mt = MessageTemplate.and(
MessageTemplate.MatchSender(shipperAgent.getAID()),
MessageTemplate.and(
MessageTemplate.MatchReplyWith("response"+propose.getReplyWith()),
MessageTemplate.or(
MessageTemplate.MatchPerformative(ACLMessage.ACCEPT_PROPOSAL),
MessageTemplate.MatchPerformative(ACLMessage.REJECT_PROPOSAL)
) ) ) ;
// Read data from GUI. The user accept or reject:
ACLMessage decisionFromGUI = shipperAgent.receive(mt);
if (decisionFromGUI != null) {
ACLMessage reply = propose.createReply();
// bla bla...
finish=true;
HandleProposes.this.restart();
} else {
block();
}
}
public boolean done() {
return finish;
}
public void handleChoice(ACLMessage propose, boolean bool, Vector<Goods> selectedGoods) {
ACLMessage reply;
if (bool){
reply = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
//...
} else {
reply = new ACLMessage(ACLMessage.REJECT_PROPOSAL);
//...
}
reply.addReceiver(shipperAgent.getAID());
reply.setReplyWith("response"+propose.getReplyWith());
shipperAgent.send(reply);
}
} // closes HandleSinglePropose
} // closes HandleProposes
}
SearchJobResponder.java
The responder is simple. The only thing of note: I extends SSContractNetResponder, don't extends ContractNetResponder.
public class SearchJobResponder extends SSContractNetResponder {
BuyerAgent buyerAgent;
public SearchJobResponder(BuyerAgent a, ACLMessage cfp) {
super(a, cfp);
buyerAgent = a;
}
/*
* override methods...
*/
}
GoodsChoiceBox.java
The GUI for show the proposals...
public GoodsChoiceBox(final Agent agent, final HandleSinglePropose behaviour, final ACLMessage propose){
/*
* graphics stuff
*/
// if goods selected and press Execute
behaviour.handleChoice(propose,true,selectedGoods);
//else
behaviour.handleChoice(propose,false,null);
/*
* bla bla
*/
}
I know, I have dwelt much, but I did not know how else to explain.
However, now my project work. But I'm open to any suggestions.
I often run into this problems of this sort. These are Finite State machine behaviours so you should be able to pause and resume a behavior but I'm not sure how. What I do is create two separate Interaction behaviors on the initiator side and one on the responder side.
Initiator Responder
| |
| |
| First behaviour |The responder only has 1 behaviour
|| CFP-> ||
|| <-Proposal ||
| ||
| Second behaviour ||
|| Accept prop-> ||
|| <-Response ||
| |
Two points to remember
(1)
Make sure that you save the conversationID
msgRecieved.getConversationID
from the first behaviour and use it in the second Behaviour.
msg.setConversationID().
(2)
The second behavior is another Contract net initiator but in prepareCFPs method set MESSAGE performative to accept proposal
class ContractServiceList extends ContractNetInitiator
{
protected Vector prepareCfps(ACLMessage cfp) {
ACLmessage AP= new ACLmessage(ACLmessage.ACCEPT_PROPOSAL)
.....
These things are hard to explain so I tried to attached a picture but have 2 little rep points.
I now have enough rep points to attach the picture which I am doing.
I just realized that there is another solution to this problem. The second solution involves using ChildBehaviours and data stores. A child behaviour can be initiated pausing the parent behaviour. The parent behaviour must then be resumed one the child is complete.
I'm attaching a pic to better explain this interaction.
So at point A in your CNI (ContractNetInitiator) parent behaviour you want to initiate the Child behaviour. You would do this by using the CNI.registerHandlePropose(new Childbehaviour).
This is what the setup() method should look like:
protected void setup()
{
ContractNetInitiator parentBehave= new ContractNetInitiator (null, null, GlobDataStore);
ContractNetInitiator.registerHandlePropose(new ChildBehavoiur (GlobDataStore));
addBehaviour(CNI);
}
In you Child behaviour you will have to check the data from the parent (GlobDataStore) and return a message to be passed back. Code to follow:
class ChildBehaviour extends OneShotBehaviour{
#Override
public void action() {
//evaluate globalestore here;
ACLMessage CNIresponse=new ACLMessage();
if(true)
{
storeNotification(ACLMessage.ACCEPT_PROPOSAL, CNIresponse);
}
else
{
storeNotification(ACLMessage.REJECT_PROPOSAL, CNIresponse);
}
}
public void storeNotification(int performative, ACLMessage original)
{
// Retrieve the incoming request from the DataStore
String incomingCFPkey = (String) ((ContractNetResponder) parent).CFP_KEY;
incomingCFPkey = (String) ((ContractNetResponder) parent).CFP_KEY;
ACLMessage incomingCFP = (ACLMessage) getDataStore().get(incomingCFPkey);
// Prepare the notification to the request originator and store it in the DataStore
ACLMessage notification = incomingCFP.createReply();
notification.setPerformative(performative);
notification.setContent(original.getContent());
String notificationkey = (String) ((ContractNetResponder) parent).PROPOSE_KEY;
getDataStore().put(notificationkey, notification);
}
}

JADE agent data communication

In my agent program(using jade), I have 5 agents, in which one agent is a coordinator others are device agents. All 4 device agents send a array to coordinator.
I want coordinator to receive all 4 arrays and store them as single array. How can I do it?
I send messages via ACLmessage(strings)
You should send your arrays in the 4 messages with msg.setContentObject((Serializable)yourObject)
At the reception :
do a cast : yourLocalObject=(yourObjectType) msg.getContentObject();
Then globalarray.merge(yourLocalObject)
I suggest using jade.proto.AchieveREInitiator behaviour by the Coordinator. This allows for a simple conversation with multiple responders.
The responders, your 4 device agents will respond with jade.proto.AchieveREResponder behiours.
The code should look alil something like this on the Coordinator side
class CoordinatorRequestService extends AchieveREInitiator
{
String agentName;
String Service;
public CoordinatorRequestService (Agent a, ACLMessage msg,String Service ,String agentLocalID) {
super(a, msg);
// TODO Auto-generated constructor stub
this.agentName=agentLocalID;
this.Service=Service;
}
protected Vector prepareRequests(ACLMessage predefinedRequest)
{
ACLMessage requestmsg=new ACLMessage(ACLMessage.REQUEST);
requestmsg.addreciever(//add your agents here)
requestMsgVector.add(requestmsg);
return requestMsgVector;
}
protected void handleInform(ACLMessage inform)
{
recieveData=inform.extractDataRecieved();
globalarray.append(recieveData)
}
Google to find more examples of Contract net and achieve communication.
Goodluck

Send Object from Service to Activity (Can't marshal non-Parcelable)

I'm trying to send data from my activity to a service and receive some information back, but i'm getting:
java.lang.RuntimeException: Can't
marshal non-Parcelable objects across
processes.
The code from activity looks like this:
Message msg = Message.obtain(null, 1);
msg.obj=1;
msg.replyTo=new Messenger(new PlanRequestIncomingHandler());
try {
msgService.send(msg);
} catch (RemoteException e) {
Log.i(tag, "Can not send msg to service");
e.printStackTrace();
}
When I set msg.obj = something I get java.lang.RuntimeException, can somebody help me?
You can pass Parcelable type objects via Messenger. Or else if you want to pass primitive data types use Bundle wrapper as below.
In Service End:
//Create a bundle object and put your data in it
Bundle bundle = new Bundle();
bundle.putInt("key", 1);
Message msg = Message.obtain(null, 123);
msg.obj = bundle;
msg.replyTo = new Messenger(new PlanRequestIncomingHandler());
try {
msgService.send(msg);
} catch (RemoteException e) {
Log.i(tag, "Can't send msg to service");
e.printStackTrace();
}
In Activity End:
switch(msg.what) {
case 123:
if(msg.obj != null) {
Bundle bundle = (Bundle) msg.obj;
System.out.println("Got integer "+ bundle.getInt("key"));
}
break;
}
cheers :-)
Old question, but I am answering so it might help someone in the future.
If you are using actual objects, then by all means, please implement Parcelable
Android: How to implement Parcelable to my objects?
However, as the OP stated that he tried using Primitives and it did not work, this is what to do.
The problem lies here msg.obj=1; This expects an actual object that implements Parcelable
Use msg.arg1 = 1;
you can then retrieve the argument on the service side using msg.arg1
For simplicity I use (straight from my code)
Message msg = Message.obtain(null, PlayerService.MSG_ACTION_SEEK, i, -1);
The -1 is just a holder for me.
Hope this helps.
Edit:
Be careful with
Message msg = Message.obtain(null, PlayerService.MSG_ACTION_SEEK, i);
This signature is equivalent to the OP's first attempt and expects a Parcelable and is what actually tripped me and got me searching in the first place. It won't throw an error until runtime.
Other than primitive data, the objects you're juggling between Activities and Services need to implement Parcelable and preferably Serializable.
I hope this helps,
Best
-serkan
You must use the Bundle to pass the conventional type data, otherwise it will be wrong:
Java.lang.RuntimeException: Can't non-Parcelable objects across marshal processes.
Because the Binder transaction data is called Parcel, the Parcelable interface must be implemented, otherwise it is not possible to communicate between the two applications. The reason why Bundle is passed because the class implements the Parcelable interface. Of course, if you want to pass the class must also implement the interface.
you can write like down:
Message msg = Message.obtain(null, 1);
msg.getData().putInt("key",1);
msg.replyTo=new Messenger(new PlanRequestIncomingHandler());
try {
msgService.send(msg);
} catch (RemoteException e) {
Log.i(tag, "Can not send msg to service");
e.printStackTrace();
}
sorry,my english is very bad
I have implemented Actor Model (like Akka) for Android, since Akka requires Java 8, i made my own implementation for it for Android, using RxJava2 , it was very easy to implement ... And once it is there, you can send messages holding any object to any receiver (Activity, Fragment, Service, Pojo, etc.) without worrying about threads or serialization
It is hard to explain my own implementation in details if you dont know what is Actor Model, but if you do, you can make an interface named "Actor" with one method
void onMessageReceived(Message message);
And you can implement this interface by any Actor you have, and to register any Actor, you can create an ActorSystem class that has methods :
static void register(Actor actor, PublishSubject<Message> mailbox);
static void unregister(Actor actor);
static void send(Message message, Class<? extends Actor> ... actors);
And when you register your Actor (Activity or Service), you decide what is your thread / scheduler to receive your messages on, through :
PublishSubject.observeOn(Schedulers.trampoline());
And you register your Actor in onCreate() and unRegister in onDestroy()
Or if you want a library for that (but i did not test it), you can take a look at this :
https://github.com/actorapp/droidkit-actors

Categories

Resources