반응형

 

안드로이드 핸들러를 잘 모르고 사용하시는 분들 중 제목과 같은 현상을 겪은 사람이 많을 것이다.

핸들러의 주 목적은 서브 쓰레드에서 주 쓰레드로 데이타를 전달해주는 것이다.

 

 예를 들면 아래와 같다.

토스트 같은 경우는 안드로이드에서 메세지 띄워주는 용도로 매우 유용한데,

메인 쓰레드에만 사용할 수 있으며 그 외의 쓰레드에서 사용하게 되면 오류를 내며 튕겨버린다.

서브 쓰레드에서 쓰기 위해 보통 아래와 같이 핸들러를 구성하고

서브 쓰레드에서 ProcessMessage() 함수를 호출하면 정상적으로 토스트가 동작한다.

 

하지만 코드에서 핸들러 부분이 블럭 처리되어있어서 무언가 찝찝하다.

마우스를 한번 갖다대보자.

 

핸들러를 정적으로 사용하지 않으면 메모리 릭이 발생할 수 있다는 의미이다.

'More'를 눌러서 더 자세한 내용을 보면 아래와 같다.

 

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less... (Ctrl+F1)
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

 

정리하면 핸들러는 내부 클래스로 선언되어 가비지 컬렉터에 의해 소멸되지 않을 수 있다는 것이다.

그래서 핸들러를 정적 내부 클래스로 선언하면 해당 이슈를 해결할 수 있다.

추가로 정적 내부 클래스로 선언하게 되면, 외부 클래스의 정적멤버 외에는 접근할 수 없다.

그래서 혹시나 외부 클래스의 멤버에 접근하고 싶다면 WeakReference 객체를 이용하면 된다.

여기서는 단순히 토스트를 띄우는 것이 목적이므로 해당 객체를 사용하지는 않을 것이다.

 

서론이 길었는데 어쨌든 해결 방법은 간단하다.

위의 핸들러 관련 코드를 아래와 같이 수정하면 된다.

 

    private final MyHandler handler = new MyHandler();

    private static class MyHandler extends Handler {
        public MyHandler() {
        }

        @Override
        public void handleMessage(Message msg) {
            if(msg.arg1 == 1)
                Toast.makeText(Global.getContext(), "오류가 발생하였습니다.", Toast.LENGTH_SHORT).show();
            else if(msg.arg1 == 2)
                Toast.makeText(Global.getContext(), "정상적으로 처리되었습니다.", Toast.LENGTH_SHORT).show();
        }
    }

 

이처럼 단순히 정적 내부 클래스로 선언하면 된다.

그럼 메모리 릭의 걱정도 없고, 문제 없이 핸들러를 사용할 수 있다.

반응형
반응형

 

개발자들이 시간 측정의 용도로 GetTickCount()를 애용하고 있다.

 

정밀도 높은 시간 측정은 아니지만 편리한 맛에 많이 사용되고 있다.

 

GetTickCount() 함수를 간단하게 설명하자면, 이 함수는 윈도우즈가 부팅된 이후부터 시간이 카운팅되는 함수이며

 

수치는 1ms 단위로 카운팅된다. 즉, 윈도우즈 부팅 후 1초가 지났다면 반환 값은 1000이 된다.

 

하지만 이 함수는 한계가 있는데 바로 반환 시간이 최대 DWORD(32비트)라는 것이다.

 

날짜로 따지면 49.7일인데, 49.7일 동안 윈도우즈를 종료하지 않을 경우 0으로 초기화되는 것이다.

 

unsigned long Timer = GetTickCount();

while(true)
{
	if(GetTickCount() >= Timer + 1000) //1초가 지났는가?
	{
	    printf("1초 지남");
	    Timer = GetTickCount();
	}
}

위와 같은 코드가 있다고 하자.

 

1초에 한번씩 1초가 지났다는 메세지를 띄워주게 된다.

 

그런데 49.7일이 거의 다 된 상태에서 만약 "1초 지남"을 프린트하고 Timer 변수를 갱신한 뒤에,

 

49.7일이 지나 GetTickCount()가 0으로 초기화된다면?

 

즉, 예로 Timer = 10000이 들어간 상태에서 GetTickCount()가 0으로 초기화가 된다면

 

1초가 지날 때마다 조건을 만족해야함에도 불구하고 초기화로 인해서 10초를 더 기다려야 하는 상황이 발생한다.

 

49.7일 이전에 윈도우즈만 재부팅한다면 이런 것을 걱정할 필요는 없으나, 서버 등 계속 켜놓아야 하는 상황에서는

 

이런 상황이 발생할 수 있다.

 

그래서, 그 대안으로 GetTickCount64() 함수를 쓸 수 있다. 기존 GetTickCount()에서 범위만 커진 것이다.

 

64비트로 확장되었기 때문에, 5억년이 넘는다. 즉, 아무리 PC를 켜놓아아도 위와 같은 문제를 일으킬 일이 없다고 봐도 된다.

 

unsigned __int64 Timer = GetTickCount64();

while(true)
{
	if(GetTickCount64() >= Timer + 1000) //1초가 지났는가?
	{
	    printf("1초 지남");
	    Timer = GetTickCount64();
	}
}

이와 같이 변수 타입과 함수명만 바꾸어주면 되니 매우 간단하다.

 

결론적으로 GetTickCount()를 사용할 경우 상황에 따라, GetTickCount64() 사용하는 것도 고려해야할 것이다.

 

반응형
반응형

안드로이드에서 빌드할 때 INSTALL_FAILED_OLDER_SDK 오류가 발생하는 경우가 있다.

 

보통 이 경우는 안드로이드 스튜디오에서 설정한 안드로이드 SDK 버전과 타겟의 버전 안맞는 경우인데,

 

일반적으로 build.gradle의 minSdkVersion을 낮게 설정하여 해결이 되지만,

 

필자의 경우는 다른 경우였다.

 

build.gradle 파일 내 최하단에 보면 아래와 같은 코드가 있다.

 

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.1'
}

 

필자의 경우는 여기서 appcompat에 대한 버전이 옳지 않아서 발생하는 오류였다.

 

 compile 'com.android.support:appcompat-v7:22.2.1'

 

 compile 'com.android.support:appcompat-v7:22.1.0'으로 수정한 뒤 해당 문제가 해결되었다.

 

반응형

+ Recent posts