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

Android App Pro-tips - GDG DevFest Taipei 2016

Android App Pro-tips - GDG DevFest Taipei 2016

Avatar for Andrew Chen

Andrew Chen

December 03, 2016

Other Decks in Programming

Transcript

  1. λ

  2. λ

  3. final StringBuilder sb = new StringBuilder(); String strLine; BufferedReader buffer

    = null; InputStreamReader reader = null; try { reader = new InputStreamReader(stream); buffer = new BufferedReader(reader); while ((strLine = buffer.readLine()) != null) { sb.append(strLine); } } catch (final IOException ignore) { // ignore } finally { try { if (buffer != null) buffer.close(); if (reader != null) reader.close(); stream.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); try-with-resource
  4. final StringBuilder sb = new StringBuilder(); String strLine; BufferedReader buffer

    = null; InputStreamReader reader = null; try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { reader = new InputStreamReader(stream); buffer = new BufferedReader(reader); while ((strLine = buffer.readLine()) != null) { sb.append(strLine); } } catch (final IOException ignore) { // ignore } finally { try { if (buffer != null) buffer.close(); if (reader != null) reader.close(); stream.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); Retrolambda try-with-resource final StringBuilder sb = new StringBuilder(); String strLine; try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { while ((strLine = buffer.readLine()) != null) { sb.append(strLine); } } catch (final IOException ignore) { // ignore } return sb.toString();
  5. try-with-resource Kotlin return InputStreamReader(stream).use { return BufferedReader(it).use { val sb

    = StringBuilder() it.forEachLine { sb.append(it) } return sb.toString() } }
  6. try-with-resource Kotlin return stream.bufferedReader().use { it.readText() } return InputStreamReader(stream).use {

    return BufferedReader(it).use { val sb = StringBuilder() it.forEachLine { sb.append(it) } return sb.toString() } } return stream.reader().readText()
  7. try-with-resource Kotlin final StringBuilder sb = new StringBuilder(); String strLine;

    try (final BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { while ((strLine = buffer.readLine()) != null) { sb.append(strLine); } } catch (final IOException ignore) { // ignore } return sb.toString(); return stream.bufferedReader().use { it.readText() }
  8. class MainActivity extends AppCompatActivity { ImageView iconView; TextView titleView; @Override

    public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iconView = (ImageView) findViewById(R.id.icon); titleView = (TextView) findViewById(R.id.title); titleView.setText("Hello, world!"); } } findView
  9. class MainActivity extends AppCompatActivity { @BindView(R.id.icon) ImageView iconView; @BindView(R.id.title) TextView

    titleView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iconView = (ImageView) findViewById(R.id.icon); titleView = (TextView) findViewById(R.id.title); ButterKnife.bind(this); titleView.setText("Hello, world!"); } } findView ButterKnife class MainActivity extends AppCompatActivity { @BindView(R.id.icon) ImageView iconView; @BindView(R.id.title) TextView titleView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); titleView.setText("Hello, world!"); } }
  10. class MainActivity : AppCompatActivity { @BindView(R.id.icon) val iconView: ImageView by

    bindView(R.id.icon) @BindView(R.id.title) val titleView: TextView by bindView(R.id.title) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ButterKnife.bind(this) titleView.text = "Hello, world!" } } findView KotterKnife
  11. import kotlinx.android.synthetic.main.activity_main.icon import kotlinx.android.synthetic.main.activity_main.title class MainActivity : AppCompatActivity { val

    iconView: ImageView by bindView(R.id.icon) val titleView: TextView by bindView(R.id.title) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) title.text = "Hello, world!" } } findView Android Extensions
  12. import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity { override fun onCreate(savedInstanceState:

    Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) title.text = "Hello, world!" } } findView Android Extensions class MainActivity extends AppCompatActivity { @BindView(R.id.icon) ImageView iconView; @BindView(R.id.title) TextView titleView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); titleView.setText("Hello, world!"); } }
  13. public class User { @NonNull private final String name; private

    final int id; public String getName() { return name; } public int getId() { return id; } public User(@NonNull String name, int id) { this.name = name; this.id = id; } // .. @Override public String toString() { return "User{" + "name=" + name + ", id=" + id + "}"; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof User) { User that = (User) o; return (this.name.equals(that.getName())) && (this.id == that.getId()); } return false; } @Override int hashCode() { return Objects.hashCode(name, id); } POJO
  14. POJO @AutoValue public abstract class User { @NonNull public abstract

    String name(); public abstract int id(); } AutoValue
  15. Date int getAge() { Date date = new SimpleDateFormat("yyyy-MM-dd").parse("1984-11-19"); return

    (new Date().getTime() - date.getTime()) / (365 * 24 * 60 * 60 * 1000L); } Date
  16. Date int getAge() { Calendar birth = Calendar.getInstance(); birth.setTime( new

    SimpleDateFormat("yyyy-MM-dd").parse("1984-11-19")); Calendar now = Calendar.getInstance(); int year1 = now.get(Calendar.YEAR); int year2 = birth.get(Calendar.YEAR); int age = year1 - year2; int month1 = now.get(Calendar.MONTH); int month2 = birth.get(Calendar.MONTH); if (month2 > month1) { age--; } else if (month1 == month2) { int day1 = now.get(Calendar.DAY_OF_MONTH); int day2 = birth.get(Calendar.DAY_OF_MONTH); if (day2 > day1) { age--; } } return age; } Calendar
  17. Date fun getAge(): Int { return "1984-11-19".between(now()).years } Kotlin fun

    String.between(from: LocalDate) = Period.between(LocalDate.parse(this), from)
  18. Date fun getAge(): Int { return ("1984-11-19" between now()).years }

    Kotlin - Bonus infix fun String.between(from: LocalDate) = Period.between(LocalDate.parse(this), from) infix fun String.between(from: String) = between(LocalDate.parse(from))
  19. Date fun getAge(): Int { return ("1984-11-19" between "2016-11-19").years }

    val yesterday = 1.days.ago val yesterday = 1 days ago Kotlin - Bonus
  20. preferences.edit { putString("first_name", "Andrew") putString("last_name", "Chen") remove("age") } inline fun

    SharedPreferences.edit( func: SharedPreferences.Editor.() -> Unit) { val editor = edit() editor.func() editor.apply() } PrefsUtils Kotlin
  21. db.beginTransaction(); try { db.delete("users", "first_name = ?", new String[] {

    "Andrew" }); db.delete("users", "first_name = ?", new String[] { "yongjhih" }); db.setTransactionSuccessful(); } finally { db.endTransaction(); } DbUtils
  22. Databases.from(db).inTransaction(it -> { it.delete("users", "first_name = ?", new String[] {

    "Andrew" }) it.delete("users", "first_name = ?", new String[] { "yongjhih" }) }); DbUtils
  23. db.inTransation { delete("users", "first_name = ?", arrayOf("Andrew")) delete("users", "first_name =

    ?", arrayOf("yongjhih")) } inline fun SQLiteDatabase.inTransaction(func: SQLiteDatabase.() -> Unit) { beginTransaction() try { func() setTransactionSuccessful() } finally { endTransaction() } } DbUtils Kotlin
  24. Lazy List<Integer> getAgeList(List<User> users) { List<Integer> ageList = new ArrayList<>();

    for (User user : users) { ageList.add(user.getAge()); } return ageList; } class User(val age: Int, val gender: Int)
  25. Lazy List<Integer> getAgeList(List<User> users) { List<Integer> ageList = new ArrayList<>();

    for (User user : users) { ageList.add(user.getAge()); } return ageList; } List<User> getFemaleList(List<User> users) { List<User> femaleList = users; Iterator<User> it = users.iterator(); while (it.hasNext()) { if (it.next().getGender() != User.FEMALE) { it.remove(); } } return femaleList; }
  26. Lazy List<Integer> getAgeList(List<User> users) { List<Integer> ageList = new ArrayList<>();

    for (User user : users) { ageList.add(user.getAge()); } return ageList; } List<User> getFemaleList(List<User> users) { List<User> femaleList = users; Iterator<User> it = users.iterator(); while (it.hasNext()) { if (it.next().getGender() != User.FEMALE) { it.remove(); } } return femaleList; } List<Integer> getFemaleAgeList(List<User> users) { return getAgeList(getFemaleList(users)); } List<Integer> femageAgeList10 = getFemaleAgeList(users).subList(0, 10);
  27. Lazy List<Integer> getAgeList(List<User> users) { List<Integer> ageList = new ArrayList<>();

    return new MapList(users, User::getAge); for (User user : users) { ageList.add(user.getAge()); } return ageList; } List<User> getFemaleList(List<User> users) { List<User> femaleList = users; Iterator<User> it = users.iterator(); while (it.hasNext()) { if (it.next().getGender() != User.FEMALE) { it.remove(); } } return femaleList; } List<Integer> getFemaleAgeList(List<User> users) { return getAgeList(getFemaleList(users)); } class MapList<T, R> extends ArrayList<R> { // lazy Function<T, R> function; List<T> list; List<R> cache; // TODO MapList(List<T> list, Function<T, R> function) { // .. } @Override public T get(int index) { return function.apply(list.get(index)); } // .. }
  28. Lazy List<Integer> getAgeList(List<User> users) { return new MapList(users, User::getAge); }

    List<User> getFemaleList(List<User> users) { List<User> femaleList = users; return new FilterList(users, user -> user.getGender() != User.FEMALE); while (it.hasNext()) { if (it.next().getGender() != User.FEMALE) { it.remove(); } } return femaleList; } List<Integer> getFemaleAgeList(List<User> users) { return getAgeList(getFemaleList(users)); } class FilterList<T> extends ArrayList<T> { // lazy Predicate<T> predicate; List<T> list; List<T> cache; listIterator<T> it; int index = 0; FilterList(List<T> list, Predicate<T> predicate) { this.it = list.listIterator(); // .. } // .. @Override public T get(int index) { if (this.index < index) { while (it.hasNext()) { T item = it.next(); if (predicate.test(item)) { cache.add(item); this.index++; } if (this.index >= index) return cache.get(index); } } else { return cache.get(index); } throw new IllegalArgumentException(); } }
  29. Lazy List<Integer> getAgeList(List<User> users) { return new MapList(users, User::getAge); }

    List<User> getFemaleList(List<User> users) { return new FilterList(users, user -> user.getGender() != User.FEMALE); } List<Integer> getFemaleAgeList(List<User> users) { return getAgeList(getFemaleList(users)); } List<Integer> femageAgeList10 = getFemaleAgeList(users).subList(0, 10);
  30. Lazy Observable<Integer> getAgeList(Observable<User> users) { return users.map(User::getAge); } Observable<User> getFemaleList(Observable<User>

    users) { return users.filter(user -> user.getGender() != User.FEMALE); } Observable<Integer> getFemaleAgeList(Observable<User> users) { return getAgeList(getFemaleList(users)); } Observable<Integer> femageAgeList10 = getFemaleAgeList(users).take(10); RxJava
  31. Lazy Sequence<Integer> getAgeList(Sequence<User> users) { return users.map(User::getAge) } Sequence<User> getFemaleList(Sequence<User>

    users) { return users.filter { it.getGender() != User.FEMALE } } Sequence<Integer> getFemaleAgeList(Sequence<User> users) { return getAgeList(getFemaleList(users)) } Sequence<Integer> femageAgeList10 = getFemaleAgeList(users).take(10) Kotlin
  32. distinct List<User> getPostedUsers(List<Post> posts) { Map<String, User> users = new

    ArrayMap<>(); for (Post post : posts) { User user = post.getUser(); users.put(user.getObjectId(), user); } return new ArrayList<>(users.values()); }
  33. merge List<User> getActivityUsers(List<Post> posts, List<Comment> comments) { Map<String, User> users

    = new ArrayMap<>(); for (Post post : posts) { User user = post.getUser(); users.put(user.getObjectId(), user); } for (Comment comment : comments) { User user = comment.getUser(); users.put(user.getObjectId(), user); } return new ArrayList<>(users.values()); } RxJava
  34. merge Observable<User> getActivityUsers(Observable<Post> posts, Observable<Comment> comments) { return Observable.merge( posts.map(post

    -> post.getUser()), comments.map(comment -> comment.getUser()) ) .distinct(user -> user.getObjectId()); } RxJava
  35. Callback Hell void login(Activity activity, LoginListenr loginListener) { loginFacebook(activity, fbUser

    -> { getFbProfile(fbUser, fbProfile -> { loginParse(fbProfile, parseUser -> { getParseProfile(fbProfile, parseProfile -> { loginListener.onLogin(parseProfile); }); }); }); }); }
  36. Callback Hell RxJava Observable<FbUser> loginFacebook(Activity activity) { return Observable.create(sub ->

    { loginFacebook(activity, (fbUser, e) -> { if (sub.isUnsubscribed()) return; if (e != null) { sub.onError(e); return; } sub.onNext(fbUser); sub.onCompleted(); }); }); } Observable<FbProfile> getFbProfile(FbUser fbUser) { // .. } Observable<ParseUser> loginParse(FbProfile fbProfile) { // .. } Observable<ParseProfile> getParseProfile(ParseUser parseUser) { // .. } void login(Activity activity, LoginListener loginListener) { Observable.just(activity) .flatMap(activity -> loginFacebook(activity)) .flatMap(fbUser -> getFbProfile(fbUser)) .flatMap(fbProfile -> loginParse(fbProfile)) .flatMap(parseUser -> getParseProfile(parseUser)) .subscribe(loginListener::onLogin); }
  37. void getRepos(String user, final Callback<Repo> callback) { String url =

    "https://api.github.com/" + "users/" + user + "/repos"; Volley.newRequestQueue(context) .add(new StringRequest(url, res -> { callback.done((List<Repo>) new Gson() .fromJson(res, new TypeToken<List<Repo>>(){}.getType())); }), e -> callback.error(e))); } API Volley
  38. void getRepos(String user, final Callback<Repo> callback) { String url =

    "https://api.github.com/" + "users/" + user + "/repos"; Volley.newRequestQueue(context) .add(new StringRequest(url, res -> { callback.done((List<Repo>) new Gson() .fromJson(res, new TypeToken<List<Repo>>(){}.getType())); }), e -> callback.error(e))); } void getStarredUsers(String repo, final Callback<User> callback)? API Volley
  39. void getRepos(String user, final Callback<Repo> callback) { String url =

    "https://api.github.com/" + "users/" + user + "/repos"; get(url, callback); } <T> void get(final Callback<T>) { Volley.newRequestQueue(context) .add(new StringRequest(url, res -> { callback.done((List<T>) new Gson() .fromJson(res, new TypeToken<List<T>>(){}.getType())); }), e -> callback.error(e))); } void getStarredUsers(String repo, final Callback<User> callback) { API Volley
  40. void getRepos(String user, final Callback<Repo> callback) { String url =

    "https://api.github.com/" + "users/" + user + "/repos"; get(url, callback); } <T> void get(final Callback<T>) { Volley.newRequestQueue(context) .add(new StringRequest(url, res -> { callback.done((List<T>) new Gson() .fromJson(res, new TypeToken<List<T>>(){}.getType())); }), e -> callback.error(e))); } void getStarredUsers(String repo, final Callback<User> callback) { String url = "https://api.github.com/" + "repos/" + repo + "/stargazers"; get(url, callback); } API Volley
  41. public interface GitHub { @GET("users/{user}/repos") void getRepos(@Path("user") String user, Callback<List<Repo>>

    repos); } Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHub github = retrofit.create(GitHub.class); github.getRepos("yongjhih", new SimpleCallback(repos -> {}, e -> {})); API Retrofit
  42. public interface GitHub { @GET("users/{user}/repos") void getRepos(@Path("user") String user, Callback<List<Repo>>

    repos); @GET("repos/{repo}/stargazers") void getStarredUsers(@Path("repo") String repo, Callback<List<User>> users); } Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHub github = retrofit.create(GitHub.class); github.getRepos("yongjhih", new SimpleCallback(repos -> {}, e -> {})); github.getStarredUsers("yongjhih/android-proguards", new SimpleCallback(repos -> {}, e -> {})); API Retrofit
  43. public interface GitHub { @GET("users/{user}/repos") Observable<List<Repo>> getRepos(@Path("user") String user, Callback<Repo>

    repos); @GET("repos/{repo}/stargazers") Observable<List<Repo>> getStarredUsers(@Path("repo") String repo, Callback<User> users); } Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHub github = retrofit.create(GitHub.class); github.getRepos("yongjhih") .subscribe(repos -> {}, e -> {}); github.getStarredUsers("yongjhih/android-proguards") .subscribe(users -> {}, e -> {}); API Retrofit + RxJava
  44. String version = "UNKNOWN"; if (computer != null) { Soundcard

    soundcard = computer.getSoundcard(); if (soundcard != null) { USB usb = soundcard.getUSB(); if (usb != null) { version = usb.getVersion(); } } } Null check class USB { String getVersion() {} } class Soundcard { USB getUsb() {} } class Computer { Soundcard getSoundcard() {} }
  45. String version = computer.getSoundcard() .flatMap(Soundcard::getUsb) .map(USB::getVersion) .orElse("UNKNOWN"); Null check class

    USB { Optional<String> getVersion() {} } class Soundcard { Optional<USB> getUsb() {} } class Computer { Optional<Soundcard> getSoundcard() {} }
  46. String version = computer.getSoundcard() .flatMap(Soundcard::getUsb) .map(USB::getVersion) .toBlocking() .singleOrDefault("UNKNOWN"); // IllegalArgumentException

    if second emit Null check RxJava1 class USB { Observable<String> getVersion() {} } class Soundcard { Observable<USB> getUsb() {} } class Computer { Observable<Soundcard> getSoundcard() {} }
  47. String version = computer.getSoundcard() .flatMap(Soundcard::getUsb) .map(USB::getVersion) .toSingle("UNKNOWN") .blockingGet() Null check

    class USB { Maybe<String> getVersion() {} } class Soundcard { Maybe<USB> getUsb() {} } class Computer { Maybe<Soundcard> getSoundcard() {} } RxJava2
  48. var version = computer.soundcard?.usb?.version ?: "UNKNOWN" Null check Kotlin class

    USB (val version: String) class Soundcard (val usb: USB?) class Computer (val soundcard: Soundcard?)
  49. class ProfileFragment extends Fragment { // .. ImageView mAvatarView; public

    ImageView getAvatarView() { if (mAvatarView == null) { mAvatarView = (ImageView) v.findViewById(R.id.avatar); } return mAvatarView; } } Lazy member
  50. class ProfileFragment extends Fragment { // .. val avatarView: ImageView

    by lazy { public ImageView getAvatarView() { if (mAvatarView == null) { mAvatarView = v.findViewById(R.id.avatar) as ImageView } return mAvatarView; } } profileFragment.avatarView.setImageResource(R.drawable.icon) Lazy member Kotlin
  51. app/proguard-rules.pro: ## RxJava1 -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long

    producerIndex; long consumerIndex; } # .. ## Dagger # .. ## retrofit # .. # Icepick -dontwarn icepick.** -keep class **$$Icicle { *; } -keepnames class * { @icepick.Icicle *;} Proguards build.gradle: proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  52. app/proguard-rules.pro: app/rxjava.pro: -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex;

    long consumerIndex; } # .. app/dagger.pro: # .. ## retrofit app/retrofit.pro: ## Icepick app/icepick.pro: ## .. Proguards build.gradle: proguardFiles getDefaultProguardFile('proguard-android.txt'), 'dagger.pro', 'retrofit.pro', 'icepick.pro', ...
  53. Proguards build.gradle: proguardFiles getDefaultProguardFile('proguard-android.txt'), fileTree(include: ['*.pro'], dir: '.') app/proguard-rules.pro: app/rxjava.pro:

    -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } # .. app/dagger.pro: # .. ## retrofit app/retrofit.pro: ## Icepick app/icepick.pro: ## ..