Android Concurrency
From GT RoboJackets
TODO:
cleanup
aliases for Techniques
add AsyncTask
Contents |
Overview
Scheduling
make sure important things have enough time to execute
Runnable which execute continuously vs ones which need to be executed repeatedly
timeouts
- behavior for handling
- maybe leave this up to the Runnable
ScheduledThreadPoolExecutor
makes the Runnable start (doesn't control exact execution times, that is handled by the hardware) after every period. If the thread is still running, it waits until it finishes and then immediately starts it.
Communication
There are numerous ways to communicate between concurrently running code in Android. You can use all of the standard Java functionality along with several Android-specific methods. Unfortunately much of the documentation for Android can be very confusing, and it is hard to know when and where each approach is appropriate to use. This section attempts to explain the differences in each method of communication.
Summary
table
Intra-Process vs Inter-Process
Data that: needs to be acted upon immediately: Intents doesn't change often and can wait around: Messages changes often and only the latest value matters: Shared? changes often and all (new?) values matters: Shared Buffer?
best way to used shared variables • want it to be part of the class
Maybe our solution is we should use a mixture of these methods:
For data that is being used by many different threads or is updated and read frequently (like the current position), I would suggest that we either use Intents(2) or a custom data type that doesn't have to be synchronized, maybe using atomic writes. Now that I think about it, Atomic writes are probably the best idea.
For data that needs to be acted on sequentially or buffered (takes a long time to process), we should use Messages(3). Using messages for things that change really fast (ex. current position) would be a bad idea because we would end up overflowing our queue and maybe losing some important messages -- and like I said, there can only be one Message queue per thread.
Personally, I would rather use native Java functionality over Android stuff, mainly because I think the Android stuff is really hard to understand. However, I would probably rather use Android features than have to import other people's libraries(4) into our project.
Other Notes:
- maybe use shared variables when multiple people need old (buffered) values -- (e.g. position data)
- - might also want to send intents for people who want updates (don't want to poll)
- if use Intents, we can supply our own buffers
- - that way we can also have more control over scheduling
- need to test Intent efficiency
- is there a way to pull multiple messages off of message queue
- - if not, we may end up having to use our own buffers anyway to prevent overflow and allow the filters to act on multiple pieces of new data at once.
Massive synchronization
Data:
- We can make everything that needs to be shared a shared variable.
Read/Writing:
- Shared variables must be either synchronized or use atomic reads and writes. Variables can be synchronized by the code which is reading/writing to the variable (using the "synchronized" keyword) or directly through the variable's class.
Synchronization has a few pitfalls. In our case, since we have classes which are circularly accessing each other's data (controls-positioning), I would be most worried about deadlocks. Also, most of our shared variables will be changing very fast, so there might be a lot of overhead if each thread has to wait every time they access it.
atomic writes
That being said, I think that synchronization would be okay for most of the things we have to communicate between threads, especially for things that aren't being updated all that fast (waypoint position, for example).
Pros:
- easy to understand
Cons:
- requires a lot of extra planning and care
- if we aren't careful, it will be inefficient or randomly die
Android Intents
Data:
- Intents contain an name (string) and extras, each of which has a name (string) and data (floats, ints, Bundle, etc.).
Sending:
- Intents can be broadcasted either to everyone who is listening or to a specific class. When sent directly to a class, the receiver can send a return value. When broadcasted, they can be sent normally (asynchronous, most efficient, no return values) or ordered (each receiver called in order, each receiver can propagate data to the next receiver or back to the sender).
Receiving:
- Intents can be received by registering a callback or by implementing the callbacks built into Activity or Service.
Pros:
- very efficient (as far as I can tell)
- several different ways to send messages
Cons:
- may mess up our attempts at scheduling (threads get interrupted directly)
- no built-in queue/buffering
- somewhat difficult to understand
Android Message Passing
Data:
- A Message has several ways to store data: a Bundle, an Object, and a couple of ints. It also has a place to store a reference to the sender (so the receiver can send a reply).
Sending: Messages are pushed onto the receiver's message queue asynchronously.
Receiving:
- Received messages are pulled off (polled) of their message queue and processed.
Pros:
- built-in queue
- gives us complete control over scheduling (we can tell each thread exactly when to run)
Cons:
- no built-in broadcasts (must know who you are sending messages to)
- only one sender/receiver/queue per thread
- slightly less efficient
- somewhat difficult to understand
Outside libraries
We can also use any number of other Java libraries to do this.
Pros:
- more choice
Cons:
- more annoying to compile
- have to learn how to use that library