Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Treatment, Architecture and Threads

686 views

Published on

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,

Published in: Technology
  • Be the first to comment

Treatment, Architecture and Threads

  1. 1. @android2ee by(MathiasSeguy ==Android2EE){ French AndroidTrainer}
  2. 2. Europe / Africa / US
  3. 3. MVP ou MVVM n-tiers
  4. 4. Architecture: Goals Simplicity Tests SplittingMaintenance EvolutionConcern Unit Tests Integration Tests Fonctionnal Tests Robustness
  5. 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. 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. 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. 8. Then came the M(VC) model (a Swing’s pattern) Model View&Controller ModifiesUpdates View Controller A piece of history
  9. 9. Manages the data displayed Displays the data and Interacts with the user The Swing M(VC) model Model View&Controller 1 1 A piece of history
  10. 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. 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. 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. 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. 14. For the TESTS ! Model Presenter View PresenterIntf ViewIntf MockPresenter MockView A piece of history
  15. 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. 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
  17. 17. Architecture Why all that complexity ? Test! Split responsabilities Reuse Simplify
  18. 18. Architecture But wait ! First remark. MVC MVP MVVM FLUX We don’t care about fashion.
  19. 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
  20. 20. Architecture But wait ! Don’t you have forget a damned element? It all View’s architecture, only View
  21. 21. Architecture You forgot your whole application!
  22. 22. N-Tier model Application Services View Presenter AndroidServices SingletonServices BroadcastReceiver ExceptionManager POJO Tools knows knows starts starts knows View = MVP Transverse I N T F Service I N T F Communication Com D.A.O. I N T F DAO knows
  23. 23. Warning BAD NOMENCLATURE The Android curse Services AndroidServices SingletonServices I N T F Service AndroidServices Service Business Treatments Logic
  24. 24. Simplicity SplittingMaintenance EvolutionConcerns Robustness Back to our goals Test Reuse Contract Mock
  25. 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
  26. 26. Simplification 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. 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
  28. 28. Evolution 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
  29. 29. NTiers Model BroadcastReceiver Communication Com D.A.O. DAO ExceptionManagerTools Testing Tests JUNIT Tests Tests Transverse Transverse
  30. 30. NTiers Model Services AndroidServices SingletonServices Service knows Communication Com I N T F knows D.A.O. DAO I N T F Tests JUNIT Testing
  31. 31. NTiers Model MockView View = MVP Presenter ViewIntf 1 1 JUNIT Services AndroidServices SingletonServices Service I N T F Tests Testing
  32. 32. NTiers Model View = MVP Presenter View 1 1 Services AndroidServices SingletonServices Service I N T F Espresso AndroidTest SDK Tests Testing
  33. 33. Complete Testing 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
  34. 34. Maintenance Robustness 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
  35. 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
  36. 36. The Application objects Business services
  37. 37. When my application is in foreground, I want all the power that I need Goals
  38. 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. 39. And I don’t want my users to experience that : Goals
  40. 40. And I don’t want my users to experience that My philosophy is LazyLoading Goals
  41. 41. 47 To do that all plays here ArchiDroid HERE
  42. 42. Aside
  43. 43. The Application object Application
  44. 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. 45. Application The Application object You have to own it public class DesignApplication extends Application { } <manifest package= <application ... android:name=".DesignApplication">
  46. 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;…}
  47. 47. Application The Application object Can be reached from anywhere, from any thread All its public methods have to be synchronized.
  48. 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; }
  49. 49. Business Services Business Services AndroidServices SingletonServices
  50. 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. 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. 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. ??
  53. 53. Business Services Services AndroidServices SingletonServices ?? Need to be launch by the system? Need to make some data caching?
  54. 54. Business Services Services AndroidServices SingletonServices ?? Need to keep going without any visible activity (like Music Player)? No particular need
  55. 55. End of Aside
  56. 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. 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
  58. 58. Threads management View Presenter HumanService Manage Threads Services Manager DAO Cancelable KeepAlive ThreadsPools
  59. 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. 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
  61. 61. Persist instantiation Application View Presenter HumanService LazyLoading Services Manager DAO Cancelable KeepAlive ThreadsPools Do not follow the Activity’s life cycle But the application’s life cycle
  62. 62. Persist instantiation Application 0-1 HumanService Services Manager 0-1 But wait ! AndroidServices
  63. 63. Persist instantiation Application 0-1 HumanService Services Manager 0-1 Dead Lock occurs ! AndroidServices
  64. 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. 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. 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. 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. 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. 69. Application’s Life Cycle React to onMemoryLow ApplicationServices Manager onLowMemory (...) System unbindAndDie() LazyLoading is our philosophy
  70. 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. 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. 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. 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. 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. 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...
  76. 76. I can train your team ! Or
  77. 77. And engage myself On the success of your Android product (with a high quality lens) I can be your Android tech lead
  78. 78. #android2ee mathias.seguy@android2ee.com www.android2ee.com
  79. 79. Thank you!Mathias Seguy Android2EE @android2ee Slides: http://fr.slideshare.net/Android2EE Code: https://github.com/MathiasSeguy-Android2EE Android2EE http://android2ee.com
  80. 80. Thank you!Mathias Seguy Android2EE @android2ee Slides: http://fr.slideshare.net/Android2EE Code: https://github.com/MathiasSeguy-Android2EE Android2EE http://android2ee.com

×