Почему я получаю это исключение NullPointerException при попытке назначить изображения моему GridView? - PullRequest
0 голосов
/ 13 июля 2020

У меня есть GridView, к которому применяются разные изображения, связанные с экземплярами объекта. Я использую объект RealPathUtil для обработки получения путей к файлам для декодирования и отображения растровых изображений.

Когда я открываю действие, в котором эти изображения назначены сетке, мое приложение автоматически вылетает и показывает исключение NullPointerException, направленное в строке, где объявлено это предложение if:

else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
       // Return the remote address
       return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
}

Я знаком с исключениями NullPointerExceptions и с тем, почему они обычно выбрасываются, но я действительно не понимаю, почему это одно. Ни одно из изображений, которые я пытаюсь назначить, даже не отображается должным образом, так как я переключил метод отображения изображений с URI на Bitmap. Когда я назначал изображения ImageViews с помощью URI, эта проблема не возникала, но мне пришлось переключиться, потому что это вызывало проблемы на других устройствах.

Вот код от адаптера, который обрабатывает GridView, это вызывает эту ошибку. Ошибка возникает, когда значение tempUri установлено ближе к концу:

override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view: View = View.inflate(activity,R.layout.layout_adapter,null)

        // Get view data from UI elements - image, name, and count
        val tv_lang = view.findViewById(R.id.itemName) as TextView
        val imageView = view.findViewById<ImageView>(R.id.itemImage)
        val itemCount = view.findViewById(R.id.itemCount) as TextView

        // Assign data to UI elements
        tv_lang.text = itemList[position].itemNote
        itemCount.text = itemList[position].itemCount.toString()
        val itemImage = itemList[position].itemImage


        // Assign each item image to corresponding grid ImageView
        tempUri = Uri.parse(itemImage)
        realPathUri = RealPathUtil.getRealPath(parent!!.context, tempUri).toString()
        val myBitmap = BitmapFactory.decodeFile(realPathUri)
        imageView.setImageBitmap(myBitmap)

        return view
}

RealPathUtil

    object RealPathUtil {
        // SDK <= 11 && SDK < 19
        /* Calls either getRealPathFromURIAPI11to19 or getRealPathFromURIAPI19, depending upon which API the software detects the user running the app through. */
        @SuppressLint("ObsoleteSdkInt")
        fun getRealPath(context: Context, fileUri: Uri): String? {
            return if (Build.VERSION.SDK_INT < 19) {
                getRealPathFromURIAPI11to18(context, fileUri)
            } else {
                getRealPathFromURIAPI19(context, fileUri)
            }
        }

        /* Uses other methods within the RealPathUtil object to determine
        * location list file path based upon the user's phone's active API (11-18). */
        @SuppressLint("NewApi")
        fun getRealPathFromURIAPI11to18(context: Context, contentUri: Uri): String? {
            val project = arrayOf(MediaStore.Images.Media.DATA)
            var result: String? = ""

            val cursorLoader = CursorLoader(context, contentUri, project, null, null, null)
            val cursor = cursorLoader.loadInBackground()

            if (cursor != null) {
                val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
                cursor.moveToFirst()
                result = cursor.getString(columnIndex)
                cursor.close()
            }
            return result
        }

        /* Uses other methods within the RealPathUtil object to determine location list file
        path based upon the user's phone's active API (19). */
        @SuppressLint("NewApi")
        fun getRealPathFromURIAPI19(context: Context, uri: Uri): String? {
            val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT

            // Create the DocumentProvider
            if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
                // Safe cast the ExternalStorageProvider
                if (isExternalStorageDocument(uri)) {
                    val docId = DocumentsContract.getDocumentId(uri)
                    val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                    val type = split[0]

                    if ("primary".equals(type, ignoreCase = true)) {
                        return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
                    }
                } else if (isDownloadsDocument(uri)) {
                    var cursor: Cursor? = null

                    try {
                        cursor = context.contentResolver.query(uri, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null, null, null)
                        cursor!!.moveToNext()

                        val fileName = cursor.getString(0)
                        val path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName

                        if (!TextUtils.isEmpty(path)) {
                            return path
                        }
                    } finally {
                        cursor?.close()
                    }

                    val id = DocumentsContract.getDocumentId(uri)

                    if (id.startsWith("raw:")) {
                        return id.replaceFirst("raw:".toRegex(), "")
                    }
                    val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads"), java.lang.Long.valueOf(id))

                    return getDataColumn(context, contentUri, null, null)
                } else if (isMediaDocument(uri)) {
                    val docId = DocumentsContract.getDocumentId(uri)
                    val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                    val type = split[0]

                    var contentUri: Uri? = null

                    when (type) {
                        "image" -> contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                        "video" -> contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                        "audio" -> contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                    }

                    val selection = "_id=?"
                    val selectionArgs = arrayOf(split[1])

                    return getDataColumn(context, contentUri, selection, selectionArgs)
                } // Media Provider
                // Downloads Provider
            } else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
                // Return the remote address
                return if (isGooglePhotosUri(uri))
                    uri.lastPathSegment
                else
                    getDataColumn(context, uri, null, null) /** Collapse this if-else block into one line if any new, unexplained I/O issues arise. */
            } else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
                return uri.path
            } // File
            // MediaStore

            return null
        }

        private fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array<String>?): String? {
            var cursor: Cursor? = null
            val column = "_data"
            val projection = arrayOf(column)

            try {
                cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)

                if (cursor != null && cursor.moveToFirst()) {
                    val index = cursor.getColumnIndexOrThrow(column)
                    return cursor.getString(index)
                }
            } finally {
                cursor?.close()
            }
            return null
        }

        /* Takes the URI being analyzed and determines whether the URI authority is ExternalFileProvider or not. */
        private fun isExternalStorageDocument(uri: Uri): Boolean {
            return "com.android.externalstorage.documents" == uri.authority
        }

        /* Takes the URI being analyzed and determines whether the URI authority is DownloadsProvider or not. */
        private fun isDownloadsDocument(uri: Uri): Boolean {
            return "com.android.providers.downloads.documents" == uri.authority
        }

        /* Takes the URI being analyzed and determines whether the URI authority is MediaProvider or not. */
        private fun isMediaDocument(uri: Uri): Boolean {
            return "com.android.providers.media.documents" == uri.authority
        }

        /* Takes the URI being analyzed and determines whether the URI authority is Google Photos or not. */
        private fun isGooglePhotosUri(uri: Uri): Boolean {
            return "com.google.android.apps.photos.content" == uri.authority
        }
    }

Я понимаю, что это связано с тем, как путь извлекается, но я действительно не знаю как. Я давно использую этот объект и никогда не сталкивался с подобной проблемой, поэтому я действительно не знаю, с чего начать отладку. Я не прошу решения моей проблемы, но если бы какой-нибудь проницательный человек с большим опытом, чем я, мог бы сказать мне, откуда эта проблема, я мог бы разобраться. Еще раз спасибо!

Ответы [ 2 ]

1 голос
/ 13 июля 2020

Предположим, что предложение if, о котором вы говорите, является следующим:

else if ("content".equals(uri.scheme!!, ignoreCase = true)) {

Очевидное место для исключения NullPointerException здесь uri.scheme!!. Оператор двойного взрыва используется для утверждения , что переменная с типом, допускающим значение NULL, на самом деле не является null, и для получения ее значения. Это иногда полезно, когда мы, люди, знаем больше, чем компилятор ... но люди также могут ошибаться!

Если uri.scheme имеет значение null, то uri.scheme!! выдаст ошибку NullPointerException.

Как решить эту проблему, зависит от остальной части вашей программы. Возможно, вы не ожидаете, что uri.scheme когда-либо будет нулевым, и в этом случае вам нужно будет выяснить более глубокую причину root. Но если вы делаете ожидаете, что uri.scheme может иногда быть нулевым, вы можете переписать свое if условие:

else if (uri.scheme?.equals("content", ignoreCase = true) == true) {
0 голосов
/ 16 июля 2020

Вам не нужно проверять Null при использовании String.equals. проверьте код

public actual fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean { 
if (this === null) 
    return other === null
return if (!ignoreCase) (this as java.lang.String).equals(other) 
       else (this as java.lang.String).equalsIgnoreCase(other) 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...