Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Treatments, Architecture and Threads

Treatments, Architecture and Threads

[DroidCon 2016]
What if we have a talk about architecture?
MVP, MVC, MVVM, Okay but it's just the view's layer architecture, what about the architecture of the rest of your application?
And by the way, what is a Business service for you?
Do you always use the framework class Service to define it?
Do you centralize their instantiation to follow the application's life cycle or do they follow the activities' life cycle?
And how do you manage your Threads? Do you centralize them? Where do you declare your Threads? Where do you instantiate your Runnables? ...
Let have a talk about those notions. I will explain you my vision, why you need a ServiceManager, why it will enhance your application and simplify it. Let me show you how necessary it is for your application, how it works, what are its concerns, what are the traps to avoid, what are the tricks to implement, what are the tips to use... Let's have a talk, you won't regret :)

Avatar for Mathias Seguy - Android2EE

Mathias Seguy - Android2EE

October 28, 2016
Tweet

More Decks by Mathias Seguy - Android2EE

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. Then came the M(VC) model (a Swing’s pattern) Model View&Controller

    Modifies Updates View Controller A piece of history
  5. 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
  6. 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
  7. 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
  8. 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 Modifies Updates Talks A piece of history
  9. 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
  10. Flavor=TestPresenter And with Gradle, happiness knocks on your door. Flavor=TestVue

    Flavor=PROD Model Presenter View 0 1 MockPresenter MockView View Presenter 0 1 0 1 MockModel A piece of history
  11. 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 Modifies Updates Talks ViewModel 1 n A piece of history
  12. 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
  13. Architecture But wait ! Don’t you have forget a damned

    element? It all View’s architecture, only View
  14. 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
  15. Warning BAD NOMENCLATURE The Android curse Services AndroidServices SingletonServices I

    N T F Service AndroidServices Service Business Treatments Logic
  16. 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
  17. 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
  18. 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
  19. 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
  20. NTiers Model MockView View = MVP Presenter ViewIntf 1 1

    JUNIT Services AndroidServices SingletonServices Service I N T F Tests Testing
  21. NTiers Model View = MVP Presenter View 1 1 Services

    AndroidServices SingletonServices Service I N T F Espresso AndroidTest SDK Tests Testing
  22. 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
  23. 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
  24. 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
  25. 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
  26. And I don’t want my users to experience that My

    philosophy is LazyLoading Goals
  27. 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
  28. Application The Application object You have to own it public

    class DesignApplication extends Application { } <manifest package= <application ... android:name=".DesignApplication">
  29. 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;…}
  30. Application The Application object Can be reached from anywhere, from

    any thread All its public methods have to be synchronized.
  31. 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; }
  32. 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
  33. 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.
  34. 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. ? ?
  35. Business Services Services AndroidServices SingletonServices ? ? Need to be

    launch by the system? Need to make some data caching?
  36. Business Services Services AndroidServices SingletonServices ? ? Need to keep

    going without any visible activity (like Music Player)? No particular need
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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 :
  45. 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.
  46. 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
  47. Application’s Life Cycle React to onMemoryLow Application Services Manager onLowMemory

    (...) System unbindAndDie() LazyLoading is our philosophy
  48. 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.
  49. 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.
  50. 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}
  51. You can use AysncTask You 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
  52. 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
  53. 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...
  54. And engage myself On the success of your Android product

    (with a high quality lens) I can be your Android tech lead