web-dev-qa-db-ja.com

Android NDKのファイル操作

パフォーマンス上の理由から、主にCでアプリケーションを作成するためにAndroid NDKを使用していますが、fopenなどのファイル操作がAndroidで正しく機能しないようです。これらの関数を使用するたびに、アプリケーションがクラッシュします。

Android NDK?

62
RyanCheu

他の答えは正しいです。 FILEおよびfopenを使用して、NDKからファイルを開くことができますが、そのためのアクセス許可を忘れないでください。

Androidマニフェスト場所:

<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"/> 
59
Ita

File IOは、JNIを使​​用してAndroidで正常に動作します。おそらく、不正なパスでファイルを開こうとして、戻りコードをチェックしていませんか? -jniの例は、実際にファイルを開いて書き込むことが可能であることを示しています。

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.Apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <string.h>
#include <jni.h>
#include <stdio.h>

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.Java
 */
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                              jobject thiz )
{
    FILE* file = fopen("/sdcard/hello.txt","w+");

    if (file != NULL)
    {
        fputs("HELLO WORLD!\n", file);
        fflush(file);
        fclose(file);
    }

    return (*env)->NewStringUTF(env, "Hello from JNI (with file io)!");
}

携帯電話で実行した後の結果は次のとおりです(SDカードを使用)。

$ adb -d Shell cat /sdcard/hello.txt
HELLO WORLD!
61
Tim Kryger

新しいデバイスは単に「/ sdcard」にマッピングしないため、SDカードへの実際のパスを取得するには、必ずJava getExternalStorageDirectory())呼び出しを使用してください。 「/ sdcard」のハードコードされた場所を使用すると失敗します。

23
niko20

また、fopen()が正常に動作することを確認できますが、アプリケーションのリソースまたはアセットフォルダー内のファイルにアクセスしようとしている場合はnotです。車輪の再発明を避けるために、アプリに同梱する必要のあるアセットは、アセットフォルダーに貼り付けて、配布用にパッケージ化することをお勧めします。

資産フォルダーの場合、ファイルがパッケージャーによって圧縮されたかどうかに応じて、2つのことのいずれかを行う必要があります。どちらもAssetManagerメソッドを使用し、context/appからAssetManagerを取得できます。ファイル名は常にアセットフォルダーに関連しています。btw:アセットフォルダーに直接ファイル「foo.png」がある場合は、「foo.png」を開きます。notのようなもの/foo.png "。

  1. ファイルが圧縮されていない場合(つまり、.pngなどの圧縮されない拡張子の1つ)、AssetManager.openFd()からファイル記述子を取得してC++に渡すことができます。次に、fdopen(dup(fd)、 "r");を使用できます。 FILE *としてファイルを開きます。 オフセットへのfseek()が必要であり、ファイルの長さを自分で追跡する必要がありますアセットパッケージ全体に対するファイルハンドルを実際に取得しており、目的のファイルは小さい部分。

  2. ファイルが圧縮されている場合は、Javaストリーミングリーダーを使用する必要があります。AssetManager.open()は、ファイルの読み取りに使用できるInputStreamを提供します。これはPITAです。 tクエリ(AFAIK)ファイルサイズ;私はアセットフォルダーで前処理ステップを実行し、それぞれのサイズのすべてのファイルのリストを生成して、たとえば、割り当てるバッファーの大きさを知ることができます。

ファイルがリソースの場合、リソースにアクセスするためにResourceクラスを通過する必要があるかもしれませんが、リソースも同じアセットパッケージにパックされているようです。ただし、上記のように、リソースにはInputStreamを取得するopenRawResource()呼び出しと、ファイル記述子を取得するopenRawResourceFd()呼び出しがあります。

がんばろう。

21
SomeCallMeTim