2021년 5월 6일 목요일

Android 개발팁메모장

  • 라이프사이클

    1. 외워서 그릴줄 알아야 한다.
      Activity/Fragment 생명주기
      https://techbooster.org/wp-content/uploads/2014/12/lifecycle.png
      https://github.com/xxv/android-lifecycle
    2. onAttach(Fragment Only)
      Fragment 에만 있는 싸이클로서 Fragment 가 Activity 에 붙을때 실행된다.
    3. onActivityCreated(Fragment Only)
    4. onCreate
      액티비티를 최초에 실행할때 호출한다. 리소스 , layout 등의 초기화 작업을 한다. 또한 액티비티가 살아있는 동안에 유지해야 할 변수나 기능들을 초기화하고 보관해놓는다.
    5. onCreateView(Fragment Only)
      Fragment 에만 있는 싸이클로서 Fragment 의 경우는 여기서 layout 처리를 한다. Fragment 에서는 onCreate 에서 view 나 ui 관련 작업을 할수 없다.따라서 이곳에서 view 의 초기화, ui 관련 사항들을 지정할수있다. 뷰의 요소등에 대한 처리등 시간이 걸리는 로직은 onViewCreated 에서 하자.
    6. onViewCreated(Fragment Only)
      Fragment 에 view 가 초기화 되었다는 것을 알려준다. 인수로 view 를 보내주기 때문에 뷰의 각 요소에 대한 초기화 등을 할 수 있다.
    7. onActivityCreated(Fragment Only)
      Activity 가 화면에 보이는 상태이다 UI 조작이 가능한다. 단 Fragment 가 처음 실행될때만 실행된다.이때는 Activity 와 Fragment 가 연결된 상태이므로 두 객체간 상호작용이 가능하다.
    8. onStart
      UI가 화면에 보이게 하는단계이다.따라서 여기서 긴 작업을 하면 그만큼 UI 화면에 보이는것에 지장을 주게 되기 때문에 되도록 긴작업은 하지않는게 좋다.
    9. onRestart
      액티비티가 onStop 상태로 되어 배터리소모를 위해 해제했던 리소스나 리스너,리시버 등을 여기서 다시 등록, 점검하도록 해야한다. 준비가 되었다면 화면을 다시 복원시킬 onStart 가 실행된다.
    10. onResume
      액티비티가 onPause 상태에서 다시 화면이 활성화 되고, 사용자입력을 받을수 있는 상태가 되기 때문에 onPause 에서 해제해두었던 서비스나 기능을 다시 활성화 한다.
    11. onPause
      다른 액티비티나 앱에 의해 화면이 일부 가려진 상태이다. 액티비티가 사용자의 입력을 멈추고 임시적인 활동정지 상태로 들어간다. 이 상태는 화면갱신,사용자입력등을 받을수없는 상태이기 때문에 불필요하게 실행되고 있는 센서,카메라,GPS, 브로드캐스트 등을 정지하여 배터리가 소모되지 않도록하는게 좋다. 단, 데이터를 저장소에 저장하거나, 데이터베이스등을 다루는 작업을 하지않는다. 왜? onPause 는 긴작업을 할만한 시간을 가지고 있지않다.onStop 에서 처리하는게 좋다.
    12. onStop
      Activity 가 완전히 백그라운드로 이동되어 화면에 UI가 완전히 보이지않을때 호출된다. 따라서 이 단계에서는 현재 액티비티에서 만들었던 모든 메모리를 해제하고, 사용자정보등을 디비나 서버에 저장하는게 좋다. 이 단계에서는 아직 UI 관련 정보와 Activity 관련 정보가 시스템 메모리에 남아있어서 onStart 에서 빠르게 화면을 다시 복구 할수 있도록 해준다.
    13. onDetachView(Fragment Only)
      액티비티가 종료될때 호출된다. 액티비티의 종료는 사용자에 의해서 일어날수도 있지만 시스템에 의해서 강제로 발생할수 있다 따라서 여기서 긴시간이 걸리는 작업을 하게되면 불안정하게 종료될 가능성이 있기 때문에, 그런것들은 onStop 에서 처리해두는게 좋다.
      단 , 프로그램에서 finish() 를 하게되면 onResume->onDestory 형태로 바로 로딩되기때문에 이떄는 onDestroy 에서 처리해주어야줄 필요가 있다.
    14. onDetach(Fragment Only)
      액티비티에서 fragment 가 완전히 연결이 끊길때 실행된다. 프래그먼트에서 사용됬던 각종 메모리를 해제한다.
    15. onDestroy
      프래그먼트가 더이상 메모리에 있을 필요가없을때 실행되기 때문에 프래그먼트에서 사용했던 메모리를 해제한다.
  • Fragment

    1. Fragment를oncreate 하거나 attach/reattach 하면 onActivityCreated가 불려지는데, 이곳에서 live 데이터의 Observer 를 등록하면 무수히 많은 observer 인스턴스가 생성된다. 따라서 메모리소비, 복수의 통지수신을 받는경우가 생길수도 있다. Observer 를 등록할때, getViewLifecycleOwner() 또는 getViewLifecycleOwnerLiveData()를 이용하자.
      viewModel.getMainTab().observe(getViewLifecycleOwner(), new Observer<Integer>() {
          @Override
          public void onChanged(@Nullable Integer integer) {
              //Do something
          }
      });
      
  • RecycleView

    1. RecycleView
      1. 리사이클뷰의 현재상태를 유지하고 싶을때는 onPause 에서 .
			protected Parcelable rcListState;
		    @Override
		    public void onPause() {
		        super.onPause();
		        rcListState= rcList.getLayoutManager().onSaveInstanceState();
		    }
		    @Override
		    public void onResume() {
		        super.onResume();
				if (rcListState!= null) {
		                rcList.getLayoutManager().onRestoreInstanceState(rcListState);
		                rcListState= null;
		        }
		    }
    2. 리사이클뷰가 스크롤이 끝났을때 콜백액션
    Cannot call this method while RecyclerView is computing a layout or scrolling
처럼에러가 나올때는 스크롤이 끝난 시점에 추가로드 등을 하기위해서 리사이클뷰의 post 에서 실행하면된다.
		~~~java
		@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
		  super.onScrolled(recyclerView, dx, dy);

		  if() {
		     recyclerView.post(callbacks); 또는
		     recyclerView.post(() -> 실행할메소드());또는
		     recyclerView.post(new Runnable() {
		                @Override
		                public void run() {
		                   실행할메소드()
		                }
		      });
		  }
		}
		~~~
  1. ViewPager
    1. Android ViewPager.OnPageChangeListener 의 이벤트 전달 순서.
      https://indienote.tistory.com/74?category=644011
      참고하자.
  2. CustomView
    1. 커스텀뷰에 에 xml 파라메터 만들기.
      <?xml version="1.0" encoding="utf-8"?>
       <resources>
       <declare-styleable name="OwnCustomView">
       	<attr name="fillColor" format="color"/>
       </declare-styleable>
       </resources>
       레이아웃에서는
       <com.customview.OwnCustomView
       	android:layout_width="match_parent"
       	android:layout_height="wrap_content"
       	app:fillColor="@android:color/holo_blue_dark"/>
      
      커스텀뷰에서는
      int fillColor;
       TypedArray ta =
       context.getTheme().obtainStyledAttributes(attributeSet,
       R.styleable.OwnCustomView, 0, 0);
      
       try {
       fillColor =
       ta.getColor(R.styleable.OwnCustomView_ocv_fillColor,
       DEFAULT_FILL_COLOR);
      
       } finally {
       ta.recycle();
       }
       
       OwnCustomView customView = new OwnCustomView(this);
       customView.setFillColor(BRIGHT_GREEN);
      
  • 빌드
    1. 빌드상수로 디버그빌드와 릴리스빌드때 서로 다른 서버URL값 설정하기.
      프로그램에서 서버 URL 등을 디버그나 테스트,개발시에 다르게 설정할수 있는데 Gradle 에서 설정한 값들은 java 에서는 “BuildConfig.속성” 처럼 상수로 접근할수 있다.

      buildTypes{
          release {
              buildConfigField "String", "SERVER_URL", '"https://www.release.com"'
          }
          debug {
              buildConfigField "String", "SERVER_URL", '"https://www.test.com"'
          }
      
    2. ProGuard
      프로가드는 minifyEnabled 를 true 로 줌으로서 활성화할수있다. 이런저런 라이브러리를 사용하여 난독화하면 단일dex 파일이 너무 커지거나 라이브러리의 난독화과정에서 빌드에러가 나오는경우도 있는데 그럴경우

      proguard-rules.pro파일에
      -dontwarn okio.**
      -dontwarn retrofit2.**
      

      처럼 추가해주면된다.
      디버그 상태에서는 별도의 프로가드 설정을 주거나 [proguardFile ‘proguard-debug.pro’] minifyEnabled 를 false 로 지정하여 원활히 디버깅이 되도록 할수있다.
      또한 각각의 라이브러리는 별도의 프로가드 파일을 지정하여 주면 좋다.

      proguardFiles 'proguard-rules-retrofit.pro'
      proguardFiles 'proguard-rules-rx.pro'
      
    3. shrinkResources
      불필요한 리소스를 제거해준다. true 로 해준다.

    4. gradlewrapper
      gradlew 명령어로 gradlew clean build assemble 하면 gradle 시에 에러 정보가 보인다.

  • Android Jetpack lifecycle 소개 및 구현 방법 (LiveData, ViewModel 연계)
    1. 미친 안드로이드가 아예 lifecycle 이라는 라이브러리를 만들어서 onResume (또는 onStart,onRestart)에서 register 를 onPause(또는 onStop) 에서 unregister 해주던 것들(리스너등) 을 lifecycle 에 옵서버형태로 옮겨서 자동으로 Activity 라이프사이클에 맞게 자동으로 on/off 될수 있도록 하는걸 만들언다.
      • Lifecycle : lifecycle 객체자체, 즉 subscriber 다.
      • LifecycleOwner : lifecycle 을 포함한 말그대로 생명유지를 하는 주체, 즉 Activiy 다.
      • LifecycleObserver : Owner 의 상태를 감지해서 이벤트를 발생시키는 옵서버.
        이런 라이프사이클을 이용해서 자동으로 라이프사이클에 맞춰 연동되도록 되어 있는 클래스들을 lifecycle-aware components 라고 한다.
    2. LiveData 라는 것은 LifecycleOnwer 의 생명주기에 맞게 연결된 UI 객체에 값변경을 통지하는 역활을 대신 편리하게 해준다. 예전에는 액티비티나 프래그먼트 생명주기에 맞게 UI 에 뭔가를 다시 출력하거나 하는게 귀찮았는데, LiveData 로 연결만해주면 Lifecycle 즉 , 액비티티나 프래그먼트의 생명주기에맞게 화면에 보일때만 변경표시를 해주고 owner 가 삭제되었을때는 연결된 데이터도 삭제해서 GC 대상이 될수 있도록 하여 메모리누수를 덜 일으키도록 도와준다.
    3. ViewModel 안드로이드에서 ViewModel 클래스는 액비티니,프래그먼트에 포함된 UI 조작(로직)을 따로 분리 할수 있도록 도와주는 클래스로서 ViewModel 에 연결된 UI 는 액티비티나 프래그먼트의 생명주기와 관계없이 언제든지 통지를 받아 변경될수있는 상태가 되도록 한다.즉 액티비티가 재실행되거나, 멈춤,복귀 되는 등(화면회전, SMS 메시지확인,전화받기등)의 과정과 관계없이 주욱 데이터를 유지시키며 이를 가능하게 하는게 LiveData 이다.
      지저분하게 Activity, Fragment 에서 + , - 이건거 하지 말자.
      (아차차…참고1 .뷰모델이 자신이 가진 메모리를 내밷고 생명을 다하는 경우는 Acitivity의 경우 finish 되었을때나 Fragment의 경우 detached 가 삭제될경우다)
      (아차차…참고2 .뷰모델은 스스로 장수하는 반면, Acitivity나 Fragment는 경우에따라 죽었다 살아나기를 반복하면서 이름을 바꾸기 때문에 직접 이들의 Context 를 직접 참조하게 되면 에러를 내뿜게 된다. ApplicationContext 하거나 AndroidViewModel 을 확장받아서한다. )
    4. LiveData 의 Transformation , LiveData의 값이 변경되어 대상객체에 통지하기 전에 항상 그값에 뭔가를 추가처리하고자 할때 사용한다. [Transformation.map(소스, 함수)=스트링 리턴값] 과 [Transformation.switchMap(소스, 함수)=라이브데이터 리턴값] 과 [mediatorLiveData가 있다.
      • Transformations.map : 라이브 데이터가 갱신되면 지정된 함수에서 처리된 결과값이 strign 으로 반환된다.
  1. TEST

    1. Androidx 테스트 지원 라이브러리 https://developer.android.com/training/testing/set-up-project
    2. 빌드상수로 디버그빌드와 릴리스빌드때 서로 다른 서버URL값 설정하기.
      프로그램에서 서버 URL 등을 디버그나 테스트,개발시에 다르게 설정할수 있는데 Gradle 에서 설정한 값들은 java 에서는 “BuildConfig.속성” 처럼 상수로 접근할수 있다.
      buildTypes{
          release {
              buildConfigField "String", "SERVER_URL", '"https://www.release.com"'
          }
          debug {
              buildConfigField "String", "SERVER_URL", '"https://www.test.com"'
          }
      
    3. 신규 프로젝트 만들고 Test 봤더니 @RunWith(AndroidJUnit4.class) 에 빨간줄갈때
      구글은 정말…어쩌라구…
      dependencies {  
      	androidTestImplementation 'androidx.test:rules:1.2.0' 이건rule 위해.
          androidTestImplementation 'androidx.test.ext:junit:1.1.2-alpha02' 추가
          androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0' Intent,hasComponent 사용		    
      }
      
    4. 런쳐액티비티 테스트
      public class TCS1_010 {  
        
          @Rule  
        public final ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);  
        
          @Test  
        public void startApp() {  
               Intent intent = new Intent();  
              // intent.putExtra("your_key", "your_value");
              mActivityTestRule.launchActivity(intent);  	  		  
          }  
        
      }
      
    5. 액티비티와 프래그먼트의 테스트는 여길참조 The Basics of Android Espresso Testing: Activities & Fragments
  2. SQLite

    1. SQLite의 느린속도때문에 쿼리스트링 만들때는 stringbuffer 를 사용하자.
    2. compileStatement 를 사용해서 안전하고 빠르게 삽입하자.
      https://medium.com/androidstarters/mastering-android-studio-templates-ed8fdd98cb78
      https://medium.com/gits-apps-insight/tutorial-create-your-own-template-for-android-studio-1aaa9b4cb18
      AndroidStudio\plugins\android\lib\templates\밑에 있는 파일대로 만들면된다.
      }
      
  3. ETC

    1. AndroidStudio 에서 액티비티,플래그먼트,리사이클뷰세트 등을 만들때, 템플릿을 이용해서 기준포맷된 화일을 빠르게 생성하도록할수 있다. 협업작업때 기존화일을 카피해서 안쓸꺼만 지우고 하다보면 시간도 가고 실수도 하는데 차라리 템플릿으로 만들어 두면 좋다.
      또, 새로운 프로젝트를 템플릿으로 만들어 두면 빠르게 새로운 프로젝트를 시작할수 있다.
      일단 , 참고 했던곳,
      https://medium.com/androidstarters/mastering-android-studio-templates-ed8fdd98cb78
      https://medium.com/gits-apps-insight/tutorial-create-your-own-template-for-android-studio-1aaa9b4cb18
      AndroidStudio\plugins\android\lib\templates\밑에 있는 파일대로 만들면된다.
      }
      

0 comments:

댓글 쓰기