DroidConUk 2016 [Barcamp]
In this conference, I want to talk about treatments.
By treatments, I mean where and how do you implement your business logic, the way your application handles data, your global algorithms,
And to do that, I need first to talk you about Architecture. But in Android, when you say "Architecture" every boby answers MVP / MVVM / ... so we will first have a look to those patterns, from an history point of view then we will discover that we have missed some layer to split concerns accross the application.
So we will talk about layer architecture (yep, also known as N-Tier architecture).
So, we have a better idea of what a generic architecture should be, now let's apply it on Android: Let's talk about the application object, and the service services layer, in particular we'll dive into the problem "do our business services have to be implemented extending Service ?".
Then I talk to you about a ServiceManager, that we have to implement, to centralize the management of all your services and all your threads pools.
And some much more :)
Enjoy the talk,
5. A piece of History
public class HistoryBattleActivity extends AppCompatActivity {
private static final String TAG = "HistoryBattleActivity";
private static final int CONTEXT_CURRENT=110274;
private static final int CONTEXT_HISTORY=131274;
/***********************************************************
* Attributes
**********************************************************/
BattleFragment battleFragment;
/**
* Current context History/current
*/
int currentContext=CONTEXT_CURRENT;
/***********************************************************
* Managing RoundTrip animation (VectorDrawable1 to VectorDrawable 2 and back again
**********************************************************
/**
* The LevelList that contains only two AnimatedVectorDrawable,
* the ones used to go from on to the other
*/
LevelListDrawable backupRoundTrip;
/**
* The current AnimatedVector diaplsyed by the RoundTrip
*/
AnimatedVectorDrawable contextDrawable;
/**
* To know is the animation have been already launched
*/
boolean backupRoundTripFirstLaunched=true;
/**
* Historical battles
*/
ArrayList<Long> battlesId=null;
/***********************************************************
* Attributes for the ViewPager
**********************************************************/
/**
* The TabLayout itself :)
*/
TabLayout tabLayout;
/**
* The page Adapter : Manage the list of views (in fact here, it's fragments)
* And send them to the ViewPager
*/
private MyPagerAdapter pagerAdapter;
/**
* The ViewPager is a ViewGroup that manage the swipe from left to right to left
* Like a listView with a gesture listener...
*/
private ViewPager viewPager;
/***********************************************************
* Managing the Life cycle
**********************************************************/
**********************************************************/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate() called");
setContentView(R.layout.activity_history);
//find the Toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
//use it as your action bar
setSupportActionBar(toolbar);
getSupportActionBar().setSubtitle(getString(R.string.history_fragment_subtitle));
getSupportActionBar().setTitle(getString(R.string.history_fragment_title));
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
//Define its gravity and its mode
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
//Define the color to use (depending on the state a different color should be disaplyed)
//Works only if done before adding tabs
tabLayout.setTabTextColors(getResources().getColorStateList(R.color.tab_selector_color));
//instanciate the PageAdapter
pagerAdapter=new MyPagerAdapter(this,true);
//Find the viewPager
viewPager = (ViewPager) super.findViewById(R.id.viewpager);
// Affectation de l'adapter au ViewPager
viewPager.setAdapter(pagerAdapter);
viewPager.setClipToPadding(true);
//Add animation when the page are swiped
//this instanciation only works with honeyComb and more
//if you want it all version use AnimatorProxy of the nineoldAndroid lib
//@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer
//TODO uncomment those lines and the opengl bug disappears
// if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){
// viewPager.setPageTransformer(true, new MyPageTransformer());
// }
//AND CLUE TABLAYOUT AND VIEWPAGER
tabLayout.setupWithViewPager(viewPager);
}
@Override
protected void onStart() {
super.onStart();
//track entrance
Log.e(TAG, "onStart() has been called");
EventBus.getDefault().register(this);
}
@Override
protected void onResume() {
super.onResume();
//track entrance
Log.e(TAG, "onResume() has been called");
}
} @Override
protected void onStop() {
super.onStop();
//track entrance
Log.e(TAG, "onStop() has been called");
EventBus.getDefault().unregister(this);
}
@Override
public void onBackPressed() {
if(((MyApplication)getApplication()).isCigaretPanelOpen){
//do nothing the fragment will just change its state
battleFragment.onBack();
}else{
super.onBackPressed();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)//EventBus
public void updateUI(FullUpdateEvent event) {
//TODO update properly
Log.e(TAG, "fullUpdate() called with: " + "notUsed = [" + event + "]");
//rebuild every thing:$
pagerAdapter.notifyRebuildAll();
if(event.isSwitchActivity()){
switchContext();
}else{
tabLayout.setupWithViewPager(viewPager);
}
}
/***********************************************************
* Managing menu
**********************************************************/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater=getMenuInflater();
inflater.inflate(R.menu.history_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.menu_switch_context:
switchContext();
break;
}
return super.onOptionsItemSelected(item);
}
/***********************************************************
* Managing backup button round trip
**********************************************************/
/**
* Switch context from history to current (and vis versa)
* Launch the animation on the currentAnimatedVectorDrawable
*/
private void switchContext(){
Intent startNewContext=new Intent(this, CurrentBattleActivity.class);
startNewContext.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(startNewContext);
}
}
When all began, we had the God Class
We need a small
evolution!
We want to test it…
Yep… We did huge shit
6. A piece of history
public class HistoryBattleActivity extends AppCompatActivity {
private static final String TAG = "HistoryBattleActivity";
private static final int CONTEXT_CURRENT=110274;
private static final int CONTEXT_HISTORY=131274;
/***********************************************************
* Attributes
**********************************************************/
BattleFragment battleFragment;
/**
* Current context History/current
*/
int currentContext=CONTEXT_CURRENT;
/***********************************************************
* Managing RoundTrip animation (VectorDrawable1 to VectorDrawable 2 and back again
**********************************************************
/**
* The LevelList that contains only two AnimatedVectorDrawable,
* the ones used to go from on to the other
*/
LevelListDrawable backupRoundTrip;
/**
* The current AnimatedVector diaplsyed by the RoundTrip
*/
AnimatedVectorDrawable contextDrawable;
/**
* To know is the animation have been already launched
*/
boolean backupRoundTripFirstLaunched=true;
/**
* Historical battles
*/
ArrayList<Long> battlesId=null;
/***********************************************************
* Attributes for the ViewPager
**********************************************************/
/**
* The TabLayout itself :)
*/
TabLayout tabLayout;
/**
* The page Adapter : Manage the list of views (in fact here, it's fragments)
* And send them to the ViewPager
*/
private MyPagerAdapter pagerAdapter;
/**
* The ViewPager is a ViewGroup that manage the swipe from left to right to left
* Like a listView with a gesture listener...
*/
private ViewPager viewPager;
/***********************************************************
* Managing the Life cycle
**********************************************************/
**********************************************************/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate() called");
setContentView(R.layout.activity_history);
//find the Toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
//use it as your action bar
setSupportActionBar(toolbar);
getSupportActionBar().setSubtitle(getString(R.string.history_fragment_subtitle));
getSupportActionBar().setTitle(getString(R.string.history_fragment_title));
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
//Define its gravity and its mode
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
//Define the color to use (depending on the state a different color should be disaplyed)
//Works only if done before adding tabs
tabLayout.setTabTextColors(getResources().getColorStateList(R.color.tab_selector_color));
//instanciate the PageAdapter
pagerAdapter=new MyPagerAdapter(this,true);
//Find the viewPager
viewPager = (ViewPager) super.findViewById(R.id.viewpager);
// Affectation de l'adapter au ViewPager
viewPager.setAdapter(pagerAdapter);
viewPager.setClipToPadding(true);
//Add animation when the page are swiped
//this instanciation only works with honeyComb and more
//if you want it all version use AnimatorProxy of the nineoldAndroid lib
//@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer
//TODO uncomment those lines and the opengl bug disappears
// if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){
// viewPager.setPageTransformer(true, new MyPageTransformer());
// }
//AND CLUE TABLAYOUT AND VIEWPAGER
tabLayout.setupWithViewPager(viewPager);
}
@Override
protected void onStart() {
super.onStart();
//track entrance
Log.e(TAG, "onStart() has been called");
EventBus.getDefault().register(this);
}
@Override
protected void onResume() {
super.onResume();
//track entrance
Log.e(TAG, "onResume() has been called");
}
} @Override
protected void onStop() {
super.onStop();
//track entrance
Log.e(TAG, "onStop() has been called");
EventBus.getDefault().unregister(this);
}
@Override
public void onBackPressed() {
if(((MyApplication)getApplication()).isCigaretPanelOpen){
//do nothing the fragment will just change its state
battleFragment.onBack();
}else{
super.onBackPressed();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)//EventBus
public void updateUI(FullUpdateEvent event) {
//TODO update properly
Log.e(TAG, "fullUpdate() called with: " + "notUsed = [" + event + "]");
//rebuild every thing:$
pagerAdapter.notifyRebuildAll();
if(event.isSwitchActivity()){
switchContext();
}else{
tabLayout.setupWithViewPager(viewPager);
}
}
/***********************************************************
* Managing menu
**********************************************************/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater=getMenuInflater();
inflater.inflate(R.menu.history_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.menu_switch_context:
switchContext();
break;
}
return super.onOptionsItemSelected(item);
}
/***********************************************************
* Managing backup button round trip
**********************************************************/
/**
* Switch context from history to current (and vis versa)
* Launch the animation on the currentAnimatedVectorDrawable
*/
private void switchContext(){
Intent startNewContext=new Intent(this, CurrentBattleActivity.class);
startNewContext.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(startNewContext);
}
}
We gonna split the view and the rest of the
application
7. public class HistoryBattleActivity extends AppCompatActivity {
private static final String TAG = "HistoryBattleActivity";
private static final int CONTEXT_CURRENT=110274;
private static final int CONTEXT_HISTORY=131274;
/***********************************************************
* Attributes
**********************************************************/
BattleFragment battleFragment;
/**
* Current context History/current
*/
int currentContext=CONTEXT_CURRENT;
/***********************************************************
* Managing RoundTrip animation (VectorDrawable1 to VectorDrawable 2 and back again
**********************************************************
/**
* The LevelList that contains only two AnimatedVectorDrawable,
* the ones used to go from on to the other
*/
LevelListDrawable backupRoundTrip;
/**
* The current AnimatedVector diaplsyed by the RoundTrip
*/
AnimatedVectorDrawable contextDrawable;
/**
* To know is the animation have been already launched
*/
boolean backupRoundTripFirstLaunched=true;
/**
* Historical battles
*/
ArrayList<Long> battlesId=null;
/***********************************************************
* Attributes for the ViewPager
**********************************************************/
/**
* The TabLayout itself :)
*/
TabLayout tabLayout;
/**
* The page Adapter : Manage the list of views (in fact here, it's fragments)
* And send them to the ViewPager
*/
private MyPagerAdapter pagerAdapter;
/**
* The ViewPager is a ViewGroup that manage the swipe from left to right to left
* Like a listView with a gesture listener...
*/
private ViewPager viewPager;
/***********************************************************
* Managing the Life cycle
**********************************************************/
**********************************************************/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate() called");
setContentView(R.layout.activity_history);
//find the Toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
//use it as your action bar
setSupportActionBar(toolbar);
getSupportActionBar().setSubtitle(getString(R.string.history_fragment_subtitle));
getSupportActionBar().setTitle(getString(R.string.history_fragment_title));
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
//Define its gravity and its mode
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
//Define the color to use (depending on the state a different color should be disaplyed)
//Works only if done before adding tabs
tabLayout.setTabTextColors(getResources().getColorStateList(R.color.tab_selector_color));
//instanciate the PageAdapter
pagerAdapter=new MyPagerAdapter(this,true);
//Find the viewPager
viewPager = (ViewPager) super.findViewById(R.id.viewpager);
// Affectation de l'adapter au ViewPager
viewPager.setAdapter(pagerAdapter);
viewPager.setClipToPadding(true);
//Add animation when the page are swiped
//this instanciation only works with honeyComb and more
//if you want it all version use AnimatorProxy of the nineoldAndroid lib
//@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer
//TODO uncomment those lines and the opengl bug disappears
// if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){
// viewPager.setPageTransformer(true, new MyPageTransformer());
// }
//AND CLUE TABLAYOUT AND VIEWPAGER
tabLayout.setupWithViewPager(viewPager);
}
@Override
protected void onStart() {
super.onStart();
//track entrance
Log.e(TAG, "onStart() has been called");
EventBus.getDefault().register(this);
}
@Override
protected void onResume() {
super.onResume();
//track entrance
Log.e(TAG, "onResume() has been called");
}
} @Override
protected void onStop() {
super.onStop();
//track entrance
Log.e(TAG, "onStop() has been called");
EventBus.getDefault().unregister(this);
}
@Override
public void onBackPressed() {
if(((MyApplication)getApplication()).isCigaretPanelOpen){
//do nothing the fragment will just change its state
battleFragment.onBack();
}else{
super.onBackPressed();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)//EventBus
public void updateUI(FullUpdateEvent event) {
//TODO update properly
Log.e(TAG, "fullUpdate() called with: " + "notUsed = [" + event + "]");
//rebuild every thing:$
pagerAdapter.notifyRebuildAll();
if(event.isSwitchActivity()){
switchContext();
}else{
tabLayout.setupWithViewPager(viewPager);
}
}
/***********************************************************
* Managing menu
**********************************************************/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater=getMenuInflater();
inflater.inflate(R.menu.history_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.menu_switch_context:
switchContext();
break;
}
return super.onOptionsItemSelected(item);
}
/***********************************************************
* Managing backup button round trip
**********************************************************/
/**
* Switch context from history to current (and vis versa)
* Launch the animation on the currentAnimatedVectorDrawable
*/
private void switchContext(){
Intent startNewContext=new Intent(this, CurrentBattleActivity.class);
startNewContext.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(startNewContext);
}
}
We gonna split the view and the rest of the
application
Model
View Controller
Modifies
Prevents
Updates
The MVC model was born!
A piece of history
8. Then came the M(VC) model (a Swing’s pattern)
Model
View&Controller
ModifiesUpdates
View Controller
A piece of history
10. The Swing M(VC) model
Manages the data
displayed
Model
Displays the data and
Interacts with the user
View&Controller
1
1
A piece of history
11. And the MVP was created
Manages the data
displayed
Model
Displays the data and
Interacts with the user
Vue&Controller
Presenter
Data management
Business Logic
And Treatments
Model
View
A piece of history
12. And the MVP was created
Manages the data
displayed
Presenter
Displays the data and
Interacts with the user
View
Data management
Business Logic
And Treatments
Model
1
1
ModifiesUpdates
Talks
A piece of history
13. The programmation by contract was born
(Interfaces)
Manages the data
displayed
Presenter
Displays the data and
Interacts with the user
View
Data management
Business Logic
And Treatments
Model
1
0
PresenterIntf
ViewIntf
0
1
A piece of history
14. For the TESTS !
Model
Presenter
View
PresenterIntf
ViewIntf
MockPresenter
MockView
A piece of history
15. Flavor=TestPresenter
And with Gradle, happiness knocks on your door.
Flavor=TestVueFlavor=PROD
Model
Presenter
View
0
1
MockPresenter
MockViewView
Presenter
0
1
0
1
MockModel
A piece of history
16. Then came MVVM
Manages the data
displayed
Presenter
Displays the data and
Interacts with the user
View
Data management
Business Logic
And Treatments
Model
1
1
ModifiesUpdates
Talks
ViewModel
1
n
A piece of history
19. Architecture
The important is to split concerns
Manages the data
displayed
Displays the data and
Interacts with the user
Data management
Business Logic
And Treatments
23. Warning
BAD NOMENCLATURE
The Android curse
Services
AndroidServices
SingletonServices
I
N
T
F
Service
AndroidServices
Service Business Treatments Logic
25. Splitting concerns
NTiers Model
Services
View Presenter
AndroidServices
SingletonServices
BroadcastReceiver
ExceptionManager
POJO
Tools
knows
knows
knows
starts
starts
knows
View =
MVP
Service Communication
Com
D.A.O.
DAO
Transverse
Application
I
N
T
F
I
N
T
F
I
N
T
F
27. Contract programming
NTiers Model
Services
View Presenter
AndroidServices
SingletonServices
BroadcastReceiver
ExceptionManager
POJO
Tools
knows
knows
knows
starts
starts
knows
View =
MVP
Service Communication
Com
D.A.O.
DAO
Transverse
Application
I
N
T
F
I
N
T
F
I
N
T
F
35. MVP + N-Tier
Establish the principlae of
separation of concerns
Being unitary testable easely
Allow adaptation to framework’s
changes
Synthesis
Evolutions with no impact on the application.
Being independent of the implementation,
able to change it and test it.
Allow evolutions (DataBase,
communication, …)
Allow to change Libraries
37. When my application is in foreground,
I want all the power that I need
Goals
38. When my applications goes in background,
I want to free as much resources to the system as I can.
(because of the tragedy of the Commons).
Fuck I am
going in
background
Take that
services !
Goals
39. And I don’t want my users to experience that :
Goals
40. And I don’t want my users to experience that
My philosophy is LazyLoading
Goals
44. Starts before any of your classes
The Application object
Keeps the global state of your application
Ends after any of your classes
Is the Application Context
Application
45. Application
The Application object
You have to own it
public class DesignApplication extends Application {
}
<manifest package=
<application
...
android:name=".DesignApplication">
46. Application
The Application object
And reach it from anywhere: Singleton Design Pattern
public class DesignApplication extends Application {
private static DesignApplication instance;
public static DesignApplication getInstance() {
return instance;}
public void onCreate() {
super.onCreate();
instance = this;…}
48. Application
The Application object
The Application object has the responsability of the
instantiation of all objects it persists, it returns.
Application
User user=null;
public void getUser() {
if(user==null) {
//Do what you need to do
//1)startActivity(LoginActivtyIntent);
//ou 2)retireve it from a SharedPreference||DataBase||File...
}
return user;
}
50. Business Services
"Service (The Android class):
Service is how an application indicates to the operating system that it
needs to perform a longer-running operation outside of the normal
Activity UI flow.
It may be self-running (through Context.startService()), or running on
behalf of of another process (through Context.bindService()).
If you do not need either of these behaviors, you should not use a
Service."
Service Android == System Service
51. Business Services
The layer service in N-Tiers architecture:
It corresponds to the functional part of the application that implements
the “Logic” and which describes the operations the application has to
do on the data.
The different Business Rules and System Controls are implemented in
this layer.
This is the treatments layer.
52. Business Services
Should be thought as an activity
without UI
6
Services
AndroidServices
SingletonServices
Insure unique instance(best to do that
using a ServiceManager instead of
static)
Executed in the Main Thread
Is made for long running process
When active, Process is keept in the
LRU-Cach as long as possible
Bigger than a simple POJO
Is lightweight (simple POJO)
Your Business services are like this on
server side
You have to manage their life cycle.
??
56. Instantiate them only when needed
With our Business Services we have to:
ArchiDroid
Instantiate them only once
Be asynchronous
Do not follow the life cycle of activities
but the life cycle of the application
Let their treatments end
57. Instantiation on demand
private void launchServiceAsync() {
// load your service...
ServiceManager.instance.getHumanService(new OnServiceCallBack() {
public void onServiceCallBack(MService service) {
// so just call the method you want of your service
((HumanService) service).getHuman("toto");
}});}
the callBack pattern
View Presenter
HumanService
LazyLoading
Services
Manager
Activity
AndroidServices
SingletonServices
59. public class ServiceManager {
/**
* The list of the bound services to this used to unbind the service. A service is pointed by its serviceConnection. */
List<ServiceConnection> boundServices = null;
List<Service> serviceAndroid = null; List<ServiceBusiness> singletonServices = null;
/** * Empty constructor to instantiate the list of bound services */
private ServiceManager() {boundServices = new ArrayList<ServiceConnection>();...}
/** Destructor **/
/** * This method is called by MApplication to unbind all the services and let them die when your application die */
public synchronized void unbindAndDie() {// Browse all the services and unbind them all
for (ServiceConnection sConn : boundServices) {
//first unbind the service
MAppInstance.ins.getApplication().unbindService(sConn);
}
for (Service servAndroid: servicesAndroid) {
servAndroid = null;} boundServices.clear();
//Do the same with your Singleton Service
for (ServiceBuisness singleton : singletonServices) {
singleton=null;}
//Kill your executor services
//the ones that has to let your thread finish
//the ones that has to finish right now
}
Service Manager
60. private ExecutorService keepAliveThreadsExcecutor = null;
/*** @return the cancelableThreadsExceutor */
public final ExecutorService getKeepAliveThreadsExecutor() {
if (keepAliveThreadsExceutor == null) {
keepAliveThreadsExceutor = Executors.newFixedThreadPool(4, new BackgroundThreadFactory());
}
return keepAliveThreadsExceutor;
}
/**ShutDown the ExecutorService but wait for thread to finish their job */
private void killKeepAliveThreadExecutor() {
if (keepAliveThreadsExceutor != null) {
keepAliveThreadsExceutor.shutdown(); // Disable new tasks from being submitted
try {// as long as your threads hasn't finished
while (!keepAliveThreadsExceutor.isTerminated()) {
// Wait a while for existing tasks to terminate
if (!keepAliveThreadsExceutor.awaitTermination(5, TimeUnit.SECONDS)) {
// Cancel currently executing tasks
keepAliveThreadsExceutor.shutdown();
Log.e("MyApp", "Probably a memory leak here");
}
}
} catch (InterruptedException ie) { keepAliveThreadsExceutor.shutdownNow();
keepAliveThreadsExceutor=null;
Log.e("MyApp", "Probably a memory leak here too");
} }
keepAliveThreadsExceutor=null;}
Service Manager
64. Keep the service alive
(because keep ServiceManager alive)
Keep the Application object alive
(still Bound with the ServiceManager)
Persist instantiation
Application
0-1
HumanService
Services
Manager
0-1
Dead Lock occurs !
AndroidServices
65. Keep the service alive
(because keep ServiceManager alive)
Keep the Application object alive
(still Bound with the ServiceManager)
Persist instantiation
Application
0-1
HumanService
Services
Manager
0-1
Dead Lock occurs !
AndroidServices
66. Garde le Service en vie
(car conserve le ServiceManager en vie)
Garde l'application en vie
(car toujours Bound avec le ServiceManager)
Persist instantiation
Application
0-1
HumanService
Services
Manager
0-1
Dead Lock occurs !
AndroidServices
DO NOT FOLLOW ACTIVITIES’ LIFE CYCLES
FOLLOW APPLICATION’S LIFE CYCLE
The solution :
67. Application’s Life Cycle
What is the life cycle of an application ?
When there is no more visible activities since one second,
I can consider the application is over.
68. Application’s Life Cycle
The One Second pattern: A release memory pattern
IsActivity
Alive
7
Application
View
Services
Manager
onStop
onStart
Runnable mServiceKiller;
Launch it
In1Second
if(false) unbindAndDie()
if false
/** Destructor **/
/** * This method is called by MApplication to unbind all
the services and let them die when your application die */
public synchronized void unbindAndDie() {
// Browse all the services and unbind them all
for (ServiceConnection sConn : boundServices) {
//first unbind the service
unbindService(sConn);}
dService = null;boundServices.clear();}
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
public void onActivityStopped(Activity activity) {isActivityAlive.set(false);}
public void onActivityCreated(Activity activity) {isActivityAlive.set(true);}});
// launch the Runnable mServiceKiller in 1 seconds
getServiceKillerHanlder().postDelayed(getServiceKiller(), 1000);
IsActivity
Alive
69. Application’s Life Cycle
React to onMemoryLow
ApplicationServices
Manager
onLowMemory (...)
System
unbindAndDie()
LazyLoading is our philosophy
70. Be Asynchronous
On Android, every body knows, we have to be asynchronous!
Those who code the D.A.O. layer, know it.
Those who code the View layer, know it.
Thread(Thread(Thread(Thread(do something))))
Those who code the Service layer, know it.
Those who code the Communication layer, know it.
71. Be Asynchronous
Only One Rule:
The Service layer is your Asynchronicity gate.
Each public method of each service class has to be executed in a
background Thread.
Nowhere else Threads are launched.
(except for animation Threads in Ginger, putain de gingerbread)
Use events to return result.
72. Be Asynchronous
public class MyBusinessService{...
public void loadDataAsync(int itemId) {
MyApp.instance.getServiceManager()
.getCancelableThreadsExecutor()
.submit(new RunnableLoadData(itemId));
}
private class RunnableLoadData implements Runnable {
public int itemId;
public RunnableLoadData(int itemId) {this.itemId=itemId}
public void run() {
loadDataSync(itemId);}
}
public void loadDataSync(int itemId) {
// your code here}
73. You can use AysncTaskYou can use AysncTask
Be Asynchronous
You should use PoolExecutor and Runnables
You can use IntentService
https://www.youtube.com/watch?v=jtlRNNhane0&index=4&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE
You can use HandlerThread
74. Instanciate them only when needed
Instanciate them only once
Be asynchronous
Do not follow the life cycle of activities
but the life cycle of the application
Let their treatments end
With our Business Services, we wanted to:
ArchiDroid
75. Available on my GitHub
I use Live Template to generate it
What about the code
arch_Service_Method_WithArgs
arch_ServManager_CancelableThread
arch_ServManager_KeepAliveThread
arch_Service_Method
And more...
La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau... 100 000 lignes de code, aucun tests
La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
Et on a inventé le MVC : decouple la vue, de l'interaction, de la representation.
On a relié View et Controller, c'est là qu'on a mis les listener dans les vues
La liaison est 1-1, on sait alors que c'est du pipo pour l'rdi que c'est fait pour les humains et pour les tests
La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
La classe Dieu, elle fait tout dans une meme classe, presentation des données, interactions utilisateurs, animations, appel BD, appel réseau...
Grace a gradle et aux saveurs on peut automatiser les confs poru executer les tests
Et aussi il y a du DataBinding
J'ai vu des flux tweeter où les gars s'engueulaient pedant 500 tweets sur le sujet (mais tu fais du MVPM toi, ... blazbla)
Principe de separation des couches, des responsabilités
Isolation des composants = Interfaces
Chaque bloc est indépendant des autres
L'importance des flêches de dependence. Qui connait qui.?
n-tier== n niveau logique (tier=niveau logique)
Les services métiers ne sont pas des services Android
ce sont les traitements métiers, la couche business/métier
Les services Android c'est une toute autre notion
On sépare le code, on le ventile, on l'aère
A scindé en plein de partie, c'est globalement compliqué, mais localement simple
Chaque couche est responsable d'une partie precise de l'application: Algorithme, HTTP, DAO, Vues...
Car chaque couche/composant possède un contrat très précis décrit par son interface
Car modulaire
Car ils ne dependent de personne
JUnit et bouchons
Les tests apportent la sécurité pour le projet
Les tests apportent la sécurité pour le projet
30 minutes
En effet, les vues possèdent le MVP qui est rodé et sont gérées plus ou moins directement par le système
Les couches Com et Dao n'ont rien de particulières dans ce système
Il nous reste donc à affiner les services, leur instanciations, leur fonctionnement
Le point fondamental de toute application est l'utilisation de l'objet application
Le point fondamental de toute application est l'utilisation de l'objet application
Tu dois te l'approprier car tu vas en avoir besoin un peu partout dans ton app.
Le principe du Singleton est ultra important, la question que je me pose encore est quand est-ce qu'il doit être garbage collecté et si ça pose un problème.
Le principe du Singleton est ultra important, la question que je me pose encore est quand est-ce qu'il doit être garbage collecté et si ça pose un problème.
Le principe du Singleton est ultra important, la question que je me pose encore est quand est-ce qu'il doit être garbage collecté et si ça pose un problème.
Le point fondamental de toute application est l'utilisation de l'objet application
HandlerThread, c'est comme l'IntentService, ca nous donne un Thread avec son looper
Pourquoi pas les AsyncTask... ben c'est que c'est facile de
All Asynctask will use the same thread to execute themself=> it will be executed in sequential (can kill your work)
How can you cancel an AsyncTask ??? => en fait n'annule pas la task mais annule le bordel et on passe dans onCancel et pas dans onPostExecite
MemoryLeak (AsyncTask declare as an inner class of the Activity fuite memoire commune, ou qui pointe vers un champ de l'activité...)
15 minutes
C'est là où GCManager ou JobScheduler entrent en action