Android programming -_pushing_the_limits
Upcoming SlideShare
Loading in...5
×
 

Android programming -_pushing_the_limits

on

  • 262 views

 

Statistics

Views

Total Views
262
Views on SlideShare
220
Embed Views
42

Actions

Likes
1
Downloads
11
Comments
2

1 Embed 42

http://de.droidcon.com 42

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Hello Ondrej,

    I checked again with two different browsers (FF, Chrome) and I don't have any problems. If you send me your email adress, I'll send you a screenshot of slide 35.
    Are you sure you want to
    Your message goes here
    Processing…
  • I don't get 35th slide? Could you please explain it?
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Android programming -_pushing_the_limits Android programming -_pushing_the_limits Presentation Transcript

  • Pushing the Limits Erik Hellman, Spotify google.com/+ErikHellman @ErikHellman Android Programming
  • 2 wiley.com/go/ptl/androidprogramming
  • 3 Improved memory management Efficient multi-threading and -processing Using Android components correctly
  • Improved memory management 4
  • 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.”
  • 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++;                          }                  }          }   }  
  • 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>
  • 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>
  • How to get more memory - Part 3 9 data  =  malloc(sizeof(data_struct));
  • 10 Improved memory management Efficient multi-threading and -processing Using Android components correctly
  • 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 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;   }  
  • Why Handler? 13 •Scheduling of messages
 •Cancelling of messages
 •Reduces GC calls
  • Using the process attribute 14
  • Why a separate process? 15 •Better isolation from crashes
 •Binder transactions uses a thread pool
 •Double the memory!
  • 16 Improved memory management Efficient multi-threading and -processing Using Android components correctly
  • 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>
  • 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...          }   }  
  • Application Context limitations 19 •Inflating layout uses default theme
 •Starting Activity creates a new task
  • 20 Activity - saving state Activity.onSaveInstanceState() Activity.onPause() or
  • 21 onSaveInstanceState() is not called… •…when user presses back
 •…when you call Activity.finish()
  • 22
  • Fragment 23 android.app.Fragment android.support.v4.app.Fragment or
  • 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'])   }  
  • 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;          }   }
  • 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 Use a Loader whenever possible and cancel remaining requests in onPause()/onDetach()!
  • Binding to Services 28 @Override   public  IBinder  onBind(Intent  intent)  {     return  null;  //  NEVER  do  this   }
  • 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!");     }   }  
  • 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!:");     }   }  
  • 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
  • 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;   }
  • 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;          }   }
  • 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;   }  
  • 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>  
  • 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 “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