안녕하세요?


이번 포스팅은 [Do it 안드로이드 앱 프로그래밍]책에서 PDF파일을 열어주는 과제를 실습해 보고, 그 내용을 포스팅 하고자 합니다. 우선은 SamplePDFView라는 새 프로젝트를 생성하도록 합니다. 그리고 나서 activity_main.xml로 가서 입력상자 하나와 버튼 하나를 추가하도록 합니다.



일단 제 경우에는 기본적으로 포함이 되어 있는 제약 레이아웃에서 다음과 같이 하도록 하였으며, 다음으로는 MainActivity.java로 가서 코딩을 시작하도록 하였습니다.



우선 시작부터 난관이기는 난관이였는게, 책에서는 안 나왔습니다만, 제 경우에는 저렇게 입력상자의 ID를 지정해서, 어느 변수가 입력상자를 의미하는 지를 지정해야 했습니다.



다음으로는 if구문을 이용해서 코딩을 시작하도록 합니다. 우선 아래의 구문은 입력상자에서 추출한 문자열을 String형태의 데이터로 바꾸고, 그 값을 변수1번에 지정한다 라는 의미를 담고 있습니다.


String 변수1번 = 입력상자를 지정하는 변수.getText().toString();


이어지는 openPDF는 이 MainActivity.java내에서 임의로 지정한 메서드이기 때문에 아래의 내용에서 따로 서술하도록 하며, if 구문은 간단합니다. 저렇게 해서 얻은 변수1번에 뭐라도 적혀 있으면 임의의 메서드를 실행하고, 아니라면 PDF파일명을 입력하라는 메세지가 출력되도록 합니다.



다음은 openPDF()메서드에 들어가는 코딩입니다. 우선 여기서 코딩은 다음과 같은 구조를 지니고 있습니다.


String 변수2번 = Environment.getExternalStorageDirectory().getAbsolutePath();


이 구문은 변수2번은 SD카드의 디렉토리를 찾아서, 그 디렉토리의 주소를 지정한다는 의미입니다. 나중에 나오지만, 원하는 PDF파일이 SD카드에 있기 때문에 이렇게 코딩을 해서, SD카드의 어디에 있는지 그 주소를 찾게 됩니다.


String 변수3번 =변수2번+File.separator+변수1번;


이 구문은 변수3번은 디렉토리+파일명 이라는 것으로 파일명까지 포함한 경로를 지정한다는 의미가 됩니다. 여기서 중간에 있는 File.separator는 폴더를 의미하는 경로와 확장자가 따로 있는 파일명을 구분하는 일종의 구분자입니다. 


File 변수4번 = new File(변수3번);


이 구문은 새로이 temporary 라고 해서 임시적인 파일명을 생성하게 해 주는데, 이 파일은 변수3번 이라는 풀 경로에 있는 파일이며, 너무 변수 3번이 길기에, 변수4번이라는 것으로 바꾸어서 지징한다는 의미가 됩니다.


제가 이해하기로는 변수3번은 집주인이 사는 집의 주소라면, 변수4번은 바로 집주인의 이름이라고 보면 될듯 합니다.



다음은 if구문을 만들어서 만약 변수4번이 존재한다면, 아래의 문장을 실행하라는 의미인데, 이 문장은 다음과 같습니다.


Uri 변수5번 = Uri.fromFile(변수4번);


변수4번이라는 파일로 부터 값을 뽑아내서 그 값을 URI라고 하는 포멧의 형식으로 만들어라. 이런 의미인데, 이렇게 하는 이유는 안드로이드에서는 인텐트의 값을 비교하기 위해서 URI포멧의 데이터를 쓰기 때문이라고 저는 이해를 하고 있습니다. 앞으로 변수4번 이라는 파일이 실제로 존재하는 지를 찾아보기 위해서 다른 화면으로 넘어가기 때문에, 인텐트를 쓰고, 인텐트를 쓰고 비교를 해야 하기 때문에 저렇게 URI포멧으로 값을 지정한다고 저는 생각합니다.



다음은 인텐트를 실행하기 위한 여타의 과정과 같은데, 여기서는 URI포멧으로 된 인텐트가 어떤 확장자를 지녔는지를 비교해야 하는 필요가 있습니다. 그래야 파일 확장자에 걸 맞는 앱을 실행시킬 수 있기 때문인데, 그것을 위해서 다음과 같은 코드가 사용된 것입니다.


변수6번.setDataAndType(변수5번, "application/pdf");


이 문장의 의미는 다음과 같습니다. URI포멧의 인텐트 변수5번은 MIME라고 해서 어떤 확장자를 지닌 실행파일인지 일일히 지정을 하는데, 뒤에 있는 ""사이에 있는 것은 주로 PDF파일을 지정할 때 사용하는 고정적인 값입니다. 실제로 많은 실행파일에 따라 이 고정적인 값이 다른데, 여기서 열거하기에는 정말로 많은 내용이기는 합니다.



원래라면 그냥 startActivity()가 와서 인텐트를 실행시켜도 되지만, 이 경우에는 SD카드에 찾고자 하는 PDF파일이 없을 수가 있습니다. 그렇기 때문에 이런 경우를 대비해서 [예외조항]이라는 것을 만들어서, 에러로 인한 앱의 종료를 막아야 합니다. 여기서 사용하는 구문은 try catch 구문입니다.


try { 문장 }

catch(예외적인 상황) { 문장2}

catch(예외적인 상황2) {문장3}


이 구문은 다음과 같은 의미를 지니고 있습니다. 우선 try안에 있는 문장을 실행하는데, 예기지 않게 오류가 발생하는 상황이 없으면 catch뒤에 있는 문장은 실행이 되지 않습니다.


그러나 이 예외적인 상황예외적인 상황2라는 것은 에러가 발생해서 앱이 종료될 수준으로 오류가 발생하는 상황인데, 이런 상황이 발생했다고 합시다. 그럼 예외적인 상황 이라는 에러가 생기면 문장2가 실행이 되고, 예외적인 상황2라는 에러가 생기면 문장3이 실행이 됩니다.


여기서는 ActivityNotFoundException이라는 것으로 startActivity()가 주어진 인텐트를 찾지 못하는 상황-여기서는 입력된 PDF파일을 볼수 있는 앱이 없는 경우를 의미합니다. 그런데 여기 다음에 붙는 ex가 무슨 뜻인지는 아무리 해도 저는 알아낼 수 없었습니다. 혹시 아시는 분 있으시면 댓글에 달아 주시기 바랍니다.


아무튼 본론으로 돌아와서, 여기서 찾고자 하는 PDF뷰어 앱이 없으면 메세지를 짧게 출력하도록 하고, 이제 다음으로 넘어가도록 합니다.



위 스크린샷에서 붉은색 박스로 표시한 부분은 아까 위에서 언급한 if(변수4번.exists())에서 PDF파일이 없는 경우를 의미합니다. 


즉, 이 경우에는 PDF파일을 열수 있는 앱은 있고, SD카드에 찾고자 하는 PDF파일이 없다면, 그에 합당한 메세지를 띄우라는 구문이 됩니다.



그리고 나서 외부 메모리를 건드리기 때문에 AndroidManifest.xml로 가서 위 스크린샷과 같이 권한을 부여하도록 합니다. 그런데, API23이상 부터는 이렇게만 해서는 이런 위험권한이 실행되지 않기 때문에, 추가적인 조치가 필요합니다. 일단 책에서는 다음과 같은 방법을 언급하고 있습니다.



먼저 Gradle Scripts폴더를 열어서 위 스크린샷에 보이는 바와 같이 build.gradle(Module:app) 파일을 더블클릭해서 열도록 합니다.



위 스크린샷에서 보이는 것처럼 targetSdkVersioin 25로 되어있는 부분을 22로 변경하도록 합니다.



그리고 나서 뜨는 Sync Now를 눌러서 변경된 설정이 적용되도록 합니다. 여기까지가 책에서 나와 있는 내용을 그대로 실습하였고, 이제 USB케이블에 연결된 스마트폰에서 실행을 시킬 차례입니다.


하지만 문제는 여기서 발생하였습니다.


분명히 Do it 안드로이드 앱 프로그래밍 이라는 책에서 본 대로 실습을 한 것 같습니다만, 이 앱은 설치하고 나서 실행하자 마자 옆의 스크린샷 처럼 그대로 종료가 되어 버리는 오류가 발생하였습니다.


무엇이 문제인지 모르겠습니다만, 해결책을 찾기가 상당히 난감할 것으로 예상이 됩니다.

+ Recent posts

티스토리 툴바