Regular Motion

개발자가 상팔자

[Android] Doubt till your Bug Tracking tool.

AndroidStudio의 Monitors로 memory monitoring 중 기이한 현상을 발견했다.

앱에서 다운로드를 시작하면 장판처럼 고요하던 메모리가 요동을 치기 시작하는 것이다.

실제로 3~5MB 정도의 메모리가 매우 빠른 속도로 할당/해제를 반복한다. 저사양 단말에서 파일 다운로드시 앱이 굉장히 느려지는 현상도 왠지 요동치는 그래프와 깊은 관련이 있을 것 같다.

다운로드 중 Monitors 툴에 나타나는 그래프는 아래와 같다.

요동치는 메모리

17초부터 다운로드가 시작됐고, 이후 요동치는 메모리를 보시라. 길에서 탕웨이를 우연히 만나면 내 심장도 저렇게 요동 치겠지.

처음에는 다운로드 중 Notification과 View를  갱신하기 때문에 메모리가 요동치는 줄 알았는데, 해당 작업을 주석처리 후 테스트 해봤지만 요동치는 메모리는 돌아오지 않았다.

범인을 잡기 위해, Allocation Tracking을 해보니 용의선상에 없던 놈이 범인이었다. 범인은 앱에서 사용하던 버그 트래킹 툴(Splunk Mint)인데,  툴에서 Network IO를 Monitoring 하는 로직이 BufferedInputStream의 read시마다 실행되도록 구현되어 있어 속도가 빠른 네트워크를 통해 대용량 파일을 다운로드 할 때 해당 로직이 매우 빈번하게 실행된다.

15초간 Allocation Tracking한 결과는 아래와 같다. 눈을 크게 뜨고 보시라.

절대 현혹되지 마라

 

다행히 Network Monitoring을 비활성화 할 수 있도록 SDK가 구현되어 있어 비활성화 후 테스트하니 메모리는 다시 고요해졌다.

이렇게…

백투장판

 

 

금일의 교훈

  1. 몸에 좋은 약도 알고 먹자.
  2. Android Studio의 Monitoring을 통해 Profiling을 가끔해보자. 버그 같지 않은 버그를 잡을 수 있다.

From Audio To Text

회사에서 종이책을 전자책으로 변환하는 중, Chapter 별로 녹음된 오디오 파일을 페이지 별로 컷팅 할 필요가 생겼다.

사실 내가 하는건 아니고, 책을 변환하시는 분들이 컷팅을 하는데 작업하는걸 옆에서 보니 약간만 도움을 드리면 일주일째 야근하는 저분도 칼퇴 할 수 있을 것 같아 방법을 찾아봤다.

기존의 작업 방식은 약 15분~1시간짜리 오디오 파일을 Seek 해가며 페이지의 마지막 문장이 끝나는 시점을 찾아 Timestamp를 기록 한 뒤, Timestamp 별로 오디오 파일을 컷팅했다. 이때 오디오를 Seek 해가며 각 페이지의 마지막 문장이 끝나는 시점을 찾는게 상당히 시간을 잡아먹는 작업이었다.

만약, 오디오 파일에서 자막 파일과 비슷하게 각 문장별 시작시간과 종료시간을 자동으로 추출 할 수 있다면, 페이지의 마지막 문장을 자막 파일에서 찾은 뒤 문장의 종료시간을 바로 찾을 수 있으니 퇴근을 못하고 파일을 컷팅하시는 분의 소중한 시간을 매우 절약 할 수 있을 것 같았다.

다행히 오디오 파일은 소음과 배경음이 없는 환경에서 원어민이 깔끔하게 녹음한 파일이라 괜찮은 Speech Recognize Engine만 있으면 자막에 준하는 파일을 자동으로 생성 할 수 있을 것 같았다.

이제 문제를 간단하게 요약 할 수 있을 것 같다.

mp3 -> smi

 

구글링을 30분 정도 해보니 괜찮은 Speech Recognizer가 3가지 정도 있는 것 같다.

  1. Google Speech  API : 2016년 06월 현재 Limited Preview라 신청 후 승인까지 시간이 소요되고, 승인이 안되서 그런건지 API Document를 찾기 어렵다.
  2. Nuance Naturally Speaking : 왠지 될 것 같은데 구매를 하지 않아 정확히는 모르겠다.
  3. CMUSphinx : 카네기멜론에서 공개한 Open Source Speech Recognition Engine. 설치와 설정이 약간 번거롭지만 무료고 명령어 기반으로 처리되기 때문에 Batch 작업 돌리기에 적합하다.

위의 3가지 옵션을 약 30분 가량 고민하다. 제일 Geek하고 Batch processing에 용이해보이는 3번으로 진행하기로 결정했다.

MP3 -> SMI 작업에는 ffmpeg과 cmu-sphinxbase, cmu-pocketsphinx이 필요하며 설치 과정은 아래와 같다.

  1. ffmpeg을 피씨 또는 서버에 추가한다. Package를 다운로드 후 bin에 추가하여 사용하시면 편합니다!
    (ffmpeg을 설치하는 이유는 CMUSphinx가 Input File로 wav파일만 받기 때문에 mp3 파일을 wav 파일로 변환해야 한다)
  2. cmu-sphinxbase를 설치한다.  (OSX 사용자 기준)
  3. cmu-pocketsphinx를 설치한다.  (OSX 사용자 기준)

* cmu-sphinxbase와 cmu-pocketsphinx의 stable 버전은 0.8인데 OSX 사용자는 HEAD revision을 설치하기를 권장합니다. 자세한 이유와 설치 방법은 https://github.com/watsonbox/homebrew-cmu-sphinx 포스팅 참고.

 

필요한 프로그램이 설치됐으면 필요한 기능들이 제대로 동작하는지 확인해보자

ffmpeg을 이용해 MP3 파일을 WAV로 변환하는 명령어 (ar: Sample Rate,  ac: Channel)

WAV 파일로 변환된 오디오에서 텍스트를 추출하는 명령어

 

위의 2가지 명령어 + 편한 Script 언어를 조합하여 오디오 파일에서 텍스트를 추출할 수 있다.

한가지 아쉬운 점은 CMUSphinx에 미국 발음 Recognizer만 기본으로 내장하고 있어 영국 발음으로 녹음된 파일의 경우 인식률이 조금 떨어진다는 점이다.

테스트 결과 미국 발음으로 녹음된 음원은 80~85%, 영국 발음은 60~65% 정도의 인식률이 나온다.

 

아래는 PHP로 작성된 MP3 -> SMI

* PHP 뉴비라 인터넷에서 몸에 좋다는 코드들 덕지 덕지 붙여서 만들었습니다;

* 고백하자면 이해를 돕기위해 자막/SMI 라는 표현을 차용했을 뿐 정확히 SMI 포맷을 생성하기 위해서는 약간의 수정이 필요합니다.

 

[Android] Unit Test

태초에 사람이 완벽하기는 커녕, 전혀 믿을게 되지 못하기 때문에 우리는 테스트가 반드시 필요하다.

 

Android에는 크게 2가지의 테스트가 존재한다. 1. Unit Test, 2. Instrumentation Test.

* Unit Tests는 Development machine의 JVM에서 실행되는 테스트를 의미하고,

* Instrumentation Tests는 실제 기기에서 수행되는 테스트를 의미한다.

Unit Tests는 Android API 사용에 제한이 있는 단점이 존재하지만, Development machine에서 각각의 기능을 빠르게 테스트를 수행 할 수 있다는 장점이 있다.

 

Android Studio에서 Unit Tests를 작성하기 위해서는 먼저 build.gradle에 아래 2개의 dependencies를 추가해줘야 한다.

 

2개의 dependency를 추가한 뒤, Build Variants의 Test Artifact를 Unit Tests로 변경한다.

 

이제 필요한 테스트 함수를 src/test/java 밑에 추가한 뒤, 구현한 Business Logic들이 정상적으로 동작하는지 검증하면된다.

추가로 각각의 테스트 함수는 상호 의존성 없이 작성되어야 한다.

 

아래는 AES Encryption/Decryption이 정상적으로 동작하는지 검증하기 위해 작성한 함수다.

Unit Tests를 통해 정상적인 경우는 물론, 예상할 수 있는 예외상황이 미리 정의된 대로 처리가 되는지 검증 할 수 있다.

 

태초에 인간은 완벽하지 않다. 쏟아지는 버그 리포트에 당황하지 말고 미리미리 대비하자.

 

* References

http://tools.android.com/tech-docs/unit-testing-support

 

[Android] Activity Transition.

Activity 전환시의 Transition은 매우 중요하다.

신부감을 고르는 것만큼은 아니지만 오늘 점심에 김치찌개를 먹을지 돈까스를 먹을지 보다는 중요하다.

중요도에 비해 적용은 매우 쉽다. 거의 공짜다.

간단한 효과의 경우 4개의 .xml 파일과 2줄의 코드면 끝이다. OMG!!!

2015년 S/S에 유행하고 있는 현재 Activity를 Step Back 시키면서 새로운 Activity를 우측에서 꺼내오는 효과는

아래 4개의 .xml 파일로 처리한다.

(참고로 UI/UX의 트렌드 세터 Airbnb도 위의 효과를 여러군데서 적용하고 있다)

slide_in.xmlslide_out.xml은 호출되는 Activity에 적용될 효과고,

step_back.xmlstep_in.xml은 호출하는 Activity에 적용할 효과다.

 

4개의 .xml 파일을 res/anim 폴더에 만들었다면 이제 8부 능선을 넘었다.

이제 만들어 놓은 .xml 파일들을 Activity를 실행하는 시점과 종료하는 시점에 적용만 하면 된다.

 

실행하는 시점의 코드는 아래와 같다.

overridePendingTransition의 첫번째 파라미터는 enter animation이고, 두번째 파라미터는 leave animation이다.

즉, 실행되는 Activity는 slide-in으로 들어오고, 현재 Activity는 step-back으로 나가는 거다.

 

실행되는것 만큼 Activity가 종료될때의 효과도 중요한데, Activity가 실행될때의 효과를 반대로 적용해주는게 좋다.

 

Activity가 종료될 때, 기존의 Activity는 step-in 하면서, 현재 Activity는 slide-out 시키는 효과다.

 

이제 당신의 앱도 섹시하게 화면전환이 되는걸 확인 할 수 있다.

아무렴 그렇고 말고.

[Android] App Indexing Introduction.

App Indexing은 짧게 요약하면 Google Search의 검색 결과에 어플리케이션의 컨텐츠를 노출시키는 방법이다.

어플리케이션에서 App Indexing을 구성하는 방법은 아래 4단계를 거치면 된다.

 

1. App Indexing을 Project의 dependencies에 추가한다. (시작은 쉽다)

 

2.  Google Search의 검색 결과에서 앱이 바로 실행되기 위해서는 intent-filter가 필요하다. (너와 나의 연결고리 말이다.)

검색 결과에서 바로 실행 될 Activity에 Deep Linking을 위한 intent-filter를 추가한다.

아래 intent-filter는 http://radioinn.com/podcast로 시작하는 broadcast를 처리하겠다는 의미다.

 

3. 연결고리까지 만들었으니, 이제 어플리케이션이 어떤 컨텐츠를 갖고 있는지 증명해야한다.

어플리케이션에서 Google Search의 검색 결과에 추가하고 싶은 컨텐츠를 아래와 같이 추가한다.

Title의 경우 Google Search의 자동 완성에 표시될 이름이고,

ContentId의 경우 Google Search의 겸색 결과에서 앱을 실행시에 dataString에 포함될 값이다.

* 실행된 Activity에서는 ContentId를 통해 원하는 컨테츠를 보여줄 수 있어야 한다.

 

3번까지 정상적으로 성공했으면, Google앱의 Search에서 Title에 포함된 내용을 검색했을 때 아래와 같이 나와야 한다.

아래 이미지에서는 ‘윤종신 허지웅의 어수선한 영화이야기’가 App Indexing을 통해 Google Search에 포함된 경우다.

우오오!!!! 이렇게 간단하다니!!!!

2015-06-02 09.59.55

 

4. 이제 Google Search에서 검색 결과를 클릭했을 때의 처리만 남아있다.

이미 2번에서 Google Search의 검색 결과에서 클릭했을 때 실행을 위한 intent-filter는 추가했었다.

intent-filter가 정상적으로 추가되어 있다면, Google Search에서 검색 결과를 클릭했을 때,

앱이 실행중이면 onNewIntent(), 앱이 실행중이 아니면 onCreate()로 intent를 넘겨준다.

이제 넘겨받은 intent에서 ContentId를 파싱해서 컨테츠를 보여주면 된다.

끝. (이제 앱이 히트를 칠 일만 남았다)

 

* 참고자료

- https://developers.google.com/app-indexing/introduction

http://io2015codelabs.appspot.com/codelabs/app-indexing#1

« Older posts

© 2016 Regular Motion

Theme by Anders NorenUp ↑