All Projects → truecaller → android-actors-library

truecaller / android-actors-library

Licence: Apache-2.0 license
Android Actors Library was inspired by the Actor model. The main purpose of this library is to help developers in creating a worker attached to a thread and make all interactions with this worker natural and simple.

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to android-actors-library

funboost
pip install funboost,python全功能分布式函数调度框架,。支持python所有类型的并发模式和全球一切知名消息队列中间件,python函数加速器,框架包罗万象,一统编程思维,兼容50% python编程业务场景,适用范围广。只需要一行代码即可分布式执行python一切函数。旧名字是function_scheduling_distributed_framework
Stars: ✭ 351 (+1362.5%)
Mutual labels:  threading
mantichora
A simple interface to Python multiprocessing and threading
Stars: ✭ 13 (-45.83%)
Mutual labels:  threading
think-async
🌿 Exploring cooperative concurrency primitives in Python
Stars: ✭ 178 (+641.67%)
Mutual labels:  threading
python-json-socket
JSON messaging based socket interface with multi-threaded server and client
Stars: ✭ 52 (+116.67%)
Mutual labels:  threading
react-native-bg-thread
react-native-bg-thread
Stars: ✭ 45 (+87.5%)
Mutual labels:  threading
People-Counting-in-Real-Time
People Counting in Real-Time with an IP camera.
Stars: ✭ 233 (+870.83%)
Mutual labels:  threading
NALib
General purpose C sourcecode collection
Stars: ✭ 16 (-33.33%)
Mutual labels:  threading
ObviousAwait
🧵 Expressive aliases to ConfigureAwait(true) and ConfigureAwait(false)
Stars: ✭ 55 (+129.17%)
Mutual labels:  threading
CodeProject
Common code for unity project develop.
Stars: ✭ 28 (+16.67%)
Mutual labels:  threading
ThreadBoat
Program Uses Thread Execution Hijacking To Inject Native Shell-code Into a Standard Win32 Application
Stars: ✭ 162 (+575%)
Mutual labels:  threading
thread-pool
BS::thread_pool: a fast, lightweight, and easy-to-use C++17 thread pool library
Stars: ✭ 1,043 (+4245.83%)
Mutual labels:  threading
Tieba-Birthday-Spider
百度贴吧生日爬虫,可抓取贴吧内吧友生日,并且在对应日期自动发送祝福
Stars: ✭ 28 (+16.67%)
Mutual labels:  threading
thread-pool
A modern thread pool implementation based on C++20
Stars: ✭ 104 (+333.33%)
Mutual labels:  threading
Quickenshtein
Making the quickest and most memory efficient implementation of Levenshtein Distance with SIMD and Threading support
Stars: ✭ 204 (+750%)
Mutual labels:  threading
Perfect-Thread
Core threading library for Perfect Server Side Swift. Includes support for serial and concurrent thread queues, locks, read/write locks and events.
Stars: ✭ 17 (-29.17%)
Mutual labels:  threading
Low Poly Procedural Trees and Vegetations Project
No description or website provided.
Stars: ✭ 14 (-41.67%)
Mutual labels:  threading
libquo
Dynamic execution environments for coupled, thread-heterogeneous MPI+X applications
Stars: ✭ 21 (-12.5%)
Mutual labels:  threading
SwiftConcurrentCollections
Swift Concurrent Collections
Stars: ✭ 40 (+66.67%)
Mutual labels:  threading
concurrent-resource
A header-only C++ library that allows easily creating thread-safe, concurrency friendly resources.
Stars: ✭ 17 (-29.17%)
Mutual labels:  threading
MoviesApp
A Movie Application uses TheMovie API, MVVM architecture and all jetpack components.
Stars: ✭ 100 (+316.67%)
Mutual labels:  threading

Android Actors Library

license Maven Central

Android Actors Library was inspired by the Actor model, but in fact, it has nothing in common with it. The main purpose of this library is to help developers in creating a worker attached to a thread and make all interactions with this worker natural and simple.

How to use the library

Configure your build

Add the following dependencies to your project:

compile group: 'com.truecaller', name: 'android-actors-library', version: <LATEST-VERSION>
annotationProcessor group:'com.truecaller', name: 'android-actors-generator', version: <LATEST-VERSION>

Specify a package for ActorsBuilder

The Actors Library needs to know which package it should use as a root for the generated classes. To provide this information create a file named package-info.java. This file should contain the package name (similar to a regular java file), but annotated with the @ActorsPackage annotation.

@ActorsPackage
package your.application.package.name;

import com.truecaller.androidactors.ActorsPackage;

You can change the generation rules slightly using the values of this annotation. See the source code for details.

Create a worker

Define an interface and mark it with the @ActorInterface annotation

@ActorInterface
interface Storage {
    void save(@NonNull User data);

    @NonNull
    Promise<User> getById(long id);
}
Limitations

Actor's implementation will work on a separate thread with its own stack, thus the following is not allowed:

  • Throw any Exceptions from actor's methods.
  • Return values directly from the actor's methods. All return values must be wrapped with a Promise class.
  • Return null from actor's methods. All methods must be annotated with a @NonNull annotation, otherwise the compilation will fail during code generation.
Example implementation
/* package */ class StorageImpl implements Storage {
    public void save(@NonNull User data) {
        // Save user to persistent storage
    }

    @NonNull
    public Promise<User> getById(long id) {
        if (!isValidId(id)) {
            return Promise.wrap(null);
        }
  
        final User data;
        // Fetch user from storage
        return Promise.wrap(data);
    }
}

In worker implementation, you should not care about threading. All methods will be executed on a single thread, so:

  • no synchronization issues
  • thread can be blocked as long as needed

Create an actor thread

During compilation, the Actors Library generates an ActorsBuilder class. It is always placed in the package marked by the @ActorsPackage annotation (name and access level can be changed) You need an instance of this class for managing actors threads.

ActorsBuilder actors = new ActorsBuilder().build();
ActorThread storageThread = actors.createThread("storage");

There are several types of threads. For more information, check the ActorsThreads interface.

Bind your actor implementation to a thread

To use the actor, you need to bind it to a thread:

ActorRef<Storage> storage = storageThread.bind(Storage.class, new StorageImpl());

Then, share this ActorRef across your application. It will take care of the calls to the actual actor implementation.

It is allowed to bind multiple actors to the same actor thread, but it is not allowed to:

  • bind one actor's implementation to several threads
  • bind one actor's implementation to the same thread more than once

Call methods from ActorRef

Invoking actor methods is as easy as telling the ActorRef to execute the required method:

storage.tell().save(user);

The actor reference will take care of providing the data to the actual actor implementation on the proper thread.

When the actor's method returns a value, it gets a bit more complex. Before the call executes, you need to explicitly tell what needs be done with the result. There are three possibilities:

#1 Forget about the result
storage.tell().getById(100L).thenNothing();
#2 Run actions on the actor's thread

Not recommended (all work should be done prior to returning a result), but it might be useful in certain cases.

storage.tell().getById(100L).then(new ResultListener<User>() {
    @Override
    public void onResult(@Nullable User result) {
        // Some actions with the returned object or whatever you want
    }
});
#3 Run actions on another thread

Very useful for UI interaction. The following example updates the UI based on the returned result:

storage.tell().getById(100L).then(actors.ui(), new ResultListener<User>() {
    @Override
    public void onResult(@Nullable User result) {
        if (result == null) {
            showEmptyView();
        } else {
            updateViews(result);
        }
    }
});

Using lambdas or method references will make this look even nicer:

class MainActivity extends Activity {

    @NonNull
    private final ActorsThreads mActors;

    @NonNull
    private final ActorRef<Storage> mStorage;

    private long mUserId;

    @Override
    protected void onResume() {
        super.onResume();
        mStorage.tell().getById(mUserId).then(mActors.ui(), this::onUserData);
    }

    private void onUserData(@Nullaable User user) {
        if (user == null) {
            showEmptyView();
        } else {
            updateViews(user);
        }
    }
}

Action handle

Whenever you provide a result listener, the link to it is stored until the actual method call. This might cause temporal memory leaks, especially when you are doing it from your activity.

To deal with this problem, all calls to ActorThread.then() return an instance of the ActionHandle interface. It contains only one method - ActionHandle.forget(). By calling this method you are telling the library that you are not interested in result anymore, allowing it to forget all links to the listener. When the forget() method is called from the result listener's thread, you can be sure that the listener will never be triggered afterward.

class MainActivity extends Activity {
  
    @NonNull
    private final ActorsThreads mActors;
  
    @NonNull
    private final ActorRef<Storage> mStorage;
  
    @Nullable
    private ActionHandle mUserHandle;
  
    private long mUserId;
  
    @Override
    protected void onResume() {
        super.onResume();
        mUserHandle = mStorage.tell().getById(mUserId).then(mActors.ui(), this::onUserData);
    }
  
    @Override
    protected void onPause() {
        super.onPause();
        if (mUserHandle != null) {
            mUserHandle.forget();
            mUserHandle = null;
        }
    }
  
    private void onUserData(@Nullable User user) {
        if (user == null) {
            showEmptyView();
        } else {
            updateViews(user);
        }
        
        mUserHandle = null;
    }
}

Android service as actor thread

You can wrap an actor thread in an Android Service. It allows you to ensure that all calls will be finished in the background if the user leaves the application.

First, define your service. It must be a subclass of ActorService. Then, pass proper parameters to the base constructor:

public class StorageService extends ActorService {
    public StorageService() {
        super("storage-worker", 0, true);
    }
}

The base constructor asks for three parameters:

  • name - the name of the worker thread
  • stopDelay - interval in milliseconds after which the service will stop itself if no new commands are passed. By default, this value is zero, which means that the service stops itself each time the messages queue is empty. This may sometimes cause UI lags, especially if you are doing infrequent calls (allowing the queue to become empty) for updating lists or something similar based on the Activity/Fragment transitions or data updates. For dealing with this problem, just provide a wanted interval.
  • useWakeLocks - obtain a partial wakelock to prevent the device from going to sleep while the methods execute. If you set it to true do not forget to ask for the android.permission.WAKE_LOCK permission in your manifest. Worker thread's name will be used as the wake lock's name.

Also, do not forget to add service in yor manifest:

<application>
    <service android:name=".StorageService" />
</application>
Limitation

Since all parameters to the actor implementation are passed as references, the service can only work in the main application process.

Useful crashes

In case you get an exception somewhere in the actor's implementation, the library will create a special throwable that contains:

  • the place where the exception happened
  • the place from where the actor's method was called
  • the string representation of all parameters that were passed to the method
Fatal Exception: ActorMethodInvokeException: uncaught exception from your.application.package.name.Storage.getById(100)
       at your.application.package.name.StorageProxy.getById(SourceFile:105)
       at your.application.package.name.MainActivity.onResume(SourceFile:923) <-- the place from where the method was called
       at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1269)
       at android.app.Activity.performResume(Activity.java:6791)
       at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3477)
       at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3546)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2795)
       at android.app.ActivityThread.-wrap12(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527)
       at android.os.Handler.dispatchMessage(Handler.java:110)
       at android.os.Looper.loop(Looper.java:203)
       at android.app.ActivityThread.main(ActivityThread.java:6251)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1073)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:934)
Caused by java.lang.NullPointerException
       at your.application.package.name.StorageImpl.getById(SourceFile:353) <-- the place where the exception actually occured
       at your.application.package.name.StorageGetByIdMessage.invoke(SourceFile:30)
       at your.application.package.name.StorageGetByIdMessage.invoke(SourceFile:7)
       at com.truecaller.androidactors.ActorService$ActorHandler.handleTransaction(SourceFile:131)
       at com.truecaller.androidactors.ActorService$ActorHandler.handleMessage(SourceFile:118)
       at android.os.Handler.dispatchMessage(Handler.java:110)
       at android.os.Looper.loop(Looper.java:203)
       at android.os.HandlerThread.run(HandlerThread.java:61)

By default, all values from parameters will be logged.

You can control this behavior, for example to avoid sending sensitive data to crash reporting services. To do so, you need to decorate your parameters with the @SecureParameter annotation.

Here are the different options:

LICENSE

Copyright (C) 2017 True Software Scandinavia AB

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

<http://www.apache.org/licenses/LICENSE-2.0>

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].