본문 바로가기
프로그래밍 공부

모바일 앱 프로그래밍 공부 5일째 - 이지뷰어 앱만들기 (2019.03.23)

by 채리 2019. 12. 4.
  • 이지뷰어 앱 만들기

    집사람이 요청한 영어단어앱을 만지작거리다보니, 이제 대충 안드로이드 개발툴도 익숙해진 것 같습니다.
    그러다보니 조금씩 간덩이가 커지기 시작하는군요. 

    흠... 이지뷰어도 모바일앱으로 한번 만들어봐? 

    모바일앱 공부를 시작한지 5일밖에 안되는 초짜가 간땡이가 붓다못해 헛생각까지 하기 시작합니다. 
    일단 저질러보죠. 뭐... 

    하다 안되면 때려치고 다시 윈도우 프로그래밍 공부를 하면 되니깐요.

  • 필요한 기능들부터 만들어보자

이지뷰어에 필요한 기능들부터 만들어봅니다.
일단 화면에 글자들을 출력할 수 있어야겠죠.
그런다음 줄간격, 여백기능도 추가해야 되겠고...
가장 중요한 건 파일을 읽어서 조합형/완성형/UTF-8/유니코드등 인코딩체크 및 기능도 있어야 하구요.

초짜가 처음부터 무에서 유를 창조할 수는 없는 노릇...
안드로이드에서 제공되는 TextView라는 걸 사용해봅니다.
윈도우 프로그래밍에서 Label과 비슷한 녀석인데, Label보다 훨씬 다양한 기능을 가지고 있는 녀석입니다.
하지만 전 그냥 글자만 출력할거라서 그 많은 기능들이 필요없습니다.

TextView를 상속받아서 클래스 하나 만든다음, 글자를 출력해봅니다.
윈도우에서는 TextOut이 있으니 안드로이드에서도 이와 비슷한 함수가 있겠죠.

Text라고 입력하니 이와 관련된 수많은 함수들이 튀어나옵니다.
안드로이드는 drawText라는 이름으로 글자출력 함수가 있네요.
하지만 drawText함수로 글자를 출력하려니 인자값으로 Canvas가 필요한데, 당최 Canvas라는 녀석을 가져올 방법이 없습니다.
윈도우에서는 언제 어디서나 그냥 TextOut을 사용해서 글자를 출력할 수 있지만, 안드로이드는 Canvas를 구해서 drawText를 사용했다고 하더라도 글자가 출력되지 않습니다.

무조건 onDraw이벤트에서 글자를 출력해야 화면에 보여지더군요.

 

으아~ 대체 어떤식으로 글자를 출력해야할지 감이 잡히질 않네요.
몇시간 곰곰히 생각해보다 이지뷰어 동작방식을 바꿔보기로 했습니다.

원래 이지뷰어 동작순서는
1. 파일읽기
2. 한글인코딩 체크
3. 파일 인코딩
4. 워드랩/여백/줄간격 적용
5. 비트맵에 직접 출력
이고, PAINT메시지가 날라올때 글자를 출력한 비트맵을 통째로 화면에 복사해주는 방식인데,

안드로이드는 화면에 직접 출력할 수 없기 때문에, 순서를 조금 바꿔봅니다.
1. 파일읽기
2. 한글인코딩 체크
3. 파일 인코딩
4. 워드랩/여백/줄간격 적용
5. invalidate로 강제로 화면 갱신 메시지 날리기
6. onDraw에서 해당 화면 영역만큼만 글자 출력

파일을 읽어서 TextView의 Text속성에 집어넣으려니 끔찍하게 속도가 느립니다.
아예 문서내용을 가지고 있는 버퍼를 따로 만들어 관리하도록 구조를 바꿔봅니다.

안드로이드의 기본 문자셋은 utf-8이니깐, 읽어온 파일도 몽땅 utf-8로 변환해서 모든 작업을 진행하도록 합니다.
utf-8로 작업을 하니 프로그래밍이 편하더군요.
아무것도 신경쓸필요 없이 기능만 만들면 되니깐요.

하지만, 20MB짜리 문서를 읽어 워드랩을 적용시켜보니 속도가 너무 느립니다.
거의 40초정도 걸린것 같습니다.


그러고보니, 모바일용 텍스트 뷰어앱에서 2-3MB이상의 텍스트 문서를 읽어본 기억이 없습니다.
파일을 읽어오는 속도가 끔찍하게 느렸기때문이죠.

흠... 다른 앱들도 속도가 느리다면, 원래 모바일앱에서는 느린게 당연한게 아닐까?
그래도 이건 아니지... 싶습니다.

간혹 32메가짜리 문서도 있는데... 그럼 이런 문서는 스마트폰에서는 읽지 말라는 거잖아요?

일단 1메가, 10메가, 20메가, 80메가짜리 테스트 문서를 만들어서 읽어오는 속도를 측정해봅니다.
파일을 읽어오는 것 자체는 1초도 안걸릴정도로 빠릅니다.
그렇다면 문제는 파일을 읽어오는 로직이 아니라, 다른 곳에 있다는 뜻입니다.

다시 로직을 재설계 해봅니다.
읽어온 파일을 utf-8로 변환하지 말고, unicode로 변환합니다.
그런다음 유니코드의 wchar_t형은 16비트니깐, 2바이트씩 묶어서 int배열로 집어넣습니다.
워드랩시 한줄씩 문자열을 잘라서 처리하지 말고, int형 ArrayList를 생성해서 각 줄의 첫위치만 저장하도록 합니다.

다시 테스트 해봅니다.
흠... 20메가 짜리 문서를 utf-8로 처리할때 40초걸렸고, unicode로 변경한후에는 20초정도 걸리네요.
많이 빨라졌긴하지만 아직도 느립니다.
뭐가 문제일까...?

그러고보니 PC용 이지뷰어를 만들때도 같은 문제를 경험한 적이 있었습니다.
각각의 글자의 Width를 구해서 뷰어가로폭을 넘길때마다 한줄씩 잘라 워드랩시키는데, 각 글자의 width를 구하는데 걸리는 시간이 상당하더군요.

그렇다면 글자한개의 폭을 구하는 함수의 호출을 최소화하면 해결될 것 같습니다.
한자의 갯수는 잘 모르겠지만, 표준완성형에 포함된 한자갯수까지 생각해보면 5천자이상은 될 것 같군요.

한글은 자음 14개, 모음 10개이지만, 고어도 생각해야하고, ㅄ같은 결합된 자음도 있으니 초/중/종성 결합 경우의 수가 3만개이상이 될 것 같습니다.

한글/한자는 width가 동일하다고 보고, 3만여개의 한글을 하나의 글자폭으로 계산하고, 한자역시 하나의 글자폭으로 계산한다면 대략 3만 5천번의 호출수를 줄일 수 있겠죠.

나머지는 호출빈도수가 낮으므로 그냥 처리...

그리고 한번 계산된 width는 캐시처리해서, 두번다시 width를 계산하지 않도록 하면 될 것 같습니다.

테스트해보니 이제 1초정도 걸리네요.

이제서야 이지뷰어의 뷰어 컴포넌트의 기본 뼈대가 만들어진 것 같습니다.

이제 남은 것은 중요 순서대로 만들어 봅니다.

EzEncode.java
한글인코딩용 클래스인데, 매번 new로 생성해 쓸필요가 없어서 static으로 만들었습니다.
자주 사용되는 함수들은 그냥 static 클래스로 만들어야 할 것 같습니다.

자바에서도 인코딩함수가 지원될 것 같지만, 아직까진 자바를 잘 모르는 관계로, PC용 이지뷰어에서 사용하던 인코딩관련 함수들을 몽땅 자바로 포팅해서 만들었습니다.

TEzPermCheck.java
엊그제 알게된 퍼미션 관련 기능들을 모아서 클래스로 만들어두었습니다.
매번 복잡하게 퍼미션 어쩌고 선언할 필요없이, 필요한 권한을 배열로 선언해서 호출하면 알아서 해당권한들을 체크하고, 권한이 없으면 앱을 종료하도록 했습니다.

TFindFile.java
파일목록창에 필요한 파일과 폴더를 읽어오는 클래스입니다.

여기까지 1주일이나 걸렸네요.

파일목록창에서 파일을 선택하고, 파일을 읽어서 인코딩시켜 화면에 보여주고...
두손가락으로 상하로 줄간격, 좌우로 글자크기를 조절할 수 있도록 만들었습니다.

사실 아직까지 액티비티간에 값을 주고받는 방법을 모르거든요. ㅠ.ㅠ
그래서 파일을 선택하면 숨겨놓은 뷰어 레이아웃을 호출해서 내용을 보여주도록 되어있습니다.
이 부분은 나중에 수정해야할 것 같습니다. ㅠ.ㅠ


댓글0