3. Context
Where does this framework project comes from
● The droid4me framework project was started in
early 2008
○ for internal usage initially
○ tested against multiple applications
○ released open-source LGPL in 2010
● It is compatible with Android v1.6+ ; works with
Android v3+ and the Android Support Package
● It is a framework, but also exposes components
● Released as a stand-alone .jar library
● Composed of about 16k lines of code
● Used in several industrial Android applications
4. Getting started
If you want to give it a try...
1. Download the source code, compile it (via
Ant)
2. Read the documentation
3. Add the .jar to the classpath
4. Derive from the SmartApplication and
implement the methods
5. Derive from the Smartable Activities and
Fragment, and implement the methods
6. Discover and use the components
7. Have fun!
5. Overview
Project design principles
● It was designed to be as much extensible as possible
○ you do not necessarily need to derive from the provided Activity,
Fragment and Application classes
○ you can turn any Activity or Fragment "droid4me-ready" through
the Droid4mizer component
● It was designed to expose components as much as
possible independent
○ most of the components are stand-alone and may be used outside of
the framework context
○ but put all together in an application makes Android application
development much expressive, more reliable and faster
● Its ambition is to be industrialized and re-usable
○ the same framework should be re-used on multiple applications
7. Focus on...
Two interesting features
There are several facets of the framework,
involving numerous concepts, but we wanted to
focus on:
● The BitmapDownloader: a widget bitmap download
and binding independent module, which is worth to be
shown
● The ActivityController: a component tightly used
in the framework, which helps developing more robust
applications, and which attempts to provide Aspect
Oriented Programming (AOP)
8. BitmapDownloader
Overview
The BitmapDownloader is a reliable low-latency,
high performance component which downloads,
caches and binds bitmaps to Android widgets in an
extensible way
● uses two tunable worker threads pools
● works with cache memory water mark levels
● integrates easily to cache persistence
● optimizes the bitmap downloads
● multi-instances
● enables to pre-load bitmaps
● re-usable download and binding policies
● unitary tested
9. BitmapDownloader
Questions
● How does your current implementation behave when
the cache memory increases?
● What happens when the end-user insanely scrolls up
and down on a ListView holding ImageViews?
○ Is the same bitmap re-downloaded several times?
○ Does the widget display a previous bound bitmap?
○ Do the widget bitmaps flicker?
● What happens if the bitmap URL is wrong/null, or the
bitmap cannot be downloaded?
● Does your current implementation take the already
downloaded bitmap from a persistence cache? Does it
save the downloaded bitmaps to such cache?
10. BitmapDownloader
Design: the BitmapDownloader API
BitmapDownloader.getInstance(N).get(
View view,
String bitmapUid,
Object imageSpecs,
Handler handler,
Instructions instructions)
● N is the instance
● view is the widget (not necessarily an ImageView)
● bitmapUid is the UID which enables to compute the bitmap URL
● imageSpecs is a free (untyped) optional (may be null) placeholder
for providing additional context information, which will be provided
during the workflow
● handler is used to run some code on the UI
● instructions is the interface which exposes callbacks which will be
requested during the overall command workflow (pull strategy)
11. BitmapDownloader
Design: the Instructions interface (1/2)
The Instructions interface is responsible for answering about the download
and binding strategy. It is responsible for performing unitary tasks directed by
the BitmapDownloader, which passes the bitmapUid and imageSpecs to
most callbacks, and consume events. Here are some key callbacks:
interface Instructions
{
String computeUrl(String bitmapUid, Object imageSpecs);
boolean hasTemporaryBitmap(String bitmapUid, Object imageSpecs);
void onBindTemporaryBitmap(ViewClass view, String bitmapUid, Object imageSpecs);
InputStream getInputStream(String bitmapUid, Object imageSpecs, String url,
InputStreamDownloadInstructor instructor) throws IOException;
InputStream downloadInputStream(String bitmapUid, Object imageSpecs, String url)
throws IOException;
InputStream onInputStreamDownloaded(String bitmapUid, Object imageSpecs, String
url, InputStream inputStream);
void onBitmapReady(boolean allright, ViewClass view, BitmapClass bitmap, String
bitmapUid, Object imageSpecs);
}
12. BitmapDownloader
Design: the Instructions interface (2/2)
The Instructions will typically be requested (the list below is not comprehensive):
● for indicating if a temporary bitmap should be bound while the target bitmap is being loaded
● for computing the bitmap URL if it needs to be downloaded or take from a persistence cache
○ the logic of computing the URL is shifted in a central place
● for attempting to extract the requested bitmap from a persistence cache
○ you may use your own persistence library
● for downloading the bitmap bytes if necessary
○ you may fine-tune the time-outs, "User-Agent" HTTP header
● for storing the downloaded bitmap into the persistence cache if any
○ use your own persistence library, or the droid4me Persistence component
● for converting the downloaded bytes into a bitmap (which is done through the BitmapFactory
by default)
○ you may apply bitmap transformations (water marks, reflection effects ...)
● for binding the bitmap to the widget
○ you may apply animations
The same Instructions implementation object may be a singleton and re-
use throughout the application!
13. BitmapDownloader
Design: recap
For binding a bitmap corresponding to a URL with the
BitmapDownloader, you need to:
1. decide what instance to use
2. define an Instructions implementation that will be
requested at runtime
3. invoke the BitmapDownloader.get() method,
provide the widget, the bitmap UID, an optional context,
and the Instructions implementation
4. let the BitmapDownloader do the work and invoke the
provided Instructions by triggering finely tuned
methods/callbacks/events
14. BitmapDownloader
Architecture: how does it work inside?
Each BitmapDownloader.get() request generates a
command
1. this "pre" command is provided to a first LIFO worker threads pool,
responsible for:
○ checking if the bitmap should be extracted from the .apk
○ searching in the memory cache if the bitmap is available and binding it
○ binding a temporary bitmap to the widget if applicable
2. if the "pre" command execution did not complete with a final binding, a
"download" command is created and submitted to a second LIFO worker
threads pool, responsible for:
○ asking the bitmap from the persistence cache
○ downloading the bitmap bytes if necessary and provide it to the persistence cache
○ binding the downloaded bitmap to the widget
15. BitmapDownloader
Why it is efficient and low-latency
The BitmapDownloader ensures various contracts, which
makes it efficient and low-latency:
● the bytes of a bitmap are never downloaded twice, even between
BitmapDownloader instances
● a command is always executed in LIFO mode, and all pending
commands attached to the same widget are cancelled
● the memory cache is always requested first
● the memory consumption is monitored and its allocation limits are
respected for each instance independently: whenever a high water
mark level is reached, a clean-up process is run, and the most
frequently accessed bitmaps are kept in memory (this policy should
be customizable in the future)
● if a OutOfMemoryException may arise, it is caught and the
cache is cleaned-up
16. BitmapDownloader
Why it is extensible
The BitmapDownloader works with the Instructions
interface, which is by nature extensible:
● the persistence cache interaction is totally at your control. However,
you may use the droid4me Persistence component on that
purpose ;
● the bitmap bytes download process is totally customizable: you
may use an HttpClient, for instance. One of the provided
implementation uses the URLConnection
● you do not necessarily need to use an ImageView, but only View-
derived widgets
● you may use it to pre-load bitmaps through the
BitmapDownloader.get() alternate method form (with a null
widget argument), which controls the internal commands threading
17. BitmapDownloader
Fine tuning
The BitmapDownloader can be fine-tuned:
● set it before using it, typically in the Application.onCreate() method
decide how many instances to use (BitmapDownloader.INSTANCES_COUNT
variable)
● for each instance, decide the water mark levels (BitmapDownloader.
MAX_MEMORY_IN_BYTES and BitmapDownloader.
LOW_LEVEL_MEMORY_WATER_MARK_IN_BYTES variables)
● you can dynamically indicate if no Internet connection if available through
the BitmapDownloader.setConnected() method, which prevents
from battery and CPU waste
● for advanced tweaking, decide how many core threads to allocate in each
threads pool (BitmapDownloader.setPreThreadPoolSize() and
BitmapDownloader.setDownloadThreadPoolSize() methods)
18. ActivityController
Overview
The ActivityController is a central
component responsible for:
● handling Activity/Fragment exceptions
● redirecting an Activity instances like a
controller
● triggering Activity/Fragment instances
life cycle events
19. ActivityController
Questions
● How do you handle connectivity issue
problems in your code? Recurring
exceptions?
● How many times do you check that some
screen has actually been displayed before
another?
● How do you integrate the analytics SDK?
● How do provide objects to
Activity/Fragment instances which have
no common inheritance?
20. ActivityController
Design
● An Android Context-independent singleton available
through the ActivityController.getInstance()
method
● Enables to register:
○ an ExceptionHandler interface, responsible for
handling exceptions
○ a Redirector interface, responsible for ensuring
complex Activity workflows
an Interceptor interface, which exposes
callbacks triggered during
Activity/Fragment entities life cycle events
21. ActivityController
The ExceptionHandler API
● The ExceptionHandler interface exposes
onXXXException() callback methods, which will be
triggered when an exception occurs in the managed
part of the framework
○ All those methods take the originating Activity and the Fragment
(null if not applicable) as an argument
○ They obviously take the exception as an argument and return a
boolean which states whether the exception has actually been
handled
● The ActivityController exposes a
handleException() method which dispatches the
exception to the right onXXXException() callback
The SmartCommands worker threads pool invokes it when a
background issues an exception
22. ActivityController
The Redirector API
The Redirector interface exposes a single callback
method, and behaves like a controller:
Intent getRedirection(Activity activity)
● This method is invoked during the Activity.onCreate() execution via
the ActivityController.needsRedirection() method, which asks
the Redirector (if not null) whether another Activity should be
started instead
● The Redirector.getRedirection() method is invoked with the
currently started activity, and if it returns a non-null Intent, it is used
to start another Activity, which is passed the initial Activity Intent
● Once finished, the redirected Activity can resume the initial Activity
by invoking the ActivityController.extractCallingIntent()
This eases the writing of workflows, without having to code
the logic on each Activity. The concept is re-usable.
23. ActivityController
The Interceptor API
The Interceptor interface exposes a single callback
method:
void onLifeCycleEvent(Activity activity, Object component,
ActivityController.Interceptor.InterceptorEvent event)
● This method is invoked at every life cycle key event of an Activity /
Fragment entity, with the hosting activity, the Fragment component
(null if the event belongs to an Activity), and the event (an
enumerated type) as arguments
● This method is invoked from the ActivityController.
onLifeCycleEvent() method, which delegates the execution to the
Interceptor if not null
This component enables to handle Activity/Fragment
entity events is a very centralized way, a bit like in AOP
24. ActivityController
Initialization
The framework offers a SmartApplication abstract
class inherited from the Android legacy Application
class, which offers to register in one point the
ActivityController aggregate.
● Either inherit from this SmartApplication and implement/override:
○ getExceptionHandler(): registers the ExceptionHandler ; a
DefaultExceptionHandler is already returned by default
○ getActivityRedirector(): registers the Redirector
○ getActivityInterceptor(): registers the Interceptor
● Or register those ActivityController components yourself before any
Activity is displayed!
25. ActivityController
Recap and use cases
The ActivityController is a central component which offers some key features
like exception handling, screen workflows management and life cycle events in
a very centralized way through 3 interfaces:
● the ExceptionHandler which lets you react to exceptions in an industrialize way.
Typical use cases:
=> Alert dialog boxes, toasts, logging of errors through analytics or error
reporting libraries like ACRA
● the Redirector which lets you handle Activity workflows. Typical use cases:
=> splash screens, authentication / registration forms
● the Interceptor which lets you react to life cycle events. Typical use cases:
=> integrating analytics
=> transmit, extract data (through the Smarted.get/setAggregate() API)
without imposing inheritance
=> invoke Activity/Fragment methods in a transversal way
26. Roadmap
Evolution thoughts
● More documentation
● Define a better project governance
● Integrate the library to a software factory
● Propose optionally to split the framework into separate
libraries
○ a core.jar
○ a .jar for each major component
● Provide more opening entry points
● Clean up some contracts
● Provide a getting started / demonstrator application
● Use a visitor pattern to the ActivityController
Redirector and Interceptor components
● Review the MenuHandler component