a simple, scalable app architecture with android...

30
A simple, scalable app architecture with Android Annotations Luke Sleeman Freelance Android developer lukesleeman.com.au Image CC: https://flic.kr/p/6oqCZB Luke Sleeman - Freelance developer specialising in Android and today I’m going to be presenting A simple, scalable app architecture with Android annotations

Upload: duongque

Post on 29-Aug-2018

230 views

Category:

Documents


0 download

TRANSCRIPT

A simple, scalable app architecture

with Android Annotations

Luke Sleeman Freelance Android developer

lukesleeman.com.au

Image CC: https://flic.kr/p/6oqCZB

Luke Sleeman - Freelance developer specialising in Android and today I’m going to be presenting A simple, scalable app architecture with Android annotations

Agenda• Introduction

• The architecture - History, Goals, Overview

• Services

• Domain object

• Testing

• UI

• Closing thoughts

• I will be presenting an overview of the architecture, then we will be diving in and exploring various components of it in more detail, including:

• Service, the domain objects, testing and the UI • Very code heavy. Medium to advanced audience.

History

• The architecture I will be presenting today grew out of my needs as a freelancer

•I would come in to a job and I would either be doing new development or enhancing and existing app, and I needed a ‘go to’ application architecture

•The architecture I have been presenting has now been used for 3 years now - stood up very well.

•There has recently been a lot of talk about how to architect android applications. What I will be showing today holds up very well against other proposals.

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

• Boring!

•A good way to understand an architecture is to understand how what its trying to achieve….

•These are the main drivers for the architecture I will be presenting today

The architecture!

User Interface

Domain objects

Services

• Three tiers - surprising yeah! Its probably the most common architecture out there

• Well, the reason its so common is it works and everybody understands it • Briefly explain the 3 tiers

• Services contain services - these things talk to the DB, talk to webservice, load and save domain objects. Whenever the UI wants to get a list of domain objects, or save them, it talks to the service

• Domain objects - contains all of our business logic. This is where the java 101 class person lives, with getName and getAge

• UI - All our activities, fragments, views, adapters etc

This is what it looks like

• You could even break these things out into their own project if it was big enough • Conversely if you only have a 3 or 4 classes, just put them all in one place!

What is AndroidAnnotations?http://androidannotations.org/

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

• There are plenty of other similar libraries (retrofit, butter knife, dagger)

There are lots of annotations - This isn’t an introduction to aneroid annotations, we will only cover the ones of interest to our architecture

So what about Android annotations?

User Interface

Domain objects

Services

AndroidAnnotations

• Android annotations acts as a glue which ties it all together • Supports and strengthens each layer - so instead of reading inner classes,

callbacks and boilerplate, its easier to see the intention of the code

Services

User Interface

Domain objects

Services

• Can do many things: DB access, WS access, Saving and loading data, files, etc. Utility code

• Big or little - as small as a few static utility methods • You can have one or many services in your app • Always singletons, always stateless

Typical methods

List<Users> getUsersFromWebservice()

AndroidDatabaseResults searchForSite(String searchText)

UserInfo getInfoFromFacebook(int key)

• Here are some typical methods that you would expect to see in a service object

• Talking to web services • Querying the DB • Talking to 3rd party libraries and services - you can wrap up and

hide all the complexity

A Simple Servicepublic 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;

}

}

• Here is a very simple service for an app with no DB and no webservices, which displays pizzas.

• The service is responsible for building the pizzas and returning them

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

• Here is a more complex service built with AndroidAnnotations. You could do all this without AA, but you wont fit it on one slide!

• Part of a social networking application • Point out parts of code … • Note that the web service code is synchronous! We will discuss this more when we get to the

UI. • Also note that it throws exception. This is because the UI might want to do different things

based off different exceptions

Domain objects

User Interface

Domain objects

Services

• POJO’s . Sometimes called model objects. • Represent entities in the business domain - Person, Article, Order,

LineItem, Message • Hold onto the data while its passed around in the app • Contain business logic - any tricky business rules go here!

A simple domain objectpublic 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) {

• Here is a simple domain object. There really is nothing special about it and I may or may not have copied it from my 1st semester java notes :-)

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;

• Again, there is really nothing to special - we do have some annotations • Explain annotations

• Point out that those annotations are not part of AA - they are from OrmLite

Domain objects Relationships

• Your domain objects are probably going to have relationships - one to one, one to many, etc

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)

• Your domain objects are probably going to have relationships - one to one, one to many, etc • There are two places you can put them - in the domain objects and in the service • Where they go depends on how the objects will be used by the UI. Put too much in the objects and you

will have to pull down the whole tree. Put too much in the service and you will just have reams of loading code

• A good heuristic is to split the domain object tree where screens are split. If you have the addresses on the same screen as the people, then include in the objects. If they are different screens in the DB.

Unit tests

User Interface

Domain objects

Services

Unit Tests

• Test talk to domain objects and services. They act like a bit like the UI. They don’t test the UI

• I’ve found that UI tests don’t give good value for money. • If you UI contains complex logic and you feel you want to test it,

thats a good signal to move it into the domain. • Tests-first is good too.

Some basic testspublic 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());

}

}

• Here is two very simple unit tests - one which uses a static service and one which uses an Android Annotations EBean

• Just wanted to point out this is how you get a hold of the EBean if you are in the service

The UI

User Interface

Domain objects

Services

• The UI contains all the standard stuff you would expect - activities, adapters, views, etc

• Importantly it also handles the threading!

This is where android annotations really shines

• @EActivity, @EFragment, @EViewGroup

• @ViewByID @FragmentByID, etc

• @DrawableRes @AnimationRes, @HtmlRes

• @Click, @ListClick

• The UI is where android annotations really shines. And thats because the UI is the part of Android that is most fraught with boilerplate

• Explain what each of the annotations do

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

}

• Here is a simple example of a basic Activity which uses android annotations. Of course you can do all this without AA

• Explain each of the annotations

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

• Threading and keeping network activity off the UI thread is a huge problem in Android. Its not that we don’t have plenty of good solutions (async task, async-http) but they all require so much boilerplate!

• Here is an AndroidAnnotations solution that is much simpler • This is just the setup - a basic activativity. The magic happens in

downloadPizza

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

}

• Here is the actual magic … not that there is much there • Background thread runs … in the background • UI threads run on the UI thread • So here we are handling some fairly complex threading work in a simple

and concise way

There is a bug!

Some of you may have noticed a bug in the previous slide.

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

}

• What happens if the @Background thread takes a long time and the user leaves the activity

• @UIThread methods will still get run and app will crash

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

• Two options don’t update ui, or cancel • Doesn't matter which you choose, but be consistent!

Closing thoughts

Closing thoughts• Architecture is important!

• Architecture doesn’t have to be complex - Keep it Simple!

• You need to be constantly thinking - ‘What does this add’, ‘is it worth it?’

• Hope that what I have presented today provides some food for thought.

• We have seen a lot of discussion about architecture over the past few years - this is good!

• However some of the proposed architectures I have seen, seem hell bent on replicating the worse excesses of ‘enterprise java’.

• Remember the first of my goals was to have an architecture that is quick and simple

• Hope that what I have demoed today provides some food for thought, encourages you to look at your own lightweight architectures and try out android annotations

Questions?