Problemas para escribir la memoria interna de Android

void launchImageCapture(Activity context) { Uri imageFileUri = context.getContentResolver() .insert(Media.INTERNAL_CONTENT_URI, new ContentValues()); m_queue.add(imageFileUri); Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri); context.startActivityForResult(i, ImportActivity.CAMERA_REQUEST); } 

El código anterior, que siempre ha funcionado, ahora está generando esta excepción para mí en insert ().

 java.lang.UnsupportedOperationException: Writing to internal storage is not supported. at com.android.providers.media.MediaProvider.generateFileName(MediaProvider.java:2336) at com.android.providers.media.MediaProvider.ensureFile(MediaProvider.java:1851) at com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:2006) at com.android.providers.media.MediaProvider.insert(MediaProvider.java:1974) at android.content.ContentProvider$Transport.insert(ContentProvider.java:150) at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:140) at android.os.Binder.execTransact(Binder.java:287) at dalvik.system.NativeStart.run(Native Method) 

No es un problema de espacio, y lo único que cambié fue el paquete de una clase no relacionada todos juntos. Además, reinicié mi teléfono.

Enfrentando el mismo problema aquí, estaba feliz de encontrar este hilo. A pesar de que dos cosas me estaban molestando en esta solución, este mensaje me hizo mirar en la dirección correcta. Me gustaría compartir mi propia solución / solución.

Permítanme empezar diciendo lo que no me veía vivir con.

En primer lugar, no quería dejar el archivo privado de la aplicación como MODE_WORLD_WRITEABLE. Esto parece no sentido para mí, aunque no puedo saber exactamente cómo otra aplicación podría acceder a este archivo a menos que sepa dónde buscarlo con el nombre completo y la ruta. No estoy diciendo que es necesariamente malo para su escenario, pero todavía me está molestando de alguna manera. Preferiría cubrir todas mis bases al tener archivos de imagen realmente privados para mi aplicación. En mi caso de negocios, las imágenes no son de ningún uso fuera de la aplicación y de ninguna manera deben ser eliminables a través de, por ejemplo, la Galería de Android. Mi aplicación activará la limpieza en un momento apropiado para no vampirizar el espacio de almacenamiento del dispositivo Droid.

En segundo lugar, openFileOutput () no deja ninguna opción excepto guardar el archivo resultante en la raíz de getFilesDir () . ¿Qué pasa si necesito alguna estructura de directorio para mantener las cosas en orden? Además, mi aplicación debe manejar más de una imagen, así que me gustaría tener el nombre de archivo generado para poder referirme a él más adelante.

Ver, es fácil capturar una foto con la cámara y guardarla en el área de imagen pública (a través de MediaStore) en el dispositivo Droid. También es fácil manipular (consultar, actualizar, eliminar) los medios de comunicación de MediaStore. Curiosamente, la inserción de la imagen de la cámara en MediaStore genera un nombre de archivo que parece único. También es fácil crear archivos privados para una aplicación con una estructura de directorios. El problema es que no puedes hacerlo directamente porque Android evita que ContentResolver utilice Media.INTERNAL_CONTENT_URI, y porque los archivos de aplicaciones privadas no son accesibles por definición (fuera) Actividad de la cámara.

Por último, adopte la siguiente estrategia:

  1. Inicie la actividad Cámara para obtener el resultado de mi aplicación con Intent to capture image.
  2. Cuando regrese a mi aplicación, inserte la captura en el MediaStore.
  3. Consulta el MediaStore para obtener el nombre del archivo de imagen generado.
  4. Cree un archivo realmente interno en cualquier ruta relativa a la carpeta de datos de aplicación privada utilizando Context.getDir () .
  5. Utilice un OutputStream para escribir datos de mapa de bits en este archivo privado.
  6. Eliminar captura de MediaStore.
  7. ( Opcional ) muestran un ImageView de la captura en mi aplicación.

Aquí está el código que inicia la leva:

 public void onClick (View v) { ContentValues values = new ContentValues (); values.put (Media.IS_PRIVATE, 1); values.put (Media.TITLE, "Xenios Mobile Private Image"); values.put (Media.DESCRIPTION, "Classification Picture taken via Xenios Mobile."); Uri picUri = getActivity ().getContentResolver ().insert (Media.EXTERNAL_CONTENT_URI, values); //Keep a reference in app for now, we might need it later. ((XeniosMob) getActivity ().getApplication ()).setCamPicUri (picUri); Intent takePicture = new Intent (MediaStore.ACTION_IMAGE_CAPTURE); //May or may not be populated depending on devices. takePicture.putExtra (MediaStore.EXTRA_OUTPUT, picUri); getActivity ().startActivityForResult (takePicture, R.id.action_camera_start); } 

Y aquí está mi actividad obteniendo resultado de la leva:

 @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { super.onActivityResult (requestCode, resultCode, data); if (requestCode == R.id.action_camera_start) { if (resultCode == RESULT_OK) { Bitmap pic = null; Uri picUri = null; //Some Droid devices (as mine: Acer 500 tablet) leave data Intent null. if (data == null) { picUri = ((XeniosMob) getApplication ()).getCamPicUri (); } else { Bundle extras = data.getExtras (); picUri = (Uri) extras.get (MediaStore.EXTRA_OUTPUT); } try { pic = Media.getBitmap (getContentResolver (), picUri); } catch (FileNotFoundException ex) { Logger.getLogger (getClass ().getName ()).log (Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger (getClass ().getName ()).log (Level.SEVERE, null, ex); } //Getting (creating it if necessary) a private directory named app_Pictures //Using MODE_PRIVATE seems to prefix the directory name provided with "app_". File dir = getDir (Environment.DIRECTORY_PICTURES, Context.MODE_PRIVATE); //Query the MediaStore to retrieve generated filename for the capture. Cursor query = getContentResolver ().query ( picUri, new String [] { Media.DISPLAY_NAME, Media.TITLE }, null, null, null ); boolean gotOne = query.moveToFirst (); File internalFile = null; if (gotOne) { String dn = query.getString (query.getColumnIndexOrThrow (Media.DISPLAY_NAME)); String title = query.getString (query.getColumnIndexOrThrow (Media.TITLE)); query.close (); //Generated name is a ".jpg" on my device (tablet Acer 500). //I prefer to work with ".png". internalFile = new File (dir, dn.subSequence (0, dn.lastIndexOf (".")).toString () + ".png"); internalFile.setReadable (true); internalFile.setWritable (true); internalFile.setExecutable (true); try { internalFile.createNewFile (); //Use an output stream to write picture data to internal file. FileOutputStream fos = new FileOutputStream (internalFile); BufferedOutputStream bos = new BufferedOutputStream (fos); //Use lossless compression. pic.compress (Bitmap.CompressFormat.PNG, 100, bos); bos.flush (); bos.close (); } catch (FileNotFoundException ex) { Logger.getLogger (EvaluationActivity.class.getName()).log (Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger (EvaluationActivity.class.getName()).log (Level.SEVERE, null, ex); } } //Update picture Uri to that of internal file. ((XeniosMob) getApplication ()).setCamPicUri (Uri.fromFile (internalFile)); //Don't keep capture in public storage space (no Android Gallery use) int delete = getContentResolver ().delete (picUri, null, null); //rather just keep Uri references here //visit.add (pic); //Show the picture in app! ViewGroup photoLayout = (ViewGroup) findViewById (R.id.layout_photo_area); ImageView iv = new ImageView (photoLayout.getContext ()); iv.setImageBitmap (pic); photoLayout.addView (iv, 120, 120); } else if (resultCode == RESULT_CANCELED) { Toast toast = Toast.makeText (this, "Picture capture has been cancelled.", Toast.LENGTH_LONG); toast.show (); } } } 

Voila! Ahora tenemos un verdadero archivo de imagen privada de aplicación, cuyo nombre ha sido generado por el dispositivo Droid. Y no se guarda nada en el área de almacenamiento público, evitando así la manipulación accidental de imágenes.

Aquí está mi código de trabajo para guardar una imagen capturada de la cámara a la aplicación de almacenamiento interno:

Primero, cree el archivo con el nombre de archivo deseado. En este caso es "MyFile.jpg", a continuación, iniciar la actividad con la intención de abajo. Usted es el método de devolución de llamada ( onActivityResult ), se llamará una vez completado. Después de que OnActivityResult haya sido llamado, su imagen debe guardarse en el almacenamiento interno. Nota clave: el modo utilizado en openFileOutput necesita ser global .. Context.MODE_WORLD_WRITEABLE funciona bien, no he probado otros modos.

 try { FileOutputStream fos = openFileOutput("MyFile.jpg", Context.MODE_WORLD_WRITEABLE); fos.close(); File f = new File(getFilesDir() + File.separator + "MyFile.jpg"); startActivityForResult( new Intent(MediaStore.ACTION_IMAGE_CAPTURE) .putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f)) , IMAGE_CAPTURE_REQUEST_CODE); } catch(IOException e) { } 

Y en el método del resultado de la actividad:

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == IMAGE_CAPTURE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { Log.i(TAG, "Image is saved."); } } 

Para recuperar su imagen:

 try { InputStream is = openFileInput("MyFile.jpg"); BitmapFactory.Options options = new BitmapFactory.Options(); //options.inSampleSize = 4; Bitmap retrievedBitmap = BitmapFactory.decodeStream(is, null, options); } catch(IOException e) { } 

Al parecer, la cámara no admite la escritura en el almacenamiento interno.

Lamentablemente esto no se menciona en la documentación.

MediaProvider.java tiene el código siguiente:

 private String generateFileName(boolean internal, String preferredExtension, String directoryName) { // create a random file String name = String.valueOf(System.currentTimeMillis()); if (internal) { throw new UnsupportedOperationException( "Writing to internal storage is not supported."); // return Environment.getDataDirectory() // + "/" + directoryName + "/" + name + preferredExtension; } else { return Environment.getExternalStorageDirectory() + "/" + directoryName + "/" + name + preferredExtension; } } 

Por lo tanto, escribir en el almacenamiento interno ha sido intencionalmente inhabilitado por el momento.

Editar – Creo que puede usar el método binnyb como un trabajo alrededor, pero no lo recomendaría; No estoy seguro si esto continuará funcionando en versiones futuras. Creo que la intención es desautorizar la escritura en el almacenamiento interno de los archivos multimedia.

Archivé un error en el rastreador de problemas de Android.

Editar – ahora entiendo por qué funciona el método binnyb. La aplicación de cámara se considera que es sólo otra aplicación. No puede escribir en el almacenamiento interno si no tiene permisos. Configurar el archivo para que se pueda escribir en el mundo le da permiso a otras aplicaciones para escribir en ese archivo.

Todavía no creo que sea una buena idea, sin embargo, por algunas razones:

  • Por lo general, no desea que otras aplicaciones escriban en su almacenamiento privado.
  • El almacenamiento interno es bastante limitado en algunos teléfonos, y las imágenes de cámaras en bruto son bastante grandes.
  • Si planea cambiar el tamaño de la imagen de todos modos, puede leerlo desde un almacenamiento externo y escribirlo usted mismo en su almacenamiento interno.
  • Captura todas las excepciones de tipo de programación de Android
  • Excepción extraña: no se puede emitir cadena a booleano cuando se utiliza getBoolean
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.