안녕하세요?


지난번 포스팅에서 브로드 캐스트 수신자를 사용하는 해서 받는 것 까지 해봤는데, 이제 이것을 화면에 띄우는 내용을 실습해서 포스팅 하고자 합니다.



우선 package explorer에 마우스 우클릭을 해서, 위 스크린샷처럼 액티비티를 만드는 메뉴를 불러 오도록 합니다. 이 액티비티의 이름은 SmsActivity로 지정을 하였습니다.



다음으로는 activity_Sms.xml로 가서 위 스크린샷처럼 입력상자 3개와 버튼 하나를 두었습니다. 제약 레이아웃의 규격이 처음에는 많이 낯설었지만, 그래도 계속 하니까 어느정도 익숙해져 가는 것을 느끼는 중입니다.



다음은 SmsActivity.java에 본격적인 코딩을 시작하는 단계입니다. 우선 editText로 먼저 입력상자의 ID랑 변수이름이랑 지정을 하도록 합니다. 그리고 나서 확인 버튼을 누르면 화면이 사라지도록 finish(); 를 입력하도록 하고, 다음으로는 브로드 캐스트로 온 인텐트(Intent)-여기서는 아마도 SMS메세지가 될 것이기 때문에, 이를 받기 위한 getIntent메서드를 사용합니다. 그리고 나서, processIntent()라는 임의의 메서드로 이어서 작업을 하도록 합니다.




먼저 onNewIntent()라는 메서드를 입력해서 새로 액티비티가 생성되는 때가 아닌, SmsActivity가 이미 켜져있는 상태에서도 SMS문자 메세지가 왔을 경우에 받을 수 있도록 합니다.




다음으로는 processIntent()메소드를 코딩하도록 합니다. 여기서 당연 if구문을 두어서 인텐트가 빈 상태가 아닐때 작동이 되도록 합니다. 


여기서는 이미 인텐트의 데이터명이 지정이 된 것으로 나와있지만, 나중에 나올 SmsReceive.java에서 SmsActivity로 인텐트를 보내는 코딩을 할때, 여기서 일일히 데이터명과 데이터 내용을 지정하게 됩니다. 지금은 이미 지정이 되어 있다고 가정하고 코딩을 하는 것입니다.


여기서는 주요 내용은 String으로 각각의 문자열 변수를 지정하고서, 인텐트에서 어떻게 어떤 데이터명의 값을 가져와라 라는 구문이고, 이어서 editText.로 시작하는 구문은 앞서 activity_Sms.xml에 배치한 입력상자의 내용을 어떻게 다음의 내용으로 바꾸어라 하는 구문입니다.



다음은 SmsReceive.java를 코딩하는 내용입니다. 여기서는 우선 책에 나오지 않아서 놓친 부분을 위에 올려진 스크린샷에 보이는 것처럼 코딩을 해 놓았습니다. 저로서는 자세한 내용은 모르겠습니다만, 일단 이것이 날짜 형식을 만드는 명령어라는 것은 알만합니다. 


public SimpleDateFormat 변수 = new SimpleDateFormat("내용");


이어서 SmsActivity로 인텐트를 보내는 코딩을 시작하도록 합니다.



이번에는 sendToActivity()라는 메서드를 만들어서 코딩합니다. 당연 브로드 캐스팅도 실제로 존재하는 화면이 없기 때문에 addFlags를 두어서 FLAG_ACTIVITY_NEW_TASK를 두어야 합니다. 그리고 이어지는 FLAG_ACTIVITY_SINGLE_TOP과 FLAG_ACTIVITY_CLEAR_TOP도 두도록 합니다.


이어지는 putExtra()메서드는 이전에 설명한 그대로이고, 여기서는 특이한 것이라면, startActivity()라고 바로 시작하는 것이 아니라 

context.startActivity(인텐트); 로 되어 있다는 것입니다.


여기서 context가 정확히 무엇인지 알기 어렵긴 합니다. 다만 제가 이해한 바로는 이 SmsReceive라는 브로드 캐스팅에서 인텐트를 보내기 위해서는 그냥 start가 아니라 이런 방법을 써야 한다는 것입니다. 그래서 인텐트를 보내기 위한 코딩도 다음과 같이 구성이 되어 있습니다.


Intent 인텐트명 = new Intent(context, 액티비티명.class);

context.startActivity(인텐트명);



여기까지 코딩을 하고서 에뮬레이터에서 실행을 해 보았습니다만, 원하는 대로 액티비티가 뜨지를 않았습니다. 어디가 문제인지 몰라서 일단 AndroidManifest.xml로 가 보았습니다.



코딩을 하는 단계에서 무언가를 잘못 오타를 내어서 위 스크린샷에서 붉은색 밑줄을 친 부분이 잘못되어 있었습니다. 그래서 하는 수 없이 이 부분을 고쳤습니다.



이렇게 고치고 나서 다시금 실행을 시켰지만, 역시 액티비티는 뜨지를 않았습니다. 그래서 이번에는 AndroidMoniter에서 logcat을 보고서 onReceive()메서드는 제대로 호출이 되는지 살펴 보았습니다.



일단 제대로 logcat에서는 onReceive()메서드가 호출이 되는 것을 알 수 있었습니다. 그래서 다시금 어디가 문제인지 SmsReceive.java를 천천히 살펴 보았습니다.



고작 단 한줄이었지만, 이 한줄이 없어서 큰 에러가 뜨는 것이였습니다. 어떤 임의의 메서드이든 그냥은 실행이 안되고, 어디서 실행하라는 명령이 있어야 하는데, 생각해 보면 이게 없는 것 자체가 이상한 일이었습니다. 아무튼 이번에야 말로 제대로 실행이 될 것을 기대를 하고 한번 에뮬레이터를 실행시켜 보았습니다.



의도한 대로 제대로 SmsActivity에서 발신자 번호와 수신시각, 그리고 문자의 내용이 나오고 있는 것을 볼 수 있습니다. 이것으로 일단 Do it 안드로이드 앱 프로그래밍 책을 거의 1/3은 했다는 생각이 듭니다. 앞으로 나오는 내용은 위젯에 관한 내용인데, 이 내용들은 비교적 쉽게 익힐 수 있기를 기원합니다.

저작자 표시
신고

안녕하세요?


이번 시간에는 브로드 캐스팅(BroadCasting)이라고 해서, 메세지를 여러객체에 걸쳐서 전달하는 것을 만하는 것에 관한 실습을 할 예정인데, Do it 안드로이드 앱 프로그래밍 이라는 책에서는 한 챕터에 걸쳐서 설명을 했지만, 직접 실습해 보니 포스팅 하나로 끝내기에는 양이 많아서, 2개의 포스팅에 걸쳐서 블로그에 올려야 할 정도로 내용이 방대합니다.


우선 실습에 들어가기 위해서 SampleReceiver라는 프로젝트를 생성하도록 합니다. 그리고 나서 package explorer 윈도우에서 우클릭을 해서 아래의 스크린샷처럼 메뉴를 불러 오도록 합니다.



New메뉴에서 Other항목을 선택해서 Broadcast Receiver를 선택하도록 합니다. 이어서 다음 스크린샷과 같이 브로드 캐스트 리시버의 이름을 입력하도록 합니다.



이번 경우에는 SmsReceiver라고 지정을 하였으며, 블로그의 화면에는 직접 올리지는 않았지만, 안드로이드 스튜디오에서는 SmsReceiver.java라는 파일이 생성이 됩니다. 먼저 맨 처음 생성될 상태의 AndroidManifest.xml을 보도록 하겠습니다.



여기서 receiver에 있는 항목에다가 다음과 같은 코드를 추가해 주도록 합니다.



위 코드의 내용은 다른 것 없이 SMS메세지를 받아볼 수 있도록 하는 것에 의의가 있습니다. 이 이상도 있는 것 같기는 한데, 아직 저로서는 이 이상의 의미는 알 수 없었습니다.


다음은 SmsReceiver.java로 가서 다음의 코딩을 시작합니다.



제일 처음 클릭해서 들어가면 위 스크린샷과 같이 있는 것을 볼 수 있습니다. 이 상태에서 먼저 TODO에서 throw와 같은 구문까지를 지우고 본격적으로 코딩에 들어가도록 합니다.



먼저 public static final String TAG = "SmsReceiver"; 라는 구문으로 아래에 등장할 Log.i에 첫 문자열을 미리미리 지정하도록 합니다. 

그리고 나서 아래에 이어지는 Log.i()구문은 책에서는 제대로 onReceive()메서드가 호출이 되는지를 알아보기 위해서 라고 했습니다만, 제 경우에는 크게 도움이 되지는 않았습니다.


이어지는 SmsMessage[]란 항목과 parseSmsMessage(bundle)이라는 항목이 붉은 색으로 되어 있는데, 우선 SmsMessage[]라는 항목이 android.telephony라는 패키지에 묶여야 하는데, 이게 자동으로 안되고, 다른 유사한 패키지가 있어서 유저가 직접 지정하라는 의미입니다. 지정을 위해서는 Alt + Enter키를 누르면 됩니다.



이렇게 android.telephony를 지정하고 나서 붉은색으로 되어 있는 부분이 사라진 것을 볼 수 있습니다. 물론 여기서 아직까지도 붉은색으로 남아 있는 parseSmsMessage(bundle)이라는 것은 임의로 정의된 메서드로, 나중에 지정을 하도록 하겠습니다. 일단은 if 구문으로 넘어가 보도록 하겠습니다.


if (messages!=null && messages.length>0){ 


이 구문에서는 message가 비어있지 않고, 그 내용이 있을 경우, 즉 SMS메세지가 도착했고, 내용이 조금이라도 있는 경우에 다음과 같이 실행을 하라는 의미입니다.


String sender = messages[0].getOriginatingAddress();


여기서 sender는 변수명이라서 다른 이름으로 지정을 해도 상관이 없습니다. 그리고 제 능력으로는 알아보려고 했는데, 왜 message[0]에서 [0]이 붙는지 이유를 모르겠습니다.아무는 뒤에 오는 getOriginatingAddress()라는 메서드가 바로 SMS메세지를 보낸 번호를 잡아내는 메서드인 것은 알겠습니다. 즉, 이 구문은 SMS메세지가 도착하고 그 내용이 있으면, 전화번호를 캐치해 내라는 의미입니다.



이어서 String contents = messages[0].getMessageBody().toString(); 이라는 구문이 옵니다. 이 구문이 위 스크린샷에서는 가려져서 안 보이는데, 아무튼 내용은 SMS메세지 내용을 받아서, 그걸 String이라는 데이터 형태로 변형 하라는 의미를 지니고 있습니다.


그리고 그 아래에 나오는 다음과 같은 구문이 있는데, 붉은색으로 에러라는 의미를 지니고 있는 것을 보실 수 있으실 겁니다.


Date receiveDate = new Date(messages[0].getTimestampMillis());


여기서 Date라는 항목은 여러개의 패키지 내용에서 하나를 선택해야 에러가 사라지기 때문에, 직접 Alt + Enter를 눌러서 java.until이라는 패키지로 묶도록 해야 합니다.



패키지를 선택하고 나서야 붉은색으로 처리된 에러가 사라진 것을 보실 수 있습니다.



이제 마지막으로 Log.i()메서드를 추가해서 제대로 작동하는 지를 나중에 Logcat을 통해서 책에서는 알아볼 수 있다고는 했습니다만, 제가 직접 해 보니 이를 알아보기는 힘들었습니다. 아무튼 Log.i() 메소드까지 추가하는 데 성공했으니, 이제 본격적으로 parseSmsMessage(bundle)메서드를 정의하러 가볼 시간입니다.



우선 시작부터 private SmsMessage[] parseSmsMessage(Bundle bundle){로 되어 있는데, 여기서 SmsMessage는 반환값이라는 것은 알만합니다. 하지만 여기서도 여전히 []이 왜 붙었는지는 아직 이해가 되지 않습니다. 아무튼 이런 시작값을 만들어 주고서 위 스크린샷과 같은 코딩을 합니다.


이는 책에서도 자세한 설명이 없어서 크게 이해는 안되지만, 한가지 알 수 있는 것이, 이 코드 전체가 바로 SMS메세지를 확인할 수 있도록 API에서 설정을 해둔 메서드라고 합니다. 제 능력으로는 이 메서드를 어떻게 변형을 가하는 것 보다는 이대로 다른 앱에서도 써먹도록 하는 것이 좋다는 생각이 듭니다.


Private SmsMessage[] pareseSmsMessage(Bundle bundle){

 Object[] objs = (Object[])bundle.get("pdus");

 SmsMessage[] messages = new SmsMessage[objs.length];

 int smsCount = objs.length;

 for(int i=0;i<smsCount;i++){

  String format = bundle.getString("format");

  messages[i] = SmsMessage.createFromPdu((byte[])objs[i],format)}

 else{

  messages[i] = SmsMessage.createFromPdu((byte[])objs[i]);}

}

return messages;

}


이제 1차적인 코딩은 끝이 났으니, 이번에는 권한을 획득하러 가야 합니다. 다시 AndroidManifest.xml로 가서 다음과 같이 코드를 추가하도록 합니다.



위와 같이 SMS 메세지를 받을 수 있도록 권한을 추가하도록 합니다. 그런데 문제는 이 권한이 API23이상 부터는 위험권한으로 처리가 되어서 그냥은 어떻게 할 수 없다고 합니다. 그래서 이를 가능하도록 하기 위해서 편법을 쓰도록 하겠습니다.



build.gradle(Module:app)으로 가서 targetSdkVersion을 원래 25로 되어 있는 것을 22로 변경하도록 합니다. 그리고 나서 이를 USB에 연결한 스마트폰에서 이 앱을 실행시키면 인스톨 과정에서 보통 에러가 나기 때문에, 하는 수 없이 안드로이드 스튜디오 내의 에뮬레이터를 통해서 실행을 시키도록 합니다.



먼저 에뮬레이터가 커피한잔 마실 정도의 시간이 흐르고 나면, 이렇게 뜹니다. 여기서 가장 아래의 ... 항목을 눌러서 아래의 스크린샷과 같이 문자를 보내도록 합니다.



여기서 Phone이라는 항목을 클릭하면 위 스크린샷과 같은 화면이 뜨는에 번호는 아무 번호나 입력하도록 하고, 메세지의 내용을 정하도록 합니다. 그리고 나서 아래에 있는 Send Message를 눌러서 가장의 에뮬레이터로 메세지를 보내도록 합니다.



Android Monitor를 통해서 logcat을 살펴본 내용입니다. 여기서 살펴보면, onReceive라는 메서드가 호출되는 것을 확인할 수 있습니다. 이것으로 어떻게 제가 SMS메세지를 폰으로 보내면 받는 것을 실습해 보았으며, 이제 이 메세지를 받아서 어떻게 하면 화면에 띄울 수 있는지를 실습해야 하는데, 이 내용은 너무 포스팅의 내용이 길어져서 part2에서 다루도록 하겠습니다.

저작자 표시
신고

안녕하세요?


지난번에 onSaveInstanceState()와 onRestoreInstanceState()메소드를 실습해 보았지만, 그 결과가 영 시원치 않아서 실패라고 결론을 내린적이 있었을 것입니다.


링크 : 안드로이드 스튜디오 독학하기 38.5일-이번 시도는 시원찮습니다.


그런데 도서관에서 관련된 서적을 여러권 찾아보니, 이게 실패가 아니고 성공이었다는 내용이었습니다. 이번 포스팅에서는 그 내용을 다루고자 합니다. 우선은 지난번 포스팅의 코딩을 그대로 가져와서, onSaveInstanceState()메소드 부분을 /* */로 주석처리를 합니다. 


먼저 USB로 연결이 된 스마트폰에서 앱을 실행시키고 입력상자에 '홍길동'이라고 입력을 하였습니다. 그리고 나서 화면 회전의 잠금을 해제하고 한번 화면을 세로에서 가로화면으로 기울여 보았습니다.



화면을 기울인 것 만으로도 위 스크린샷에서 보실 수 있는 것처럼, 홍길동이라고 입력한 내용이 그대로 사라져 버리는 것을 볼 수 있습니다. 그래서 아래와 같이 코딩을 바꾸도록 하였습니다.



코딩의 내용은 간단합니다. 먼저 저장을 하기 위한 onSaveInstanceState()메소드는 다음과 같은 식으로 코딩을 하였습니다.


public void onSaveInstanceState(Bundle 변수1){

   String 변수2 = 입력상자.getText().toString();

   변수1.putString("데이터 이름", 변수2);

}


그리고 이어지는 onRestoreInstanceState()메소드는 다음과 같은 식으로 코딩을 하였습니다.


public void onRestoreInstanceState(Bundle 변수1){

  super.onRestoreInstanceState(변수1);

  String 변수3 = 변수1.getString("데이터 이름");

  입력상자.setText(변수3);

}


이렇게 코딩을 한 결과는 다음과 같았습니다. 



이번에는 화면을 회전시켜도, 입력했는 '홍길동'이 날라가지 않는 것을 제대로 확인할 수 있었습니다. 그런데 무엇이 문제였길래 지난번에는 실패한 것으로 생각했느냐 했더니, onSaveInstanceState()와 SharedPreferences를 이용해서 저장한 것이 차이가 있다는 것이였습니다.


onSaveInstanceState() = 임시적인 정보

SharedPreferences = 영구적인 정보


일단 영구적인 정보는 앱을 종료해도 저장이 되고, 심지어 기기를 재부팅하여도 저장이 되는 정보를 의미합니다. 그런데 반대로 임시적인 정보란 것은 앱이 실행되는 동안만 저장이 되고, 뒤로가기 두번을 눌러서 정상적으로 종료를 하면 자연히 소멸하는 저장방식이라고 합니다.


이러니 지난번에 앱을 종료했다가 다시 시작했을 때 입력상자에 들어간 내용이 사라진 것이 다 설명이 된다는 생각이 듭니다. 아무튼 임시적인 정보는 영구적인 정보 저장방법에 비해서, 장점이 있다고 하면 하나 있는 것이 일단 저장된 정보를 불러오는 onRestoreInstanceState()에서 if구문으로 저장된 내용이 비었는지 체크할 필요가 없다는 것입니다. 그리고 앱 안에 따로 파일을 생성하지 않는다는 장점이 있지만, onPause에 저장메서드를 생성하는 방법에 비해서 저장되는 기간이 비교도 안되게 짧다는 것이 단점이라는 생각이 듭니다.


참고로 영구적인 정보는 다음에 다시 액티비티가 실행이 될때까지 이 정보가 저장이 된다고 합니다. Do it 안드로이드 앱 프로그래밍 책에 이 내용이 씌여져 있었으면 어떻게 간단하게 해결이 되었을 것인데, 그게 아니어서 많이 헤메었다는 생각이 듭니다.

저작자 표시
신고
안녕하세요?

지난번 포스팅에서 앱에서 서비스를 만들고 나서, 그 서비스에다가 인텐트를 통해서 데이터를 전달하는 것을 한번 실습해 보았습니다. 그리고 이번 시간에는 서비스에서 인텐트를 통해 액티비티에다가 데이터를 전달하는 것을 실습해 보았고, 그 내용을 포스팅 하고자 합니다.




먼저 MyService.java에 있는 processCommand()메서드를 정의하는 곳 아래에다가 위 스크린샷에 나와 있는 것 처럼 코딩을 합니다.


Intent 변수1 = new Intent(getApplicationContext(),MainActivity.class);


우선 위 코딩을 하여서, 새로운 인텐트를 생성되도록 합니다. 여기서 변수1은 당연 기존의 다른 인텐트를 지정하는 변수명과 다르게 지정해야 합니다. 왜냐하면 받는 인텐트 따로이며, 보내는 인텐트는 마치 새로운 편지봉투를 준비하는 것 처럼 일일히 준비해 두어야 하기 때문입니다.


그리고 이어지는 코딩은 다음과 같습니다.


변수1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|

Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_CLEAR_TOP);


여기서 먼저 플래그라는 것을 생성하라는 의미가 됩니다. 저도 정확하게 플래그가 무엇인지는 잘은 모르지만, 일단은 액티비티의 순서를 정리한다는 것은 알고 있습니다. 아무튼 각각의 플래그는 다음과 같은 의미를 지니고 있습니다.


Intent.FLAG_ACTIVITY_NEW_TASK


먼저 서비스는 액티비티처럼 화면이 없기 때문에, 인텐트를 보내기 위해서는 새로 Task를 만들어야 한다고 합니다. 그래서 위 명령어는 Task를 새로 만들어라는 의미입니다.


Intent.FLAG_ACTIVITY_SINGLE_TOP

Intent.FLAG_ACTIVITY_CLEAR_TOP


위 두개의 플래그는 정확하게 제가 그 의미는 모르지만, 설명이 되어 있는 것으로 미루어 보아서는 MainActivity를 다시 재사용 하기 위해서 추가하는 플래그라고 합니다.



다음에 추가되는 구문은 이전 포스팅에서 언급한 것과 마찬가지로 putExtra()메서드를 사용해서 인텐트안에 데이터를 집어넣는 과정을 거치게 됩니다. 그리고 이번에는 서비스가 아니기 때문에, startActivity()메서드를 사용하게 됩니다.



아까전까지 MyService.java에서 인텐트에 데이터를 집어 넣어서 보내기 위한 과정이었다고 하면, 이번에는 MainActivity.java에서 인텐트를 받기 위한 과정을 거칩니다.


먼저 onCreate()는 액티비티가 생성되자 마자 바로 실행이 되는 메서드이기 때문에, 여기서는 getIntent()메서드가 사용이 되었습니다.


Intent 변수1 = getIntent();


이 코딩은 순전히 새로 액티비티가 생성이 되었을 때, 인텐트를 만드는 것이 아니라, MainActivity.java로 전달된 인텐트가 있으면 받으라는 의미입니다. 무엇에 비유를 해야 할까요? 가판대를 설치하자 마자 배달되는 소포를 받을 준비를 하라는 명령어라고 해야 할까요?


그리고 이어지는 processIntent(변수1); 라는 코딩은 액티비티가 시작되자 마자 받은 인텐트를 가지고서 임의로 지정된 메서드에서 처리하라는 명령어입니다. 아직 processIntent()라는 메소드를 정의하지 않았기 때문에, 아직은 빨간색 글씨가 뜨면서 에러라는 의미를 내포하고 있는 것을 볼 수 있습니다.



그 다음으로는 onCreate()메소드 밖에서 마우스 우클릭을 한 다음, generate항목을 선택한 다음 Override Methods항목을 선택에서 onNewIntent()라는 메서드를 선택하도록 합니다.



이 onNewIntent()라는 메서드는 기존의 액티비티를 재사용해서 인텐트를 받을 때 사용하는 메서드라고 나와있습니다. 그래서 이런 onCreate()에서 getIntent()를 지정해서 액티비티가 시작되자 마자 오는 인텐트를 받도록 하고, 이번에는 재사용했을 경우.... 즉, 액티비티를 종료하지 않은채 인텐트를 받는 경우라면 사용되는 메서드로 이해를 하고 있습니다.



이번에는 아까부터 계속해서 내용을 지정하지 않은 processIntent()라는 메서드를 정의할 시간입니다. 먼저 if구문으로 인텐트가 비어있는 상황이 아닌지 여부를 체크하도록 합니다.

그리고 이어지는 getStringExtra()메서드는 part1에서 언급한 적이 있기 때문에 생략하도록 하겠습니다. 여기까지의 의미는 인텐트에서 각각 문자열을 찾아서, 메세지 상자에 그 값을 띄우라는 의미입니다. 그렇게 한 결과는 다음과 같습니다.



먼저 앱을 실행하자 마자입니다. 입력상자에 '홍길동'이라고 입력을 한 다음 버튼을 누르면, 데이터가 인텐트를 타고서 서비스로 배달이 됩니다. 그리고 나서 서비스에서 이 내용이 다시 MainActivity로 돌아오기 까지 몇 초가 걸리긴 했습니다. 아무튼 몇 초를 기다리면...........



위 스크린샷과 같이 서비스에서 MainActivity.java로 전달이 된 인텐트를 처리해서 메세지가 뜬 것을 볼수 있습니다. 이것으로 제대로 서비스에서 액티비티로 데이터를 전달한 것을 볼 수 있었습니다.

저작자 표시
신고

안녕하세요?


이번 포스팅에서는 서비스(Service)라고 해서, 백 그라운드에서 실행되는 프로세스를 의미하는 것을 실습해 보았고, 그 내용을 포스팅하고자 합니다. 생각보다 실습해 보니까, 그 양이 많아져서 포스팅을 2개로 나누어서 올리고자 합니다. 이번 포스팅에서는 서비스를 생성하고, 거기다가 데이터를 전달하는 것을 포스팅하겠습니다.


먼저 SampleService라는 프로젝트를 생성하고, Package explorer에서 아래 스크린샷과 같이 java폴더에서 우클릭을 해서 서비스를 생성하는 메뉴를 불러 오도록 합니다.



New메뉴 아래에 있는 Service항목이 있으며, 거기서 저는 그냥 Service라고 되어 있는 항목을 클릭하였습니다. 서비스의 이름은 디폴트로 설정된 이름인 MyService라는 것으로 지정을 하고, 나머지는 하나도 건드리지 않고 그대로 생성을 시켰습니다.



서비스를 실행시키고 나면 위 스크린샷에서 보이는 것 처럼, MyService.java라는 파일이 생성이 되면서, 기본적인 몇 가직 코딩이 되어 있는 것을 볼 수 있습니다. 이제 아래의 스크린샷에서 보이는 것 처럼, MyService.java파일 안에다가 우클릭을 하고서, Generate메뉴를 눌러서 Override Methods를 클릭합니다.


onCreate(), onDestroy(), onStartCommand()라는 메소드를 선택한 다음 OK버튼을 눌러서 선택된 메서드 들이 실행이 되도록 합니다.



먼저 onStartCommand()메소드아래에서 위 스크린샷처럼 빨간줄이 쳐지면서 에러가 뜨는데, 이 부분만을 삭제하도록 하였습니다.



다음으로는 activity_main.xml의 design탭으로 가서 입력상자 하나와 버튼하나를 배치하도록 합니다. 이번에도 onClick속성을 지정하도록 합니다.



이번에는 MainActivity.java에서 위 스크린샷처럼 코딩을 시작합니다. 여기서 중요한 형식은 이전 같으면 startActivity()로 되었겠지만, 이번에는 서비스를 생성했기 때문에 startService()로 시작을 합니다.


Intent 변수1 = new Intent(this,서비스명.class);

startService(변수1);


이어서 인텐트에 데이터를 넣기 위해서 putExtra() 메소드가 사용된 것도 볼 수 있습니다. 


변수1.putExtra("데이터 이름", 데이터 내용);


여기서 데이터 이름이라는 것은 어떤 데이터인지 알 수 있도록 이름으로, 가령 일련의 두자리수 숫자를 입력할 경우 [시험성적]같은 식으로 지정할 수 있습니다. 그리고 당연 데이터 내용이란 실질적으로 들어가는 숫자나 문자열을 의미합니다.




다음으로는 MyService.java로 가서 위 스크린샷과 같이 코딩을 합니다. 여기서는 Log.d라고 해서 debug를 위해서 로그를 출력하는 메소드입니다. 이 메소드로 로그 출력을 위해서는 첫번째로 문자열을 써야 하는데, 여기서는 위 스크린샷에 보시는 것처럼 "MyService"라는 문자열을 사용하였습니다.


Private static final String TAG = "MyService";


위 구문에서 TAG란 MyService라는 것을 의미합니다. 그래서 아래에서 Log.d()메소드를 다음과 같이 작성하게 됩니다.


Log.d("문자열","로그에 출력할 문구");


위 스크린샷에서는 Log.d(TAG,"로그에 출력할 문구");로 되어 있지만, TAG항목 대신에 "MyService"를 입력해도 됩니다. 다만 일일이 이렇게 여러번 입력하는 것이 번거롭기 때문에 여기서는 TAG로 간단하게 바꾼 것으로 보입니다.



이어서 onStartCommand()메소드의 코딩을 시작합니다. 먼저 onStartCommand()메서드는 인텐트 객체를 전달 받는 메서드인데, 여기서 if 구문을 먼저 다음과 같이 두어서, 인텐트가 비었는지 여부를 체크하게 합니다.


if (변수1 == null){return Service.START_STICKY;}


여기서 변수1은 당연히 MainActivity.java에서 지정하였는 그 변수1로 startService(변수1); 에 들어가 있던 그 변수1입니다. 그리고 뒤에 따라오는 return Service.START_STICKY;는 서비스가 비정상적으로 종료되었을 시에 시스템을 자동으로 재시작하라는 의미입니다.


그리고 이어지는 else이하의 구문은 실질적으로 행동을 하는 구문인데, 너무 많은 코드를 쓰면 지저분해 보일 수 있으므로 따로 메서드를 임의로 지정-processCommand()라는 메서드로 지정해서 넣었습니다. 아직 이 메서드가 어떤 것인지 정의하지 않았기 때문에 에러라고 빨간줄이 켜져 있는 것을 볼수 있습니디ㅏ. 그리고 그 아래에 있는 return super.onStartCommand()는 그대로 두었습니다.



다음으로는 processCommand()라는 메소드를 정의하는 코딩을 하였습니다. 먼저 여기서는 인텐트를 받기 때문에 당연 (Intent 변수1)을 주어야 했습니다. 저는 정확하게는 모르지만, 실제로 해보니 저 구문을 안 쓰면 에러가 발생하였습니다. 아마 인텐트를 받는 메서드를 정의할 때는 어떤 인텐트인지도 지정을 해야 하는 가 봅니다.


String 변수2 = 변수1.getStringExtra("데이터 이름");


이 구문은 인텐트인 변수1에서 문자열(String)형태의 데이터를 가져오고자 할때 사용이 되는 구문입니다. 당연 여기서는 문자열을 가져오기 때문에 getStringExtra이며, 정수형태의 데이터라면 getIntExtra()메서드가 사용되게 됩니다. 그리고 이어서 아래에 이어지는 Log.d()메서드에서 이 변수2에 해당하는 command와 name을 가지고서 따로 출력할 로그를 작성하게 됩니다.



다음으로는 위 스크린샷과 같은 코딩을 이어가는데, 여기서는 for문이 등장하게 됩니다. 이 for문은 반복문인데, 다음과 같은 의미를 지니고 있습니다.


for(시작시 조건 ; 언제까지 반복할 것인지 정하는 조건 ; 반복되는 동안 할 것


이렇게 구성이 됩니다. 그래서 for(int i=0;i<5;i++)는 다음과 같은 의미가 됩니다.


시작시 조건 => 정수인 변수 i는 시작시 0이다.

언제까지 반복할 것인가? => i가 5 미만이면 계속 반복한다.

반복되는 동안 할 것 => i에 +1씩 더한다.


그리고 위 스크린샷에서 보시면 알 수 있는 것이 Thread.sleep(1000);이라는 것이 있는데, 이 구문의 의미는 다음과 같습니다.


Thread.sleep(밀리초);


밀리초(1000 = 1초)만큼 실행중에 잠시 대기를 하라 는 의미입니다. 


이제 준비는 갖추어 졌고, USB에 스마트폰을 연결해서 실제로 실행을 시켜 보았습니다. 서비스의 경우는 눈에 보이는 것이 아니라서, 안드로이드 스튜디오의 하단 화면에서 logcat이라는 것을 열어 보아야 합니다. 



위 스크린샷을 보시면 Android Monitor에서 logcat이라는 항목이 있는 것을 볼 수 있습니다. 그리고 위에서 코딩된 로그가 제댁로 나와서 [command : show, name : Name]이라는 항목이 뜨는 것도 볼 수 있습니다. 


이것으로 1차적으로 액티비티에서 서비스로 데이터를 보내는 것에는 성공하였습니다. 그럼 이제는 반대로 서비스에서 액티비티로 데이터를 보내서 처리하는 것을 실습해 봐야 하는데, 여기까지 하는데 많은 분량이 걸려서 포스팅을 부득이하게 반으로 나누게 되었습니다.

저작자 표시
신고

안녕하세요?


지난번 포스팅에 약속드린 대로 onSaveInstanceState()와 onRestoreInstanceState()라는 메소드를 사용해서 한번 실습을 해 보았습니다만, 그 결과는 생각대로 잘 되지는 않았고, 시원찮은 결과가 나왔다는 것을 포스팅하고자 합니다.



먼저 기존에 있던 메소드는 지우고, 일단 먼저 onPause아래에다가 위 스크린샷처럼 코딩을 시작하였습니다. 하지만, 저는 여기서는 잘 몰랐는 것이....... 이 메소드는 저렇게 다른 메소드 안에서 또 정의되는 것이 아니라는 것입니다. 그래서 한참을 에러가 떠서 어떻게 처리해야 할지 몰랐는데, 겨우 아래와 같이 코딩을 하여서 에러를 잡는데 성공하였습니다.



일단 onSaveInstanceState()와 onRestoreInstanceState()라는 메서드 둘다 이전 포스팅에서 다루었는 것 처럼 onPause와 onResume에 작성하지 않고, 따로 띄워놓은 다음에 코딩을 해도 작동하는 것으로 생각이 됩니다. 하지만 문제는 다음이였습니다. 


USB에 연결된 스마트폰에서 작동하는지 시험을 해 보려고 했습니다만, 빌드하는 동안 에러만 없었을 뿐이지, 액티비티를 종료하고 다시 실행시키면, 입력상자에 입력한 값이 제대로 저장이 되지 않는 현상이 발생하는 것이었습니다.



한번 확인사살이라고 해야 할까요? onSaveInstanceState()메소드 부분을 /* */ 로 주석처리를 해서 알아본 결과, 전혀 차이없이 제대로 오작동하는 것을 알수 있었습니다. 아마도 불러오는 기능에 오류가 있는 것이 아니라 저장하는 기능에 오류가 있는 듯 합니다만, 현재로서는 어디가 에러인지 알기가 어렵다는 생각이 듭니다.

저작자 표시
신고

안녕하세요?


이번 포스팅에서는 [Do it 안드로이드 앱 프로그래밍]이라는 책을 읽으면서, 이번 포스팅에서는 액티비티의 수명주기라고 해서, 액티비티(화면)가 띄워지고 나서 꺼질때 까지의 주기를 알아보는 실습을 하였으며, 동시에 데이터를 저장해서 화면이 바뀌거나 종료되어도, 데이터가 지워지지 않는 것을 실습해 보았고, 그 내용을 포스팅 하고자 합니다.


먼저 SampleLifecycle이라는 프로젝트를 생성한 다음, 아래의 그림과 같이 activity_main.xml을 생성하도록 합니다.



책에서는 텍스트뷰라는 식으로 이야기를 했지만, 여기서는 입력상자를 배치하도록 하고, 그 아래에서는 버튼을 배치하도록 하고, 앞으로의 코딩을 편하게 하기 위해서 onClick속성을 위 스크린샷과 같이 배치하도록 합니다.



먼저 MainActivity.java로 가서, 위 화면과 같이 코딩을 하는데, 이번에는 일일히 손으로 직접 입력하지 않고, 코딩화면에서 우클릭을 한 다음, Generagte메뉴를 클릭합니다. 그리고 나서 Override Methods를 선택하면 여러개의 화면으로 뜨는 리스트가 있는데, 여기서 onStart, onStop, onResume, onPause, onDestroy를 차례대로 고르도록 합니다.


다음에는 이 메서드들이 실행되었는지 여부를 알아보기 위해서 다음 화면과 같이 Toast를 이용해서 메세지를 띄우도록 해봅니다.



각자 메세지가 뜨도록 코딩을 하였으면, 이제는 예제 프로젝트를 뜯어보고서, 거기서 나온 MainActivity.java를 가지고서 빠진 곳을 코딩하도록 합니다.



먼저 밑줄이 쳐진곳을 코딩해서 입력상자에 대한 것이 인식이 되도록 합니다. 



다음은 붉은색 박스가 쳐진 곳을 입력해서 버튼을 누르면 MenuActivity라는 것이 띄워지도록 만듧니다. 그런데 아직 새 Activity를 만들지 않았기 때문에, 새로이 액티비티를 만들도록 합니다.



Package explorer에서 Java폴더에서 우클릭을 한 다음, New메뉴를 클릭해서 새 액티비티를 만들도록 합니다. 여기서는 Activity Name은 MenuActivity로 지정을 하도록 합니다.



USB에 연결이 된 스마트폰에서 실제 앱을 실행시켜본 결과 위 스크린샷처럼 자동으로 메서드가 실행이 되는데, 이렇듯이 시스템상에서 자동으로 호출하는 메서드를 [콜백 메서드(Callback Method)]라고 합니다.

먼저 앱이 실행되면 다음과 같은 순서로 콜백 메서드가 실행이 되었습니다.


onCreat -> onStart -> onResume 


여기서 다른 액티비티를 띄우는 버튼을 눌러서 MenuActivity를 띄웠을 경우 다음과 같이 콜백 메서드가 실행되었습니다.


onPause


그리고 MenuActivity를 스마트폰의 뒤로가기 버튼을 눌러서 뒤로 갔을 경우 다음과 같이 콜백 메소드가 실행되었습니다.


onStart -> onResume


그리고 앱을 종료했을 경우 다음과 같이 콜백 메소드가 실행되었습니다.


onPause -> onStop -> onDestroy


그래거 이를 통해서 알수 있는 것이.......... 최초로 앱을 실행했을 경우에는 onCreat가 실행이 되고, 그 다음에 onStart와 onResume이 차례로 실행이 된다는 것을 알 수 있습니다.


그리고 앱이 종료되면 가장 먼저 onPause가 실행이 되고, 앱이 종료될 경우 onStop과 onDestroy가 차례대로 실행이 됩니다.


이제 화면인 액티비티가 종료되어도 데이터를 저장하는 코딩을 실습하기 위해서 먼저 아래와 같이 코딩을 합니다.



먼저 화면인 액티비티가 다른 화면으로 넘어가거나 종료되거나 하면, onPause가 실행되기 때문에, 여기다가는 save를 하는 saveState()메서드를 지정합니다.


그리고 화면인 액티비티가 다시 시작되면 onStart이후에 onResume이 실행되기 때문에, 여기서는 불러오는 load에 해당한다고 해야 할까요? resoreState()메소드를 지정합니다. 


하지만 위 스크린샷에서 보시다시피 saveState()와 restoreState()는 안드로이드 스튜디오에서 기본적으로 지정하는 메서드가 아니기 때문에, 일일히 지정해야 할 필요성이 있습니다.



위 스크린샷에서 나와있는 것 처럼 임의의 메서드를 지정하였습니다. 일단 SharedPreferences를 이용해서 따로 파일을 생성하라는 명령을 지정하지 않아도, 앱 내부에 알아서 파일을 생성, 지정된 데이터를 저장합니다. 이 방법이 주로 쓰이는 곳이 게임같은 것에서 전화가 와서 받거나 게임을 종료해도 게임내 스코어나 주인공의 레벨등을 저장할 때 사용이 됩니다.


SharedPreference 변수1 = getSharedPreferences("변수1",Activity.MODE_PRIVATE);


먼저 변수1을 지정하고 나서, getSharedPreferences() 메소드를 지정해야 데이터의 저장이나 불러오기가 가능해 집니다. 그리고 먼저 데이터의 저장을 하는 경우를 먼저 생각해 보도록 하겠습니다.


SharedPreferences.Editor 변수2 = 변수1.edit();


위 코딩은 변수2를 지정하고, 변수1에 변화를 주겠다는 의미입니다. 어떤 변화를 주는지는 이어지는 코드의 내용에 따라서 달라지게 됩니다.


변수2.putString("name",nametag.getText(),toString());


먼저 변수2는 . 뒤에 나오는데, putString()메서드인 만큼, 이 뒤에서 나오는 메서드에서 나온 문자열값을 가지겠다는 의미가 됩니다. 

우선 "name"은 여기서 가져온 문자열값의 이름입니다. 그리고 뒤에 따라오는 nametag이라는 것은 activity_main.xml에서 만들어 놓은 입력상자의 ID입니다.

이후 따라오는 getText()는 이 입력상자에 들어가 있는 텍스트를 가져오고, toString()은 이 텍스트를 String이라는 데이터 타입으로 바꾸라는 의미가 됩니다.


변수2.commit();


이 구문은 다른 의미도 아닌, 제대로 변경사항이 반영되라는 의미가 됩니다. 이 구문이 들어가야만 제대로 된 입력사항의 변화가 저장이 됩니다.

여기까지가 saveState()메서드의 내용입니다.


그럼 restoreState()메서드의 내용은 어떻냐 하면 이는 다음과 같이 됩니다.


SharedPreference 변수1 = getSharedPreferences("변수1",Activity.MODE_PRIVATE);


여기까지는 똑같습니다. 하지만, 이후는 if구문을 동원해서 다음과 같이 내용을 확인해야만 합니다.


if((변수1!=null)&&(변수1.contains("name"))){


위 코드의 내용은 변수1이 null값이 아니며, name이라는 이름의 값이 있을 경우, 아래의 내용을 실행하라는 의미가 됩니다.


String 변수3 = 변수1.getString("name","");

입력상자ID.setText(변수3);


위 코드의 내용은 다음과 같습니다. 먼저 변수3을 지정하는데, 이 내용은 문자열(String)타입의 값입니다. 이 변수3이라는 값은 변수1에서 name이라는 항목에서 값을 찾아낸 다음, activity_main.xml에서 지정한 입력상자의 텍스트를 미리 저장된-변수1에 미리 저장된 값에서 name이라는 이름의 값을 찾아내서 그 문자열을 가저와라 라는 의미가 됩니다.



그리하여 앱을 먼저 실행을 시키고 나면, 위 스크린샷과 같은 화면이 나오는데, 여기다가 저는 '저장'이라는 단어릴 지정하고 나서 앱을 종료해 보았습니다. 그리고 다시 앱을 실행시켰을 때 화면이 다음과 같았습니다.



위 화면과 같이 저장이라는 이름이 그대로 저장되어 있는 것을 볼 수 있었습니다. 어떻게 이번에는 제대로 무언가가 나온 듯 하다는 생각이 듭니다. 책에서는 더 간단한 메서드로 onSaveInstanceState()와 onRestoreInstanceState()메서드가 있다고 하는데, 제가 한번 실습을 해보고 나서, 이를 포스팅에 올리도록 해 보겠습니다.


저작자 표시
신고

안녕하세요?


지난번 시간에 제가 어떻게 해서 외부 저장소 경로를 살펴 보려고 했습니다만, 그게 생각처럼 잘 되지가 않았다는 내용을 포스팅 하고자 합니다.



먼저 지난번에 코딩을 하였는 MainActivity.java에다가 위 스크린샷에 보이는 것 처럼 코딩을 시작해 줍니다. 위 코딩들은 각각 다른 경로를 찾아내는 코드입니다.



USB에 연결된 갤럭시 S4에서 확인을 해본 결과, 일단 겉 보기가 엉망인 것은 둘째치고서, 제대로 SD카드의 경로를 지정한 경우가 하나도 없습니다. 스마트폰에 들어가 있는 SD메모리카드의 경로는 다음과 같습니다.



우선 getExternalStorageDirectory()라는 메서드로 찾아낸 경로에서 Storage까지는 제대로 찾았습니다만, 그 이후에 나오는 경로는 도저히 어떻게 코딩을 해야 찾을 수 있는지 알길이 없습니다. 일단 권한의 문제인가 생각을 하여서, 권한을 부여하는 작업을 시작했습니다.



먼저 AndroidMainfest.xml파일로 가서 외장 메모리를 읽을 권한을 부여해 주는 권한을 위 스크린샷과 같이 지정을 하도록 합니다. 그리고 나서는 API23부터 메모리 카드를 읽는 권한이 위험권한으로 부여가 되었으므로, Do it 안드로이드 앱 프로그래밍이라는 책에서 나온 것처럼, gradle에서 아래 스크린샷과 같은 작업을 시작해 줍니다.



targetSdkVersion이 원래는 25로 되어 있는데 22로 바꾸어서 권한을 획득하지 않고도 제대로 작동을 하고자 했습니다. 그런데 이렇게 하고 나니, 이전과는 다르게 아예 앱이 스마트폰에 인스톨 하는 과정에서 에러가 발생해서 제대로 인스톨이 되지도 못했습니다. 아니, 오히려 아예 [앱을 삭제하겠냐]는 물음까지 나왔습니다.


이래저래 시도를 하기는 해 보았습니다만, 지금의 저로서는 어떻게 해결할 방법이 없는게 SD카드 안의 파일을 읽는 방법이었습니다. 아직 Do it 책에서 남은 부분도 많기에, 지금은 이 부분은 어떻게 미루어 두고, 계속 진도를 나가야 겠다는 생각이 들었습니다.

저작자 표시
신고

안녕하세요?


이번 시간에는 오랫만에 안드로이드 스튜디오를 독학하면서 한가지 해결책이 나오는 것과 동시에 한가지 골칫꺼리가 생겨서, 그 내용을 포스팅 하고자 합니다.



먼저 TextSDcardPath라는 프로젝트를 생성하도록 합니다. 지난번 부터 계속해서 SD카드에 들어가 있는 pdf파일을 읽을 수 없었는데, 그래서 여러가지 서적을 참고해 보고나서 한번 경로만이라도 읽도록 하자는 생각이 들어서 이런 프로젝트를 새로 만들었습니다.



먼저 activity_main.xml로 가서, design탭에다가 위와 같은 디자인을 하였습니다. 일단 텍스트 상자를 먼저 상단에다가 배치를 하여서, 나중에 여기다가 코딩을 입력했을 시, SD카드의 경로를 어떻게 읽는지를 표시하도록 하고자 합니다. 위 화면에서 보이는 바와 같이 먼저 ID는 Text001로 지정을 하였습니다.



다음으로는 MainActivity.java로 가서 위 스크린샷과 같이 코딩을 시작합니다. 주용한 것은 EditText인 입력상자가 아니라, 텍스트뷰이기 때문에 TextView로 입력을 해야 합니다. 먼저 setContentView아래에다가 텍스트뷰에 관한 ID를 지정하도록 합니다.



다음으로는 String 변수이름 = Environmet.getExternalStorageState();를 입력해서 외부 메모리가 현재 들어와 있는지 아닌지를 검사해야 합니다. 그리고 이어지는 구문은 다음과 같은 의미를 지니고 있습니다.


if(변수이름.equals(Environment.MEDIA_MOUNTED)){


변수이름이 외장 메모리가 들어와 있는 상태(Environment.MEDIA_MOUNTED)와 같은(equals)가? 만약에 같다면 아래와 같이 작동을 한다.


변수이름2 = Environment.getExternalStorageDirectory().getAbsolutePath();}


여기서는 변수이름2가 바로 외장 메모리의 경로를 찾아내서, 그 값을 경로로 지정한다는 의미가 됩니다. 당연 아래에 있는 else이후는 변수이름2에 외장 메모리가 없다는 값을 의미하는 경우가 됩니다.



다음으로는 텍스트뷰 아래에게다가 버튼을 하나 추가해서 한글로 [경로확인]이라고 지정을 하였습니다. 그리고 onClick속성에는 pathfinding이라고 지정을 하여서, 버튼을 누르면 텍스트뷰에서 외장 메모리-여기서는 SD카드의 경로를 표시하도록 할 준비를 마쳤습니다.



여기서는 public void로 시작하는 곳에다가 onClick속성을 지정해서 버튼을 누르면 일어나는 일을 지정하도록 합니다. 여기서 저는 정확한 형식은 모르지만, setText가 다음과 같이 사용될 수 있다는 것은 알 수 있었습니다.


텍스트뷰의 변수이름.setText(String.format("표시될 문자=%s",표시될 값-문자열값의 변수이름);


당연 %s라는 것은 , 뒤에 올 변수이름을 의미하는 값이 됩니다. 여기서는 한개의 변수 이름만 지정을 했지만, 사실은 ""사이에 여러개의 %s를 지정하고 나서, 여러개의 변수이름을 지정하는 방법도 있습니다.


예) setText(String.format("문자1=%s/n문자2=%s/n문자3=%s",변수이름1,변수이름2,변수이름3);


아무튼 이렇게 대략적인 경로를 지정하고 나서, 다음으로는 USB케이블로 스마트폰을 연결해서 실제 폰에서 제대로 동작을 하는지 알아보려 했습니다.



먼저 앱을 구동하자 제대로 앱이 실행되는 것을 확인할 수 있었습니다. 하지만, 버튼을 누르자 마자 앱이 종료가 되었는데, 그 이유는 바로 아랫쪽에 있던 finish(); 때문이였습니다. 그래서 이 finish();를 없애고 나서, 다시 USB에 연결된 스마트폰에서 앱을 실행시켰습니다.



제대로 경로가 뜨는 것을 볼 수 있었습니다. 하지만 이것으로 모든 문제가 해결이 된 것이 아니었습니다. 우선 근본적으로 위 경로는 다음과 같습니다.



즉 외장 메모리인 SD카드가 아니라, 폰 자체의 내장 메모리의 경로를 정확하게 지정하고 있는 것이였습니다. 이 문제는 따로 해결을 해야 할듯 합니다만, 당장에 해결책을 가지고 오기는 힘들어 보인다는 생각이 듭니다. 그래서 이번 포스팅에서는 어떻게 해야 제대로 내장 메모리의 경로라도 찾을 수 있는지를 다음과 같이 하면 알 수 있었습니다.


1)먼저 외장 메모리가 띄워져 있는지 여부를 java상에서 확인을 한다.

2)그리고 나서 getExternalStorageDirectory().getAbsolutePath()로 경로를 가지고 온다.


다만, 결과물이 내장 메모리를 확인해서 이 문제는....... 또 서적을 뒤져서 해결책을 찾아 봐야 겠다는 생각이 듭니다.

저작자 표시
신고

안녕하세요?


지난번 part1에서는 parcelable을 이용해서 데이터를 전달하는 방법을 다 설명하지 못했는데, 좀 헤메고 나서야 어느정도 이해가 되었고, 그 내용을 포스팅 하고자 합니다.



지난번 part1에서 마지막으로 끝낸 부분입니다. 일단 위 스크린샷에서 붉은 박스로 쳐진 부분은 다른 의미도 없이, parcel에다가 쓴다는 의미입니다. 당연 구조는 다음과 같이 됩니다.


public void writeToParcel(Parcel 임의의 이름, int flags){

임의의 이름.writeInt(정수 변수);

임의의 이름.writeString(문자 변수);

}


여기서 flags라는 것은 변경이 가능한지 아닌지 여부는 잘 모르겠습니다. 하지만 여기에 들어가는 임의의 이름은 어떻게 짓든 간에 큰 문제는 없어 보입니다. 당연 여기서 언급하는 정수 변수와 문자 변수는 위에서 SimpleData에 들어가는 내용으로 정의를 내린 변수명입니다.



다음은 activity_main.xml의 design탭으로 가서, 버튼을 하나 추가하는 화면입니다. 여기서 onClick속성은 미리 지정을 하여서 나중에 있을 작업을 편하게 합니다. 그리고 여기는 제약 레이아웃이기 때문에 옆에 있는 청사진에서 버튼의 사방을 끌어서 화면의 모서리에 연결시켜 주는 작업을 해야 제대로 버튼이 화면의 중앙에 위치할 수 있게 됩니다.



public void onClick속성(View v){}로 버튼을 누르면 메인 액티비티에서는 인텐트가 실행이 되도록 코딩을 시작합니다. 그리고 당연하다면 여기서 SimpleData라는 메서드는 원래는 처음에 없는 것입니다. 단지 새로운 class로 SimpleData를 생성하면서 만들어 주어서 에러가 뜨지 않고 이렇게 값을 입력하라는 메세지도 나오게 된느 것입니다.


당연 처음에는 정수 변수인 num을 입력했기 때문에 정수를 입력하라고 뜨고, 그 다음으로는 msg라는 문자열 변수를 SimpleData.java에서 입력했기 때문에 위 스크린샷과 같은 메세지가 뜨는 것입니다.



그리고 이어서 인텐트에 변수로 code1을 위 스크린샷에 주었기 때문에, parcel로 보내기 위한 절차에 들어가야 합니다. 제가 알아낸 바로는 다음과 같은 구성으로 되어 있다는 것을 알았습니다.


인텐트를 의미하는 변수.putExtra("입력될 데이터의 이름",parcel로 보낼 데이터);


[Do it 안드로이드 앱 프로그래밍]이라는 책에서는 일단 이름과 parcel로 보낼 데이터를 모두 data라고 선언을 했는데, 이러면 공부하는 사람이 헷갈릴 수 있으니, 저렇게 KEY_SIMPLE_DATA="data"; 라는 변수선언을 미리 한 것으로 보입니다. 그리고 당연 데이터를 주고 받기 위해서 startActivityForResult를 사용했으며, request code란에는 원래 숫자가 들어가야 하는데 여기서는 숫자를 대용한 코드가 들어갔습니다.



다음은 activity_menu.xml을 디자인 한 것입니다. 여기서는 선형(Linear) 레이아웃이기에 제약 레이아웃처럼 컴포넌트를 청사진에서 이리저리 선으로 연결할 필요는 없습니다.


일단 여기서는 버튼 한개와 텍스트 박스 한개를 추가하고 다음으로는 MenuActivity.java로 가서 코딩을 시작합니다.



일단 버튼을 누르면 돌아가기 위해서 MenuActivity를 종료하라는 finish();만 있어도 충분할 것 같기는 합니다만, 왜 여기다가 인텐트를 의미하는 코드를 사용했는지는 아직은 모르겠습니다. 아마도 버튼을 눌렀을 때 MainActivity로 보낼 데이터를 만들기 위함이 아닌가 하는 추측은 합니다만, 아직까지는 잘 모르겠습니다.



그리고 다음은 인텐트를 전달받아서 이를 실행시키는 과정입니다. 일단 여기서 processIntent라는 메서드는 원래 없었는데, 아래에서 새로 정의하여서 나온 것입니다.


private void processIntent(Intent 인텐트를 의미하는 코드){ 유저가 정의한 코드 }


여기서 if조건의 안쪽은 이런 의미가 됩니다. code3라는 인텐트가 null이라고 빈 값이 아닐 경우 getExtras()라는 메서드로 인텐트의 값을 받아서 와라는 의미가 됩니다. 그리고 여기서는 많은 데이터를 parcel을 통해서 묶었기 때문에 getExtras()라는 명령어만 가지고는 부족한지, 이어서 여기서는 SimpleData라는 메서드의 값을 이 MenuActivity에서 설정하는데 getParcelable이라는 메서드로 parcel안에 들어가 있는 값을 가지고 오라는 의미가 됩니다.


다시 말하지만, SimpleData안에는 그 어떠한 값도 들어가 있지 않습니다. 단지 먼저 숫자가 나오고, 그 다음에는 문자열이 오는 메소드일 뿐입니다. 이 메소드를 여기 MenuActivity에서 구현하는데, 들어가는 값은 MainActivity에서 지정한 값을 계산하라는 의미가 됩니다.


다만 여기서 이후 setText()라는 메소드로 텍스트 박스안에 있는 값을 변경시키려고 했습니다만, 여기서 getNumber와 getMessage가 원래 안드로이드 스튜디오에 없는 메서드라서 구현하는데 어려움이 있었습니다.



다시 SimpleData.java로 돌아와서 위 스크린샷에 보이는 빨간색 박스에 들어간 코드를 입력해야 합니다. 일단 저로서는 더는 깊게 들어갈 수 없어서, 여기서 필요한 것만 이야기를 하자면 다음과 같은 형식이라는 것을 발견했습니다.


public 변수타입 get임의의 메소드명(){

   return 변수타입에 맞는 변수명;

}


public void set임의의 메소드명(변수타입 변수명){

   this.변수명 = 변수명;

}


더 들어가려고 하면 들어갈 여지가 얼마든지 있겠습니다만, 언제까지고 이걸 붙들고서 있을 수만은 없습니다. 일단 제가 이해한 것은 여기까지 입니다. 이런 식으로 getNumber()와 getMessage()가 위에서 처럼 저런식으로 구현이 되었으며, 이런 메서드는 특정 클래스나 메소드안에 정의된 변수를 따로 지징하는데 사용된다는 것을 알 수 있었습니다.



그래서 USB에 스마트폰을 연결한 다음 실행시켰을 때, 맨 처음 뜨는 메인 액티비티에서는 저렇게 버튼이 하나만 나와 있는  것을 볼 수 있었습니다. 저 버튼을 누르면.............



메인 액티비티에서 정의한 SimpleData의 내용이 이렇게 메뉴 액티비티(MenuActivity)로 전달되는 것을 볼 수 있었습니다. 여기서 왜 이렇게 복잡하게 parcel을 쓰느냐고 할 수 있겠습니다만, 일단 이와 같은 형식은 ID와 Password를 입력받아서 전달하는 경우에는 이런 parcel이 유용하게 사용될 수 있다고 합니다.


이것으로 parcelable이라는 부분에서 몇 일간의 길고도 길었던 씨름을 이제서야 끝낼 수 있었다는 생각이 듭니다.

저작자 표시
신고

+ Recent posts

티스토리 툴바