안녕하세요. 안드로이드 초보개발자 refeel입니다.

 

여기서 상당히 많은 정보를 얻어가면서, 이자리를 빌어 활동하시는 여러분들에게 감사의 인사를 전해드리고 싶습니다.

 

 

다름이 아니라, 액티비티간에 인텐트로 Parcelable 인스턴스를 통신시키고 있습니다.

 

메인액티비티에서 Parcelable 인터페이스를 구현해둔 DataAccess 인스턴스를 서브액티비티로 보내고,

 

서브액티비티에서는 작업쓰레드가 시간을 재고, UI쓰레드가 게임진행을 하며, 게임의 현황을 DataAccess인스턴스로 전달하는 ...

 

뭐, 이런 구조를 띄게 코딩을 하는데 ...

 

서브액티비티에서 Back버튼으로 onPause() 메서드가 호출 시, 메인액티비티에 저장된 DataAccess 인스턴스를 setResult 메서드로 보내게 하였습니다.

 

그런데, setResult의 첫번째 인자인 resultCode에 분명 RESULT_OK 상수를 전달했음에도 불구하고, 메인액티비티에서는 RESULT_CANCEL이 전달되어, 서로 통신이 되지 않고있습니다.

 

분명 서브액티비티의 onPause 메서드에서 무언가 오작동을 하고 있다는것은 느끼고 있는데 ...

 

아무리 찾아보고 어떤짓을 해봐도, 서로 통신이 되지 않으니 갑갑하기만 합니다.

 

책과 구글링으로 씨름과 씨름끝에 결국 해답을 찾지 못하여, 여기에 또 조언을 구하고자 찾아왔습니다.

 

급히 벼락치기를 해야하는 상황인지라, 개념과 기초가 많이 부실하니, 선배개발자분들께서는 이 점을 가여이 여겨, 단비같은 조언 한마디라도 해주시면 마음속 깊이 새겨 자기계발에 힘쓰도록 하겠습니다.

 

 

우선, 아래의 코드는 서브액티비티의 코드입니다.

 

  1. package project.shake;
  2. import project.shake.*;
  3. import android.app.Activity;
  4. import android.os.*;
  5. import android.app.*;
  6. import android.content.*;
  7. import android.os.*;
  8. import android.util.*;
  9. import android.view.*;
  10. import android.widget.*;
  11. import android.graphics.drawable.*;
  12. import android.hardware.*;
  13. public class GameStart extends Activity implements SensorEventListener
  14. {
  15. // 게임 논리를 구성하는 필드
  16. float forceAccel = 0; // 가속도의 벡터합(x + y + z)을 스칼라로 표현하는 변수.
  17. long lastTime;
  18. Bundle gameBundle = null; // 인텐트가 넘겨준 실제 데이터를 담는 번들. onPause, onStart 메서드에서 데이터 저장 및 복원 논리를 세워라.
  19. DataAccess gameData; // 인텐트로 넘겨받는 DataAccess 인스턴스를 담는 인스턴스변수.
  20. DataAccess tempData = null;
  21. // 가속도 변화율 구하는 변수
  22. float lastX = 0;
  23. float lastY = 0;
  24. float lastZ = 0;
  25. float x, y, z;
  26. // 가속도 센서를 가져오는 센서매니저, 센서 인스턴스, 바이브레이터 인스턴스
  27. SensorManager sm = null;
  28. Sensor accel = null;
  29. Vibrator mVib;
  30. // 디버깅용 토스트, 스트링, 기타 등등 ...
  31. String temp;
  32. TimeThread timeThread; // 시간재는 스레딩
  33. TextView mBackText; // 디버깅 텍스트뷰
  34. /** Called when the activity is first created. */
  35. public void onAccuracyChanged(Sensor arg0, int arg1) { }
  36. // 실험용 쓰레드가 초단위로 메세지를 보냄. ^ㅅ^
  37. Handler timeHandler = new Handler() {
  38. public void handleMessage(Message msg)
  39. {
  40. mBackText.setText("경과시간 : " + (msg.arg1+1)); // 텍스트뷰 쳐묵쳐묵
  41. Log.e("시간 체크(핸들러 메세지)", ""+msg.arg1);
  42. if(msg.arg1 >= 30) // 제한시간이 된다면,
  43. {
  44. Intent intent = new Intent(GameStart.this, GameResult.class); // 게임 리절트로 넘어간다.
  45. intent.putExtra("GameData", gameData);
  46. startActivity(intent);
  47. finish();
  48. }
  49. }
  50. };
  51. @Override
  52. public void onCreate(Bundle savedInstanceState)
  53. {
  54. Log.e("GameStart.java/onCreate 호출", "ㅇㅋ");
  55. super.onCreate(savedInstanceState);
  56. Log.e("gameData 인스턴스 생성", "ㅇㅋ");
  57. // 메인액티비티에서 넘겨온 Parcelable 인스턴스를 저장.
  58. gameBundle = getIntent().getExtras();
  59. gameData = gameBundle.getParcelable("GameData");
  60. //tempData = gameData; // 주석처리해도 상관없지 싶다.
  61. timeThread = new TimeThread(timeHandler);
  62. timeThread.setDaemon(true);
  63. timeThread.start();
  64. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 앱실행중 자동꺼짐 방지
  65. sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
  66. accel = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  67. setContentView(R.layout.gamestart);
  68. mVib = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE); // 바이브레이터 인스턴스 끌어씀
  69. mBackText = (TextView)findViewById(R.string.count);
  70. }
  71. public void onSaveInstanceState(Bundle outState)
  72. {
  73. Log.e("onSaveInstanceState()로 현재 인스턴스 저장", "ㅇㅋ");
  74. gameData.threadDead = DataAccess.RUNNABLE;
  75. outState.putParcelable("saved", gameData);
  76. }
  77. // 차라리 여기에, GameMain으로 현재 Parcelable 인스턴스를 메인에게 리턴해줄까 ;; ... GameMain에서는 리턴받은 인스턴스를 이쪽으로 넘기면 되는거고. 그럼 깔끔하냐.
  78. public void onDestroy()
  79. {
  80. super.onDestroy();
  81. Log.e("GameStart.java/onDestroy() 호출", "ㅇㅋ");
  82. }
  83. public void onStart()
  84. {
  85. super.onStart();
  86. Log.e("GameStart.java/onStart() 호출", "ㅇㅋ");
  87. gameData.threadDead = DataAccess.RUNNABLE;
  88. if (accel != null)
  89. sm.registerListener(this, accel, SensorManager.SENSOR_DELAY_GAME);
  90. }
  91. public void onResume()
  92. {
  93. super.onResume();
  94. gameData.threadDead = DataAccess.RUNNABLE;
  95. Log.e("GameStart.java/onResume() 호출", "ㅇㅋ");
  96. }
  97. // 여기서 나는 계속 RESULT_OK를 보내지만, 게임메인.java에서는 RESULT_CALCEL을 받게되네 -_- ...
  98. public void onPause()
  99. {
  100. super.onPause();
  101. if(sm != null) // Pause상태에서 센서를 해제.
  102. sm.unregisterListener(this);
  103. gameData.threadDead = DataAccess.DEAD;
  104. Log.e("GameStart.java/onPause() 호출", "ㅇㅋ");
  105. Log.e("GameStart.java/onPause() 상태에서 저장된 시간 : ", ""+gameData.currentTime);
  106. Intent intent = new Intent();
  107. intent.putExtra("GameSaved", gameData);
  108. Log.e("게임 저장 후 데이터 : ", ""+gameData.currentTime);
  109. Log.e("게임 저장 후 데이터 : ", ""+gameData.forceSumForScore);
  110. Log.e("게임 저장 후 데이터 : ", ""+gameData.limitGameTime);
  111. Log.e("게임 저장 후 데이터 : ", ""+gameData.shakeCount);
  112. Log.e("게임 저장 후 데이터 : ", ""+gameData.threadDead);
  113. setResult(RESULT_OK, intent);
  114. finish();
  115. }
  116. // 가속도센서를 감지하여, 게임 논리를 진행하는 코드
  117. ...
  118. //타이머 쓰레드 -> 이너 쓰레드로 교체;
  119. class TimeThread extends Thread
  120. {
  121. int currentGameTimeThread; // 실제 카운팅되는 상대시간. 초단위로 계산되며,
  122. // if(currentGameTime == limitTime) 게임끗();
  123. Handler timeHandler; // 현재시간을 초단위로 UI쓰레드에게 넘김.
  124. TimeThread(Handler handler)
  125. {
  126. timeHandler = handler;
  127. currentGameTimeThread = gameData.currentTime;
  128. }
  129. public void run()
  130. {
  131. //currentGameTime = gameData.currentTime;
  132. Log.e("run() 호출", "ㅇㅋ");
  133. while(true)
  134. {
  135. Message msg = Message.obtain(timeHandler, 0, currentGameTimeThread, 0);
  136. timeHandler.sendMessage(msg);
  137. currentGameTimeThread++;
  138. gameData.currentTime = currentGameTimeThread;
  139. //Log.e("run() 내부 while 루프 진입", "ㅇㅋ");
  140. if(currentGameTimeThread >= gameData.limitGameTime || gameData.threadDead == DataAccess.DEAD)
  141. {
  142. //Log.e("run() 내부 while 루프 진입", "ㅇㅋ");
  143. //Log.e("currentGameTime : ", ""+currentGameTime);
  144. //Log.e("첫번째 boolean : ", ""+(currentGameTime >= gameData.limitGameTime));
  145. //Log.e("두번째 boolean : ", ""+(gameData.threadDead == DataAccess.DEAD));
  146. gameData.currentTime = currentGameTimeThread-1; // 현재까지 카운트한 시간을 저장한다.
  147. break; // 여기다가 || 게임중단시 종료를 시켜줘야겠구만.
  148. }
  149. try
  150. {
  151. Thread.sleep(1000);
  152. }
  153. {
  154. e.printStackTrace();
  155. }
  156. }
  157. Log.e("run() 메서드 반환", "ㅇㅋ");
  158. }
  159. }
  160. }

 

아래의 코드는 메인액티비티의 코드입니다.

 

  1. package project.shake;
  2. import project.shake.*;
  3. import android.app.Activity;
  4. import android.os.*;
  5. import android.app.*;
  6. import android.content.*;
  7. import android.os.*;
  8. import android.util.*;
  9. import android.view.*;
  10. import android.widget.*;
  11. import android.graphics.drawable.*;
  12. import android.hardware.*;
  13. public class GameMain extends Activity {
  14. /** Called when the activity is first created. */
  15. DataAccess gameData;
  16. public static final int GAMESTART = 0;
  17. public static final int GAMEOPTION = 1;
  18. @Override
  19. public void onCreate(Bundle savedInstanceState) {
  20. // 스플래시 화면 띄우기.
  21. startActivity(new Intent(this, GameMainLoading.class));
  22. // public DataAccess(int vive, int limit, int shake, float sumScore, int threadLife, int cTime)
  23. gameData = new DataAccess(DataAccess.VIVE_ON, DataAccess.DEFAULT_TIME, 0, 0.0f, DataAccess.RUNNABLE, 0); // 전체 데이터를 관리하는 인스턴스 생성!
  24. // 앞으로 이놈은 여러 액티비티를 타고 이동하며 게임을 컨트롤한다.
  25. super.onCreate(savedInstanceState);
  26. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 앱실행중 자동꺼짐 방지
  27. setContentView(R.layout.gamemain);
  28. Log.e("생성후 진동 토글 : ", ""+gameData.toggleVive);
  29. Log.e("생성후 게임 시간 : ", ""+gameData.limitGameTime);
  30. Log.e("생성후 흔든 횟수 : ", ""+gameData.shakeCount);
  31. Log.e("생성후 벡터 합계 : ", ""+gameData.forceSumForScore);
  32. Log.e("생성후 진동 토글 : ", ""+gameData.toggleVive);
  33. Log.e("생성후 쓰레드 삶 : ", ""+gameData.threadDead);
  34. }
  35. public void mOnClick(View v)
  36. {
  37. Intent intent;
  38. switch(v.getId())
  39. {
  40. case R.id.gamestart : // 게임시작 버튼
  41. intent = new Intent(this, GameStart.class);
  42. intent.putExtra("GameData", gameData); // 인텐트 통신. (게임에 필요한 실제 논리를 전달)
  43. startActivityForResult(intent, GAMESTART);
  44. break;
  45. case R.id.gamehelp : // 게임방법 버튼
  46. intent = new Intent(this, GameHelp.class);
  47. startActivity(intent);
  48. break;
  49. case R.id.gameoption : // 게임옵션 버튼
  50. intent = new Intent(this, GameOption.class);
  51. intent.putExtra("GameOption", gameData); // 인텐트 통신. (옵션에서 시간 및 진동여부를 토글)
  52. startActivityForResult(intent, GAMEOPTION);
  53. break;
  54. }
  55. }
  56. protected void onActivityResult(int requestCode, int resultCode, Intent data)
  57. {
  58. switch(requestCode)
  59. {
  60. case GAMESTART :
  61. Log.e("GameMain.java/case GAMESTART : 진입", "ㅇㅋ");
  62. if(resultCode == RESULT_OK)
  63. {
  64. Log.e("GameMain.java/GAMESTART if문 진입", "ㅇㅋ");
  65. Bundle dataBackup = data.getParcelableExtra("GameSaved"); // GameData.java에서 수정된 액티비티가 넘어와서 gameData에 저장됨.
  66. gameData = dataBackup.getParcelable("GameData");
  67. Log.e("GameMain.java/진동 토글 : ", ""+gameData.toggleVive);
  68. Log.e("GameMain.java/게임 시간 : ", ""+gameData.limitGameTime);
  69. Log.e("GameMain.java/흔든 횟수 : ", ""+gameData.shakeCount);
  70. Log.e("GameMain.java/벡터 합계 : ", ""+gameData.forceSumForScore);
  71. Log.e("GameMain.java/진동 토글 : ", ""+gameData.toggleVive);
  72. Log.e("GameMain.java/저장 시간 : ", ""+gameData.currentTime);
  73. }
  74. else
  75. Log.e("전달된 값 : ", ""+RESULT_CANCELED);
  76. break;
  77. case GAMEOPTION :
  78. // 코드 처리할것.
  79. break;
  80. }
  81. }
  82. public void onDestroy()
  83. {
  84. super.onDestroy();
  85. Log.e("GameMain/onDestroy() 호출", "ㅇㅋ");
  86. }
  87. public void onPause()
  88. {
  89. super.onPause();
  90. Log.e("GameMain/onPause() 호출", "ㅇㅋ");
  91. }
  92. }

 

아래의 코드는 DataAccess의 클래스바디 입니다.

 

  1. package project.shake;
  2. import android.os.*;
  3. // 시간나면 클래스들 전체의 은닉, 캡슐화를 추진할것.
  4. // 게임의 점수, 옵션셋팅, 기타등등 잡다한것 넣어둔 데이터 클래스. 일단 전부 퍼블릭. -_-.
  5. public class DataAccess implements Parcelable, Cloneable
  6. {
  7. // 옵션에서 셋팅.
  8. public int toggleVive; // 1 : on, 0 : off
  9. public int limitGameTime;
  10. // 상수.
  11. public static final int GAME_RUN = 0x76545400; // 게임 계속
  12. public static final int GAME_END = 0x76545401; // 게임 끝 (시간이 다되었음.)
  13. public static final int GAME_PAUSE = 0x76545402; // 게임 일시중지.
  14. public static final int VIVE_ON = 1; // 진동 on
  15. public static final int VIVE_OFF = 0; // 진동 off
  16. public static final int DEAD = 0; // 쓰레드 죽음
  17. public static final int RUNNABLE = 1; // 쓰레드 살음(살리거나.)
  18. public static final int DEFAULT_TIME = 30; // 시간 : 디폴트로 30초
  19. // 흔든 횟수 및 점수 스코어 계산.
  20. public int shakeCount; // 흔든 횟수. 타 클래스에서도 인스턴스 없이 참조하도록 static
  21. public float forceSumForScore; // forceAccel 의 누적합을 구함.
  22. // forceSumForScore += forceAccel;
  23. // 쓰레드 컨트롤(게임중 홈버튼/백버튼 누르면 GameStart.java에서 onPause가 호출되므로, 이 값을 적절히 토글시켜서 쓰레드를 죽여야한다.
  24. // public boolean toggleGamePause = false;
  25. public int threadDead;
  26. public int currentTime;
  27. // GameMain에서 생성한 인스턴스는 초기화되어서 온갖 액티비티에서 다 참조하지롱. -_-
  28. // 은닉 캡슐화따윈 캡슐에 개밥뿌려먹어.
  29. public DataAccess(int vive, int limit, int shake, float sumScore, int threadLife, int cTime)
  30. {
  31. // 최초 디폴트 옵션. 물론, 환경설정에서 바꿀 수 있는데 이게 개나소나 참조할 수 있으니 문제지. -_-...
  32. toggleVive = vive;
  33. limitGameTime = limit;
  34. // 최초 흔든 횟수랑 점수 스코어는 0회/0점.
  35. shakeCount = shake;
  36. forceSumForScore = sumScore;
  37. threadDead = threadLife;
  38. currentTime = cTime;
  39. }
  40. public DataAccess(Parcel src)
  41. {
  42. toggleVive = src.readInt();
  43. limitGameTime = src.readInt();
  44. shakeCount = src.readInt();
  45. forceSumForScore = src.readFloat();
  46. threadDead = src.readInt();
  47. currentTime = src.readInt();
  48. }
  49. public void initZeroScore() // 게임종료후, 흔든횟수랑 점수를 0으로 초기화한다. 이건 반드시 게임 리절트 후, 호출시켜라.
  50. {
  51. shakeCount = 0;
  52. forceSumForScore = 0.0f;
  53. }
  54. public void scoreCalculation() // 구한 가속도의 합을 토대로 0~100 사이의 점수로 계산지을 것!
  55. {
  56. // 구현하세요. 근데 0~100점내로 어떻게 합산하지... =_=...
  57. }
  58. public int describeContents() {
  59. // TODO Auto-generated method stub
  60. return 0;
  61. }
  62. // 파슬 인스턴스에 데이터를 출력시킴.
  63. public void writeToParcel(Parcel desc, int arg1)
  64. {
  65. // TODO Auto-generated method stub
  66. desc.writeInt(toggleVive);
  67. desc.writeInt(limitGameTime);
  68. desc.writeInt(shakeCount);
  69. desc.writeFloat(forceSumForScore);
  70. desc.writeInt(threadDead);
  71. desc.writeInt(currentTime);
  72. }
  73. public void readFromParcel(Parcel in)
  74. {
  75. toggleVive = in.readInt();
  76. limitGameTime = in.readInt();
  77. shakeCount = in.readInt();
  78. forceSumForScore = in.readFloat();
  79. threadDead = in.readInt();
  80. currentTime = in.readInt();
  81. }
  82. public Object clone()
  83. {
  84. // 깊은복사를 위한 클론메서드. 일단 나중에 구현해보자.
  85. return (DataAccess)this;
  86. }
  87. public static final Parcelable.Creator<DataAccess> CREATOR = new Parcelable.Creator<DataAccess>() {
  88. public DataAccess createFromParcel(Parcel in) {
  89. return new DataAccess(in);
  90. }
  91. public DataAccess[] newArray(int size) {
  92. return new DataAccess[size];
  93. }
  94. };
  95. }

 

미리 올라올 조언에 감사드립니다.