[Android / Troubleshooting] class com.bumptech.glide.load.engine.GlideException: Failed to load resource There was 1 root cause: java.lang.SecurityException(Permission Denial: opening provider

Subeen·2024년 1월 21일
1

Troubleshooting

목록 보기
1/1

주소록 팀과제를 진행하며 제일 애를 먹었던 부분에 대해 트러블슈팅 경험을 정리해보겠다.
이번에 구현한 앱의 경우 갤러리에서 이미지를 불러와 이미지의 URI 재사용하는 부분이 있었는데, 처음에 갤러리에서 이미지를 불러와서 뷰에 세팅하고 이미지 URI를 저장하는 것까지는 문제가 없었다.
리스트에 이미지의 URI를 저장하여 해당 URI를 다른 화면에서 재사용하려고 하니 이미지뷰에 이미지가 뜨지 않고 아래와 같은 에러가 발생하는 것이다.
처음에는 코드 문제인줄 알고 코드를 계속 수정해봤지만 문제는 해결 되지 않았다. 해결법을 찾지 못해 튜터님께 여쭤보니 보통 이미지를 불러오면 데이터베이스에 저장하고 데이터베이스에서 가져와서 사용하는 방식으로 진행하는게 맞는데, 우리의 경우 아직 데이터 베이스 사용을 안하고 앱 내에서 URI를 저장해서 재사용하다보니 이러한 문제가 발생하는 것 같다고 하셨고, 구글에서 처음 URI를 가지고 오는 것은 허용하지만 그 다음부터는 경로를 제어하지 못해 막아놓는 것 같다고 하셨다.
그래서 해결 방법을 제시해 주셨는데, URI를 저장해서 재사용하지 말고 디바이스에 있는 파일을 직접 경로를 가지고 온다던가 URI를 가지고 패스를 알 수 있는데 패스를 따서 저장하는 방식으로 수정해보라고 하셔서 그렇게 진행 했더니 해당 문제를 해결할 수 있었다 ! 🎆

Load failed for content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F1000000037/ORIGINAL/NONE/image%2Fjpeg/1874459411 with size [138x138]
class com.bumptech.glide.load.engine.GlideException: Failed to load resource
There was 1 root cause:
java.lang.SecurityException(Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{8f0c4bb 19464:com.example.colorcontacts/u0a169} (pid=19464, uid=10169) that is not exported from UID 10116)
call GlideException#logRootCauses(String) for more detail
Cause (1 of 1): class java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{8f0c4bb 19464:com.example.colorcontacts/u0a169} (pid=19464, uid=10169) that is not exported from UID 10116
2024-01-19 10:17:25.559 19464-19464 Glide                   com.example.colorcontacts            I  Root cause (1 of 1)
java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.impl.MediaContentProvider from ProcessRecord{8f0c4bb 19464:com.example.colorcontacts/u0a169} (pid=19464, uid=10169) that is not exported from UID 10116
at android.os.Parcel.createExceptionOrNull(Parcel.java:3011)
at android.os.Parcel.createException(Parcel.java:2995)
at android.os.Parcel.readException(Parcel.java:2978)
at android.os.Parcel.readException(Parcel.java:2920)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5224)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:7007)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:3420)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2526)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:2011)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1842)
at android.content.ContentResolver.openInputStream(ContentResolver.java:1518)
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.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:311)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:280)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:235)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:393)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ContentProviderHelper.checkAssociationAndPermissionLocked(ContentProviderHelper.java:651)
at com.android.server.am.ContentProviderHelper.getContentProviderImpl(ContentProviderHelper.java:264)
at com.android.server.am.ContentProviderHelper.getContentProvider(ContentProviderHelper.java:140)
at com.android.server.am.ActivityManagerService.getContentProvider(ActivityManagerService.java:6470)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2395)

기존에 사용하던 방식

  • glide (build.gradle)
dependencies {
	...

    // add
    implementation("com.github.bumptech.glide:glide:4.12.0")
    annotationProcessor("com.github.bumptech.glide:compiler:4.12.0")
}
  • Glide 사용
    fun ImageView.setFavoriteTag(uri: Uri) {
        clipToOutline = true
        Glide.with(context)
            .load(uri)
            .into(this)
    }
    private fun initView() {
        // 갤러리에서 이미지를 볼러오는 결과에 대한 처리
        galleryResultLauncher = registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                val data: Intent? = result.data
                // 이미지 uri
                selectedImage = data?.data!!
                loadTagImage(selectedImage)
            }
        }
    }

    // 가져온 이미지 Uri를 Glide를 사용하여 이미지뷰에 세팅
    private fun loadTagImage(uri: Uri) {
        binding.ivRegisterTagImage.setFavoriteTag(file)
    }

    // 이미지 선택을 위해 갤러리를 불러오는 인텐트를 생성한다.
    private fun openGallery() {
        val intent =
            Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        galleryResultLauncher.launch(intent)
    }        

수정한 방식

  • coil library (build.gradle)
dependencies {
	...

    // add
    implementation("io.coil-kt:coil:1.4.0")
}
  • 이미지의 Uri를 가지고 파일 경로 찾기
object FilePath {
    fun Context.absolutelyPath(path: Uri): String? {
        val proj: Array<String> = arrayOf(MediaStore.Images.Media.DATA)
        val c: Cursor? = contentResolver.query(path, proj, null, null, null)
        val index = c?.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
        c?.moveToFirst()
        if (index == null) return null

        return c.getString(index)
    }
}
    private fun initView() {
        // 갤러리에서 이미지를 볼러오는 결과에 대한 처리
        galleryResultLauncher = registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                val data: Intent? = result.data
                // 이미지 uri
                selectedImage = data?.data!!
                // 이미지의 실제 경로를 저장하기 위한 처리
                val path = requireActivity().absolutelyPath(selectedImage!!)
                // 문자열 형식인 파일 경로를 파일 형식으로 변환 
                val file = File(path)
                loadTagImage(file)
            }
        }
    }
    
  // 이미지 경로를 가지고 coil library를 사용하여 이미지뷰에 세팅
    private fun loadTagImage(file: File) {
        binding.ivRegisterTagImage.load(file)
    }
    
    // 이미지 선택을 위해 갤러리를 불러오는 인텐트를 생성한다.
    private fun openGallery() {
        val intent =
            Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        galleryResultLauncher.launch(intent)
    }    
profile
개발 공부 기록 🌱

0개의 댓글