우물안에서 보는 하늘도 맑다

힐링 #18 - 오틸라의 안드로이드 앱 만들기 강좌 본문

경단녀에서 "나"로 돌아가기/안드로이드

힐링 #18 - 오틸라의 안드로이드 앱 만들기 강좌

몽염이 2020. 12. 22. 22:44
반응형

* 이벤트를 안 받았을 때, 글쓰기 버튼이 안보이는 문제 발생

- 어떤 현상인지 확인은 못해봄. 나중에 확인 해 볼 것~!!!

 

* Firebase에서 board에 데이타를 추가 할 때, 키값으로 데이타를 입력함

- setValue 로 데이타를 넣으면서 자동으로 지정된 키값 말고

날짜와 시간으로 가공한 idx 값을 이용하여 데이타를 입력 해서, 검색하고 reply를 count 할 수 있게 수정 함

더불어, "0" String으로 지정된, reply와 count와 heart를 0 int 형으로 변경함

firebase와 code상에도 모두 변경함

- 기존에 키값으로 자동으로 입력된 상태

 

 

- 변경 후, idx로 입력된 상태

 

 

 

* 댓글을 입력하면 댓글 수가 표시되고, Firebase에 reply 컬럼이 변경됨

 

댓글 수가 2로 표기됨
입력한 댓글

 

* 오류1

- MainAdapter.java 에서
iviewholder.itemReply.setText(idata.reply);
iviewholder.itemCount.setText(idata.count);

iviewholder.itemReply.setText(String.valueOf(idata.reply));
iviewholder.itemCount.setText(String.valueOf(idata.count)); 
변경함

   에러메세지
  D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bliss.csc.agmhealing, PID: 11129
android.content.res.Resources$NotFoundException: String resource ID #0x0
at android.content.res.Resources.getText(Resources.java:444)
at android.widget.TextView.setText(TextView.java:6412)
at com.bliss.csc.agmhealing.Adapter.MainAdapter.onBindViewHolder(MainAdapter.java:117)

 

* 오류2

- 첫번째 글과 두번째 글 사이에 이미지가 없을 때, 글 아래 댓글 등 기능이 있는 부분이 나타나지 않음

- 이미지 영역이 너무 확장 되어 기능 버튼이 보이지 않거나

 

 

- 이미지가 없는 글에 경우, 기능 버튼 아래에 불필요한 하얀 영역이 보임

 

 

- CardView에 높이layout_height를 match_parent 에서 wrap_content로 변경 한 후, 아래 댓글 기능 부분이 잘 보임

 

 

 

 * 스토리지에 저장된 이미지가 보이지 않음

  - 원인 : Storage에 저장된  이미지Url과 이벤트 Task에서 받아온 Url이 다름

  Image URL  
Storage 저장된 Image URL firebasestorage.googleapis.com/v0/b/agmhealing3.appspot.com/o/board%2F20201226_113859_0.jpg?alt=media&token=72c525e0-01f0-48f5-89fa-fbb104281328 이미지 보임
addOnSuccessListener이벤트 내에서 downloadUrlTask.getResult()로 받아온 Image URL firebasestorage.googleapis.com/v0/b/agmhealing3.appspot.com/o?name=board%2F20201004_144804_1.jpg&uploadType=resumable&upload_id=ABg5-UxO7PiemtnQeB7dvxlNIXrOW4En5ELHFibbQLedqkosQ56dzeoNOGPwyqBlfRZpH0r0K-NinVFK7xkWUCnHdaJdLSqcEA&upload_protocol=resumable 잘못된 경로 이미지 안보임

- 해결방법

Firebase 에 Storage 개발자문서(아래)를 참고하여 URL을 받아 올 수 있게 수정하고

실제 실행 했을 때, Storage에 이미지가 정상적으로 저장되어도 task.isSuccessful()이 false로 되어 

이미지URL을 받아오지 않는 문제가 추가로 발생 했다

그래서 이미지 저장 성공여부를 task.isSuccessful()로 확인하지 않고

task.getResult()로 받아온 URL에 정보가 있는지 없는지 여부를 확인하여 처리 했더니 정상적으로 동작하고

URL도 잘 받아와졌다

- 참고 : firebase.google.com/docs/storage/android/upload-files?authuser=0

다운URL을 받아오는 방법 : Firebase Storage 문서

- 수정 전 소스

  WriteActivity.java 중
 
final ItemData idata = new ItemData();

String image_time = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());


idata.idx = image_time;
idata.reg_user = ap.getNickname();
idata.profile = ap.getProfileImage();
idata.title = etTitle.getText().toString();
idata.summary = etContent.getText().toString();

//idata.image = "";

idata.heart = 0;
idata.reply = 0;
idata.more = "0";
idata.count = 0;
idata.reg_date = new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date());

if(mList.size() > 0){


int imageCount = 0;

//이미지를 업로드를 할 경우
for(ContentData cdata : mList){

ByteArrayOutputStream baos = new ByteArrayOutputStream();
cdata.Bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] data = baos.toByteArray();

mountainsRef = storageRef.child("board/"+image_time+"_"+imageCount+".jpg");

UploadTask uploadTask = mountainsRef.putBytes(data);


//async 비동기
uploadTask.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {

idata.image += "none,";
}
}).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {


Task<Uri> downloadUrlTask = storageRef.getDownloadUrl();//getDownloadUrl
Uri downUri = downloadUrlTask.getResult();


idata.image += downUri.toString()+",";


doInsert(idata, true);


}


});


imageCount++;

}//for end

 

- 수정 후 소스

  WriteActivity.java 중
 
final ItemData idata = new ItemData();

String image_time = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());


idata.idx = image_time;
idata.reg_user = ap.getNickname();
idata.profile = ap.getProfileImage();
idata.title = etTitle.getText().toString();
idata.summary = etContent.getText().toString();

//idata.image = "";

idata.heart = 0;
idata.reply = 0;
idata.more = "0";
idata.count = 0;
idata.reg_date = new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(new Date());

if(mList.size() > 0){


int imageCount = 0;

//이미지를 업로드를 할 경우
for (ContentData cdata : mList) {

ByteArrayOutputStream baos = new ByteArrayOutputStream();
cdata.Bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] data = baos.toByteArray();

mountainsRef = storageRef.child("board/" + image_time + "_" + imageCount + ".jpg");


final StorageReference finalMountainsRef = mountainsRef;

Task<Uri> uploadTask = mountainsRef.putBytes(data).continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
@Override
public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {

if (!task.isSuccessful()) {
throw task.getException();
}

return finalMountainsRef.getDownloadUrl();

}
}).addOnCompleteListener(new OnCompleteListener<Uri>() {
@Override
public void onComplete(@NonNull Task<Uri> task) {

Uri downloadUri = task.getResult();


//Toast.makeText(WriteActivity.this, "succesful : 이미지 첨부 파일 있음" + downloadUri.toString(), Toast.LENGTH_SHORT).show();

Log.d("WriteActivity : ","succesful : 이미지 첨부 파일 있음 : downloadUri.toString() = "+downloadUri.toString());

if(downloadUri != null){

idata.image += downloadUri.toString() + ",";
doInsert(idata, true);

}else{

idata.image += "none,";
doInsert(idata, false);

}


}
});


imageCount++;

}//for end

- 수정 중 발생한 오류 메세지

.FileNotFoundException 이 발생함

  오류메세지
  Load failed for none with size [378x1750]
class com.bumptech.glide.load.engine.GlideException: Failed to load resource
There were 3 causes:
java.io.FileNotFoundException(/none: open failed: ENOENT (No such file or directory))
java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
call GlideException#logRootCauses(String) for more detail
Cause (1 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class java.io.InputStream, LOCAL
There was 1 cause:
java.io.FileNotFoundException(/none: open failed: ENOENT (No such file or directory))
call GlideException#logRootCauses(String) for more detail
Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Fetch failed
There was 1 cause:
java.io.FileNotFoundException(/none: open failed: ENOENT (No such file or directory))
call GlideException#logRootCauses(String) for more detail
Cause (1 of 1): class java.io.FileNotFoundException: /none: open failed: ENOENT (No such file or directory)
Cause (2 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class android.os.ParcelFileDescriptor, LOCAL
There was 1 cause:
java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
call GlideException#logRootCauses(String) for more detail
Cause (1 of 1): class com.bumptech.glide.load.engine.GlideException: Fetch failed
There was 1 cause:
java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
call GlideException#logRootCauses(String) for more detail
Cause (1 of 1): class java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
Cause (3 of 3): class com.bumptech.glide.load.engine.GlideException: Fetching data failed, class android.content.res.AssetFileDescriptor, LOCAL
There was 1 cause:
java.io.FileNotFoundException(open failed: ENOENT (No such file or directory))
call GlideException#logRootCauses(String) for more detail
Cause (1 of 1): class java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
2021-03-23 13:19:43.952 7911-7911/com.bliss.csc.agmhealing I/Glide: Root cause (1 of 3)
java.io.FileNotFoundException: /none: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:492)
at java.io.FileInputStream.<init>(FileInputStream.java:160)
at java.io.FileInputStream.<init>(FileInputStream.java:115)
at android.content.ContentResolver.openInputStream(ContentResolver.java:1473)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResourceFromUri(StreamLocalUriFetcher.java:74)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:50)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:13)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:279)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:234)
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)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
at libcore.io.Linux.open(Native Method)
at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:254)
at libcore.io.ForwardingOs.open(ForwardingOs.java:166)
at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7542)
at libcore.io.IoBridge.open(IoBridge.java:478)
at java.io.FileInputStream.<init>(FileInputStream.java:160) 
at java.io.FileInputStream.<init>(FileInputStream.java:115) 
at android.content.ContentResolver.openInputStream(ContentResolver.java:1473) 
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResourceFromUri(StreamLocalUriFetcher.java:74) 
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:50) 
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:13) 
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44) 
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100) 
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70) 
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63) 
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310) 
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:279) 
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:234) 
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) 
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393) 
2021-03-23 13:19:43.956 7911-7911/com.bliss.csc.agmhealing I/Glide: Root cause (2 of 3)
java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:344)
at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:231)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1793)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1714)
at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:20)
at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:12)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310)
at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherFailed(DecodeJob.java:408)
at com.bumptech.glide.load.engine.SourceGenerator.onLoadFailedInternal(SourceGenerator.java:160)
at com.bumptech.glide.load.engine.SourceGenerator$1.onLoadFailed(SourceGenerator.java:83)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:167)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:154)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:49)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:279)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:234)
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)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
2021-03-23 13:19:43.998 7911-7911/com.bliss.csc.agmhealing I/Glide: Root cause (3 of 3)
java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
at android.os.ParcelFileDescriptor.openInternal(ParcelFileDescriptor.java:344)
at android.os.ParcelFileDescriptor.open(ParcelFileDescriptor.java:231)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1793)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1714)
at com.bumptech.glide.load.data.AssetFileDescriptorLocalUriFetcher.loadResource(AssetFileDescriptorLocalUriFetcher.java:20)
at com.bumptech.glide.load.data.AssetFileDescriptorLocalUriFetcher.loadResource(AssetFileDescriptorLocalUriFetcher.java:11)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310)
at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherFailed(DecodeJob.java:408)
at com.bumptech.glide.load.engine.SourceGenerator.onLoadFailedInternal(SourceGenerator.java:160)
at com.bumptech.glide.load.engine.SourceGenerator$1.onLoadFailed(SourceGenerator.java:83)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:167)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:154)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:49)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310)
at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherFailed(DecodeJob.java:408)
at com.bumptech.glide.load.engine.SourceGenerator.onLoadFailedInternal(SourceGenerator.java:160)
at com.bumptech.glide.load.engine.SourceGenerator$1.onLoadFailed(SourceGenerator.java:83)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.startNextOrFail(MultiModelLoader.java:167)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.onLoadFailed(MultiModelLoader.java:154)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:49)
at com.bumptech.glide.load.model.MultiModelLoader$MultiFetcher.loadData(MultiModelLoader.java:100)
at com.bumptech.glide.load.engine.SourceGenerator.startNextLoad(SourceGenerator.java:70)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:63)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:310)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:279)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:234)
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)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)

java.io.IOException: Invalid device key response.

at huk.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):46)
at hui.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):41)
at hud.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):0)
at hug.a(:com.google.android.gms@201817022@20.18.17 (040700-311416286):10)
at fzn.call(:com.google.android.gms@201817022@20.18.17 (040700-311416286):5)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at sji.b(:com.google.android.gms@201817022@20.18.17 (040700-311416286):12)
at sji.run(:com.google.android.gms@201817022@20.18.17 (040700-311416286):7)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at spj.run(:com.google.android.gms@201817022@20.18.17 (040700-311416286):0)
at java.lang.Thread.run(Thread.java:923)

 

* 오틸라의 안드로이드 앱 만들기 강좌 : 힐링 # 18

-youtu.be/18bFgiOUyKI

 

* 다음은 삭제 기능 고고고~!

하지만!! 위에 기능버튼(댓글수 표시, 하트수 표시 등)들이 안보이는 오류 수정하고 19강으로 갈것!!

 

 

* Festina lente 님 블로그 글 참조~! 

- Cloud Firestore 와 Realtime database에 차이를 깔끔하게 정리 해 놓으셨음

wild-learning.tistory.com/37

 

[Firebase][Cloud Firestore] 이미지 데이터 모델 만들어 저장하기

데이터 모델 만들기 - 앞서 연습하는 예시에서 이미지를 업로드 하는 기능을 만들었는데, 여기서 업로드하는 이미지에 대해서 효율적으로 데이터 관리를 하기 위해서 Cloud Firestore를 사용할 것임

wild-learning.tistory.com

반응형