项目作者: Ahmed-Adel-Ismail

项目描述 :
An Implementation of the Actor-Model in pure Android components, also supports inter-process communication between Actors
高级语言: Java
项目地址: git://github.com/Ahmed-Adel-Ismail/AndroidActorModel.git
创建时间: 2017-08-26T05:17:26Z
项目社区:https://github.com/Ahmed-Adel-Ismail/AndroidActorModel

开源协议:Apache License 2.0

下载


AndroidActorModel

An Implementation of the Actor-Model in pure Android components, also supports inter-process communication between Actors

Introduction

Android’s Architecture in it’s core is designed in a way similar to Actor-Model, where there are Handlers running on different Loopers, and every Looper queues Messages delivered to it’s Handler, and this Handler can reply to the Sender Handler through a Messenger … and this can be done across multiple processes.

Actor-Model References

This is a link that can help understanding the Actor-Model : http://www.brianstorti.com/the-actor-model/

Getting Started

in the Application Class, you will need to initialize the ActorSystem in the Applications onCreate() as follows :

  1. private ActorSystem actorSystem;
  2. public void onCreate() {
  3. ...
  4. ActorSystem.with(this, actorSystem -> this.actorSystem = actorSystem);
  5. }

The Actor-Model is reactive by nature, there are no blocking methods, if a method will return a value, this will be done through Continuation Passing Style (Callbacks), for example in this example, you can use RxJava2’s “ReplaySubject” to save the returned instance, and then subscribe to this “ReplaySubject” across the application to emit this Actor-System in the “onNext()” when ever it is ready

So let’s modify the code to use RxJava2 Observable :

  1. private static MainApplication instance;
  2. private final Subject<ActorSystem> actorSystem = ReplaySubject.create(1);
  3. @Override
  4. public void onCreate() {
  5. super.onCreate();
  6. ActorSystem.with(this, actorSystem::onNext);
  7. instance = this;
  8. }
  9. public static Observable<ActorSystem> getActorSystem() {
  10. return instance.actorSystem;
  11. }

Creating the First Actor Service

For Any Service that will be used as an Actor, it should extend “ActorService” and add functions that will be executed when ever a “Message” with a specefic ID (or Message.what) is received, for example :

  1. public class MainService extends ActorService {
  2. public static final int MSG_PING = 1;
  3. public MainService() {
  4. onMessageReceived(MSG_PING, replyPing()); // here we add the replyPing() function to be executed when the received Message.what is MSG_PING
  5. }
  6. private Command<Message> replyPing() {
  7. return message -> {
  8. // this function will be trigerred when the incoming Message.what is MSG_PING
  9. Message newMessage = MessageBuilder // start building a new Message
  10. .prepareMessage(MainActivity.MSG_SHOW_TOAST) // set it's Message.what
  11. .serializable("Service Pinged") // add a Serializable as an extra to this Message
  12. .build(); // create the "android.os.Message" to be sent
  13. MessageReader messageReader = MessageReader.with(message); // read the incoming message in a MessageReader Object
  14. messageReader.getReplyTo().send(newMessage); // we reply with this "newMessage" to the Actor who sent the MSG_PING Message
  15. };
  16. }

}

ActorService sub-classes execute there functions in the Main Thread, it is safe to make them start in a different process, since the communication style will stay the same … you can add this to the manifest :

  1. <service android:name=".MainService" android:process=":main" ></service>

For Services, there is no more that could be done, all the steps required are
1- extend ActorService
2- add the Command that will be executed when a Message is received through “onMessageReceived()”

Creating the First Actor Activity / Fragment / …

for Actors other than Services, they do not extend another classes, all they need to do is to create a “Mailbox” (which is a Handler) that will run on a “Looper”, and you can add “Command” instances (functions) to be executed when this Mailbox receives Messages with certain Ids (or Message.what)

also those Mailboxes need to be registered and unregistered to be active, also ActorServices require to be registered and unregistered … an example for an Actor Activity communicating with our “MainService” will be as follows :

  1. public static final int MSG_SHOW_TOAST = 1;
  2. private Mailbox mailbox;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. ...
  6. mailbox = new Mailbox(MainActivity.class, Looper.getMainLooper()); // create a Mailbox with the address MainActivity.class, and will run on the Main Looper
  7. mailbox.addCommand(MSG_SHOW_TOAST, showToast()); // here we add the showToast() function to be executed when the received Message.what is MSG_SHOW_TOAST
  8. MainApplication.getActorSystem()
  9. .subscribe(actorSystem -> {
  10. actorSystem.register().actor(mailbox); // register the Mailbox of this Activity in the Actor System
  11. actorSystem.register().actor(MainService.class); // register the MainService to communicate with it, this calls bindService() internally
  12. });
  13. }
  14. private Command<Message> showToast() {
  15. return message -> Toast.makeText(MainActivity.this,
  16. MessageReader.with(message).getSerializable().toString(),
  17. Toast.LENGTH_SHORT).show();
  18. }
  19. public void onResume() {
  20. super.onResume();
  21. MainApplication.getActorSystem()
  22. .subscribe(actorSystem -> actorSystem.actorOf(MainService.class)
  23. .prepareMessage(MainService.MSG_PING) // prepare a Message with MainService.MSG_PING in it's Message.what
  24. .replyTo(MainActivity.class) // tell the receiver that it can reply to the Actor's Mailbox with the address MainActivity.class
  25. .serializable("pinging") // add a Serializable extra to the message
  26. .send()); // send the message in a non-blocking manner
  27. }
  28. @Override
  29. protected void onDestroy() {
  30. MainApplication.getActorSystem().subscribe(actorSystem -> {
  31. actorSystem.unregister().actor(MainService.class); // unregister the MainService.class Actor, this invokes the unbindService() method
  32. actorSystem.unregister().actor(mailbox); // unregister the Mailbox of this Activity
  33. });
  34. super.onDestroy();
  35. }

For registering and unregistering ActorServices, it is safe to register and unregister multiple times, since all these calls will not resultin any new instances, it’s Android’s job to handle the bind / unbind multiple calls to the same Service

Messaging between Actors

There are multiple classes that helps creating and reading “android.os.Message” without caring about Parcelable or Serializable Objects delivery between multiple processes, the “MessageBiulder” and “MessageSender” are 2 classes that Boxes a Message that is guaranteed to be delivered to another process even with Serializable Extras, and the “MessageReader” is a class that Unboxes the Message created by “MessageBuilder” and “MessageSender” … in the end, these are just wrapper classes around the “android.os.Message”

Gradle dependency

Step 1. Add it in your root build.gradle at the end of repositories:

  1. allprojects {
  2. repositories {
  3. ...
  4. maven { url 'https://jitpack.io' }
  5. }
  6. }

Step 2. Add the dependency

  1. dependencies {
  2. compile 'com.github.Ahmed-Adel-Ismail:AndroidActorModel:0.0.1'
  3. }