작성
·
729
0
안녕하세요 선생님, 우선 강의 내용과 직접적으로 관련된 질문이 아니라 죄송합니다. 댓글 관련 오류가 몇 가지 발생해 여쭤봅니다.
BoardReadActivity.kt
생략
// 댓글 목록 정보 가져옴
fun getCommentListData(key: String) {
생략
// 댓글 헤더에 댓글 개수 출력
binding.commentCountText.text = commentKeyList.count().toString()
생략
}
기존 댓글 2개가 있는 상황에서 새 댓글을 1개 입력하면 댓글 개수가 3이 되어야 하는데요,
2(기존) + 3(기존2업뎃1) = 5(누적)로 출력이 됩니다.
또한 방금 막 작성한 댓글 a의 수정 페이지로 넘어가면
추가된 댓글이 즉시 반영되지 않아 맨 위 댓글인 user1이 불러와지며
댓글 a가 아닌 맨 위 댓글이 수정됩니다.
마찬가지로 a를 삭제하려해도 user-1이 불러와지며
삭제되는 댓글도 a가 아닌 user-1입니다.
수정과 삭제를 반복하면 댓글 카운트는 계속 늘어나고, 이 과정에서 앱이 죽기도 합니다.
게시글을 한 번 나갔다 들어와야 댓글 수가 제대로 카운트 돼서 어떻게 해야 할지 도움 요청드립니다. 코드 전체는 깃허브에 업로드 해 놓았습니다. https://github.com/shinyelee/my-solo-life
답변 3
0
또한 Firebase Realtime database의 경우 데이터 변경이 일어날 경우, 새롭게 데이터를 받아오는 것이 자동으로 실행되니 이 부분 고려하셔서 로그를 찍어보시면 좋을 것 같습니다.
https://github.com/shinyelee/my-solo-life
로그를 확인해보니 댓글 데이터 자체는 제대로 받아오고 있습니다.
2022-09-17 00:01:38.535 534-896/system_process I/ActivityTaskManager: START u0 {cmp=com.shinyelee.my_solo_life/.board.BoardReadActivity (has extras)} from uid 10122
2022-09-17 00:01:38.627 6349-6349/com.shinyelee.my_solo_life D/BoardReadActivity: DataSnapshot { key = -NBR-KRzCh8X18kzS04y, value = {uid=qwgynbq1wsWwVlxdrieokzQRXpd2, main=1212222, time=22-09-08 16:30} }
2022-09-17 00:01:38.627 6349-6349/com.shinyelee.my_solo_life D/BoardReadActivity: DataSnapshot { key = -NBR-NYf7D93gn1mACPo, value = {uid=qwgynbq1wsWwVlxdrieokzQRXpd2, main=313333, time=22-09-08 16:30} }
2022-09-17 00:01:38.627 6349-6349/com.shinyelee.my_solo_life D/BoardReadActivity: DataSnapshot { key = -NC4bUFJ0_6BeJ43hmm6, value = {uid=pJYqGCYe5JcGI5Qrfc5w6F8kJ633, main=1111, time=22-09-16 09:25} }
2022-09-17 00:01:38.630 534-580/system_process I/ActivityTaskManager: Displayed com.shinyelee.my_solo_life/.board.BoardReadActivity: +94ms
// 댓글 목록 정보 가져옴
private fun getCommentListData(key: String) {
// 데이터베이스에서 컨텐츠의 세부정보를 검색
val postListener = object : ValueEventListener {
// 데이터 스냅샷
@SuppressLint("NotifyDataSetChanged")
override fun onDataChange(dataSnapshot: DataSnapshot) {
// 댓글 목록 비움
// -> 저장/삭제 마다 데이터 누적돼 게시글 중복으로 저장되는 것 방지
commentList.clear()
// 데이터 스냅샷 내 데이터모델 형식으로 저장된
for(dataModel in dataSnapshot.children) {
// 로그
Log.d(TAG, dataModel.toString())
// 아이템(=댓글)
val item = dataModel.getValue(CommentModel::class.java)
// 댓글 목록에 아이템 넣음
commentList.add(item!!)
// 댓글 키 목록에 문자열 형식으로 변환한 키 넣음
commentKeyList.add(dataModel.key.toString())
}
// 반복문임 -> 아이템'들'
// 댓글 키 목록을 출력
commentKeyList
// 댓글 목록도 출력
commentList
// 댓글 헤더에 댓글 개수 출력
binding.commentCountText.text = commentKeyList.count().toString()
// 동기화(새로고침) -> 리스트 크기 및 아이템 변화를 어댑터에 알림
commentLVAdapter.notifyDataSetChanged()
}
// 오류 나면
override fun onCancelled(databaseError: DatabaseError) {
// 로그
Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
}
}
// 파이어베이스 내 데이터의 변화(추가)를 알려줌
FBRef.commentRef.child(key).addValueEventListener(postListener)
}
위 코드에서
binding.commentCountText.text = commentKeyList.count().toString()
를
binding.commentCountText.text = commentList.count().toString()
로 바꿔주니 댓글 개수가 제대로 출력됩니다!
댓글 카운팅은 고쳤는데, 댓글이 매칭되지 않는 문제가 재발했습니다.
댓글을 3개 등록해 111 -> 11 -> 1 순으로 삭제 시도했는데
111 대신 11이 삭제되고
다시 111을 삭제 시도하니
111 대신 1이 삭제되고
111은 게시글을 나갔다 들어와서 한 번 튕긴 후 겨우 삭제가 됩니다.
package com.shinyelee.my_solo_life.comment
import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.ValueEventListener
import com.shinyelee.my_solo_life.databinding.ActivityCommentEditBinding
import com.shinyelee.my_solo_life.utils.FBAuth
import com.shinyelee.my_solo_life.utils.FBRef
class CommentEditActivity : AppCompatActivity() {
// (전역변수) 바인딩 객체 선언
private var vBinding : ActivityCommentEditBinding? = null
// 매번 null 확인 귀찮음 -> 바인딩 변수 재선언
private val binding get() = vBinding!!
// 게시글 키
private lateinit var key: String
// 댓글 키
private lateinit var commentKey: String
// 태그
private val TAG = CommentEditActivity::class.java.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 자동 생성된 뷰바인딩 클래스에서의 inflate 메서드 활용
// -> 액티비티에서 사용할 바인딩 클래스의 인스턴스 생성
vBinding = ActivityCommentEditBinding.inflate(layoutInflater)
// getRoot 메서드로 레이아웃 내부 최상위에 있는 뷰의 인스턴스 활용
// -> 생성된 뷰를 액티비티에 표시
setContentView(binding.root)
// 뒤로가기 버튼 -> 댓글수정 액티비티 종료
binding.backBtn.setOnClickListener { finish() }
// 게시판 프래그먼트에서 게시글의 키 값을 받아옴
key = intent.getStringExtra("key").toString()
// 글읽기 액티비티에서 댓글의 키 값을 받아옴
commentKey = intent.getStringExtra("commentKey").toString()
// 댓글 키 값을 바탕으로 댓글 하나의 정보를 가져옴
getCommentData(key, commentKey)
// 수정하기 버튼 -> 키 값을 바탕으로 불러온 댓글 수정
binding.commentEditBtn.setOnClickListener { editCommentData(key, commentKey) }
// 삭제하기 버튼 -> 키 값을 바탕으로 불러온 댓글 삭제
binding.commentDeleteBtn.setOnClickListener { deleteCommentData(key, commentKey) }
}
// 댓글을 삭제
private fun deleteCommentData(key: String, commentKey: String) {
// 댓글 삭제
FBRef.commentRef.child(key).child(commentKey).removeValue()
// 삭제 확인 메시지
Toast.makeText(this, "댓글이 삭제되었습니다", Toast.LENGTH_SHORT).show()
// 댓글수정 액티비티 종료
finish()
// 댓글1, 댓글2, 댓글3 연달아 수정/삭제시 문제 발생
// 순서 무관하게 1은 정상적으로 수정되나 2나 3을 클릭하면 실제로는 둘 다 1이 수정됨
// 123 순서로 삭제시 -> 1은 정상적으로 삭제되나 2부터 댓글 내용을 못 불러옴
// 321 순서로 삭제시 -> 3을 클릭했는데 실제로는 1이 삭제되고 3은 남아있음
// 간헐적으로 게시판 메인화면으로 튕기는 현상
// 게시글을 나갔다가 다시 들어오면 모든 기능이 정상 작동함
// -> setComment 실행 후, commentEditActivity 종료 후 BoardReadActivity를 새로고침하면 해결될 듯
}
// 댓글을 수정
private fun editCommentData(key: String, commentKey: String) {
// 수정한 값으로 업데이트
FBRef.commentRef.child(key).child(commentKey).setValue(CommentModel(
// 제목 및 본문은 직접 수정한 내용으로,
binding.commentMainArea.text.toString(),
// uid와 시간은 자동 설정됨
FBAuth.getUid(),
FBAuth.getTime()
))
// 수정 확인 메시지
Toast.makeText(this, "댓글이 수정되었습니다", Toast.LENGTH_SHORT).show()
// 댓글수정 액티비티 종료
finish()
// 댓글1, 댓글2, 댓글3 연달아 수정/삭제시 문제 발생
// 순서 무관하게 1은 정상적으로 수정되나 2나 3을 클릭하면 실제로는 둘 다 1이 수정됨
// 123 순서로 삭제시 -> 1은 정상적으로 삭제되나 2부터 댓글 내용을 못 불러옴
// 321 순서로 삭제시 -> 3을 클릭했는데 실제로는 1이 삭제되고 3은 남아있음
// 간헐적으로 게시판 메인화면으로 튕기는 현상
// 게시글을 나갔다가 다시 들어오면 모든 기능이 정상 작동함
// -> setComment 실행 후, commentEditActivity 종료 후 BoardReadActivity를 새로고침하면 해결될 듯
}
// 댓글 하나의 정보를 가져옴
private fun getCommentData(key: String, commentKey: String) {
// 데이터베이스에서 컨텐츠의 세부정보를 검색
val postListener = object : ValueEventListener {
// 데이터 스냅샷
@SuppressLint("NotifyDataSetChanged")
override fun onDataChange(dataSnapshot: DataSnapshot) {
// 예외 처리
try {
// 데이터 스냅샷 내 데이터모델 형식으로 저장된 아이템(=게시글)
val item = dataSnapshot.getValue(CommentModel::class.java)
// 본문 해당 영역에 넣음(작성자 및 시간은 직접 수정하지 않음)
binding.commentMainArea.setText(item?.main)
// textView -> .text
// editText -> .setText(집어넣을 데이터)
// 오류 나면
} catch (e: Exception) {
// 로그
Log.e(TAG, "getBoardData 확인")
}
}
// getCommentListData()와 달리 반복문이 아님 -> '단일' 아이템
// 오류 나면
override fun onCancelled(databaseError: DatabaseError) {
// 로그
Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
}
}
// 파이어베이스 내 데이터의 변화(추가)를 알려줌
FBRef.commentRef.child(key).child(commentKey).addValueEventListener(postListener)
}
// 액티비티 파괴시
override fun onDestroy() {
// 바인딩 클래스 인스턴스 참조를 정리 -> 메모리 효율이 좋아짐
vBinding = null
super.onDestroy()
}
}
실제로 저 삭제 버튼을 눌렀을 때 원하는 게시글이 클릭되고 있는게 맞나요?
Adapter부분에서 onclick을 했을 때, 어떤 게시글이 가고 있는지 로그를 찍어봐야 할 것 같습니다.
그리고, 앱이 죽는 부분은 죽는 순간의 로그를 한번 확인해보시겠어요?
0
안녕하세요.
1/2 번 문제같은 경우에는 로그를 보면 알 것 같습니다.
저 앱이 죽는 순간에 logcat에 찍히는 정보를 공유해주시겠어요?
3번 문제의 경우, Firebase에서 받아온 댓글을 읽어와서 보여주는 것이 맞으실까요?
그렇다면 읽어오는 부분에 로그를 찍어서 저 부분이 어떻게 실행되는지 알려주시겠어요?
https://github.com/shinyelee/my-solo-life
댓글 연달아 등록시 logcat입니다.
2022-09-16 18:25:04.725 4916-5233/com.shinyelee.my_solo_life E/StorageException: StorageException has occurred.
Object does not exist at location.
Code: -13010 HttpResult: 404
2022-09-16 18:25:04.726 4916-5233/com.shinyelee.my_solo_life E/StorageException: { "error": { "code": 404, "message": "Not Found." }}
java.io.IOException: { "error": { "code": 404, "message": "Not Found." }}
at com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
at com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
at com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2022-09-16 18:25:10.409 4916-5244/com.shinyelee.my_solo_life E/StorageException: StorageException has occurred.
Object does not exist at location.
Code: -13010 HttpResult: 404
2022-09-16 18:25:10.410 4916-5244/com.shinyelee.my_solo_life E/StorageException: { "error": { "code": 404, "message": "Not Found." }}
java.io.IOException: { "error": { "code": 404, "message": "Not Found." }}
at com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
at com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
at com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2022-09-16 18:25:12.772 4916-4916/com.shinyelee.my_solo_life E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.shinyelee.my_solo_life, PID: 4916
java.lang.NullPointerException
at com.shinyelee.my_solo_life.board.BoardReadActivity.getBinding(BoardReadActivity.kt:33)
at com.shinyelee.my_solo_life.board.BoardReadActivity.access$getBinding(BoardReadActivity.kt:27)
at com.shinyelee.my_solo_life.board.BoardReadActivity$getCommentListData$postListener$1.onDataChange(BoardReadActivity.kt:177)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75)
at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2022-09-16 18:25:13.952 5261-5291/com.shinyelee.my_solo_life E/StorageException: StorageException has occurred.
Object does not exist at location.
Code: -13010 HttpResult: 404
2022-09-16 18:25:13.953 5261-5291/com.shinyelee.my_solo_life E/StorageException: { "error": { "code": 404, "message": "Not Found." }}
java.io.IOException: { "error": { "code": 404, "message": "Not Found." }}
at com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
at com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
at com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2022-09-16 18:25:20.656 5261-5307/com.shinyelee.my_solo_life E/StorageException: StorageException has occurred.
Object does not exist at location.
Code: -13010 HttpResult: 404
2022-09-16 18:25:20.657 5261-5307/com.shinyelee.my_solo_life E/StorageException: { "error": { "code": 404, "message": "Not Found." }}
java.io.IOException: { "error": { "code": 404, "message": "Not Found." }}
at com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
at com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
at com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2022-09-16 18:25:22.551 5261-5261/com.shinyelee.my_solo_life E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.shinyelee.my_solo_life, PID: 5261
java.lang.NullPointerException
at com.shinyelee.my_solo_life.board.BoardReadActivity.getBinding(BoardReadActivity.kt:33)
at com.shinyelee.my_solo_life.board.BoardReadActivity.access$getBinding(BoardReadActivity.kt:27)
at com.shinyelee.my_solo_life.board.BoardReadActivity$getCommentListData$postListener$1.onDataChange(BoardReadActivity.kt:177)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75)
at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2022-09-16 18:27:33.572 1957-5380/com.google.android.gms E/ciiw: ~~*~ Channel {0} was not shutdown properly!!! ~*~*~*
Make sure to call shutdown()/shutdownNow() and wait until awaitTermination() returns true.
java.lang.RuntimeException: ManagedChannel allocation site
at ciiv.<init>(:com.google.android.gms@201817022@20.18.17 (040700-311416286):3)
at ciiw.<init>(:com.google.android.gms@201817022@20.18.17 (040700-311416286):2)
at cicn.b(:com.google.android.gms@201817022@20.18.17 (040700-311416286):21)
at sdn.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):24)
at sdn.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):31)
at afgm.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):12)
at com.google.android.libraries.matchstick.net.SilentRegisterIntentOperation.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):179)
at com.google.android.libraries.matchstick.net.SilentRegisterIntentOperation.f(:com.google.android.gms@201817022@20.18.17 (040700-311416286):38)
at com.google.android.libraries.matchstick.net.SilentRegisterIntentOperation.onHandleIntent(:com.google.android.gms@201817022@20.18.17 (040700-311416286):164)
at com.google.android.chimera.IntentOperation.onHandleIntent(:com.google.android.gms@201817022@20.18.17 (040700-311416286):2)
at qgf.onHandleIntent(:com.google.android.gms@201817022@20.18.17 (040700-311416286):4)
at cui.run(:com.google.android.gms@201817022@20.18.17 (040700-311416286):5)
at cuh.run(:com.google.android.gms@201817022@20.18.17 (040700-311416286):9)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2022-09-16 18:27:34.298 1957-5380/com.google.android.gms E/MS_RegisterService: Exception during register request.
ciau: UNAUTHENTICATED: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
at ciat.c(:com.google.android.gms@201817022@20.18.17 (040700-311416286):3)
at sdn.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):48)
at afgm.b(:com.google.android.gms@201817022@20.18.17 (040700-311416286):6)
at com.google.android.libraries.matchstick.net.SilentRegisterIntentOperation.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):178)
at com.google.android.libraries.matchstick.net.SilentRegisterIntentOperation.f(:com.google.android.gms@201817022@20.18.17 (040700-311416286):38)
at com.google.android.libraries.matchstick.net.SilentRegisterIntentOperation.onHandleIntent(:com.google.android.gms@201817022@20.18.17 (040700-311416286):164)
at com.google.android.chimera.IntentOperation.onHandleIntent(:com.google.android.gms@201817022@20.18.17 (040700-311416286):2)
at qgf.onHandleIntent(:com.google.android.gms@201817022@20.18.17 (040700-311416286):4)
at cui.run(:com.google.android.gms@201817022@20.18.17 (040700-311416286):5)
at cuh.run(:com.google.android.gms@201817022@20.18.17 (040700-311416286):9)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
https://github.com/shinyelee/my-solo-life
댓글 연달아 삭제시 debug입니다.
--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.shinyelee.my_solo_life, PID: 6205
java.lang.NullPointerException
at com.shinyelee.my_solo_life.board.BoardReadActivity.getBinding(BoardReadActivity.kt:33)
at com.shinyelee.my_solo_life.board.BoardReadActivity.access$getBinding(BoardReadActivity.kt:27)
at com.shinyelee.my_solo_life.board.BoardReadActivity$getCommentListData$postListener$1.onDataChange(BoardReadActivity.kt:177)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75)
at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
안녕하세요.
뭔가 길어지니깐 저도 헷갈리기 시작하네요
오류 메세지를 보니깐 404 not found / null point exception 입니다.
잘못된 정보를 받아와서 보여주는 것 같은데
로직을 생각해보자면
댓글을 받아와서
뭔가 수정 및 삭제를 해주고 있는데
삭제 및 수정을 하고나서 변경된 데이터가 앱에 저장된 것과 firebase에 다른 상태로 보입니다.
이럴 때 가장 간단한 해결방법은 저 수정 삭제를 누르고 난 이후에, 이전에 firebase에서 받아온 정보를 저장하는 변수의 값을 초기화 시켜주는 방법이 있습니다.
저기 에러가 발생하는 라인이 몇번쨰인지를 보시고, 어떤 변수에서 문제가 발생하는지 살펴보시고 어려우시면 스크린샷도 함께 공유해주시겠어요?
StorageException과 NullPointerException이 뜨는 원인을 찾은 것 같습니다. 현재 댓글 수정/삭제 로직은 아래와 같은데요.
1) BoardReadActivity(게시글 하나 보는 페이지)에서 수정/삭제하고자 하는 댓글 클릭
2) CommentEditActivity(댓글 수정/삭제 페이지) 시작
3) 댓글 삭제 버튼을 누르면 댓글이 수정/삭제됨과 동시에 BoardEditActivity가 종료됨
4) BoardReadActivity로 돌아감(실제로는 CommentEditActivity에 가려져있던 액티비티가 드러난 것)
이 때 4번의 BoardActivity를 새로고침해 댓글 수정/삭제 내역을 반영해야 하는데 실제로는 2번의 (댓글 수정/삭제 전인) 화면을 그대로 띄워줘 문제가 발생하는 것으로 보입니다.
2022-09-21 03:49:15.763 17067-17186/com.shinyelee.my_solo_life E/StorageException: StorageException has occurred.
Object does not exist at location.
Code: -13010 HttpResult: 404
2022-09-21 03:49:15.763 17067-17186/com.shinyelee.my_solo_life E/StorageException: { "error": { "code": 404, "message": "Not Found." }}
java.io.IOException: { "error": { "code": 404, "message": "Not Found." }}
at com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
at com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
at com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
at com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
at com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
at com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
2022-09-21 03:49:16.059 17067-17067/com.shinyelee.my_solo_life E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.shinyelee.my_solo_life, PID: 17067
java.lang.NullPointerException
at com.shinyelee.my_solo_life.board.BoardReadActivity.getBinding(BoardReadActivity.kt:32)
at com.shinyelee.my_solo_life.board.BoardReadActivity.access$getBinding(BoardReadActivity.kt:26)
at com.shinyelee.my_solo_life.board.BoardReadActivity$getCommentListData$postListener$1.onDataChange(BoardReadActivity.kt:176)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75)
at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
1) BoardReadActivity에서 NullPointerException이 뜰 때 댓글 개수를 다시 받아오거나
2) BoardEditActivity가 종료될 때 BoardReadActivity가 댓글 개수를 다시 받아오게 만들고자 하는데
http://daplus.net/android-android%EC%97%90%EC%84%9C-%ED%99%9C%EB%8F%99-%EC%9E%AC%EB%A1%9C%EB%93%9C/
위 글들을 참고해봐도 해당 기능이 잘 구현되지 않아 재질문드립니다.
에러가 발생하는 코드는 주석으로 표시했습니다.
package com.shinyelee.my_solo_life.board
생략
class BoardReadActivity : AppCompatActivity() {
//////////////////// 위 코드에서 에러 발생(BoardReadActivity.kt:26) ////////////////////
// (전역변수) 바인딩 객체 선언
private var vBinding : ActivityBoardReadBinding? = null
// 매번 null 확인 귀찮음 -> 바인딩 변수 재선언
private val binding get() = vBinding!!
//////////////////// 위 코드에서 에러 발생(BoardReadActivity.kt:32) ////////////////////
// 게시글 키
private lateinit var key: String
// 댓글(=본문+uid+시간) 목록
private val commentList = mutableListOf<CommentModel>()
// 댓글의 키 목록
private val commentKeyList = mutableListOf<String>()
// 리스트뷰 어댑터 선언
private lateinit var commentLVAdapter : CommentLVAdapter
// 태그
private val TAG = BoardReadActivity::class.java.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 자동 생성된 뷰바인딩 클래스에서의 inflate 메서드 활용
// -> 액티비티에서 사용할 바인딩 클래스의 인스턴스 생성
vBinding = ActivityBoardReadBinding.inflate(layoutInflater)
// getRoot 메서드로 레이아웃 내부 최상위에 있는 뷰의 인스턴스 활용
// -> 생성된 뷰를 액티비티에 표시
setContentView(binding.root)
// 리스트뷰 어댑터 연결(댓글 목록)
commentLVAdapter = CommentLVAdapter(commentList)
val cLV : ListView = binding.commentLV
cLV.adapter = commentLVAdapter
// 댓글 목록(리스트뷰)
cLV.setOnTouchListener(object : View.OnTouchListener {
// 리스트뷰를 터치했을 때
override fun onTouch(p0: View?, p1: MotionEvent?): Boolean {
// 스크롤뷰(화면 전체)의 터치 이벤트를 막으면 -> 리스트뷰(댓글 영역)의 스크롤뷰가 작동함
binding.boardReadSV.requestDisallowInterceptTouchEvent(true)
return false
}
})
// 게시판 프래그먼트에서 게시글의 키 값을 받아옴
key = intent.getStringExtra("key").toString()
// 게시글 키 값을 바탕으로 게시글 하나의 정보를 가져옴
getBoardData(key)
getImageData(key)
getCommentListData(key)
// 뒤로가기 버튼을 클릭하면
binding.backBtn.setOnClickListener {
// 글읽기 액티비티 종료
finish()
}
// 게시글 설정 버튼
binding.boardSettingBtn.setOnClickListener {
// 명시적 인텐트 -> 다른 액티비티 호출
val intent = Intent(this, BoardEditActivity::class.java)
// 키 값을 바탕으로 게시글 받아옴
intent.putExtra("key", key)
// 글수정 액티비티 시작
startActivity(intent)
}
// 파이어베이스의 댓글 키를 기반으로 댓글 데이터(=본문+uid+시간) 받아옴
cLV.setOnItemClickListener { parent, view, position, id ->
// 명시적 인텐트 -> 다른 액티비티 호출
val intent = Intent(baseContext, CommentEditActivity::class.java)
// 댓글수정 액티비티로 댓글의 키 값 전달
intent.putExtra("key", key)
// 댓글수정 액티비티로 댓글의 키 값 전달
intent.putExtra("commentKey", commentKeyList[position])
// 댓글수정 액티비티 시작
startActivity(intent)
}
// 댓글쓰기 버튼
binding.commentBtn.setOnClickListener {
// -> 작성한 댓글을 등록
setComment(key)
}
}
// 댓글 목록 정보 가져옴
private fun getCommentListData(key: String) {
// 데이터베이스에서 컨텐츠의 세부정보를 검색
val postListener = object : ValueEventListener {
// 데이터 스냅샷
@SuppressLint("NotifyDataSetChanged")
override fun onDataChange(dataSnapshot: DataSnapshot) {
// 댓글 목록 비움
// -> 저장/삭제 마다 데이터 누적돼 게시글 중복으로 저장되는 것 방지
commentList.clear()
// 데이터 스냅샷 내 데이터모델 형식으로 저장된
for(dataModel in dataSnapshot.children) {
// 로그
Log.d(TAG, dataModel.toString())
// 아이템(=댓글)
val item = dataModel.getValue(CommentModel::class.java)
// 댓글 목록에 아이템 넣음
commentList.add(item!!)
// 댓글 키 목록에 문자열 형식으로 변환한 키 넣음
commentKeyList.add(dataModel.key.toString())
}
// 반복문임 -> 아이템'들'
// 댓글 키 목록을 출력
commentKeyList
// 댓글 목록도 출력
commentList
// 댓글 헤더에 댓글 개수 출력
binding.commentCountText.text = commentList.count().toString()
//////////////////// 위 코드에서 에러 발생(BoardReadActivity.kt:176) ////////////////////
// 동기화(새로고침) -> 리스트 크기 및 아이템 변화를 어댑터에 알림
commentLVAdapter.notifyDataSetChanged()
}
// 오류 나면
override fun onCancelled(databaseError: DatabaseError) {
// 로그
Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
}
}
// 파이어베이스 내 데이터의 변화(추가)를 알려줌
FBRef.commentRef.child(key).addValueEventListener(postListener)
}
// 작성한 댓글을 등록
private fun setComment(key: String) {
// 댓글의 데이터(본문, uid, 시간)
val main = binding.commentMainArea.text.toString()
val uid = FBAuth.getUid()
val time = FBAuth.getTime()
// 키 값 하위에 데이터 넣음
FBRef.commentRef
.child(key)
.push()
.setValue(CommentModel(main, uid, time))
// 등록 확인 메시지 띄움
Toast.makeText(this, "댓글이 등록되었습니다", Toast.LENGTH_SHORT).show()
// 댓글 입력란 비움
binding.commentMainArea.text = null
}
// 게시글에 첨부된 이미지 정보를 가져옴
private fun getImageData(key: String) {
// 이미지 파일 경로
val storageReference = Firebase.storage.reference.child("$key.png")
// 이미지 넣을 곳
val imgDown = binding.imageArea
// 글라이드로 이미지 다운로드
storageReference.downloadUrl.addOnCompleteListener( { task ->
// 이미지 첨부
if(task.isSuccessful) {
Glide.with(this)
.load(task.result)
.into(imgDown)
// 첨부 이미지 없으면 imageArea 안 보이게 처리
} else {
binding.imageArea.isVisible = false
}
})
}
// 게시글 하나의 정보를 가져옴
private fun getBoardData(key: String) {
// 데이터베이스에서 컨텐츠의 세부정보를 검색
val postListener = object : ValueEventListener {
// 데이터 스냅샷
@SuppressLint("NotifyDataSetChanged")
override fun onDataChange(dataSnapshot: DataSnapshot) {
// 예외 처리
try {
// 데이터 스냅샷 내 데이터모델 형식으로 저장된 아이템(=게시글)
val item = dataSnapshot.getValue(BoardModel::class.java)
// 제목, 시간, 본문 해당 영역에 넣음
binding.titleArea.text = item!!.title
binding.timeArea.text = item.time
binding.mainArea.text = item.main
// 게시글 작성자와 현재 사용자의 uid를 비교해
val writerUid = item.uid
val myUid = FBAuth.getUid()
// 작성자가 사용자면 수정 버튼 보임
binding.boardSettingBtn.isVisible = writerUid.equals(myUid)
// 오류 나면
} catch (e: Exception) {
// 로그
Log.d(TAG, "getBoardData 확인")
}
}
// getBoardListData()와 달리 반복문이 아님 -> '단일' 아이템
// 오류 나면
override fun onCancelled(databaseError: DatabaseError) {
// 로그
Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
}
}
// 파이어베이스 내 데이터의 변화(추가)를 알려줌
FBRef.boardRef.child(key).addValueEventListener(postListener)
}
// 액티비티 파괴시
override fun onDestroy() {
// 바인딩 클래스 인스턴스 참조를 정리 -> 메모리 효율이 좋아짐
vBinding = null
super.onDestroy()
}
}
0
Activity 라이프 사이클에 변화가 있을 때마다
저 댓글 수를 불러오는 것으로 코드가 되어 있다면
라이프사이클이 변경될 떄 값을 초기화해주시면 됩니다.
자바로 되어있긴 하지만, 한번 라이프사이클에 대해서 공부해보시겠어요?
네, 유튜브 다 봤고 안드로이드 공식 문서도 읽어봤습니다.
BoardReadActivity.kt
// 작성한 댓글을 등록
fun setComment(key: String) {
// 댓글의 데이터(본문, uid, 시간)
val main = binding.commentMainArea.text.toString()
val uid = FBAuth.getUid()
val time = FBAuth.getTime()
// 키 값 하위에 데이터 넣음
FBRef.commentRef
.child(key)
.push()
.setValue(CommentModel(main, uid, time))
// 등록 확인 메시지 띄움
Toast.makeText(this, "댓글이 등록되었습니다", Toast.LENGTH_SHORT).show()
// 댓글 입력란 비움
binding.commentMainArea.text = null
// 댓글 헤더에 +1 반영하기 위해 글읽기 액티비티 종료
finish()
// 각각 액티비티 실행시 화면 전환 효과 무시, 액티비티를 새 태스크에서 시작, 태스크의 맨 위에 있는 다른 모든 액티비티 제거
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION and Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
// 댓글 헤더에 +1 반영하기 위해 글읽기 액티비티 재시작
startActivity(intent)
// 검정 화면으로 변하는 현상
}
이제 댓글을 등록할 때는 댓글 개수가 제대로 카운트 되고
수정/삭제할 때도 알맞은 댓글을 불러옵니다.
하지만 댓글 수정/삭제시의 댓글 개수는 여전히 누적으로 카운트 되며
댓글 등록/수정/삭제시 랜덤하게 블랙 스크린이 뜨며 앱이 튕길 때가 있습니다.
이걸 어떻게 고쳐야 할지 구글링해봐도 감이 잘 오지 않습니다.
CommentEditActivity.kt
// 댓글을 삭제
private fun deleteCommentData(key: String, commentKey: String) {
// 댓글 삭제
FBRef.commentRef.child(key).child(commentKey).removeValue()
// 삭제 확인 메시지
Toast.makeText(this, "댓글이 삭제되었습니다", Toast.LENGTH_SHORT).show()
// 댓글수정 액티비티 종료
finish()
// 기존 댓글은 삭제 오류 없음
// 방금 단 댓글 삭제하면 게시판 메인화면으로 튕김
// -> BoardReadActivity로 돌아가야 함
// 댓글 개수 계속 누적으로 올라감
}
// 댓글을 수정
private fun editCommentData(key: String, commentKey: String) {
// 수정한 값으로 업데이트
FBRef.commentRef.child(key).child(commentKey).setValue(CommentModel(
// 제목 및 본문은 직접 수정한 내용으로,
binding.commentMainArea.text.toString(),
// uid와 시간은 자동 설정됨
FBAuth.getUid(),
FBAuth.getTime()
))
// 수정 확인 메시지
Toast.makeText(this, "댓글이 수정되었습니다", Toast.LENGTH_SHORT).show()
// 댓글수정 액티비티 종료
finish()
// 기존 댓글은 수정 오류 없음
// 방금 단 댓글 수정하면 게시판 메인화면으로 튕김
// -> BoardReadActivity로 돌아가야 함
// 댓글 개수 계속 누적으로 올라감
}
현재 문제점들을 요약하자면 아래와 같은데 어떻게 해결해야 할 지 방향을 잡아주실 수 있으실까요? 전체 코드는 깃허브에 올려 놓았습니다. https://github.com/shinyelee/my-solo-life
댓글 작성 후 글읽기 액티비티가 재시작할 때 페이지가 로딩되는 동안 블랙 스크린이 뜨는 현상
댓글 등록/수정/삭제시 간헐적으로 튕기는 현상
댓글 수정/삭제 후 댓글 개수가 제대로 반영되지 않는 현상(누적으로 업데이트됨)
https://github.com/shinyelee/my-solo-life
3번 문제 관련해 BoardReadActivity.kt 코드입니다.