Search

'컴퓨터공부/안드로이드'에 해당되는 글 12건

  1. 2019.08.11 RxJava - Create 함수
  2. 2019.08.10 RxJava - Observable, Observer
  3. 2019.07.15 안드로이드 Loader
  4. 2019.07.15 onSaveInstanceState
  5. 2019.04.03 JAVA의 static
  6. 2019.03.19 안드로이드 Service
  7. 2019.03.13 AsyncTask
  8. 2019.03.05 ViewPager와 PageAdapter
  9. 2019.02.16 px, dp, sp 개념 정리
  10. 2019.02.16 Device screen dpi 값에 따라 처리하기
  11. 2019.01.26 안드로이드 선 추가하기
  12. 2019.01.10 SharedPreference

RxJava - Create 함수

컴퓨터공부/안드로이드 2019.08.11 13:36 Posted by 아는 개발자 아는 개발자

앞서 작성한 포스트에선 Observable의 역할이 어떤 데이터를 Observer가 처리할 수 있도록 포장해주는 역할 이라고 설명했다. Observable은 데이터를 관찰 할 수 있는 형태로 만들 기 위해 여러 가지 오퍼레이터 함수를 가지고 있다. 이번 포스트에선 이중에서 대표적으로 사용되는 것들만 소개해보려고 한다.


1. create


백그라운드 스레드에서 옵저버가 처리할 넘겨주는 방법. 아래 코드를 보면 create 함수의 인자로 익명 ObservableOnSubscribe 클래스를 선언하고 이 안의 오버라이드 함수 인자인 emitter 변수에 onNext로 0~9까지 값을 넣어 호출 하는 것을 볼 수 있다.


Observable<Integer> observable = Observable
        .create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                Log.d(TAG, "subscribe: " + Thread.currentThread().getName());

                for (int i = 0; i < 10; i++) {
                    if (!emitter.isDisposed())
                        emitter.onNext(i);
                }

                if (!emitter.isDisposed())
                    emitter.onComplete();
            }
        })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

observable.subscribe(new Observer<Integer>() {
    @Override
    public void onNext(Integer integer) {
        Log.d(TAG, "onNext: " + integer);
    }
});


실행결과 아래와 같은 로그가 출력된다. 0~9까지 값이 옵저버에게 전달 됐다. 추가적으로 subscribe에서 돌고 있는 쓰레드 이름을 출력해보니 RxCachedThreadScheduler-1이란 것이 출력됐다. 이는 subscribeOn 함수에 백그라운드 함수중 하나인 Schedulers.io()를 넣었기 때문이다. 이 함수를 사용하면 Observable한 데이터를 만드는 작업을 백그라운드에서 실행 할 수 있게 된다.



2. just 


최대 10개까지의 배열 데이터를 Observable하게 만들 수 있는 함수. 그런데 10개로 제한돼서 배열을 전달하는 경우는 없고 단일 데이터를 전달 할 때 주로 사용된다.


Observable<Integer> observable = Observable
        .just(1,2,3)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

observable.subscribe(new Observer<Integer>() {
    @Override
    public void onNext(Integer integer) {
        Log.d(TAG, "onNext: " + integer);
    }
});


이 함수를 실행해보면 다음과 같은 로그가 나온다.



3. range 


범위를 지정해주는 방법. for문을 생성 함수의 하나로 뒀다고 생각하면 쉽다. 단 range 함수를 사용하면 map같은 형변환 오퍼레이터를 사용하지 않으면 옵저버에선 Integer의 형태로 값을 받게 된다.


Observable<Integer> observable = Observable
        .range(0, 5)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

observable.subscribe(new Observer<Integer>() {
    @Override
    public void onNext(Integer integer) {
        Log.d(TAG, "onNext: " + integer);
    }
});


4. fromIterable 


이미 생성된 배열의 값을 Observable하게 바꿔주는 함수. 앞서 설명한 just는 최대 10개까지 밖에 담지 못한 반면 fromIterable은 개수에 상관 없이 모두 Observable하게 바꿔준다.


List<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);

Observable<Integer> observable = Observable
        .fromIterable(integers)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

observable.subscribe(new Observer<Integer>() {
    @Override
    public void onNext(Integer integer) {
        Log.d(TAG, "onNext: " + integer);
    }
});


'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

RxJava - Create 함수  (0) 2019.08.11
RxJava - Observable, Observer  (0) 2019.08.10
안드로이드 Loader  (0) 2019.07.15
onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19

RxJava - Observable, Observer

컴퓨터공부/안드로이드 2019.08.10 14:52 Posted by 아는 개발자 아는 개발자

좀더 리액티브한(reactive) UI를 만들기 위해서 프로젝트에 RxJava, RxAndroid를 도입하면서 가장 어려웠던 점은 Observable과 Observer 클래스를 개념적으로 이해하는 것이었다. 옵저버 패턴을 응용한 라이브러리이기 때문에 익숙할 것이라고 여러 문서에서 설명하고 있는데 애초 옵저버 패턴을 많이 써보지 않았고 또 디자인 패턴 책을 여러 번 보고 난 뒤에 문서를 읽어봐도 쉽사리 와닿지 않았다.


여러 번의 블로그 방문과 삽질을 반복한 후 다행히 유튜브 강좌를 통해서 어느정도 개념을 잡을 수 있었다. 혹시나 나처럼 어려움을 겪고 계신 분은 Coding with Mitch 라는 유튜브 채널 강좌를 들어보시면 도움이 될 것 같다. 모두 듣고 소화하는데 하루 정도 소요되는데 개인적으로 충분히 들어볼만한 강좌였다.


서론이 너무 길었다. 아무튼 이번 포스트에선 앞서 말한 Observable과 Observer 클래스를 개념적으로 정리해보려고 한다. 개인 공부를 위해 정리한 글인 만큼 다른 블로그이나 문서에서 설명한 것과 차이가 있을 수 있을 것 같은데 혹시나 이 글을 읽으신 분들은 이점 참고하셨으면 좋겠다.


1. Observable


이 클래스에 포함된 함수는 매우 다양하나 어떤 데이터를 관찰 할 수 있는 형태로 바꾸는 것이 이 클래스의 기본적인 임무다. Observable의 의미인 '관찰 할 수 있는'을 생각해보면 이 클래스가 어떤 일을 해야하는지 짐작 할 수 있을 것이다. 이 클래스는 개념적으로는 어떤 데이터를 Observer가 처리할 수 있도록 포장하는 작업을 담당한다고 보면 이해하기엔 편하다. 추가적인 기능으로는 값을 변형시키고 다른 타입으로 바꾸기도 하고... 등등 많은데 일단 이 정도만 이해하도록 하자.


2. Observer


스타크래프트의 정찰용 옵저버 유닛을 생각하면 이해하기 어려워진다. 게임 유닛은 잊고 순수 의미인 '관찰자'의 의미에서 보면 Observer는 Observable에서 관찰 할 수 있는 형태로 전달한 데이터를 받고 이에 대한 행동을 취한다. 전달 받은 데이터를 가지고 화면 UI를 업데이트 하든지 아니면 어떤 인자를 서버에 요청해보는지 등등.. 최종적으로 처리할 작업은 이 클래스에서 담당한다


3. Observable & Observer Diagram


출처: 깃헙


위 그림을 보면 ObservableonNext, onComplete, onError 같은 함수를 통해 Observer에게 무언가를 전달하고 있는 것을 볼 수 있다. onNext는 데이터를 포장할 때마다  Observer에게 완료된 작업물을 전달하는 것이고 onComplete는 포장 작업이 끝날 때 호출하는 함수다. onError는 Observable 내에서 어떤 문제가 생겼을 때 호출되는 함수다.


4. 코드 


아래 코드를 보면 Observable 이라는 변수는 String형태의 Observable 클래스 타입이다. 앞서 설명한 내용 대로라면 Observable은 String 형태의 데이터 타입을 Observable 하게 만들어야 한다. 이를 위해 just라는 함수를 통해 관찰할 수 있는 형태로 포장할 값으로 "selfish"와 "developer"를 넣었다. subscribeOn과 observeOn 함수는 어떤 쓰레드에서 작업을 실행할 지 정하는 함수인데 이번 포스트와는 관련이 없으니 일단 무시하자.


선언 후 subscribe 함수 내에 익명의 Observer 객체를 선언했다. 이는 만들어둔 Observable과 Observer를 매핑하는 함수다. Observer 객체를 보면 onNext, onComplete 처럼 앞의 그림에서 설명한 함수들이 등장한다. Observable에서 전달 받은 작업을 처리하기 위한 함수들이다.


Observable<String> observable = Observable
        .just("selfish", "developer")
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

observable.subscribe(new Observer<String>() {
    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onNext(String s) {
        Log.d(TAG, "onNext: " + s);

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {
        Log.d(TAG, "onComplete: ");
    }
});


위 코드를 안드로이드 에뮬레이터로 실행해보면 다음과 같은 로그가 나온다.




'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

RxJava - Create 함수  (0) 2019.08.11
RxJava - Observable, Observer  (0) 2019.08.10
안드로이드 Loader  (0) 2019.07.15
onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19

안드로이드 Loader

컴퓨터공부/안드로이드 2019.07.15 21:20 Posted by 아는 개발자 아는 개발자

앞선 포스트에서 onSaveInstanceState 콜백을 통해 화면을 전환하는 경우에도 데이터를 저장할 수 있는 방법을 배웠다. 그런데 AsyncTask 처럼 진행중인 작업에 대해서는 데이터를 저장할 수 있는 기능이 무의미할 것이다. 어디까지 데이터 작업을 처리 했으니 이때부터 다시 시작하라고 세세하게 할 수도 없는 노릇이고.


그래서 안드로이드에서는 Loader라는 라이브러리를 뒀다. 공식 문서에서는 FragmentActivity에 넣을 디스플레이 소스를 로드할 수 있는 기능으로 소개되고 있는데 일단은 별도의 쓰레드에서 돌아 Activity 생성 주기에 영향을 받지 않는 컴포넌트 정도로 이해하면 될 것 같다. 사용법은 아래와 같다.


1. implements LoaderManager.LoaderCallbacks<String>


MainActivity는 Loader API를 사용하는 Activity임을 명시 해둬서 Loader 콜백 함수들을 호출하도록 만든다.

public class MainActivity extends AppCompatActivity implements
        LoaderManager.LoaderCallbacks<string> { {

2. public Loader<String> onCreateLoader 


Activity에서 사용할 Loader 객체를 생성하는 콜백 함수를 구현한다. Loader는 AsyncTaskLoader와 CursorLoader가 있는데 CursorLoader 의 경우에는 DB에서 값을 읽어올 때 사용하고 AsyncTaskLoader 는 좀더 범용적으로 사용된다. 


2.1 protected void onStartLoading()


AsyncTaskLoader 가 생성 되면서 가장 먼저 실행되는 함수다. AsyncTask의 onPreExecute() 의 역할을 하는 것과 비슷하다. 백그라운드 작업 실행 하기 전에 필요한 셋팅 작업을 여기에 넣는다.


2.2 public String loadInBackground()


백그라운드 작업을 실행하는 함수다. 이름을 보면 감이 오겠지만  AsyncTask의 doInBackground(Void... voids)  와 동일한 기능을 하는 함수다. 반환 타입으로 세팅된 String은 결과 값의 타입이며 앞서 콜백을 implements 할 때 어떤 타입을 넣느냐에 따라 바꿀 수 있다.


2.3 public deliverResult(String result)


결과 값을 전달하는 함수다. 이 함수내에는 반드시 super.deliverResult(result);  가 포함되어 있어야지 결과 값이 최종적으로 전달 된다. 인자인 result 는 loadInBackground()에서 반환한 값이다


3. public void onLoadFinished(Loader<String> loader, String data)


AsyncTaskLoader 작업이 끝난 후에 불리는 함수이며 함수의 인자로 결과 값을 전달 받는다.  결과값을 화면에 업데이트 할 때 이 콜백 함수 내에 작업을 넣는다.


4. initLoader,restartLoader


생성한 Loader가 실행 될 수 있도록 호출한다. 아래 코드는 initLoader,restartLoader 함수를 실행한 예제 코드다.


LoaderManager loaderManager = getSupportLoaderManager();
Loader<string> searchLoader = loaderManager.getLoader(SEARCH_LOADER);
if (searchLoader == null) {
    loaderManager.initLoader(SEARCH_LOADER, queryBundle, this);
} else {
    loaderManager.restartLoader(SEARCH_LOADER, queryBundle, this);
}

함수의 첫번째 인자 값은 ID다. Loader마다 가지고 있는 고유한 Key값에 해당한다. 두번째 인자 값은 Bundle형태의 데이터 값이다. AsyncTaskLoader에게 이 데이터 값을 통해 값을 전달 할 수 있다. 세번째는 콜백함수다. 현재는 Activity가 Loader 콜백 함수를 구현해뒀기 때문에 this로 입력했다.

'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

RxJava - Create 함수  (0) 2019.08.11
RxJava - Observable, Observer  (0) 2019.08.10
안드로이드 Loader  (0) 2019.07.15
onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19

onSaveInstanceState

컴퓨터공부/안드로이드 2019.07.15 20:39 Posted by 아는 개발자 아는 개발자

onCreate() onDestroy() 는  애플리케이션이 생성될 때와 종료 될 때 한 번씩만 불리는 콜백 함수로 알려져 있지만 디바이스의 설정 값이 갑자기 바뀌어 화면을 처음부터 새로 그려줘야 할 때도 불린다. 대표적으로 스마트폰을 회전 시키는 경우(rotate)가 이에 해당한다. 



확인해보기 위해 테스트 애플리케이션의 콜백 함수들에 로그를 넣고 에뮬레이터(오레오 8.1) 에 설치한 후 오른쪽으로 회전 해봤다. onPause() 함수부터 불리는 부분이 회전 후에 나온 로그며 이중에는 onCreate() onDestroy()도 포함되어 있다.


onCreate() onDestroy() 함수가 다시 불린다는 뜻은 회전하기 전까지 설정해둔 변수 값들이 모두 초기화 된다는 것을 의미하기도 한다. 만약 애플리케이션 화면에 특정 값을 바꾼 상태로 회전을 했다면 처음 애플리케이션을 실행한 상태로 화면이 바뀌기 때문에 지금까지 작업한 것들이 모두 날라가게 된다.


안드로이드에선 이런 상황을 대처하기 위해 onSaveInstanceState(Bundle outState) 라는 콜백 함수를 뒀다. 이 함수는 모든 인자들이 초기화 되는 onDestroy() 함수 호출 전에 실행되며 함수의 인자에 key-value로 여러가지 데이터를 넣을 수 있다. 변경된 인자는 최종적으로 onCreate(Bundle savedInstanceState) 함수의 인자 값으로 전달 된다.


구구절절히 코드로 보는 것이 더 이해하기 쉬울 것 같다. 먼저 onSaveInstanceState 함수내에 아래와 같이 임의의 문자열 데이터 값을 입력했다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    logAndAppend("onSaveInstanceState");

    outState.putString(SAVE_INSTANCE_KEY,
            "onSaveInstanceState is called!\n");
}

그리고  onCreate 함수에선 인자에 key 값이 저장되어 있는지 확인 한 후 있으면 그 값을 TextView에 표시하도록 했다. 

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mLifecycleDisplay = (TextView) findViewById(R.id.tv_lifecycle_events_display);

    if (savedInstanceState != null
            && savedInstanceState.containsKey(SAVE_INSTANCE_KEY)) {
        String savedString = savedInstanceState.getString(SAVE_INSTANCE_KEY);
        mLifecycleDisplay.setText(savedString);
    }
    logAndAppend(ON_CREATE);
}

그 결과 아래와 같이 회전 후 TextView에 로그 메시지가 추가되는 것을 확인 할 수 있었다



'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

RxJava - Observable, Observer  (0) 2019.08.10
안드로이드 Loader  (0) 2019.07.15
onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13

JAVA의 static

컴퓨터공부/안드로이드 2019.04.03 21:50 Posted by 아는 개발자 아는 개발자

0. JAVA Static

절차 지향인 C언어에서 static 변수는 함수 또는 변수를 작성 중인 파일 내에서만 사용하고 싶을 때만 사용하기 때문에 사용법이 비교적 간단했는데 JAVA에서는 객체지향 개념과 연관되어 있어 사용 시 다소 주의가 필요하다. 이번 포스트에서는 JAVA 언어에서 static의 쓰임새에 대해서 정리를 해본다.


1. Static/Non-static 멤버

자바에서는 클래스 내의 함수 또는 변수를 멤버(Member)라고 통칭해서 부르며 앞에 static 변수를 붙이면 static 멤버, 붙이지 않으면 Non-static 멤버로 분류한다.

public class MainActivity extends AppCompatActivity {
  static int staticVar = 0;       // static Member
  static void staticMethod() {}   // static Member

  int nonStaticVar = 0;           // Non-static Member
  void nonStaticMethod() {}       // Non-static Member
}

Static 멤버는 별도의 객체 생성과정 없이 바로 사용할 수 있고 클래스당 하나씩만 존재해 동일한 클래스로 생성된 복수의 객체가 동일한 변수의 값을 공유하게 된다. C/C++의 전역 변수와 함수와 원리가 비슷한데 이와 같은 특징은 Static 멤버들이 객체를 생성하기 전에 이미 메모리에 로딩되며 객체가 제거와 상관없이 프로그램이 종료될 때까지 메모리에 남아있는 구조적인 특징 때문에 발생한다.

Non-Static 멤버는 Static 멤버와 원리가 정반대다. 반드시 객체를 생성한 후에 사용할 수 있으며 생성된 객체별로 멤버의 값이 다를 수 있다. 이는 멤버 인스턴스들이 객체의 생명주기에 따라서 메모리에 생성되고 제거되는 원리 때문이다. Non-Static 멤버들은 객체지향적인 관점에 근거해서 동작하는 인스턴스다.


2. 사용 예시

2.1 Singleton Pattern

프로그램에서 클래스당 하나의 객체만 사용하도록 유도하는 Singleton Pattern은 static을 적절히 사용한 대표적인 예다. getInstance 함수를 별도의 객체 생서 없이 호출할 수 있도록 static 멤버로 선언했으며 클래스 변수인 instance도 static으로 선언해 여러개의 객체가 생성되더라도 동일한 객체만 리턴할 수 있도록 했다.

public class Singleton {
  private static Singleton instance;
  private LazySingleton() {}
  public static synchronized Singleton getInstance() {
      if(instance == null) {
          instance = new Singleton();
      }
      return instance;
  }
}

2.2 Math

private int getAbs(int a) {
	return Math.abs(a);
}

별다른 객체 생성 없이 바로 실행하는 Math 클래스의 함수들은 모두 static 멤버들로 선언된 것들이다. JAVA는 객체지향 관점에 따라서 모든 함수들은 반드시 객체를 통해서 호출되기 때문에 위와 같이 static 함수를 적극적으로 활용해 라이브러리를 호출할 수 있도록 했다.


3. 주의사항

3.1 Out of Memory

객체가 제거되도 static 멤버는 메모리에 남아있기 때문에 코드 상에서 별도로 처리해두지 않으면 모든 메모리 공간을 static 인스턴스가 차지하는 메모리 Leak이 발생할 수 있다.

public class OomClass {
  static Vector oomVector;
  static void addOomVector(int a) {
      oomVector.add(a);
  }
  public OomClass() {
      for (int i = 0; i < 100000; i++)
          addOomVector(i);
  }
}

위의 OomClass는 생성 할 때마다 static 변수는 oomVector 벡터 클래스에 0~99999까지 엔트리를 추가하는데 OomClass 한 두 개 정도 생성할 때는 별 문제가 되지 않지만 그 수가 많아지면 메모리에서 차지하는 비율이 높아져 OOM이 발생할 수 있다.

3.2 Static 함수의 제약사항

Static 함수 내에서는 Static 멤버만 사용할 수 있고 this 호출이 불가능하다. 이런 제약사항은 역으로 Non-Static 멤버도 접근 할 수 있고 this를 호출 할 경우 어떤 문제점이 발생할지 예측해보면 이해하기 쉽다. Non-Static 멤버들은 객체가 생성되어야 메모리에 한자리 차지하게 되는데 객체 생성 없이 바로 호출되는 static 함수에서 생성 됐는지 확인되지 않는 멤버 변수들에게 접근한다면 이는 invalid access가 된다. 구조적인 특징에 대해서 다시 한번 생각해보게 된다.



'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

안드로이드 Loader  (0) 2019.07.15
onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05

안드로이드 Service

컴퓨터공부/안드로이드 2019.03.19 21:42 Posted by 아는 개발자 아는 개발자

0. 소개


Service 클래스는 안드로이드 앱에서 백그라운드 작업을 수행할 때 대표적으로 사용하는 라이브러리다. 앞서 설명한 AsyncTask와는 사용법이 완전히 다른데 2~3초 이내로 짧게 끝내야하는 AsyncTask와는 달리 Service는 오래도록 실행 할 수 있으며 AsyncTask처럼 UI 쓰레드와 Interaction 할 수 있는 기능은 없다. 그래서 화면과 무관한 작업인 네트워크 트랜잭션 처리나 음악을 재생할 때 사용된다.


1. 생성주기


액티비티와 독립된 생성주기를 가지고 있으며 별도의 컴포넌트로 동작하기 때문에 애플리케이션을 사용하지 않은 상태(UI가 화면에 있지 않은 상태) 여도 백그라운드에서 실행 될 수 있다. 폰 게임을 하는 중에도 카카오톡 메시지가 오고 쿠팡에서 쇼핑을 하는 도중에도 멜롬의 음악을 들을 수 있는 이유가 바로 이런 서비스 클래스의 특징 덕분이다. 경우에 따라선 애플리케이션 내에서 별도의 프로세스로 분리할 수도 있다.


2. 콜백함수 


Service 클래스의 콜백 함수를 통해 간단히 사용 방법을 익혀보자.


public void onCreate()


서비스가 생성 될 때 가장 먼저 실행되는 함수. 생성시 딱 한번만 호출되며 서비스 동작에 필요한 인스턴스를 선언하는 작업으로 주로 사용 한다.


public void onDestroy()


서비스가 종료될 때 호출되는 함수. onCreate에서 수행한 작업과 반대로 서비스가 종료하면서 인스턴스에 정리할 작업을 처리하는 일을 한다.


public int onStartCommand(Intent intent, int flags, int startId)


사용자의 요청을 처리하는 함수. 서비스를 처음 생성할 경우에는 onCreate 함수 다음에 불리나 생성 이후에는 이 함수로 바로 불린다. 전달 인자인 Intent에 필요한 작업을 전달할 수 있다.


3. 주의사항


단 주의할 것이 무한루프처럼 오래 걸리는 작업을 onStartCommand에 넣으면 ANR(Application Not Responding)에러가 발생하게 된다. 오래 실행하는 작업을 수행할 때 사용되는 라이브러린데 정작 사용하려니 에러가 발생하는게 생뚱맞게 들릴 수 있는데 이는 서비스 클래스 컴포넌트의 구조적인 이유 때문이다. onStartCommand함수는 서비스 작업 뿐만 아니라 사용자 interaction도 처리하는 메인쓰레드에서 담당하는데 이 함수가 빨리 종료되지 않는다면 사용자는 UI 작업을 처리하지 못하게 된다.


이러한 이유 때문에 오래 걸리는 작업은 onStartCommand에서 처리하되, 아래의 안드로이드 소스처럼 별도의 쓰레드를 생성해서 작업을 처리하도록 한다.


 
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    new Thread() {
        @Override
        public void run() {
            while (true) {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }.start();

    return START_STICKY;
}


4. IntentService 


매번 쓰레드를 만들어서 작업을 처리한다면 여러 가지 작업을 동시에 처리할 수 있다는 이점도 있지만 개수가 많아짐에 따라 시스템 자원을 소모하게 되고 경우에 따라선 처리 시간은 중요하지 않고 작업이 순차적으로 처리하는 일이 필요한 경우 불필요한 오버헤드를 발생시키기도 한다. 차라리 이런 경우에는 여러 개의 쓰레드를 만들지 말고 하나의 쓰레드에서 작업을 하나씩 전달해주는 방식이 좋을 것 같기도 하다. 안드로이드에서는 이미 이런 경우를 고려해 IntentService 라는 클래스를 만들어 뒀다.


IntentService를 사용하면 메인 thread와 따로 도는 workqueue쓰레드가 생성되며 액티비티로부터 전달 받은 Intent를 순차적으로 아래의 onHandleIntent 함수에서 처리한다. 


protected void onHandleIntent(Intent intent)


onStartCommand 함수에서는 긴 작업을 별도의 쓰레드에서 처리했어야 했다면 IntentService의 onHandleIntent 함수안에서는 바로 길게 실행되는 작업을 둬도 상관 없다. 안드로이드가 알아서 작업을 처리해준다.

'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16

AsyncTask

컴퓨터공부/안드로이드 2019.03.13 23:57 Posted by 아는 개발자 아는 개발자

0. 소개


AsyncTask는 파일 다운로드 완료후 사용자에게 완료됐다는 Toast 메시지를 보내는 유스케이스처럼 특정 백그라운드 작업이 종료되고 사용자에게 화면으로 알림을 전달해야하는 경우 유용한 클래스다. 코드가 직관적이고 응용하기 쉬워 자주 사용되는 라이브러리지만 몇초 이내에 종료될 수 있는 작업인 경우에만 사용이 가능하며 그것보다 긴 작업은 다른 클래스를 사용해야 한다.


안드로이드 공식문서의 예제 코드를 통해 사용 방법을 익혀보자.


private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected void onPreExecute(Integer... progress) {
         /* do nothing */
     }
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }
     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }
     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }



1. 상속 함수


클래스의 작업은 진행 순으로 크게 네가지로 구분된다.


protected void onPreExecute


이름에서 직감할 수 있듯이 AsyncTask 실행시 가장 먼저 불리는 함수다. 주로 여기에 백그라운드를 시작하기 전에 초기화해야하는 변수나 객체를 선언하는 용도로 사용한다.


protected Long doInBackground


백그라운드 작업이 실행되는 함수다. 예제 코드에서 확인 할 수 있듯이 여러 개의 인자를 전달받을 수 있으며 작업이 종료되면 특정 자료형의 결과 값을 리턴한다. 파라미터와 결과의 타입을 설정하는 방법은 다음 챕터에서 설명할 예정이다.


protected void onPostExecute

 

doInBackground  함수가 종료된 후 실행되는 함수다. doInBackground 함수의 결과값을 파라미터로 받으며 주로 예제 코드처럼 실행 완료메시지를 사용자에게 알림하는데 사용한다.


protected void onProgressUpdate


doInBackground 내에서 publishProgress 함수를 호출 할 때 불리는 함수이며 주로 작업의 진행 상황을 표시하는데 사용된다. 예제 코드에서는 다운로드 완료를 퍼센테이지로 전달했다.  



2. AsyncTask 인자


예제 코드의 DownloadFilesTask 클래스는 AsyncTask<URL, Integer, Long>을 상속하는데 여기서 부모클래스의 template 인자의 타입은 순서에 따라서 앞서 설명한 네가지 작업의 파라미터의 타입으로 매핑된다. 


첫번째 인자 (URL)


백그라운드로 실행하는 작업인 doInBackground 함수의 파라미터 타입이다. 주로 처리해야할 작업의 데이터를 전달하는 용도로 사용한다. 예제 코드에서는 URL 타입으로 설정해서 다운로드 받을 파일의 주소를 전달하는 용도로 사용하고 있다.


두번째 인자 (Integer)


진행 상황을 업데이트하는 onProgressUpdate 함수의 파라미터 타입이다. 예제 코드의 doInBackground 함수에서는 다운로드 진행 상황을 publishProgress 함수로 퍼센트로 전달하고 있으며 onProgressUpdate 함수는 Integer 형태로 받아서 화면에 표시하고 있다.


세번째 인자 (Long)


백그라운드 작업 종료후 실행하는 onPostExecute 함수의 파라미터 타입이다. doInBackground 함수의 리턴값이 onPostExecute 함수의 인자 값으로 전달되며 주로 작업의 정상종료 유무를 전달하기 위해 사용한다.



'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16
Device screen dpi 값에 따라 처리하기  (0) 2019.02.16

ViewPager와 PageAdapter

컴퓨터공부/안드로이드 2019.03.05 23:38 Posted by 아는 개발자 아는 개발자

0. ViewPager와 PagerAdapter


ViewPager와 PagerAdapter는 아래 네이버 앱의 뉴스, 연애 스포츠 탭처럼 큰 화면 안에서 여러 개의 작은 화면을 좌우로 움직이는 UI를 구성할 때 사용하는 안드로이드 라이브러리다. ViewPager는 TextView, Button과 같이 화면의 인터페이스로 사용자가 조작할 수 있는 컴포넌트고 PagerAdapter는 ViewPager가 생성한 공간을 채우는 데이터 공간으로 보면 된다. 


특별히 화면에 이펙트를 넣을 생각이 없으면 ViewPager는 안드로이드에서 제공하는 라이브러리를 그대로 쓰고 내가 넣고 싶은 화면을 담은 별도의 PagerAdapter를 만들어서 ViewPager에 연결 시키면 된다.


이버의 뉴스, 연예, 스포츠 탭은 ViewPager와 PagerAdapter로 만들었을 것 같다. (아닐수도 있고)


1. Fragment + PagerAdapter 


큰 화면의 단위는 Activity이고 작은 화면의 단위는 주로 Fragment를 사용하기 때문에 안드로이드에서는 PagerAdater를 Fragment를 사용해서 구현하기 쉽도록 FragmentPagerAdatper를 만들어 뒀다. 이것과 이름이 비슷한 FragmentStatePagerAdapter가 있는데 Fragment 메모리 관리를 어떤식으로 할 것인가에 따라서 조금 차이가 난다. 깊게 들어가면 어려울 수 있으니 간단하게 ViewPager가 관리하는 페이지가 얼마 되지 않는 경우에는 FragmentPagerAdapter를, 많으면 FragmentStatePagerAdapter를 사용하도록 하자. 이 둘로 나눴지만 상속해서 작성하게될 Custom FragmentPagerAdatper의 코드상의 차이는 거의 없다.


2. Custom FragmentPagerAdapter


앞서 말한대로 여러 개의 화면을 좌우로 슬라이드 할 수 있는 화면을 만들기 위해선 별도의 PageAdapter가 필요하다. 안드로이드에서 FragmentPagerAdapter 클래스를 만들어 뒀으니 이를 상속해서 Custom PageAdapter를 만들 수 있다. 몇가지 오버라이드 함수들만 살펴보자.


public Fragment getItem(int position)


n번째 포지션의 Fragment Item 오브젝트 정보를 리턴한다. 채울 화면의 순서를 변경하고 싶다면 여기서 return 하는 Fragment 객체를 position 에 따라서 변경하면 된다. 반드시 구현해야하는 함수다.

    @Override
    public Fragment getItem(int position) {
        Fragment tab = null;

        switch (position) {
        case 0:
            tab = new PreviewTab();
            break;
        case 1:
            tab = new CodeTab();
            break;
        }

        return tab;
    }


public int getCount()


ViewPager에 넣을 화면의 개수를 리턴하는 함수다. 이것도 반드시 구현해야 하는 함수다.

    @Override
    public int getCount() {
        return this.mNumOfTabs;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles[position];
    }


public Object instantiateItem


View에 넣을 Page 객체를 리턴해주는 작업. 이미 FragmentPagerAdapter에서 구현을 해뒀기 때문에 따로 오버라이드 할 필요는 없다. 커스텀한 fragment를 사용할 수도 있긴 할텐데 가능하면 부모 함수를 바로 쓰는 것을 추천한다.

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredTabs.put(position, (IMarkdownTab)fragment);
        return fragment;
    }


public void destroyItem


생성한 Fragment를 제거하는 작업이다. instantiateItem 함수에서 부모 함수를 이용했다면 destroyItem 에서도 부모 함수를 이용해서 처리하는 것이 좋다. 그래야만 부모 함수의 변수들과 싱크를 맞출 수 있으니까. 마찬가지로 가능하면 부모 함수를 바로 쓰는 것을 추천한다.

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
    }


전체 코드는 여기서 볼 수 있다.


3. ViewPager + Custom PageAdapter 


이제 만든 CustomPageAdapter를 xml의 ViewPager과 연결하면 된다. 아래와 같이 adapter 객체를 선언해준 후 ViewPager의 setAdapter 함수를 통해 연결 해줄 수 있다. 직관적인 코드라 이해하는데 별로 어렵지 않을 것 같다


adapter = new MarkdownPagerAdapter(getSupportFragmentManager(), Titles,
        selectedImgSrc, unselectedImgSrc, Titles.length);
viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(adapter);


4. ViewPager 페이지 선택 콜백함수


ViewPager 객체 내에 PageChange 리스너를 등록해서 페이지가 변경 될 때마다 특정한 작업을 하도록 만들 수 있다.


viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int curPosition) {
        IMarkdownTab curImt = adapter.getRegisteredTab(curPosition);
        IMarkdownTab preImt = adapter.getRegisteredTab(prePosition);

        if (curPosition == prePosition || curImt == null)
            return;

        if (preImt!=null)
            preImt.cbPageUnSelected();

        curImt.cbPageSelected();
        prePosition = curPosition;
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
});


이또한 함수명으로 충분히 언제 불리는지 이해할 수 있을 것 같다. onPageSelected 함수에 넣은 코드는 사용자가 선택한 페이지의 화면을 새로고침 하도록 만든 작업이다.  상황에 맞춰서 커스터마이즈하면 된다. 아 그리고 리스너는 여러 개를 등록 할 수 있다. 난 이 점을 이용해 아래 그림처럼 화면 포커스를 바꿀 때 새로고침과 동시에 바의 위치와 그림을 바꾸기도 했다.      

     

   

리스너를 이용해 화면 포커스가 바뀔 때 그림 이미지와 바의 위치를 변경 할 수 있었다

'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16
Device screen dpi 값에 따라 처리하기  (0) 2019.02.16
안드로이드 선 추가하기  (0) 2019.01.26

px, dp, sp 개념 정리

컴퓨터공부/안드로이드 2019.02.16 16:04 Posted by 아는 개발자 아는 개발자

XML 파일 안에서 UI 컴포넌트의 크기를 정할 때 dp, sp, px 단위의 원리를 제대로 알지 않고 사용하는 경우가 있는데 이렇게 막 사용하다 보면 디바이스 해상도가 변경 될 때 화면이 깨지는 경우가 생긴다. 이번 포스트에서는 지금까지 막무가내로 사용해온(?) dp, sp, px 단위를 총 정리 해보려고 한다.


0. px (Pixel)



주소화 할 수 있는 화면의 가장 작은 단위. 점을 찍을 수 있는 가장 작은 단위라고 생각하면 된다. 화면 해상도와 관계 없이 컴포넌트의 크기를 절대값으로 세팅할 수 있으며 위의 그림으로 알 수 있듯이 오른쪽으로 갈 수록 폰의 해상도가 높아지면서 텍스트 상자와 이미지 아이콘이 작아진 것을 알 수 있는데 값을 픽셀 단위로 설정했기 때문에 그렇다. 이런 단위로는 여러 디바이스 환경마다 아이콘의 크기가 천차만별이 될 수 있어서 안드로이드 환경에서는 잘 사용하지 않는다.


1. dp (Density-independent Pixels)


화면의 해상도값에 따라서 크기가 조정되는 단위다. 160dpi 스크린 해상도에서는 1px만큼의 크기를 가지고 해상도 값이 커질수록 픽셀의 값이 커진다. 320dpi 해상도면 2px 만큼의 크기를 가지고 480dpi 면 3px만큼 가지는 원리다. 위의 그림에서도 보이듯이 해상도가 높아져도 텍스트상자와 이미지의 크기가 일정한 것을 알 수 있는데 이는 해상도에 따라서 크기가 재설정 됐기 때문이다. 해상도에 따라 크기가 자동으로 늘어나고 줄어들 수도 있으니 스펙이 다양한 안드로이드 디바이스 환경에서 적용할 때 유용한 단위다.


2.  sp (Scale-independent Pixels)


dp랑 비슷한 원리로 동작하며 사용자의 글짜 크기(font-size)를 조정할 때 사용되는 단위다. 

'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16
Device screen dpi 값에 따라 처리하기  (0) 2019.02.16
안드로이드 선 추가하기  (0) 2019.01.26
SharedPreference  (0) 2019.01.10

Device screen dpi 값에 따라 처리하기

컴퓨터공부/안드로이드 2019.02.16 14:46 Posted by 아는 개발자 아는 개발자

안드로이드 앱을 만들때면 무수히 많은 종류의 화면과 모두 호환 할 수 있는 UI를 만드는 일이 꽤나 골칫 거리다. 아이폰과 달리 안드로이드 폰은 제조사가 한군데만 있는 것도 아니고 또 같은 회사에서 만든 폰이라도 디스플레이 스펙이 제각각이라 이에대한 고려 없이 막만들었다간 화면이 깨져버리는 참사가 벌어지고 만다.


Android에서는 UI 개발자들이 이런 점들에 대해서 유연하게 대처 할 수 있도록 여러가지 API를 제공하는데 이번 포스트에서는 getResources().getDisplayMetrics().density 에 대해서 알아보려고 한다.


이 값은 디바이스의 160dpi 대비 디바이스의 screen density (dpi) 의 비율을 나타내며 실행되고 있는 디바이스 화면 스펙별로 값이 다르다. 디바이스의 화면이 320dpi면 2이고 420dpi면 2.625의 값을 가진다. 이 비율 값을 이용하면 다양한 하드웨어 스펙 별로 화면 값을 자동으로 조정 할 수 있게 된다.


int padding = (int) (TAB_IMAGE_VIEW_PADDING_PIV 
       / (getResources().getDisplayMetrics().density * 2));
imageView.setPadding(padding, padding, padding, padding);


이 값을 이용해 애플리케이션 오른쪽 상단 아이콘의 image view 주변 패딩 값을 조절하는데 사용 해봤다. View 클래스의 public void setPadding 함수는 인자를 절대 수치인 pixel 단위로 값을 받기 때문에 변환된 값을 전달해야한다. 그래서 getResources().getDisplayMetrics().density 값을 나눠서 화면 스크린 화질이 좋을 수록 값이 작아지도록 변경했다. 결과 아래 그림처럼 다른 화질을 가진 환경에서도 아이콘의 크기를 맞춰줄 수 있게 됐다.


'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16
Device screen dpi 값에 따라 처리하기  (0) 2019.02.16
안드로이드 선 추가하기  (0) 2019.01.26
SharedPreference  (0) 2019.01.10

안드로이드 선 추가하기

컴퓨터공부/안드로이드 2019.01.26 15:19 Posted by 아는 개발자 아는 개발자

안드로이드 앱을 사용하다보면 기사나 댓글 화면에는 여러 개의 아이템을 구분짓기 위해서 빨간 상자로 표시한 것처럼 선을 사용한다.


1. 원리


안드로이드에선 따로 선에 해당하는 라이브러리가 있지는 않고 직사각형 두개를 겹쳐서 선처럼 보이게하는 트릭을 사용한다. 아래의 그림처럼 한 사각형은 선으로 사용할 색으로 채우고 다른 사각형은 배경색 채우는 식이다. 두 사각형을 겹칠 때 흰색 사각형을 선색 사각형 위에 올리고 위치를 위쪽을 조금 올리면 선색 사각형의 남은 부분이 선처럼 보이는 착시효과를 줄 수 있다.



2. 코드


코드상으로 표현하면 다음과 같다.

// res/drawable/line.xml


    
    
        
            
            
        
    

    
    
        
            
            
        
    


Line color rectangle은 앞의 그림에서 왼쪽 사각형에 해당하는 코드고 Background rectangle은 오른쪽 사각형에 해당한다. Background rectangle 코드를 보면 android:bottom 값이 1dp 가 설정돼있는데 이것은 사각형을 아래에서 1dp만큼 띄어두겠다는 뜻이다. 


각 사각형마다 stroke와 solid 속성값이 있다. stroke 는 선의 색을 의미하고 solid는 사각형 내부 색을 말한다. 왼쪽 사각형은 선과 내부 색이 회색 int 값인 #FF5E5E5E 으로, 왼쪽 사각형은 흰색인 #FFFFFFFF 으로 채워져있다.


3. 적용


완성한 xml 파일을 선을 표시할 영역의 backgroud로 설정하면 아래와 같이 적용된다. 타이틀 텍스트 뷰 밑에 선을 넣고자 LinearLayout의 background 값으로 설정했다.




        
    



'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16
Device screen dpi 값에 따라 처리하기  (0) 2019.02.16
안드로이드 선 추가하기  (0) 2019.01.26
SharedPreference  (0) 2019.01.10

SharedPreference

컴퓨터공부/안드로이드 2019.01.10 21:52 Posted by 아는 개발자 아는 개발자

애플리케이션을 종료한 이후에도 수정한 값을 유지하려면 데이터를 간단한 텍스트 파일에 써두거나 특정 데이터베이스의 형태로 저장해야 한다. 안드로이드에서는 관계형 데이터베이스로는 SQLite를, 그리고 단순히 key-value 값을 저장할 수 있는 용도인 Shared Preference를 제공한다.


Shared Preference 


애플리케이션의 설정 값처럼 데이터가 key-value의 형태이고 단순한 타입을 사용하며(boolean, int, float, long, string) 기록해야할 양이 얼마 되지 않는다면 관계형 데이터베이스를 사용하는 것보다 SharedPreference를 쓰는 것이 훨씬 편하다. SharedPreferece는 복잡하고 길고긴 쿼리문 없이 객체와 key값을 이용해 데이터를 쉽게 수정하고 가져올 수 있다.


1. 객체 생성


settings = Context.getSharedPreferences(PREF_NAME, MODE);


데이터를 수정할 수 있는 SharedPreference 객체는 Context 객체내에 내장된 getSharedPreference 함수를 통해 가져올 수 있다. 상황에 따라서 다르나 나는 주로 getApplicationContext를 사용한다.


인자 값으로는 두개를 받는데 첫번째는 PREF_NAME, SharedPreference로 저장되는 데이터베이스의 이름을 말한다. 이 값이 달라지면 서로 다른 데이터베이스 파일에 저장되게 되고 별도의 key값으로 관리된다. PREF_NAME은 storage에 XML파일의 이름으로 들어간다.


두번째 인자 값은 동작하는 형태다. 주로 0을 넣거나 MODE_PRIVATE, MODE_WORLD_READABLE, MODE_WORLD_WRITEABLE을 혼합해서 사용한다.


2. 기록


SharedPreferences.Editor editor = settings.edit();
editor.putLong(FILE_ID, fileId);
editor.commit();


값을 기록할 때는 생성한 SharedPreference 객체로부터 editor 객체를 가져온다. 이 객체내에는 putInt, putBoolean, putString ... 함수가 있는데  모두 key값에 해당하는 데이터가 없으면 데이터를 추가하고 있으면 업데이트 하는 함수다. 저장하는 데이터 타입은 함수 명으로 유추할 수 있다.


첫번쨰 인자 값은 key값이고 두번째 인자 값은 저장할 데이터의 값이다. key값은 나중에 데이터를 읽어올 때 필요하니 static 변수로 저장해두는 것이 편하다.


putXXX 함수를 호출한 다음에는 에디터 함수 내의 commit 또는 apply함수를 호출해서 데이터 값을 최종적으로 기록한다. 호출하지 않으면 수정한 값이 파일에 저장되지 않는다. commit은 동기 방식으로 apply는 비동기 방식으로 값을 업데이트하니 이 또한 상황에 따라서 적절히 활용하면 된다.


3. 읽어오기 


return settings.getLong(FILE_ID, -1);


값을 수정할 때 처럼 SharedPreference 함수에 내장된 getXXX 함수를 통해서 읽어올 수 있다. 첫번재 인자는 읽어올 key 값이고 두번째 인자는 만약 key값에 해당하는 데이터가 없거나 읽어오는데 실패했을 때 기본으로 얻게될 데이터 값이다. 


4. 주의사항


공식문서에는 SharedPreference의 동작 형태중 하나인 MODE_MULTI_PROCESS 가 API23 이후로 Deprecated 됐고 앞으로 안드로이드에서도 지원할 생각이 없다고 한다.


MODE_MULTI_PROCESS 에 해당하는 대표적인 예로 안드로이드 UI 컴포넌트와 안드로이드 서비스가 별도의 프로세스로 분리된 경우가 있다. 개발하다보면 안드로이드 액티비티와 서비스가 데이터를 공유해야하는 일이 종종 생기는데 Context와 데이터 이름만 지정하면 얻어올 수 있는 SharedPreference 객체를 사용하는 것이 가장 심플하게 구현할 수 있는 방법이라 자주 사용된다.


액티비티와 서비스가 동일한 프로세스라면 상관 없지만 둘이 별도의 프로세스인 경우엔 실행도중에 동기화 문제가 발생한다. 글쓴이가 몸소 체험한 동기화 문제는.. 서비스를 먼저 실행시킨 다음 액티비티 에서 값을 변경했더니 백그라운드 서비스에서 수정한 값을 읽어오지 않고 예전 값을 계속 읽어오던 일이 있었다. 처음에는 데이터 수정이 제대로 되지 않았는지가 의심스러워 commit() 함수 리턴값만 확인 했었는데 알고보니 별도의 프로세스 환경에서는 사용할 수 없었다. 이것 때문에 몇시간을 잡았는지 모른다.



'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16
Device screen dpi 값에 따라 처리하기  (0) 2019.02.16
안드로이드 선 추가하기  (0) 2019.01.26
SharedPreference  (0) 2019.01.10