a simple scalable app architecture with android
play

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


  1. A simple, scalable app architecture with Android annotations Luke Sleeman Freelance Android developer lukesleeman.com.au Image CC: https://flic.kr/p/6oqCZB

  2. Agenda • Introduction • The architecture - an overview • Services • Domain object • Testing • UI • Other issues • Closing thoughts

  3. Introduction - why we need this at all

  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!

  5. The architecture! User Interface Domain objects Services

  6. This is what it looks like

  7. So what about Android annotations? AndroidAnnotations User Interface Domain objects Services

  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 :-)

  9. Services User Interface Domain objects Services

  10. Typical methods List<Users> getUsersFromWebservice() � AndroidDatabaseResults searchForSite(String searchText) � UserInfo getInfoFromFacebook(int key)

  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; } }

  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); 
 } 
 }

  13. Domain objects User Interface Domain objects Services

  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) {

  15. A more complex domain object @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; 


  16. Domain objects Relationships

  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)

  18. Unit tests User Interface Domain objects Unit Tests Services

  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()); } }

  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()); 
 } }

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

  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); 
 } }

  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... }

  24. The UI User Interface Domain objects Services

  25. This is where android annotations really shines • @EActivity, @EFragment, @EViewGroup • @ViewByID @FragmentByID, etc • @DrawableRes @AnimationRes, @HtmlRes • @Click, @ListClick

  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); 
 } }

  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(); 
 }

  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); 
 … 
 } }

  29. There is a bug!

  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); 
 … 
 } }

  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(); 
 }

  32. Other issues

  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 objects - move logic from UI into them. 3. If you don’t have them start creating services - have them mange your domain objects

  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

  35. Closing thoughts

  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!

  37. Questions?

  38. Bonus slides

  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

Recommend


More recommend