[Android] Android Architecture Component

in kr •  7 years ago  (edited)



Android Architecture Component

안드로이드 개발하며 대표적으로 MVC - MVP - MVVM 패턴을 많이 사용한다.
나 역시 Todo project 를 참조하여 MVP 패턴을 주로 사용해왔다.



최근 다녀온 GDG DevFest Seoul 2017 에서 Android Architecture Component Codelab 세션을 통해

구글에서 제공하는 AAC(Android Architecture Component) library 를 접하게 되었다.

처음에 코드랩 신청할때는 그냥 기존 MVP나 MVVM 설명인줄... 사실 AAC 도 MVVM 의 일종 인거 같지만...

구글에서 친절히 codelab 을 제공하여 설명하고 있다면 한번이라도 살펴보는게 맞다고 생각한다.

그리고 구글 코드랩 페이지는 정말 좋다, 배울게 많다 :)

또한 Developer 문서에도 따로 정리까지 해주었다. (감동쓰...)

Android Architecture Component library 는 1.0 stable 버전이라 개인 프로젝트에 적용해보고자 AAC 를 좀더 분석해 보기로 하였다.

1. ViewModel

  • activity 나 fragment에 데이터를 제공하는 역할을 한다.
  • 데이터 로드, 제공, 사용자 수정 내용을 전달하기 위해 다른 구성요소를 호출하는등 데이터 처리의 비지니스 부분과의 통신을 처리한다.
  • View 에 대해 알지 못하며, 회전으로 인한 activity 재생성 등의 구성 변경에 영향을 받지 않는다. --> 즉, UI에 독립적이다!!!! (별표 백만개 *******) UI 테스트 할때 좋겠다.

주의해야 할 점은 ViewModel 에 Context나 View 클래스를 참조하면 안된다.

메모리 누수의 원인이 될 수 있기 때문이다.

아래 그림이 ViewModel 의 LifeScope 를 좀 더 명확히 설명해 주고 있다.
aac1

2. LiveData

  • Observable 데이터 홀더 이다.
  • app 안의 component 들이 명시적으로 dependency 를 갖지 않도록 하면서, 변경 사항을 LiveData 객체에서 관찰 할 수 있도록 한다.
  • Lifecycle 을 인지하고 있기 때문에 더이상 data가 필요하지 않게되면 자동으로 reference 를 clean up 한다. (오 좋다)
public class LiveDataTimerViewModel extends ViewModel {

   private static final int ONE_SECOND = 1000; //1초 스케줄링 (카운팅)

   private MutableLiveData<Long> mElapsedTime = new MutableLiveData<>(); // LiveData

   private long mInitialTime;

   public LiveDataTimerViewModel() {
       mInitialTime = SystemClock.elapsedRealtime();
       Timer timer = new Timer();

       // Update the elapsed time every second.
       timer.scheduleAtFixedRate(new TimerTask() {
           @Override
           public void run() {
               final long newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000;
               // setValue() cannot be called from a background thread so post to main thread.
               mElapsedTime.postValue(newValue);
           }
       }, ONE_SECOND, ONE_SECOND);

   }

   /**
    * LiveData ElapsedTime 를 반환한다.
    */
   public LiveData<Long> getElapsedTime() {
       return mElapsedTime;
   }
}
public class ChronoActivity3 extends AppCompatActivity {

    private LiveDataTimerViewModel mLiveDataTimerViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.chrono_activity_3);

        // ViewModel을 생성(등록?) 한다.
        mLiveDataTimerViewModel = ViewModelProviders.of(this).get(LiveDataTimerViewModel.class);

        subscribe();
    }

    /**
     * LiveData ElapsedTime 을 구독한다.
     */
    private void subscribe() {
        final Observer<Long> elapsedTimeObserver = new Observer<Long>() {
            @Override
            public void onChanged(@Nullable final Long aLong) {
                // 데이터 변경시 UI update
                String newText = ChronoActivity3.this.getResources().getString(
                        R.string.seconds, aLong);
                ((TextView) findViewById(R.id.timer_textview)).setText(newText);
                Log.d("ChronoActivity3", "Updating timer");
            }
        };
        // LiveDat elapsedTime에 observer를 등록한다.
        mLiveDataTimerViewModel.getElapsedTime().observe(this, elapsedTimeObserver);
    }
}

3. LifecycleOwner/LifecycleRegistryOwner

  • lifecycle 을 주관(?)하는 클래스이다. stable 버전에서 AppCompatActivity 와 Support Fragment 클래스에서 implement 하고 있다.
  • 해당 인터페이스를 implement 하고 다른 component를 주입하여 사용할 수 있다.

기존엔 아래와 같이 nCreate(), onStart(), onStop() 메소드를 통해 lifecycle을 직접 handling 해 왔었다.

class MyLocationListener {
     public MyLocationListener(Context context, Callback callback) {
         // ...
     }

     void start() {
         // connect to system location service
     }

     void stop() {
         // disconnect from system location service
     }
 }

 class MyActivity extends AppCompatActivity {
       private MyLocationListener myLocationListener;

       @Override
       public void onCreate(...) {
           myLocationListener = new MyLocationListener(this, (location) -> {
               // update UI
           });
       }

       @Override
       public void onStart() {
           super.onStart();
           myLocationListener.start();
           // manage other components that need to respond
           // to the activity lifecycle
       }

       @Override
       public void onStop() {
           super.onStop();
           myLocationListener.stop();
           // manage other components that need to respond
           // to the activity lifecycle
       }
 }



그러나 이제는 LifecycleObserver 를 implement 하여 Annotation 을 이용한 Lifecycle Handling 을 할 수 있다.

class MyLocationListener implements LifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START) // lifecycle event : onStart()
    void start() {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP) // lifecycle event : onStop()
    void stop() {
        // disconnect if connected
    }
}


class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}




dagger2와 espresso 도 적용해 보고싶은데... AAC 를 적용한 reference 가 별로 없다.
직접 해보고 판단하는 수밖에... 아니면 기다리거나... 무튼 삽질이라도 해보고 후회하는게 나을것 같다. 경험은 중요하니깐.


google Refern 이외 정리 및 이해하는데 참고한 페이지

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Congratulations! This post has been awarded a 100% upvote by @lottobot! This post was selected from among all recent posts as the winner of lottery #796, which had no valid entrants. You can win again by entering in @lottobot's regular lottery! To nominate a post for the regular lottery, just send 0.1 SBD or STEEM to @lottobot, and include the url of the post you would like to nominate as a memo. Learn more by reading the introductory post! Good luck!