A simple, scalable app architecture with Android annotations Luke - - PowerPoint PPT Presentation

a simple scalable app architecture with android
SMART_READER_LITE
LIVE PREVIEW

A simple, scalable app architecture with Android annotations Luke - - PowerPoint PPT Presentation

A simple, scalable app architecture with Android annotations Luke Sleeman Freelance Android developer lukesleeman.com.au Image CC: https://flic.kr/p/6oqCZB Agenda Introduction The architecture - an overview Services Domain


slide-1
SLIDE 1

A simple, scalable app architecture with Android annotations

Luke Sleeman Freelance Android developer lukesleeman.com.au Image CC: https://flic.kr/p/6oqCZB

slide-2
SLIDE 2

Agenda

  • Introduction
  • The architecture - an overview
  • Services
  • Domain object
  • Testing
  • UI
  • Other issues
  • Closing thoughts
slide-3
SLIDE 3

Introduction - why we need this at all

slide-4
SLIDE 4

Goals

  • Quick, simple
  • Typically data driven apps - get some stuff from a web service, store it

locally, show it to the user, submit stuff back to the web service

  • Scalable - Handle small apps and big ones
  • Unsurprising, easy to jump in and out of, clear and concise, makes

sense to anybody. ‘Obvious’.

  • Testable
  • Need to be able to incrementally evolve towards it from an existing

codebase

  • Boring!
slide-5
SLIDE 5

The architecture!

User Interface Domain objects Services

slide-6
SLIDE 6

This is what it looks like

slide-7
SLIDE 7

So what about Android annotations?

User Interface Domain objects Services

AndroidAnnotations

slide-8
SLIDE 8

What is AndroidAnnotations?

  • Annotate your code - works of JDK annotations
  • Code generation - you can look behind the

curtain to see how the magic works. MyActivity_

  • Includes annotation support for a lot of other

libraries I use such as ormlite

  • You don’t have to use it
  • But you would be silly not to :-)
slide-9
SLIDE 9

Services

User Interface Domain objects Services

slide-10
SLIDE 10

Typical methods

List<Users> getUsersFromWebservice()

  • AndroidDatabaseResults searchForSite(String searchText)
  • UserInfo getInfoFromFacebook(int key)
slide-11
SLIDE 11

A Simple Service

public class PizzaService { private static List<Pizza> pizzas; public static List<Pizza> getPizzas(){ if(pizzas == null){ pizzas = new ArrayList<Pizza>(); pizzas.add(new Pizza("Luke Special", 10.50)); pizzas.add(new Pizza("Margherita", 9.50)); } return pizzas; } }

slide-12
SLIDE 12

A more complex service

@EBean(scope = EBean.Scope.Singleton)
 public class FriendService { @OrmLiteDao(helper = ExampleDBHelper.class, model = Friend.class)
 protected RuntimeExceptionDao<Friend, String> friendsDao; @RestService
 protected FriendListWebservice webservice; public List<Friend> getFriends(){
 return friendsDao.queryForAll();
 } public void downloadAndSaveFriend(int id)throws IOException {
 Friend newFriend = webservice.getFriend(id);
 friendsDao.create(newFriend);
 }
 }

slide-13
SLIDE 13

Domain objects

User Interface Domain objects Services

slide-14
SLIDE 14

A simple domain object

public class Pizza {

  • private String name;

private double price;

  • public Pizza(String name, double price) {

this.name = name; this.price = price; }

  • public String getName() {

return name; }

  • public void setName(String name) {
slide-15
SLIDE 15

A more complex domain

  • bject

@DatabaseTable
 public class Friend {

  • @DatabaseField(generatedId = true)


private int id;

  • @DatabaseField


private String userName;

  • public int getId() {


return id;
 }

  • public void setId(int id) {


this.id = id;
 }

  • public String getUserName() {


return userName;


slide-16
SLIDE 16

Domain objects Relationships

slide-17
SLIDE 17

Domain objects Relationships

  • Option 1 - In the objects

@ForeignCollectionField(foreignFieldName = "user")
 private List<EmailAddress> addresses;

  • Option 2 - In the service

public List<EmailAddress> getFriendEmails(int id)

slide-18
SLIDE 18

Unit tests

User Interface Domain objects Services Unit Tests

slide-19
SLIDE 19

A basic test

public class PizzaTest extends AndroidTestCase{ public void testGetPizzas(){ List<Pizza> pizzaList = PizzaService.getPizzas(); assertNotNull(pizzaList); } }

  • public class FriendTest extends AndroidTestCase {

public void testFriend(){ FriendService friendService = FriendService_.getInstance_(getContext()); assertNotNull(friendService.getFriends()); } }

slide-20
SLIDE 20

A more advanced test

public class FriendTest extends AndroidTestCase { @Override
 protected void setUp() throws Exception {
 File database = getContext().getDatabasePath(ExampleDBHelper.DB_NAME);
 if (database.exists()) {
 database.delete();
 }
 } public void testSearchFriends(){
 FriendService service = FriendService_.getInstance_(getContext());
 service.createOrUpdateFriend(new Friend("Luke"));
 assertEquals(1, friendService.search("lu").size());
 } }

slide-21
SLIDE 21

A nifty trick

public void downloadAndPatchFriendList() 
 throws IOException{ String patchFile = downloadPatchFile();
 List<Friend> updatedFriends = parsePatch(patchFile);
 mergeFriendsWithDB(updatedFriends); }

slide-22
SLIDE 22

A nifty trick

@EBean(scope = EBean.Scope.Singleton)
 public class TestFriendService extends FriendService { @Override
 public void mergeFriendWithDB(List<Friend> updatedFriends){
 super.mergeFriendsWithDB(updatedFriends);
 } @Override
 public List<Friend> parsePatch(String patchFile) {
 return super.parsePatch(patchFile);
 } }

slide-23
SLIDE 23

A nifty trick

private TestFriendService testFriendService = 
 TestFriendService_.getInstance_(getContext());

  • public void testParseEmpty(){

List<Friend> friends = testFriendService.parsePatch(EMPTY_PATCH); assert... }

  • public void testParseModifyFriends(){

List<Friend> friends = testFriendService.parsePatch(MODIFY_PATCH); assert... }

slide-24
SLIDE 24

The UI

User Interface Domain objects Services

slide-25
SLIDE 25

This is where android annotations really shines

  • @EActivity, @EFragment, @EViewGroup
  • @ViewByID @FragmentByID, etc
  • @DrawableRes @AnimationRes, @HtmlRes
  • @Click, @ListClick
slide-26
SLIDE 26

Getting services and domain objects

@EActivity(R.layout.activity_best_friend)
 public class BestFriendActivity extends Activity { @ViewById(R.id.best_friend_text)
 protected TextView bestFriendText; @Bean
 protected FriendService friendService; private Friend bestFriend; @AfterViews
 protected void setupBestFriend(){
 bestFriend = friendService.getBestFriend();
 bestFriendText.setText(bestFriend.getName());
 } @Click(R.id.poke_friend_button)
 protected void poke(){
 friendService.poke(bestFriend);
 } }

slide-27
SLIDE 27

Threading

@EActivity(R.layout.activity_friend_list)
 public class FriendListActivity extends Activity { @ViewById(R.id.friend_list)
 protected ListView list; @ViewById(R.id.loading_progress)
 protected ProgressBar progressSpinner; @Bean
 protected FriendService service; @AfterViews
 protected void startDownload(){
 progressSpinner.setVisibility(View.VISIBLE);
 downloadFriends();
 }

slide-28
SLIDE 28

Threading

@Background
 protected void downloadFriends(){
 try{ List<Friends> friendList = service.downloadFriendList();
 displayFriends(friendList);
 }
 catch(IOException e){
 displayDownloadError();
 }
 } @UiThread
 protected void displayFriends(List<Friends> friendList){
 progressSpinner.setVisibility(View.GONE);
 …
 }

  • @UiThread


protected void displayDownloadError(){
 progressSpinner.setVisibility(View.GONE);
 …
 } }

slide-29
SLIDE 29

There is a bug!

slide-30
SLIDE 30

Threading

@Background
 protected void downloadFriends(){
 try{ List<Friends> friendList = service.downloadFriendList();
 displayFriends(friendList);
 }
 catch(IOException e){
 displayDownloadError();
 }
 } @UiThread
 protected void displayFriends(List<Friends> friendList){
 progressSpinner.setVisibility(View.GONE);
 …
 }

  • @UiThread


protected void displayDownloadError(){
 progressSpinner.setVisibility(View.GONE);
 …
 } }

slide-31
SLIDE 31

Threading - dealing with activity shutdown

  • Option 1 - Let our background task run and don’t update ui

@UiThread
 protected void displayFriends(List<Friend> friendList){
 if(isDestroyed()) return;
 …

Option 2 - Cancel background task
 


@Background(id = "download")
 protected void downloadFriends(){ …. }
 
 @Override
 protected void onDestroy() {
 BackgroundExecutor.cancelAll("download", false);
 super.onDestroy();
 }

slide-32
SLIDE 32

Other issues

slide-33
SLIDE 33

Evolving an existing apps architecture

  • 1. Add in android annotations and clean out

boilerplate

  • 2. If you don’t have them start creating domain
  • bjects - move logic from UI into them.
  • 3. If you don’t have them start creating services -

have them mange your domain objects

slide-34
SLIDE 34

Things to look at changing

  • Different libraries replace AA (dagger, retrofit,

butterknife)

  • EventBus or RXJava
  • Pinch ideas from the google io app.
  • Come up with a good way to manage fragments
slide-35
SLIDE 35

Closing thoughts

slide-36
SLIDE 36

A humble request

  • Architecture is important!
  • Don’t be satisfied with just the simple architecture

presented …

  • Think about architecture!
  • Talk about architecture!
  • Develop new architectures!
slide-37
SLIDE 37

Questions?

slide-38
SLIDE 38

Bonus slides

slide-39
SLIDE 39

When not to use this architecture

  • Games
  • If your building one app over the course of years
  • If you can develop a large internal library of

reusable components