Android programming -_pushing_the_limits

949 views
793 views

Published on

Published in: Technology
2 Comments
4 Likes
Statistics
Notes
No Downloads
Views
Total views
949
On SlideShare
0
From Embeds
0
Number of Embeds
58
Actions
Shares
0
Downloads
24
Comments
2
Likes
4
Embeds 0
No embeds

No notes for slide

Android programming -_pushing_the_limits

  1. 1. Pushing the Limits Erik Hellman, Spotify google.com/+ErikHellman @ErikHellman Android Programming
  2. 2. 2 wiley.com/go/ptl/androidprogramming
  3. 3. 3 Improved memory management Efficient multi-threading and -processing Using Android components correctly
  4. 4. Improved memory management 4
  5. 5. Use a static factory method whenever possible 5 Effective Java (2nd ed.): Item 1! “A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they’re invoked.”
  6. 6. Message.java 6 public  final  class  Message  implements  Parcelable  {          /*package*/  Message  next;   !        private  static  final  Object  sPoolSync  =  new  Object();          private  static  Message  sPool;          private  static  int  sPoolSize  =  0;   !        private  static  final  int  MAX_POOL_SIZE  =  50;   !        public  static  Message  obtain()  {                  synchronized  (sPoolSync)  {                          if  (sPool  !=  null)  {                                  Message  m  =  sPool;                                  sPool  =  m.next;                                  m.next  =  null;                                  sPoolSize-­‐-­‐;                                  return  m;                          }                  }                  return  new  Message();          }      public  void  recycle()  {                  clearForRecycle();   !                synchronized  (sPoolSync)  {                          if  (sPoolSize  <  MAX_POOL_SIZE)  {                                  next  =  sPool;                                  sPool  =  this;                                  sPoolSize++;                          }                  }          }   }  
  7. 7. How to get more memory - Part 1 7 <application          android:allowBackup="true"          android:icon="@drawable/ic_launcher"          android:label="@string/app_name"          android:name=".MyApplication"          android:largeHeap="true"          android:theme="@style/AppTheme"  >          …   </application>
  8. 8. How to get more memory - Part 2 8 <service          android:name="se.hellsoft.apptl.app.MyService"          android:enabled="true"          android:exported="false"          android:process="se.hellsoft.apptl.service">   </service>
  9. 9. How to get more memory - Part 3 9 data  =  malloc(sizeof(data_struct));
  10. 10. 10 Improved memory management Efficient multi-threading and -processing Using Android components correctly
  11. 11. 11 Always use a Handler!* * Except when you shouldn’t… @Override   protected  void  onCreate(Bundle  savedInstanceState)  {          super.onCreate(savedInstanceState);          HandlerThread  backgroundThread  =  new  HandlerThread("background");          backgroundThread.start();          mBackgroundHandler  =  new  Handler(backgroundThread.getLooper(),  this);          mUiHandler  =  new  Handler(this);   }   ! @Override   protected  void  onDestroy()  {          super.onDestroy();          mBackgroundHandler.removeCallbacksAndMessages(null);          mBackgroundHandler.getLooper().quit();   }
  12. 12. 12 Always use a Handler!* * Except when you shouldn’t… public  void  onStartBackgroundJob(View  view)  {          mBackgroundHandler.obtainMessage(BACKGROUND_JOB).sendToTarget();   }   ! @Override   public  boolean  handleMessage(Message  msg)  {          switch  (msg.what)  {                  case  BACKGROUND_JOB:                          Bitmap  result  =  null;                          //  Processing  goes  here...                            mUiHandler.obtainMessage(UPDATE_UI,  result).sendToTarget();                          break;                  case  UPDATE_UI:                          ((ImageView)  findViewById(R.id.photo))                                                                              .setImageBitmap((Bitmap)  msg.obj);                          break;          }          return  true;   }  
  13. 13. Why Handler? 13 •Scheduling of messages
 •Cancelling of messages
 •Reduces GC calls
  14. 14. Using the process attribute 14
  15. 15. Why a separate process? 15 •Better isolation from crashes
 •Binder transactions uses a thread pool
 •Double the memory!
  16. 16. 16 Improved memory management Efficient multi-threading and -processing Using Android components correctly
  17. 17. Application component 17 <application          android:allowBackup="true"          android:icon="@drawable/ic_launcher"          android:label="@string/app_name"          android:name=“.MyApplication"          android:theme="@style/AppTheme"  >   !                ...   </application>
  18. 18. Android Singletons using Context 18 public  class  MySingleton  {          private  static  MySingleton  sInstance;          private  Context  mContext;   !        private  MySingleton(Context  context)  {                  mContext  =  context;          }                    public  MySingleton  getInstance(Context  context)  {                  if(sInstance  ==  null)  {                          sInstance  =  new  MySingleton(context.getApplicationContext());                  }                  return  sInstance;          }                    public  void  doStuffThatRequireContext()  {                  //  Do  stuff  here...          }   }  
  19. 19. Application Context limitations 19 •Inflating layout uses default theme
 •Starting Activity creates a new task
  20. 20. 20 Activity - saving state Activity.onSaveInstanceState() Activity.onPause() or
  21. 21. 21 onSaveInstanceState() is not called… •…when user presses back
 •…when you call Activity.finish()
  22. 22. 22
  23. 23. Fragment 23 android.app.Fragment android.support.v4.app.Fragment or
  24. 24. Support library can be upgraded! 24 dependencies  {          compile  'com.android.support:appcompat-­‐v7:+'          compile  'com.android.support:support-­‐v4:19.1.+'          compile  fileTree(dir:  'libs',  include:  ['*.jar'])   }  
  25. 25. Typical Fragment crash 25 public  static  class  FirstFragment  extends  Fragment  {          private  ImageView  mImageView;   !        @Override          public  View  onCreateView(LayoutInflater  inflater,  ViewGroup  container,                                                            Bundle  savedInstanceState)  {                  View  rootView  =  inflater.inflate(R.layout.fragment_main,  container,  false);                  mImageView  =  (ImageView)  rootView.findViewById(R.id.photo);   !                new  AsyncTask<Void,  Void,  Bitmap>()  {                          @Override                          protected  Bitmap  doInBackground(Void...  params)  {                                  SystemClock.sleep(10000);  //  Fake  long  processing...                                  return  BitmapFactory.decodeResource(getResources(),                                                                                                                                  R.drawable.happy_android);                          }   !                        @Override                          protected  void  onPostExecute(Bitmap  bitmap)  {                                  mImageView.setImageBitmap(bitmap);                          }                  }.execute();   !                return  rootView;          }   }
  26. 26. Typical Fragment crash 26 E/AndroidRuntime(  1245):  FATAL  EXCEPTION:  AsyncTask  #1   E/AndroidRuntime(  1245):  Process:  se.hellsoft.apptl.app,  PID:  1245   E/AndroidRuntime(  1245):  java.lang.RuntimeException:  An  error  occured  while  executing  doInBackground()   E/AndroidRuntime(  1245):          at  android.os.AsyncTask$3.done(AsyncTask.java:300)   E/AndroidRuntime(  1245):          at  java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)   E/AndroidRuntime(  1245):          at  java.util.concurrent.FutureTask.setException(FutureTask.java:222)   E/AndroidRuntime(  1245):          at  java.util.concurrent.FutureTask.run(FutureTask.java:242)   E/AndroidRuntime(  1245):          at  android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)   E/AndroidRuntime(  1245):          at  java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)   E/AndroidRuntime(  1245):          at  java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)   E/AndroidRuntime(  1245):          at  java.lang.Thread.run(Thread.java:841)   E/AndroidRuntime(  1245):  Caused  by:  java.lang.IllegalStateException:  Fragment  FirstFragment{52836468}  not  attached   to  Activity   E/AndroidRuntime(  1245):          at  android.support.v4.app.Fragment.getResources(Fragment.java:601)   E/AndroidRuntime(  1245):          at  se.hellsoft.apptl.app.MainActivity$FirstFragment $1.doInBackground(MainActivity.java:115)   E/AndroidRuntime(  1245):          at  se.hellsoft.apptl.app.MainActivity$FirstFragment $1.doInBackground(MainActivity.java:109)   E/AndroidRuntime(  1245):          at  android.os.AsyncTask$2.call(AsyncTask.java:288)   E/AndroidRuntime(  1245):          at  java.util.concurrent.FutureTask.run(FutureTask.java:237)   E/AndroidRuntime(  1245):          ...  4  more
  27. 27. 27 Use a Loader whenever possible and cancel remaining requests in onPause()/onDetach()!
  28. 28. Binding to Services 28 @Override   public  IBinder  onBind(Intent  intent)  {     return  null;  //  NEVER  do  this   }
  29. 29. Binding to Services 29 @Override   public  IBinder  onBind(Intent  intent)  {     String  action  =  intent.getAction();     if(ACTION_FIRST_BINDER.equals(action))  {       return  mFirstBinder;     }  else  if(ACTION_SECOND_BINDER.equals(action))  {       return  mSecondBinder;     }  else  {       throw  new  RuntimeException("Unexpected  error!");     }   }  
  30. 30. Binding to Services 30 @Override   public  IBinder  onBind(Intent  intent)  {     Uri  data  =  intent.getData();     String  param  =  data.getQueryParameter("param");     if(FIRST_BINDER.equals(param))  {       return  mFirstBinder;     }  else  if(SECOND_BINDER.equals(param))  {       return  mSecondBinder;     }  else  {       throw  new  RuntimeException("Unexpected  error!:");     }   }  
  31. 31. Service considerations 31 •A binder Intent is identified by the action string and data Uri! ! •onBind() and onUnbind() only called once per unique Intent
 •START_STICKY will give a null  Intent when Service is restarted by the system
 •Binder calls run on local thread for process-local service
  32. 32. Most common ContentProvider mistake :) 32 @Override   public  Cursor  query(Uri  uri,  String[]  projection,  String  selection,                                          String[]  selectionArgs,  String  sortOrder)  {          SQLiteDatabase  db  =  mDatabaseHelper.getReadableDatabase();          int  match  =  sUriMatcher.match(uri);          Cursor  cursor  =  null;          switch  (match)  {                  case  ALL_ROWS:                          cursor  =  db.query(Contract.TABLE_NAME,  projection,  selection,                                            selectionArgs,  "",  "",  sortOrder);                          break;                  case  SINGLE_ROW:                          String  id  =  uri.getLastPathSegment();                          cursor  =    db.query(Contract.TABLE_NAME,  projection,  "_id  =  ?",                                            new  String[]{id},  "",  "",  sortOrder);                          break;          }          if(cursor  !=  null)  {                  cursor.setNotificationUri(getContext().getContentResolver(),  uri);          }          return  cursor;   }
  33. 33. Most common ContentProvider mistake :) 33 @Override   public  Uri  insert(Uri  uri,  ContentValues  values)  {        SQLiteDatabase  db  =  mDatabaseHelper.getWritableDatabase();          int  match  =  sUriMatcher.match(uri);          switch  (match)  {                  case  ALL_ROWS:                          long  newId  =  db.insert(Contract.TABLE_NAME,  "",  values);                          if(newId  !=  -­‐1)  {                                  getContext().getContentResolver().notifyChange(uri,  null);                          }                          return  Uri.withAppendedPath(uri,  String.valueOf(newId));                  default:                          return  null;          }   }
  34. 34. Don’t forget bulkInsert() !!! 34 @Override   public  int  bulkInsert(Uri  uri,  ContentValues[]  values)  {          SQLiteDatabase  db  =  mDatabaseHelper.getWritableDatabase();          int  match  =  sUriMatcher.match(uri);          int  inserted  =  0;          switch  (match)  {                  case  TASKS_CODE:                          try  {                                  db.beginTransaction();                                  for  (ContentValues  value  :  values)  {                                          long  id  =  db.insert(Contract.TABLE_NAME,  "",  value);                                          if  (id  <=  0)  throw  new  SQLException("Failed  with  inserting.");                                          inserted++;                                  }                                  db.setTransactionSuccessful();                                  getContext().getContentResolver().notifyChange(uri,  null);                          }  finally  {                                  db.endTransaction();                          }          }          return  inserted;   }  
  35. 35. Useful Broadcasts - Auto-starting 35 <receiver  android:name="se.hellsoft.myapp.AutoStartReceiver"  >          <intent-­‐filter>                  <action  android:name="android.intent.action.BOOT_COMPLETED"  />        </intent-­‐filter>   </receiver>   <receiver  android:name=“se.hellsoft.myapp.AutoStartReceiver"                      android:process=“:autostarter”>          <intent-­‐filter>                  <action  android:name="android.intent.action.USER_PRESENT"  />        </intent-­‐filter>   </receiver>  
  36. 36. Useful Broadcasts - Monitoring the network 36 <receiver  android:name=“se.hellsoft.myapp.NetworkMonitor">          <intent-­‐filter>                  <action  android:name="android.net.conn.CONNECTIVITY_CHANGE"  />                  <action  android:name="android.net.wifi.supplicant.CONNECTION_CHANGE"  />                  <action  android:name="android.net.wifi.WIFI_AP_STATE_CHANGED"  />        </intent-­‐filter>   </receiver>  
  37. 37. 37 “Many problems with Android apps can be fixed with 
 a proper use of the Handler class.” - Erik Hellman Get my book at wiley.com/go/ptl/androidprogramming

×