5
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); 
}

上記のコードは常に機能していましたが、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)

これはスペースの問題ではなく、私が変更したのは、無関係のクラスのパッケージだけでした。また、電話を再起動しました。

4

3 に答える 3

12

ここで同じ問題に直面して、私はこのスレッドを見つけてうれしかったです。この回避策では2つのことが私を悩ませていましたが、この投稿では私は正しい方向を向いていました。私自身の回避策/解決策を共有したいと思います。

私が自分と一緒に住んでいるのを見なかったことを述べることから始めましょう。

まず、アプリケーションのプライベートファイルをMODE_WORLD_WRITEABLEのままにしたくありませんでした。これは私には意味がないように見えますが、完全な名前とパスでファイルを探す場所がわからない限り、別のアプリケーションがこのファイルにアクセスする方法を正確に把握することはできません。それがあなたのシナリオにとって必ずしも悪いと言っているわけではありませんが、それでもどういうわけか私を悩ませています。自分のアプリに本当にプライベートな画像ファイルを用意して、すべての拠点をカバーしたいと思います。私のビジネスケースでは、写真はアプリケーションの外部では役に立たず、Androidギャラリーなどから削除することはできません。私のアプリは、Droidデバイスのストレージスペースを無駄にしないように、適切なタイミングでクリーンアップをトリガーします。

次に、openFileOutput()はオプションを残さず、結果のファイルをgetFilesDir()のルートに保存します。物事を整理するためにディレクトリ構造が必要な場合はどうなりますか?さらに、私のアプリケーションは複数の画像を処理する必要があるため、後で参照できるようにファイル名を生成してもらいたいと思います。

ほら、カメラで写真をキャプチャして、Droidデバイスのパブリックイメージエリア(MediaStore経由)に保存するのは簡単です。MediaStoreからメディアを操作(クエリ、更新、削除)するのも簡単です。興味深いことに、MediaStoreにカメラの画像を挿入すると、一意に見えるファイル名が生成されます。ディレクトリ構造を持つアプリケーションのプライベートファイルを作成することも簡単です。「カメラの画像をキャプチャして内部メモリに保存する」問題の核心は、AndroidがContentResolverによるMedia.INTERNAL_CONTENT_URIの使用を禁止しているため、またプライベートアプリファイルは定義上(外部)からアクセスできないため、直接保存できないことです。カメラアクティビティ。

最後に、私は次の戦略を採用しました。

  1. 画像をキャプチャするインテントを使用して、アプリからの結果のカメラアクティビティを開始します。
  2. アプリに戻ったら、MediaStoreにキャプチャを挿入します。
  3. MediaStoreにクエリを実行して、生成された画像ファイル名を取得します。
  4. Context.getDir()を使用して、プライベートアプリケーションデータフォルダーからの相対パスに真の内部ファイルを作成します。
  5. OutputStreamを使用して、ビットマップデータをこのプライベートファイルに書き込みます。
  6. MediaStoreからキャプチャを削除します。
  7. オプション)アプリでキャプチャのImageViewを表示します。

カムを開始するコードは次のとおりです。

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);
}

そして、これがカム結果を取得する私の活動です:

@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 ();
        }
    }
}

出来上がり!これで、Droidデバイスによって生成された名前の真のアプリケーションプライベート画像ファイルができました。また、公共の保管場所には何も保管されていないため、誤って画像を操作することはありません。

于 2011-10-19T15:36:11.937 に答える
5

キャプチャした画像をカメラからアプリの内部ストレージに保存するための作業コードは次のとおりです。

まず、目的のファイル名でファイルを作成します。この場合は「MyFile.jpg」で、以下のインテントでアクティビティを開始します。あなたはコールバックメソッド(onActivityResult)であり、完了すると呼び出されます。onActivityResultが呼び出された後、画像を内部ストレージに保存する必要があります。キーノート:で使用されるモードはopenFileOutputグローバルである必要があります。Context.MODE_WORLD_WRITEABLE正常に動作します。他のモードはテストしていません。

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) {

}

およびアクティビティ結果メソッド:

@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.");
    }
}

画像を取得するには:

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) {

}
于 2011-03-09T21:42:59.590 に答える
2

カメラは明らかに内部ストレージへの書き込みをサポートしていません。

残念ながら、これはドキュメントに記載されていません。

MediaProvider.javaのコードは次のとおりです。

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;
    }
}

そのため、当面の間、内部ストレージへの書き込みは意図的に無効にされています。

編集-回避策としてbinnybの方法を使用できると思いますが、お勧めしません。これが将来のバージョンで引き続き機能するかどうかはわかりません。メディアファイルの内部ストレージへの書き込みを禁止するつもりだと思います。

Androidの課題追跡システムにバグを報告しました。

編集-binnybのメソッドが機能する理由がわかりました。カメラアプリは単なる別のアプリケーションと見なされます。アクセス許可がない場合、内部ストレージに書き込むことはできません。ファイルを誰でも書き込み可能に設定すると、他のアプリケーションにそのファイルへの書き込みを許可します。

ただし、いくつかの理由から、これはあまり良い考えではないと思います。

  • 通常、他のアプリがプライベートストレージに書き込むことは望ましくありません。
  • 一部の電話では内部ストレージが非常に限られており、生のカメラ画像は非常に大きくなります。
  • とにかくイメージのサイズ変更を計画している場合は、外部ストレージからイメージを読み取り、内部ストレージに自分で書き込むことができます。
于 2011-03-09T21:20:45.813 に答える