Android NDK を使用して、パフォーマンス上の理由から主に C でアプリケーションを作成していますが、fopen などのファイル操作が Android で正しく動作しないようです。これらの機能を使用しようとすると、アプリケーションがクラッシュします。
Android NDK でファイルを作成/書き込みするにはどうすればよいですか?
Android NDK を使用して、パフォーマンス上の理由から主に C でアプリケーションを作成していますが、fopen などのファイル操作が Android で正しく動作しないようです。これらの機能を使用しようとすると、アプリケーションがクラッシュします。
Android NDK でファイルを作成/書き込みするにはどうすればよいですか?
他の答えは正しいです。FILE
とを使用して NDK 経由でファイルを開くことができますがfopen
、アクセス許可を設定することを忘れないでください。
Android マニフェストの場所:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
ファイル IO は、JNI を使用して Android で正常に動作します。おそらく、パスが正しくないファイルを開こうとしていて、リターン コードをチェックしていないのではないでしょうか? hello-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!
getExternalStorageDirectory()
新しいデバイスでは単に「/sdcard」にマッピングされないため、Java 呼び出しを使用して SD カードへの実際のパスを取得してください。その場合、「/sdcard」のハードコードされた場所を使用しようとすると失敗します。
fopen() が正しく動作することも確認できますが、アプリケーションのリソースまたはアセット フォルダー内のファイルにアクセスしようとしている場合は動作しません。車輪を再発明する必要がないように、アプリと共に出荷するアセットはすべて、配布用にパッケージ化されるアセット フォルダーに貼り付けることをお勧めします。
assets フォルダーの場合、ファイルがパッケージャーによって圧縮されたかどうかに応じて、2 つのいずれかを行う必要があります。どちらも AssetManager メソッドを使用し、コンテキスト/アプリから AssetManager を取得できます。ファイル名は常にアセット フォルダーに相対的です。ところで、ファイル「foo.png」がアセット フォルダーに直接ある場合は、「assets/foo.png」のようなものではなく、「foo.png」を開きます。
ファイルが圧縮されていない場合 (つまり、.png のように圧縮されない拡張子の 1 つである場合)、AssetManager.openFd() からファイル記述子を取得して C++ に渡すことができます。次に、 fdopen(dup(fd),"r"); を使用できます。ファイルを FILE* として開きます。オフセットまで fseek() し、ファイルの長さを自分で追跡する必要があることに注意してください。実際には、アセット パッケージ全体のファイル ハンドルを取得していますが、対象のファイルはほんの一部です。
ファイルが圧縮されている場合は、Java ストリーミング リーダーを使用する必要があります。 AssetManager.open() は、ファイルの読み取りに使用できる InputStream を提供します。アセットフォルダーで前処理ステップを実行して、すべてのファイルとそれぞれのサイズのリストを生成し、たとえば、割り当てるバッファーの大きさを知ることができます。
ファイルがリソースの場合、Resource クラスを介してアクセスする必要がある場合がありますが、リソースも同じアセット パッケージにパックされているように見えます。ただし、リソースには、上記のように、InputStream を取得するための openRawResource() 呼び出しと、ファイル記述子を取得するための openRawResourceFd() 呼び出しがあります。
幸運を。