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

Android Networking (Without Going Crazy)

Android Networking (Without Going Crazy)

Avatar for Phil Shadlyn

Phil Shadlyn

August 13, 2015
Tweet

More Decks by Phil Shadlyn

Other Decks in Programming

Transcript

  1. Why do I care? • Most Android apps need to

    talk to a server or somewhere on the Internet • Data sync, user authentication, stream video, show ads, etc.
  2. A bit about the UI thread • Android apps run

    in a single threaded environment • Long-running operations “block” the UI thread, causing lag / frustration / swearing • As of API 11, Android won’t allow networking operations on the UI thread
  3. What should I do? • Spawn separate thread to handle

    any network calls • Update the UI when complete • UI elements (Views) must be updated on thread that created them (UI thread)
  4. Example • Download list of baseball players from a specified

    team, through a REST API • Display player list to the user • Assume we have Java client to talk to API
  5. java.lang.Thread final int teamId = 21; new Thread(new Runnable() {

    @Override public void run() { ArrayList<Player> players = ApiClient. getInstance().getPlayerList( teamId); // send data to UI thread to update Message msg = new Message(); Bundle data = new Bundle(); data.putParcelableArrayList( "player_list" , players); msg.setData(data) ; mHandler.sendMessage(msg) ; } }).run();
  6. java.lang.Thread • Spawn separate thread for network request • When

    completed send data back to UI thread to update • Remember, View objects can only be updated on UI thread
  7. AsyncTask new AsyncTask<Integer , Void, List<Player>>(){ @Override protected List<Player> doInBackground

    (Integer... params) { int teamId = params[ 0]; return ApiClient.getInstance().getPlayerList(teamId) ; } @Override protected void onPostExecute (List<Player> players) { // this method runs on UI thread, so update list of players } }.execute(teamId) ;
  8. AsyncTask • doInBackground method runs on a separate thread, passes

    results to onPostExecute • onPostExecute runs on UI thread, can update Views
  9. AsyncTask Problems • Activity is destroyed and recreated on rotation,

    so running AsyncTask tries to update views from old Activity • AsyncTask.cancel() is broken • Best used for very short tasks (< 1s)
  10. IntentService FTW! public class PlayerListService extends IntentService { public static

    final String ACTION_PLAYER_LIST_DOWNLOADED = "com.test.network.ACTION_PLAYER_LIST_DOWNLOADED" ; public static final String EXTRA_PLAYER_LIST = "com.test.network.EXTRA_PLAYER_LIST" ; @Override protected void onHandleIntent (Intent intent) { List<Player> players = ApiClient. getInstance().getPlayerList(teamId) ; // Send results back to UI thread Intent i = new Intent(ACTION_PLAYER_LIST_DOWNLOADED); i.putParcelableArrayListExtra( EXTRA_PLAYER_LIST, players); LocalBroadcastManager. getInstance(this).sendBroadcast(i) ; } } // Start with intent from UI thread startService(new Intent(this, PlayerListService. class));
  11. IntentService • Runs on separate thread, override onHandleIntent() method •

    Completely separate from Activity lifecycle, shuts down when work is complete • Send data back to UI thread via Broadcast
  12. Sending data to the UI Thread • Primitive data types

    can be sent in message data bundle or as Intent extra • Custom objects must implement Serializeable or Parcelable interfaces to be sent • http://www.parcelabler.com/ to generate code
  13. Sending data to the UI Thread • Can also use

    ResultReceiver or an event bus to send data to UI thread • Event Bus uses publisher/subscriber model, deliver events on bus to all subscribers • Popular event buses include EventBus (greenrobot) or Otto (Square)
  14. EventBus (greenrobot) // Event Class public class PlayerListEvent { private

    List<Player> players; public PlayerListEvent(List<Player> players) { this.players = players; } public List<Player> getPlayers() { return players; } } // Register in onCreate, onResume, onAttach, etc EventBus.getDefault().register( this); // Send Player list from IntentService EventBus.getDefault().post(new PlayerListEvent (players)); // Public method to which EventBus delivers event public void onEventMainThread (PlayerListEvent event) { mPlayers = event.getPlayers() ; // update UI... }
  15. An Even Better Idea? • Retrofit turns REST API calls

    into a Java interface and a series of async method calls • Built on top of OkHttp (Java networking) and Okio (java.io replacement) • http://square.github.io/retrofit/
  16. Retrofit • Define interface methods public interface SportsApi { @GET("/teams/{id}/players")

    void getPlayerList(@Path("id") int teamId, Callback<List<Player>> callback); } • Handle method callbacks SportsApiClient.getInstance().getPlayerList(teamId, new Callback<List<Player>>() { @Override public void success(List<Player> players, Response response) { // called on main thread, update UI... } @Override public void failure(RetrofitError error) { // uh oh, there was a problem... } });
  17. Summary • No network calls on the UI thread! •

    View objects must be updated from UI thread • Android networking can be hard, but there are some great tools out there to help you