최근에 kotlin + aac 로 안드 개발을 하였고, 개발 도중 여러가지 드는 생각과 질문들을 이곳에 우선 적어볼까한다. 내가 직접 실험하고 비교분석할 순있지만 이미 kotlin + aac 개발을 많이 해온 사람들이 충분히 있다고 생각되기에 검색으로 쉽게 해답을 얻기를 기대해 본다.

- UI가 top, middle, bottom으로 분리되어 있고 ViewModel이 monolithic하게 되는걸 방지하고자 1 Fragment, 3 ViewModel을 만들경우 Fragment는 3개의 ViewModel에 의존성이 생기게 되는데. top, middle, bottom에 대응하는 UIModel을 제공하는 ViewModel 3개를 만들고 이 세개의 ViewModel을 field로 갖고 View(Acitivity, Fragment)에는 이 ViewModel하나에만 의존하도록 하는건 어떨까? 그러면 View는 ViewModel 하나에만 의존하게 되고 ViewModel 3개와 이를 field로 같는 ViewModel은 parent-child 계층 구조로 설계하면 View 입장에서는 interface가 간편해지고 ViewModel을 선택해서 특정 요청을 해야할지 고민할 필요가 없을것 같다. 간단한 코드 샘플로 비교해 보면 좀더 명확해 질거 같은데? 근데 ViewModel builder function이 Fragment, Activity에 있네;;;;
- liveData() extension function으로 LiveData<T> 를 만들고 emit(), emitSource(), LiveData<T>.switchMap() 를 활용하는 방법이 코드 라인을 몇줄더 절약 가능한 방법인거 같은데. 아직 liveData() 함수 사용에 대한 이해도가 낮으니 좀더 공부해 봐야겠다.
  # LiveData.switchMap() 을 사용하는 예제
    https://gist.github.com/JoseAlcerreca/f2f2743ccd933f701a93beb00973e679#file-myviewmodel-kt

  # LiveData.switchMap() 을 사용하지 않고 Flow을 사용하는 예제. Flow가 더 많은 operation을 제공하고 코드도 간결해 보인다.
    https://gist.github.com/JoseAlcerreca/9a17b1faf1c76bd9ba35e15901f964c5#file-myviewmodel-kt

- kotlin에 Pair<T, V>, Triple<A, B, C>는 있는데, 4, 5, 6 field를 같는 data class가 필요하면 naming 규칙 만들어서 팀원들과 쓰자고 해야겠다.

'android' 카테고리의 다른 글

Firebase  (0) 2016.06.11
Android N Preview 대응  (0) 2016.06.04
RecyclerView에 Endless Scrolling(무한 스크롤)을 구현해 보자  (0) 2016.03.23
ButterKnife를 왜 쓰냐구요?  (0) 2016.02.29
뭐? 이벤트? 버스 태워 보내! #2  (0) 2016.02.29
Posted by 제이제이랩
,

Firebase

android 2016. 6. 11. 23:53

이번 구글 IO 2016 에서 소개된(?) Firebase에 대해서 글을 쓸 필요는 없을 것 같습니다.ㅋ

왜냐하면 아래와 같이 구글에서 친절하게 Firebase에 대한 설명 동영상을 여러개 만들었습니다. 시간이 있으실 때 살펴보시면됩니다.



유튜브 동영상 내용만 보면 벤처나 중소업체에게 희소식일 것 같습니다. 별도의 푸쉬 서버를 구비/구현하지 않아도 Firebase를 사용하면 앱 사용자들에게 간단한 타입의 푸쉬를 보낼 수 있습니다. 동영상 내용으로만 보면 간단한 데이터베이스 또한 제공이 되어 Firebase admin 페이지에서 DB 내용을 변경하면 실시간으로 앱 UI에 그 변경사항이 적용됩니다. 충격적입니다. 


우선은 key, value 형식의 데이터를 저장하는 것으로 보이는데, 이 부분은 좀더 살펴보고 어느 수준까지 제공되는지는 좀더 봐야 할것 같습니다. 괴물을 내놓은 느낌적인 느낌입니다.


아래는 파이어 베이스 링크입니다.


https://firebase.google.com/

Posted by 제이제이랩
,

Android N Preview 대응

android 2016. 6. 4. 11:14

구글 IO에서 Android N Preview를 대대적으로 발표한지 몇주가 지났습니다. Android Studio 및 개발툴에 대한 대대적 보강에 초점이 맞춰진 것같습니다.


http://googledevkr.blogspot.kr/2016/06/android-studio-22-preview-new-ui.html


Android N Preview에 소개된 내용을 한글로 정리한 글은 위 링크에서 보실 수 있습니다.


우선 저는 모든 스터디를 급 홀딩하고  ConstraintLayout, Firebase, Java 8 일부 특징, Expresso Recorder등을 중점적으로 봐야되겠습니다. 다행히 Marshmallow 릴리즈 될 때 런타임 퍼미션이나 도즈 모드와 같은 추가는 없는것 같아 급박하게 앱 대응할게 없을 것 같아 다행입니다. ConstraintLayout에 대한 내용은 좀 더 분석하여 블로그에 올려야겠네요.


현재 개발중인 안드로이드 프로젝트를 Jack&Jill 로 컴파일 해 보고 싶으시면 아래 링크를 참고하시기 바랍니다. Lambda expression 사용할 수 있고 multidex, proguard가 하던일을 Jack이 해줘서 더이상 multidex, proguard를 사용할 필요가 없으리라 보이는데, 우선 저는 샘플앱에 적용해 본후 정말 그런지, 좋은지 나쁜지 경험해 보고 legacy 앱에 신속히 적용해야 되는 것인지 늦게 적용해도 상관없을지 판단해야 겠습니다.


https://source.android.com/source/jack.html

http://tools.android.com/tech-docs/jackandjill


2016.06.04 현재 DI Dagger2를 적용한 앱이라면 Jack&Jill 사용하기엔 무리가 있을 것 같습니다. 제가 샘플앱에 적용하려는데 apt plugin 관련해서 이슈가 있는것 같은데 아직 나이스한 해결책이 없는것 같습니다. annotation processing 사용하는 라이브러리들에서 문제가 우후준순 쏟아지는거 아닌가 걱정됩니다. 아무튼 현재 제 샘플앱에는 Android Studio 2.1.1에서 Jack&Jill 로 컴파일을 못하겠습니다.





Posted by 제이제이랩
,


오랜만에 UI 코드에 손 대다가 RecyclerView에 무한 스크롤링 기능을 넣어야했습니다. CodePath를 보니 RecyclerView.OnScrollListener를 상속하여 복잡한 계산을 한  EndlessRecyclerViewScrollListener.java가 소개되어 있습니다.[CodePath Endless scrolling page 링크


좀 더 심플한 방법이 있을것 같아 RecyclerView JAVADOC 문서를 좀 보고 다른 블로그도 보았지만. 전 아래와 같은 해결책을 CodePath에 추가했습니다.[CodePath에 추가한 글 링크] RecyclerView에서 main thread 점유는 최소화 하는게 좋아서 최대한 스크롤링에 대한 계산을 별도로 안하는 걸 고민 하다가 RecyclerView.Adapter.onBindViewHolder()를 이용했습니다. 한번 써보시고 이상하시면 답글 달아주세요. 테스트 해보니 딱히 문제는 없지만, fine tuning 하시려면 if(position == getItemCount() - 1) 에서 1을 변경해야 할 것 같습니다. RecyclerView에 보여지는 각 뷰의 크기에 따라 1을 3으로 변경하던지 다른 값으로 변경해서 fine tunig 하세요. (전 아래와 같이 글을 올렸는데 CodePath 담당자가 문장을 좀 바꾸었군요ㅋ)


Instead of using EndlessRecyclerViewScrollListener.java introduced in the following section. There's more simple and less computational way to implement endless scrolling. Make use of the following code snippet for endless scrolling.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
  EndlessScrollListener endlessScrollListener
  
  ...
  public void setEndlessScrollListener(EndlessScrollListener endlessScrollListener) {
      this.endlessScrollListener = endlessScrollListener;
  }
  
  @Override
  public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
      final Data data = dataset.get(position);

      // you can cache getItemCount() in a member variable for more performance tuning
      if(position == getItemCount() - 1) {  
          if(endlessScrollListener != null) {
              endlessScrollListener.onLoadMore(position);
          }
      }

      ...
  }
  
  @Override
  public int getItemCount() {
      if(dataset == null)
          return 0;
      else
          return dataset.size();
  }
  ...
  
  public interface EndlessScrollListener {
      /**
       * Loads more data.
       * @param position
       * @return true loads data actually, false otherwise.
       */
      boolean onLoadMore(int position);
  }
}


'android' 카테고리의 다른 글

Firebase  (0) 2016.06.11
Android N Preview 대응  (0) 2016.06.04
ButterKnife를 왜 쓰냐구요?  (0) 2016.02.29
뭐? 이벤트? 버스 태워 보내! #2  (0) 2016.02.29
뭐? 이벤트? 버스 태워 보내! #1  (0) 2016.02.29
Posted by 제이제이랩
,

개인적으로 안드로이드 UI 개발할 때 ButterKnife를 애용합니다.


제가 ButterKnife를 쓰는 가장 중요한 이유는 각 View, Resource Look-up 타임을 없애주고 다수의 안드로이드 개발자들과 협업할때 View, Resource를 바인딩 하는 convention을 제공하기 때문입니다. 성능상 단점이나 장점은 없지만 코드의 가독성을 높여주는데 참 좋은것 같습니다.


http://jakewharton.github.io/butterknife/ 


위 사이트에 가면 제가 중요하게 생각하는 점이 다 설명되어 있는데요.  Activity에서 @Bind, @BindColor, @BindString등의  annotation을 onCreate() 메소드 위에서 명시하여 사용하는게 일반적입니다. 각 멤버 변수가 레이아웃의 어느 뷰와 연결되어 있는지 한눈에 알 수 있습니다. String, Color, Drawable같은 리소스도 아래와 같이 onCreate() 메소드 위에서 다 바인딩하여 사용하는게 일반적인데 이러한 컨벤션을 지키면 소스 코드가 정말 깔끔해 집니다. 그리고 각 리소스와 뷰가 어디에 바인딩 되어 있는지 1초도 안되어 알 수 있습니다.

class ExampleActivity extends Activity {
  @Bind(R.id.title) TextView title;
  @Bind(R.id.subtitle) TextView subtitle;
  @Bind(R.id.footer) TextView footer;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.bind(this);
    // TODO Use fields...
  }
}

만일 Activity.findViewById() 메소드를 사용하면 어떤 일이 벌어질까요? 아래 코드를 보시죠.

class ExampleActivity extends Activity {
  TextView title;
  TextView subtitle;
  TextView footer;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);

    title = findViewById(R.id.title);
    subtitle = findViewById(R.id.subtitle);
    footer = findViewById(R.id.footer);
    // TODO Use fields...
  }
}

ButterKnife 없는 코드는 아래와 같이 Activity.findViewById()를 사용하여 바인딩을 해줘야 하는데요. 일명 boilerplate 코드이지요. 또 중요한 건 여러 개발자들과 같이 작업하다보면 Activity.findViewById() 메소드를 이곳 저곳 그곳에서 호출하여 어떤 멤버 변수가 어떤 View, Resource와 바인딩 되어 있는지 찾기가 쉽지 않습니다. ButterKnife로는 1초만에 걸리던게 적게는 수초 많게는 수십초이상이 걸리는데요. UI 개발하면서 개발자가 각 멤버 변수가 어떤 뷰, 리소스에 바인딩 되어있는지 항상 기억하지 못하기 때문에 이를 알기 위해 look-up 을 자주 합니다. 이렇게 자주하는 행위를 ButterKnife를 이용해 해쉬 처럼 빠르게 스트레스 안받고 작업하는 것과 바인딩 포인트를 찾을 때까지 탐색하는 것은 분명 차이가 있습니다. 게으른 코딩을 하기 위해 ButterKnife를 사용해 보시는건 어떠시나요? 완벽히 익히는데 1시간도 걸리지 않습니다. 그리곤 게을러 질 수 있어요. 아래 사이트 참고하셔서 굿 코딩하세요.


http://jakewharton.github.io/butterknife/


추가로 ButterKnife는  클릭 이벤트 핸들러를 annotation으로 명시하고 처리할 수 있습니다. 이 또한 기존의 클릭 이벤트 처리하면서 발생한 boilerplate 코드를 제거한겁니다.


요 아래는 안드로이드 스튜이오 플러그인입니다. 자동으로 layout xml의 뷰에 해당하는 멤버변수를 만들어 주네요~! 엄청 편합니다.

https://github.com/avast/android-butterknife-zelezny

Posted by 제이제이랩
,

이벤트 Bus Otto에 대한 두번째 글입니다.


http://tmondev.blog.me/220610369558


Posted by 제이제이랩
,

회사에서 예전에 기술 아티클로 작성한 내용입니다.


앱 개발할 때 이벤트 버스 Otto를 사용하여 이벤트 처리를 편하게 하자라는게 요지입니다. 서두의 추상화 뭐라고 하는건 무시해주세요;; 추상화 어쩌거 저쩌고는 크게 관심도 없는데 굳이 어떤분이 서두에 그런 문장을 넣으라고 해서 억지로 넣었네요;;;

미국 쪽 블로그 보면 RxJava가 핫하게 쓰이고 있는데, learning curve가 아주 높더군요. 저도 공부할 려면 시간 좀 걸릴것 같고 모든 팀원이 공부를 좋아한다는 보장이 없기 때문에 일차적으로 이벤트 처리는 learning curve가 짧고 낮은 이벤트 버스 Otto가 좋을 것 같아 보입니다. 


http://tmondev.blog.me/220605524250


Posted by 제이제이랩
,

안드로이드 앱 개발하실 때 어떤 비트맵 캐쉬 라이브러리를 사용하시나요?

지금 회사 오기전에는 UIL부터 Picasso까지 써보았는데요. 처음 입사했을 때 큰 회사인데도 불구하고 앱의 bitmap 캐쉬 관리가 엉망이어서 Picasso를 적용하려 했었죠. 소스 코드 분석하고 그러던 중 갑자기 여러 이슈가 떨어져서 앱의 문제점만 분석한 문서만 만들어 놓았는데.. 몇 개월 후 입사하신 다른 분이 Glide를 소개해 주셔서 Glide를 제 개인 샘플앱에도 적용해보았습니다. 회사 앱에 적용은 다른분이 하셨다능.. 구글링 해보면 Picasso와의 벤치마킹은 Glide가 여러면에서 압승이었습니다.

(Square에서 Picasso 업그레이드 하려는 움직임을 보이는 것 같은데... 차후에 두 라이브러리의 대결이 궁금하네요ㅋㅋ)


여러 상품의 이미지를 앱에서 노출해야 하는데, 간혹 서버에서 이미지 URL 변경없이 이미지를 변경하는 경우가 있는데, 앱에는 캐쉬된 이전 이미지가 계속해서 나타나는 경우가 있죠. 저 같은 경우 Glide가 알아서 처리 해준다고 착각했었습니다. 혹시나 해서 Glide측에 문의 해보니 아니나 다를까 제가 기대한 cache invalidation은 없었습니다. (https://github.com/bumptech/glide/issues/996 참조)

저희 회사의 고객 서비스 센터에서 안드로이드는 최신 이미지가 정상적으로 노출된다 길래 okhttp에서 알아서 처리하나 추측했지만 아무리 봐도 Glide와 okhttp 연동에는 그런 작업이 없길래 도저히 안되겠다 싶어 제가 직접 확인해 보았는데, 역시나 HTTP 통신을 okhttp를 쓰도록 세팅하고 bitmap cache는 Glide로 하더라도 최신 이미지가 노출되지 않더라구요. 


제가 기대한 건 각 이미지의 ETag를 conditional GET request의 If-None-Match 헤더에 태워 보내서 서버에서 이미지가 변경되면 변경되었다고 응답하고 변경 안되면 안되었다고 응답하여 그 응답을 라이브러리 단에서 처리하여 포워딩 캐쉬가 되는 거였는데... 아무래도 conditional request 보내는 것도 적으면 몇 ms, 많으면 몇십, 몇백 ms가 걸릴수도 있어서 이를 피하기 위해 memory, disk 캐쉬 hit이 발생하면 바로 바로 캐쉬된 이미지를 가져다 뿌려주는 것으로 보입니다.

그러면 사용자에게 가장 최신의 이미지만을 노출하고 싶은데, 어떻게 해야할까요?


결론부터 말하면 이미지가 바뀌면 그 이미지 리소스를 가리키는 URL도 변경해서 클라이언트에 내려주면 된다입니다. Glide에서 cache invalidation을 위해 StringSignature라는 것을 제공하지만 이보다는 서버쪽에서 이미지가 변경되면 이미지 리소스에 대한 URL도 변경되도록 처리하고 이를 클라이언트가 전달 받아 뿌려지도록 하는 구조가 맞을 것 같습니다. 그러면 클라이언트측에서는 디스크, 메모리 캐쉬를 사용하여 앱 속도를 보장하면서 항상 사용자에게는 최신 이미지를 노출할 수가 있습니다.

Posted by 제이제이랩
,