Slide 1

Slide 1 text

Reusable Libraries Ryan Harter @rharter +RyanHarter

Slide 2

Slide 2 text

My Apps Fragment Shift

Slide 3

Slide 3 text

Harter’s Tips for (Code) Happiness • Identify good candidates • Limit scope and requirements • Design a good API • Document and Communicate • Treat it like a client project

Slide 4

Slide 4 text

Identify Good Candidates

Slide 5

Slide 5 text

Identify Good Candidates • It’s not a library if it’s only used once • Don’t reinvent the wheel • Focus on core of app

Slide 6

Slide 6 text

Identify Good Candidates • Google for libraries • Check Android Arsenal • Look at what other apps use • Be Confident • Count Stars • Inspect Source

Slide 7

Slide 7 text

Fragment

Slide 8

Slide 8 text

Fragment

Slide 9

Slide 9 text

Fragment

Slide 10

Slide 10 text

Fragment

Slide 11

Slide 11 text

Fragment

Slide 12

Slide 12 text

Fragment

Slide 13

Slide 13 text

Fragment

Slide 14

Slide 14 text

Fragment

Slide 15

Slide 15 text

Fragment

Slide 16

Slide 16 text

Fragment

Slide 17

Slide 17 text

Fragment

Slide 18

Slide 18 text

Fragment

Slide 19

Slide 19 text

Fragment

Slide 20

Slide 20 text

Fragment

Slide 21

Slide 21 text

Fragment

Slide 22

Slide 22 text

Fragment

Slide 23

Slide 23 text

Fragment

Slide 24

Slide 24 text

Fragment

Slide 25

Slide 25 text

Fragment

Slide 26

Slide 26 text

Fragment

Slide 27

Slide 27 text

Fragment

Slide 28

Slide 28 text

Fragment

Slide 29

Slide 29 text

Fragment

Slide 30

Slide 30 text

Fragment

Slide 31

Slide 31 text

Fragment

Slide 32

Slide 32 text

Fragment

Slide 33

Slide 33 text

Fragment

Slide 34

Slide 34 text

Limit Scope and Requirements

Slide 35

Slide 35 text

Limit Scope and Requirements • Choose a single problem to solve • Picasso - Efficient image loading and caching • Retrofit - Easy REST API access • Avoid “Apache Syndrome” • Every Apache library depends on every other Apache library

Slide 36

Slide 36 text

Limit Scope and Requirements • Library mission statement • One sentence • What the library does “Allow the user to load an image from any source.”

Slide 37

Slide 37 text

“Allow the user to load an image from any source.” Limit Scope and Requirements • Library mission statement • One sentence • What the library does

Slide 38

Slide 38 text

“Allow the user to load an image from any source.” Limit Scope and Requirements • Library mission statement • One sentence • What the library does

Slide 39

Slide 39 text

“Allow the user to load an image from any source.” Limit Scope and Requirements • Library mission statement • One sentence • What the library does

Slide 40

Slide 40 text

Design a Good API

Slide 41

Slide 41 text

Design a Good API • Who is your user? • If user needs to mess with code, it’s not a library • Design from the outside in • Package your library

Slide 42

Slide 42 text

API Strategies - Utility Requirements: Persist filter state to support undo/redo.

Slide 43

Slide 43 text

API Strategies - Utility Requirements: Persist filter state to support undo/redo. public interface HistoryManager { public T undo(); public T redo(); public void push(T state); public void clear(); }

Slide 44

Slide 44 text

API Strategies - Utility Requirements: Persist filter state to support undo/redo. public class HistoryManager { public T undo() { … } public T redo() { … } public void push(T state) { … } public void clear() { … } } public class HistoryManager { public T undo() { … } public T redo() { … } public void push(T state) { … } public void clear() { … } }

Slide 45

Slide 45 text

public class HistoryManager { public HistoryManager(File file) { … } public T undo() { … } public T redo() { … } public void push(T state) { … } public void clear() { … } } API Strategies - Utility Requirements: Persist filter state to support undo/redo. public class HistoryManager { public HistoryManager(File file) { … } public T undo() { … } public T redo() { … } public void push(T state) { … } public void clear() { … } }

Slide 46

Slide 46 text

public class HistoryManager { public HistoryManager(File file) { … } public T undo() { … } public boolean canUndo() { … } public T redo() { … } public boolean canRedo() { … } public void push(T state) { … } public void clear() { … } } API Strategies - Utility Requirements: Persist filter state to support undo/redo. public class HistoryManager { public HistoryManager(File file) { … } public T undo() { … } public boolean canUndo() { … } public T redo() { … } public boolean canRedo() { … } public void push(T state) { … } public void clear() { … } }

Slide 47

Slide 47 text

API Strategies - Superclass Fragment Shift

Slide 48

Slide 48 text

API Strategies - Superclass public class BaseExportActivity extends Activity { /** * Returns a list of preferred packages which appear above the * divider with a slightly larger icon than the rest. * * @return the package ids of the preferred packages. */ protected List getPreferredPackages() { return DEFAULT_PREFERRED_PACKAGES; } /** * @return the public folder name to save files to. */ protected File getSaveFolder() { return new File(getPublicPicturesDirectory(), "Pixite"); } /** * Allows the AppInfo to be updated for things like Refragment or * Refilter. Default implementation does nothing. * * @param info The app info to update. */ protected void updateAppInfo(AppInfo info) { } }

Slide 49

Slide 49 text

API Strategies - Superclass public class BaseExportActivity extends Activity { /** * Returns a list of preferred packages which appear above the * divider with a slightly larger icon than the rest. * * @return the package ids of the preferred packages. */ protected List getPreferredPackages() { return DEFAULT_PREFERRED_PACKAGES; } /** * @return the public folder name to save files to. */ protected File getSaveFolder() { return new File(getPublicPicturesDirectory(), "Pixite"); } /** * Allows the AppInfo to be updated for things like Refragment or * Refilter. Default implementation does nothing. * * @param info The app info to update. */ protected void updateAppInfo(AppInfo info) { } } public class BaseExportActivity extends Activity { /** * Returns a list of preferred packages which appear above the * divider with a slightly larger icon than the rest. * * @return the package ids of the preferred packages. */ protected List getPreferredPackages() { return DEFAULT_PREFERRED_PACKAGES; } /** * @return the public folder name to save files to. */ protected File getSaveFolder() { return new File(getPublicPicturesDirectory(), "Pixite"); } /** * Allows the AppInfo to be updated for things like Refragment or * Refilter. Default implementation does nothing. * * @param info The app info to update. */ protected void updateAppInfo(AppInfo info) { } }

Slide 50

Slide 50 text

API Strategies - Superclass public class BaseExportActivity extends Activity { /** * Returns a list of preferred packages which appear above the * divider with a slightly larger icon than the rest. * * @return the package ids of the preferred packages. */ protected List getPreferredPackages() { return DEFAULT_PREFERRED_PACKAGES; } /** * @return the public folder name to save files to. */ protected File getSaveFolder() { return new File(getPublicPicturesDirectory(), "Pixite"); } /** * Allows the AppInfo to be updated for things like Refragment or * Refilter. Default implementation does nothing. * * @param info The app info to update. */ protected void updateAppInfo(AppInfo info) { } } public class BaseExportActivity extends Activity { /** * Returns a list of preferred packages which appear above the * divider with a slightly larger icon than the rest. * * @return the package ids of the preferred packages. */ protected List getPreferredPackages() { return DEFAULT_PREFERRED_PACKAGES; } /** * @return the public folder name to save files to. */ protected File getSaveFolder() { return new File(getPublicPicturesDirectory(), "Pixite"); } /** * Allows the AppInfo to be updated for things like Refragment or * Refilter. Default implementation does nothing. * * @param info The app info to update. */ protected void updateAppInfo(AppInfo info) { } }

Slide 51

Slide 51 text

API Strategies - Superclass public class BaseExportActivity extends Activity { /** * Returns a list of preferred packages which appear above the * divider with a slightly larger icon than the rest. * * @return the package ids of the preferred packages. */ protected List getPreferredPackages() { return DEFAULT_PREFERRED_PACKAGES; } /** * @return the public folder name to save files to. */ protected File getSaveFolder() { return new File(getPublicPicturesDirectory(), "Pixite"); } /** * Allows the AppInfo to be updated for things like Refragment or * Refilter. Default implementation does nothing. * * @param info The app info to update. */ protected void updateAppInfo(AppInfo info) { } }

Slide 52

Slide 52 text

API Strategies - Isolated Activity Fragment Shift

Slide 53

Slide 53 text

API Strategies - Isolated Activity Intent i = new Intent(context, InspirationActivity.class); i.putExtra(EXTRA_USER_ID, "1234567890"); i.putExtra(EXTRA_ACCESS_TOKEN, "super_secret_access_token"); i.putExtra(EXTRA_SKIP_TAG, "fragmentpromo"); i.putExtra(EXTRA_USERNAME, "fragmentapp"); startActivity(i);

Slide 54

Slide 54 text

API Strategies - Isolated Activity

Slide 55

Slide 55 text

API Strategies - Isolated Activity

Slide 56

Slide 56 text

API Strategies - Isolated Activity <item name="inspirationActivityStyle">@style/FragmentInspirationStyle</item> <item name=“inspirationListStyle">@style/FragmentInspListStyle</item> <item name=“inspirationTextStyle">@style/FragmentInspTextStyle</item>

Slide 57

Slide 57 text

API Strategies - Isolated Activity <item name="inspirationActivityStyle">@style/ShiftInspirationStyle</item> <item name=“inspirationListStyle">@style/ShiftInspListStyle</item> <item name=“inspirationTextStyle">@style/ShiftInspTextStyle</item>

Slide 58

Slide 58 text

API Strategies - Hybrid

Slide 59

Slide 59 text

API Strategies - Hybrid Intent i = new Intent(this, ImageChooserActivity.class); startActivityForResult(i, REQUEST_CHOOSE_PHOTO);

Slide 60

Slide 60 text

Design a Good API UI Library Utility Library public interface ImageLoader {/ }/

Slide 61

Slide 61 text

Distribution • Open Source (jcenter, Maven Central) • Private Maven Repo

Slide 62

Slide 62 text

Document and Communicate

Slide 63

Slide 63 text

Document and Communicate • If a library is only used once, is it a library? • Communicate outside of engineering • Sales needs to know what to sell • Design needs to know components and limitations • Javadoc for Engineering, Wiki for others

Slide 64

Slide 64 text

Manage the Project

Slide 65

Slide 65 text

Manage the Project • Treat internal libraries like a client project • Dedicate resources • Manage requests • Define requirements • Enforce versioned release cycle

Slide 66

Slide 66 text

Reusable Libraries Ryan Harter @rharter +RyanHarter

Slide 67

Slide 67 text

Good Open Source Libraries

Slide 68

Slide 68 text

Android Arsenal https://android-arsenal.com

Slide 69

Slide 69 text

Retrofit public interface GithubService { List listRepos(String user); } http://square.github.io/retrofit/ A type-safe REST client for Android and Java

Slide 70

Slide 70 text

Retrofit public interface GithubService { @GET("/users/{user}/repos") List listRepos(@Path("user") String user); } http://square.github.io/retrofit/ A type-safe REST client for Android and Java

Slide 71

Slide 71 text

Retrofit public interface GithubService { @GET("/users/{user}/repos") List listRepos(@Path("user") String user); } RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); http://square.github.io/retrofit/ A type-safe REST client for Android and Java

Slide 72

Slide 72 text

Retrofit public interface GithubService { @GET("/users/{user}/repos") List listRepos(@Path("user") String user); } RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GithubService service = restAdapter.create(GithubService.class); http://square.github.io/retrofit/ A type-safe REST client for Android and Java

Slide 73

Slide 73 text

Retrofit public interface GithubService { @GET("/users/{user}/repos") List listRepos(@Path("user") String user); } RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GithubService service = restAdapter.create(GithubService.class); List repos = service.listRepos("octocat"); http://square.github.io/retrofit/ A type-safe REST client for Android and Java

Slide 74

Slide 74 text

Retrofit public interface GithubService { @GET("/users/{user}/repos") List listRepos(@Path("user") String user); } RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build(); GithubService service = restAdapter.create(GithubService.class); List repos = service.listRepos("octocat"); http://square.github.io/retrofit/ A type-safe REST client for Android and Java

Slide 75

Slide 75 text

Picasso http://square.github.io/picasso/ Picasso.with(context) .load(“http://i.imgur.com/DvpvklR.png") .into(imageView); A powerful image downloading and caching library for Android

Slide 76

Slide 76 text

Android Support https://developer.android.com/tools/support-library/features.html • AppCompat • ActionBar and friends • Material Themes • CardView • GridLayout • MediaRouter • Palette • RecyclerView

Slide 77

Slide 77 text

Calligraphy https://github.com/chrisjenx/Calligraphy Custom fonts in Android an OK way. <item name="fontPath">fonts/Roboto-ThinItalic.ttf</item> Styles Direct

Slide 78

Slide 78 text

Butterknife http://jakewharton.github.io/butterknife/ View "injection" library for Android class ExampleActivity extends Activity { @InjectView(R.id.title) TextView title; @InjectView(R.id.subtitle) TextView subtitle; @InjectView(R.id.footer) TextView footer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.inject(this); // TODO Use "injected" views... } }

Slide 79

Slide 79 text

Butterknife http://jakewharton.github.io/butterknife/ View "injection" library for Android class ExampleActivity extends Activity { @InjectView(R.id.title) TextView title; @InjectView(R.id.subtitle) TextView subtitle; @InjectView(R.id.footer) TextView footer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.inject(this); // TODO Use "injected" views... } }

Slide 80

Slide 80 text

Reusable Libraries Ryan Harter @rharter +RyanHarter