Build an App with Blindfold - Britt Barak

Preview:

Citation preview

Build An App With Blindfold

Britt BarakDroidcon TLV

26.9.16

Upcoming Events

● Android Beginners - coming on 30/10 !

● Android UI / UX

● Community Hackathon

● Android Performance

● Mentors Program

My First Startup

“As You Like It” / W. Shakespeare

,All the world’s a stage״

And all the men and women merely players;

They have their exits and their entrances,

And one man in his time plays many parts:״

His acts being seven ages. At first, the

infant,

Mewling and puking in the nurse’s arms.

Then the whining schoolboy, with his

satchel

And shining morning face, creeping like

snail

Unwillingly to school. And then the lover,

Sighing like furnace, with a woeful ballad

Made to his mistress’ eyebrow. Then a

soldier,

Full of strange oaths and bearded like the

pard,

Jealous in honor, sudden and quick in

quarrel,

Seeking the bubble reputation

Even in the cannon’s mouth. And then the

justice,

In fair round belly with good capon lined,

With eyes severe and beard of formal cut,

Full of wise saws and modern instances;

And so he plays his part. The sixth age

shifts

Into the lean and slippered pantaloon,

With spectacles on nose and pouch on side;

His youthful hose, well saved, a world too

wide

For his shrunk shank, and his big manly

voice,

Turning again toward childish treble, pipes

And whistles in his sound. Last scene of

all,

That ends this strange eventful history,

Is second childishness and mere oblivion,

Sans teeth, sans eyes, sans taste, sans

everything.

“exits and entrances”

The part can change

But there’s always a defined part.

File → New Project

New Startups

● Uncertainty● Many changes● Low on resources

○ Time○ Developers○ Money

We Want To Deliver Fast!

Premature optimizations are bad

1. Mess Grows2. Structure Stays

Recipe

1. Big picture - who are the “players”?

2. Small use cases - what are the “parts”?

3. Choose POCs - order “exits and entrances”

High Level Structure

Presentation Layer Data LayerDomain Layer

High Level Structure

Presentation Layer

● View● ViewEntity● Presenter

→ Framework

Data Layer

● Repository● Entity

→ Java

Domain Layer

● Interactor● Use case

→ Java

Example : Add Chat Feature To App

Big Picture - The Players

● Add chat feature to the app● Chat between groups of members

Separation

App Module

Chat Module

Why New Module?

- Encapsulate implementation- Easy to replace- Easy to reuse

Structure:

Presentation Layer - App Module

Data Layer - Chat Module

let‘s talk about the data structure.

Firebase DB

- Easy. - noSQL- Flat- Get data by reference

- ref/messages/chat_one_id

Don’t - Nest{

"chats": {

"chatOneId": {

"title": "As You Like It",

"messages": {

"msg1Id": { "sender": "melancholyJaques", "message": "All world’s a stage." },

"msg2Id": { ... },

// a very long list of messages

}

},

"chatTwoId": { ... } }}

Do - Flat : Chats{

"chats": {

"chatOneId": {

"title": "As You Like It"

},

"chatTwoId": { ... },

"chatThreeId": { ... }

}

Do - Flat : Members{

"members": {

"chatOneId": {

"melancholyJaques": true,

"dukeSenior": true,

},

"chatTwoId": { ... },

"chatThreeId": { ... }

},

Do - Flat : Users{

"users": {

"melancholyJaques": {

"name": Melancholy Jaques",

"photoId": "23h42",

"chats": {

"chatOneId": true,

"chatTwoId": true

}

},

...

},

Do - Flat : Messages "messages": {

"chatOneId": {

"msg1Id": {

"sender": "melancholyJaques",

"message": "All world’s a stage.",

"timestamp": 1459361875337

},

"msg2Id": { ... },

// a very long list of messages

},

"chatTwoId": { ... },

}

}

On my code

Chat module: individual separate pieces:

- Chats Repository- Members Repository- Users Repository- Messages Repository

Repository (Data Layer)

Part - provide and update data.

Encapsulates implementation.

public class UsersRepo implements IUsersRepository {

MyFirebaseDBClient firebaseClient;

@Override public void getChatIDs(Callback callback) { firebaseClient.fetchChats(callback); }

public class UsersRepo implements IUsersRepository {

MyFirebaseDBClient firebaseClient; MyCache cache;

@Override public void getChatIDs(Callback callback) { if (cache.hasChats()) { callback.success(cache.getChatIDs()); } else { firebaseClient.fetchChatIDs(callback); } }

public class UsersRepo implements IUsersRepository {

MyFirebaseDBClient firebaseClient; MyCache cache; ChatsLocalDB localDB;

@Override public void getChatIDs(Callback callback) { if (cache.hasChatIDs()) { callback.success(cache.getChatIDs()); } else if (localDB.hasChatIDs()) { callback.success(localDB.getChatIDs()); } else { firebaseClient.fetchChatIDs(callback); } }

public class UsersRepo implements IUsersRepository {

MyApiClient apiClient; MyCache cache; ChatsLocalDB localDB;

@Override public void getChatIDs(Callback callback) { if (cache.hasChatIDs()) { callback.success(cache.getChatIDs()); } else if (localDB.hasChatIDs()) { callback.success(localDB.getChatIDs()); } else { apiClient.fetchChatIDs(callback); } }

Recipe

1. Big picture - who are the “players”?

2. Small use cases - what are the “parts”?

3. Choose POCs - order “exits and entrances”

Domain layer - Use Cases

Use cases / interactors

What does the app do? What are its use cases?

Example: getChatsPreviewList (use case)

Get user’s chat IDs

Get chat’s info

Chats Repo

Get chats preview list

Chat list activity

Users Repo

Get Chat Preview List

Get chats preview list

List<Chat>List<String>

ChatRaw

Get Chat Preview List

List<ChatRaw>

Get Chat Preview Listpublic class GetChatPreviewListImpl implements GetChatsPreviewList {

public void execute(final Callback<List<Chat>> callback) {

usersRepo.getChatIDs(new Callback<List<String>>() {

@Override

public void success(List<String> chatIDs) {

chatsRepo.getChats(new Callback<List<RawChat>>() {

@Override

public void success(List<RawChat> rawChats) {

List<Chat> chats = proccessChats(rawChats);

callback.success(chats);

}

});

}

});

}

Get chats preview list

Get chat members

Get chat messags ….

App Module Will Ask For More

Chat list activity

Recipe

1. Big picture - who are the “players”?

2. Small use cases - what are the “parts”?

3. Choose POCs - order “exits and entrances”

Choose POC

- Play the App Module Part:

- Forget the chat implementation!

- ChatClient - POC class

Chat Client

Get chats preview list

Get chat members

Get chat messags ….

Choose POC

App Module

Get Chat Preview List

Chat list activity

Chat Client

Users Repo

Chats Repo

userId chatIds

Demand

On chats preview list:

show last message

Get Chat Preview List 2

Too messy. Too many listeners and threads. Too much time.

Chat Client

Users Repo

Chat list activity

Chats Repo

userId chatIds

Msgs Repo

Get chats preview list

Get Chat Preview List 3

Add data straight to chat Info node"chats": {

"chatOneId": {

"title": "As You Like It"

"lastMessage": "All world’s a stage" },

"chatTwoId": { ... },

"chatThreeId": { ... }

},

Get Chat Preview List 3

Too messy. Too many listeners and threads. Too much time.

Chat Client

Users Repo

Chat list activity

Chats Repo

userId chatIdsGet chats preview list

But now: more work when sending a message

Send : Before

Chat activity

Chat Client

Msgs RepoSend

Send : After

Chat activity

Chat Client

Msgs RepoSend

Chats Repo

Demand

Message visibility per user.

“Last message”

isn’t general anymore.

Structure - Message

"message1": {

...

"visibleTo": {

"userId1": true,

"userId2": true

},

}

Structure - Personal Chat Info

Chats

General Info

Personal Info

title Last msg

Personal Chat Info Node{

"personalInfo": {

"melancholyJaques": {

"lastMessage": All world’s a stage",

"timestamp": 1459361875337

},

...

},

Get Chat Preview List 4

Too messy. Too many listeners and threads. Too much time.

Chat Client

Users Repo

Chat list activity

userId chatIdsGet chats preview list

personal Info Repo

general Info Repo

Send : Update Per User

App Module

Chat Client

Msgs Repo

userId chatIds

personal Info Repo

Send

Is visible?

But I have many recipients….

Takes long to update each user repo...

Send :

Send

Message Repo

FCM

Receive :

Send Receive

Personal Info Repo

Message Repo

FCM

Give the part to a differenet Player

Sum Up

- Small defined use cases:- Easy to maintain- Easy to change- Easy to test- Mix and match

- “Play the part” → create POC class- Encapsulate implementation- Organize code

- Be mindful to what needs a change

Example changing the part

FCM Usages

- Notify user- Update DB- Update UI- Fetch new data- Start service- ……...

How Does It Work?public class MyFCMService extends FirebaseMessagingService {

@Override

public void onMessageReceived(RemoteMessage remoteMessage) {

//...

}

}

Remote Message Part

Notify about an event.

FCM Handling

Type per message

RemoteMessage

Data….< “type” , “new_chat_message” >….

FCM Handlingpublic class MyFCMService extends FirebaseMessagingService {

@Override

public void onMessageReceived(RemoteMessage remoteMessage) {

String type = data.get(StaticKeys.KEY_TYPE);

switch (type) {

//....

}

}

}

It Worked Great...

We added up more and more events…..

- New chat message- New member added- Member removed- New data available- ………………..

…for some time :-/

- Too many event types

- Too complex code

- Hard to change

- Hard to combine

- Sometimes one usecase represented multiple event types and forced few fcm messages

- The ugly switch-case...

Demand

- Easily support new types- Change remotely

Wait….

Take a step back.

What did we mean to do?

1. Notifying users2. Update data / ui3. Update DB

Remote Message Part

Notify an event.

Notify about stuff that need to be done.

Handle By Data Attributes

RemoteMessage

Data….<“notify” , “true” ><“title” , “New Message” ><“icon” , “ic_chat” ><“click_action”,

“com.figure8.app.action.OPEN_CHAT”><“refresh_members”, chat_id>….

Handle By Data Attributes

Messaging Service

Notif Producer

Data Refresher

DB Updater

Remote Message

Remote Message

Pros

- Flexible

- Update remotely

- Test:

- Test the data producer

- Test the data handler

What Did We Do?

Gave the component a different part.

“exits and entrances”

The part can change

But there’s always a defined part.

Questions?

Thank you :)