2019년 4월 10일 수요일

유니코드를 한글로 변환하기

파이썬에서 유니코드 형태의 한글을 정상적으로 표시되도록 하는 방법에 대한 것이다.
아래 변수 a에 다음과 같은 형태의 유니코드 값이 있을 때 이것을 정상적으로 한글로 표시되게 할려면 encode() 함수를 이용해서 byte형으로 변환 후 decode() 함수를 이용해서 unicode_escape 처리를 해 주면 된다.
아래에서 #-*- coding:utf-8 -*-는 파이썬에서 한글을 사용하고자 할때 필요한 코드이다.
비록 주석이라 할지라도 이 코드로 지정하지 않으면 한글이 들어간 코드는 컴파일 단계에서 에러를 내뿜는다.
#-*- coding:utf-8 -*-
a = "\uc790\uc720 \ub300\ud55c\ubbfc\uad6d \ub9cc\uc138"

#유니코드 문자열을 한글로 변환하기 위해서는 먼저 문자열을 encode 함수를 통해
# bytes 형으로 변환하고, decode 함수를 사용하여 'unicode_escape' 처리를 해 주면
# 한글이 제대로 출력된다.

a = a.encode('utf-8')
a = a.decode('unicode_escape')
print a
위 코드의 실행 결과는 아래와 같이 표현된다.
자유 대한민국 만세

2019년 4월 3일 수요일

앱의 Build And Run시에 앱에 등록된 기존의 데이터 특히 AlarmManager 정보의 존재 유무


어떤 앱을 개발중이다. 그 앱에는 AlarmManager를 이용해서 매일 정해진 시각에 알람 통지 + 알람 사운드를 울려준다고 할때
기 등록된 알람 시각 값이 SharedPreferences에 저장이 되어 있다.
그리고 그렇게 등록된대로 정해진 시간에 알람이 울린다.

그런데 개발 중이기 때문에 코드를 수정해 가면서 새롭게 앱을 Build and Run(맥에서 Control-R 혹은 Control-Shift-R) 시키면
기 등록된 알람 데이터(SharedPreferences에 저장된 데이터)는 어떻게 될까?
사라질까? 그대로 남아 있을까?
즉 그렇게 Build And Run을 기키면 기존 등록된 알람 시간에 정상적으로 알람을 울려줄까?

즉 Android 시스템에 등록된 AlarmManager의 등록된 객체(등록된 알람 시간)는 어떻게 될까? 그대로 존재할까 아니면 사라질까?

물론 앱을 폰에서 삭제한 후에 다시 Build and Run을 시키면 당연히 저장되어 있던 알람 데이터는 삭제된다.
이때는 해당 앱과 관계된 모든 데이터는 삭제된다.


그런데 폰에서 앱을 삭제하지 않고 수정된 코드를 테스트 및 확인하기 위해 Build and Run을 시키면 어떻게 될까?
구글의 API Reference 문서에 나와있는지 모르겠으나 내가 테스트해 본 바로는 첫 번째 Build and Run 때에는 기존의 설정 데이터가 그대로 보존되었다.
그러나 이것이 두 번, 세번 반복될때부터는 기존의 데이터들이 사라지게 된다.
정확히 말하면 SharedPreferences에 저장된 데이터는 삭제가 되지 않는다.
다만 Android 시스템에 등록되어 있던 AlarmManager의 등록된 객체가 사라진다는 것이다.


아무튼 결론은 Build and Run은 기존의 데이터 특히 AlarmManager에 대해 보장해 주지 않는다는 점이다.

이렇게 Android 시스템에 등록된 AlarmManager 객체의 존재 여부를 확인할려면 adb 명령을 통해서 확인할 수 있다.


DOS 창(리눅스나 Mac OS의 경우 터미널 창)에서 
Android SDK가 설치된 경로의 platform-tools 폴더의 하위 경로로 이동해서(adb.exe가 있는 위치로 이동해서)
스마트폰을 PC와 연결한 후 아래 커맨더로 안드로이드 시스템에 등록된 AlarmManager의 정보를 확인할수 있다.

adb shell dumpsys alarm

그러면 아래와 같은 복잡한 정보가 나온다.
    RTC_WAKEUP #0: Alarm{198895e type 0 when 1554422926096 com.sweettracker.smartparcel}
      tag=*walarm*:com.sweettracker.smartparcel/.halibut.HalibutSendAlarmReceiver
      type=0 whenElapsed=+17h46m43s806ms when=2019-04-05 09:08:46
      window=0 repeatInterval=0 count=0 flags=0x5
      operation=PendingIntent{481513f: PendingIntentRecord{251a79d com.sweettracker.smartparcel broadcastIntent}}
Batch{c31876b num=13 start=1116619814 end=1118823179 flgs=0xc}:
    ELAPSED_WAKEUP #12: Alarm{cdfc404 type 2 when 1116619814 com.google.android.gms}
      tag=*walarm*:com.google.android.gms.fido.authenticator.autoenroll.FIDO_KEY_VALIDITY_CHECK_DELAY_COMPLETE
      type=2 whenElapsed=+17h50m44s97ms when=+17h50m44s97ms
      window=+23h58m48s19ms repeatInterval=0 count=0 flags=0x0
      operation=PendingIntent{817dded: PendingIntentRecord{4961bdd com.google.android.gms startService}}
    ELAPSED_WAKEUP #11: Alarm{45e5238 type 2 when 1114137316 com.android.vending}
      tag=*walarm*:com.google.android.finsky.scheduler.FALLBACK3
      type=2 whenElapsed=+17h9m21s599ms when=+17h9m21s599ms
      window=+13h29m59s997ms repeatInterval=0 count=0 flags=0x0
      operation=PendingIntent{8864211: PendingIntentRecord{16720ff com.android.vending startService}}
    RTC_WAKEUP #10: Alarm{e07e04e type 0 when 1554418207741 com.example.exalarm_repeat}
      tag=*walarm*:com.example.exalarm_repeat/.AlarmReceiver
      type=0 whenElapsed=+16h28m5s451ms when=2019-04-05 07:50:07
      window=+12h38m59s998ms repeatInterval=0 count=0 flags=0x4
      operation=PendingIntent{a06756f: PendingIntentRecord{1faa808 com.example.exalarm_repeat broadcastIntent}}
    RTC_WAKEUP #9: Alarm{a15e422 type 0 when 1554416429513 com.hubizict.orientalhospital}
      tag=*walarm*:com.hubizict.orientalhospital/com.com.hubizict.orientalhospital.util.AlarmReceiver
      type=0 whenElapsed=+15h58m27s223ms when=2019-04-05 07:20:29
      window=+18h0m0s0ms repeatInterval=86400000 count=0 flags=0x0
      operation=PendingIntent{fa09fd1: PendingIntentRecord{82563d9 com.hubizict.orientalhospital broadcastIntent}}
    RTC_WAKEUP #8: Alarm{b20aeb3 type 0 when 1554414158867 com.hubizict.orientalhospital}
      tag=*walarm*:com.hubizict.orientalhospital/com.com.hubizict.orientalhospital.util.AlarmReceiver
      type=0 whenElapsed=+15h20m36s577ms when=2019-04-05 06:42:38
      window=+18h0m0s0ms repeatInterval=86400000 count=0 flags=0x0
      operation=PendingIntent{2767809: PendingIntentRecord{3bbc438 com.hubizict.orientalhospital broadcastIntent}}
    ELAPSED_WAKEUP #7: Alarm{3717f76 type 2 when 1092537187 com.android.vending}
      tag=*walarm*:com.google.android.finsky.scheduler.FALLBACK2
      type=2 whenElapsed=+11h9m21s470ms when=+11h9m21s470ms
      window=+8h59m59s911ms repeatInterval=0 count=0 flags=0x0
      operation=PendingIntent{76d6977: PendingIntentRecord{c06475d com.android.vending startService}}
    ELAPSED #6: Alarm{5883bd1 type 3 when 1086423185 com.google.android.gms}
      tag=*alarm*:com.google.android.gms.common.download.START
      type=3 whenElapsed=+9h27m27s468ms when=+9h27m27s468ms
      window=+8h59m59s994ms repeatInterval=0 count=0 flags=0x0
      operation=PendingIntent{1e15636: PendingIntentRecord{1f064f1 com.google.android.gms startService}}
    RTC #5: Alarm{5298f9c type 1 when 1554392921828 com.sec.android.app.sns3}
      tag=*alarm*:com.sec.android.app.sns.profile.ACTION_CHECK_OLD_DATA
      type=1 whenElapsed=+9h26m39s538ms when=2019-04-05 00:48:41
      window=+18h0m0s0ms repeatInterval=86400000 count=0 flags=0x0
      operation=PendingIntent{5b099c3: PendingIntentRecord{8cfdc97 com.sec.android.app.sns3 broadcastIntent}}
    ELAPSED_WAKEUP #4: Alarm{b1bdda5 type 2 when 1086297010 com.google.android.gms}
      tag=*walarm*:com.google.android.gms.auth.authzen.CHECK_REGISTRATION
      type=2 whenElapsed=+9h25m21s293ms when=+9h25m21s293ms
      window=+15h38m54s702ms repeatInterval=0 count=0 flags=0x0
      operation=PendingIntent{b03d57a: PendingIntentRecord{4cedd0f com.google.android.gms broadcastIntent}}

이상의 정보에서 위의 색상으로 표시된 내용을 보면 패키지가 com.example.exalarm_repeat로 되어 있는 앱에 의해서
RTC_WAKEUP형태로 등록되어 있고 알람 시각은 when=2019-04-05 07:50:07으로 등록되어 있음을 확인하게 된다.
그런데 앱을 Build And Run을 반복하게 되면 안드로이드 시스템에 등록된 AlarmManager 객체(정보)가 사라진다는 것이다.
그러나 SharedPreferences에 저장된 값은 그대로 존속한다. 따라서 이로 인해 등록된 정보가 SharedPreferences에 있기 때문에 정해진 시간에 알람이 울리리라 생각하지만 실상은 이상과 같은 일들이 내부에서 벌어진 것이다.

2019년 4월 2일 화요일

java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/message/HeaderGroup 에러 해법

  • 안드로이드 SDK가 Pie로 업그레이드 된 폰에서 다음과 같은 에러가 발생할 경우 대처 법
java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/message/HeaderGroup

(1) 앱 수준의 build.gradle의 targetSdkVersion을 28로 된 것을 27로 변경해 준다. 28에서 발생하는 문제이다. Api Level 28은 Pie(Android version 9)이고 27은 Oreo(Android version 8.1)이다. 

(2) Manifest.xml의 <application> 속성 아래에 다음 코드를 추가한다.
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

   <uses-library android:name=“org.apache.http.legacy” android:required=“false” />

…. />

구글의 설명을 들어보면 play-service map을 사용할때 발생하는 문제로 설명되어 있다.

If you are using com.google.android.gms:play-services-maps:16.0.0 or below and your app is targeting API level 28 (Android 9.0) or above, you must include the following declaration within the <application> element ofAndroidManifest.xml.

  <uses-library
      android:name="org.apache.http.legacy"
      android:required="false" />
This is handled for you if you are using com.google.android.gms:play-services-maps:16.1.0 and is not necessary if your app is targeting a lower API level.