このサイトでは、分析、カスタマイズされたコンテンツ、および広告に Cookie を使用します。このサイトを引き続き閲覧すると、Cookie の使用に同意するものと見なされます。
Hi, Developers,
straightapps.com
作成 December 22, 2017、最終更新 December 12, 2019(様式変更)
トップページ > Android 開発トップ > JNI によるパブリックなパスの取得
line
Android 開発
line

ここでは、JNI によるパブリックなパスの取得方法を記載しています。

Android アプリは、自身の apk がインストールされたフォルダのサブフォルダに、自由にファイルを作成できます。 struct android_app* app から、そのパスを取得できますので、 fopen 関数等で、読み書きできます。 しかし、接続したPCなどからは見えないため、非常に不便です。

ここでは、Download のような、PC等からでも見える(パブリックな)フォルダにファイルを作成するための方法を記述しています。 正しいパスを取得するためには、Java を使用する必要があるようで、JNI と呼ばれる手法を使用しています。

ご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。
また、本サイトが初めての方は、まずこのページの注意事項をご覧ください。

▼ セクション一覧

JNI によるパブリックなパスの取得(JNI の準備)
JNI によるパブリックなパスの取得
DIRECTORY_DOWNLOADS を取得できたら


JNI によるパブリックなパスの取得(JNI の準備)

投稿 December 22, 2017, 最終更新 July 23, 2018(様式変更)

アプリのプライベートな領域に作成したファイルは、USB 接続したPCからも、ES ファイルエクスプローラのようなものでも、見えませんでした。 開発時のログなどは、見えなければ意味がありません。 かといって、どうも logcat は使いにくい

そこで、ネットからダウンロードしたファイル等が保存される、Download フォルダにファイルを作りたいと思いました。 このパスは、決め打ちでもいいのかも知れませんが・・・やはり正式に取得すべきでしょう。

方法としては、 JNI ( Java と他の言語をつなぐ Java Native Interface の略です ) を利用、すなわち Java Virtual Machine を取得して、Java を使用する必要があるようです。 ネイティブアプリでなければ(Java からネイティブコードを呼び出すのであれば)、呼び出し元で簡単に実現できるわけですが、 ネイティブアプリではかなり大変です。

まず、 JVM ( Java Virtual Machine ) 使用前に、struct android_app* appapp->activity->vm(JavaVM*)にカレントスレッドをアタッチする必要があります。

JNIEnv* env;
JavaVM* vm = app->activity->vm;
vm->AttachCurrentThread(&env, NULL);

プログラムの終了時には、デタッチする必要があるようです。

JavaVM* vm = g_app->activity->vm;
vm->DetachCurrentThread();

いずれも、メインスレッドから呼び出すことを想定しています。

▲ページ先頭へ

JNI によるパブリックなパスの取得

投稿 December 22, 2017, 最終更新 July 23, 2018(様式変更)

AttachCurrentThread して得られた JNIEnv* env を、JNI で使用します。 最初に1回だけアタッチして、env を保存しておいて再利用、というのは、うまくいかないようです。 何度アタッチしても、アタッチ済みの場合は何もしないようですから、毎回行うべきであるようです。

Java の手順としては、

import android.os.Environment;
String path = Environment.getExternalStorageDirectory().getPath();

とすることで、SD カードのパス(Nexus7 のような外部ストレージがない場合は内部ストレージへのパス)を取得できるようです。

Android Developers の Environment(android.os.Environment)を見ると、 getExternalStorageDirectory 関数は static 定義で、 File(java.io.File)を返します。 そして、File の getPath 関数は、 String(java.lang.String)で、 絶対パスを返します。

getExternalStorageDirectory 関数は引数を取らず、外部ストレージへのパスを取得します。 代わりに、Environment の getExternalStoragePublicDirectory( String type ) を呼び出して File を得れば、 パブリックなパスを得られる、ということです。 この関数も static 定義で、File を返します。

C/C++ で Java の Environment クラスを取得するには、次のようにします。

jclass environmentClass = env->FindClass("android/os/Environment");

jclass は、クラスへのポインタ(ハンドル?)になります。 Java のクラスは何でも、jclass として扱うようです。

"android/os/Environment" の部分、大文字・小文字を含め、1文字でも間違えれば NULL が返されるので、注意が必要です。 また、jclass はクラス(の定義)であり、インスタンス(オブジェクト・実体)ではありません。

次に、static File getExternalStoragePublicDirectory(String type) 関数を取得します。

static 定義ですので、Environment にある関数ポインタを取得すれば、直接呼出しができます。

jmethodID getStoragePDirMethod =
env->GetStaticMethodID(environmentClass, "getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;");

jmethodID は、関数ポインタ(ハンドル?)を保持する型で、 Java の関数は何でも、jmethodID として扱うようです。

GetStaticMethodID 関数は、static の関数を取得するためにのみ、使用可能となっているようです。 第1引数は jclass、第2引数は関数名(1文字でも間違えると NULL が返されます)、第3引数が関数の引数と戻り型です。 表記方法はネットのあちこちで解説されていますが、カッコ内に引数、その右側に戻り型です。 Java クラスのときは "L" で始めて、クラス名をスラッシュ区切りで書いて、";" で終わる、ということです。

GetStaticMethodID 関数の引数に、Environment の static String として定義されている DIRECTORY_DOWNLOADS を指定すれば、 Download へのパスを取得できますが、DIRECTORY_DOWNLOADS がどんな値なのかを取得する必要があります。

▲ページ先頭へ

DIRECTORY_DOWNLOADS を取得できたら

投稿 January 11, 2018, 最終更新 July 23, 2018(様式変更)

ここまでで、 getExternalStoragePublicDirectory 関数 の jmethodID を getStoragePDirMethod 変数に取得していますが、引数に Environment.DIRECTORY_DOWNLOADS 等を渡す必要があります。

これらの値は定数で、おそらく調べれば何であるかがわかるとは思いますが、ハードコーディングするより、ちゃんと取得したほうが良いでしょう。 取得方法を、「JNI でスタティックな定数を取得する」に記載しましたので、必要でしたらご参照ください。

jobject DIRECTORY_DOWNLOADS を得られたら、getExternalStoragePublicDirectory 関数に渡して、 File(java.io.File)を得ます。

jobject fileObject = env->CallStaticObjectMethod(environmentClass, getStoragePDirMethod, DIRECTORY_DOWNLOADS);

得られた fileObject には、DIRECTORY_DOWNLOADS へのパスが含まれていますので、 String を返す File.getPath(void) 関数を呼び出して、完了です。

jclass fileClass = env->FindClass("java/io/File");
jmethodID getPathMethod = env->GetMethodID(fileClass, "getPath", "()Ljava/lang/String;");
jobject strPathObject = env->CallObjectMethod(fileObject, getPathMethod);

が、得られた strPathObject は String(java.lang.String)を指す jobject ですので、文字列に変換しないと見えません。

const char* pubDir = env->GetStringUTFChars((jstring)strPathObject, 0);
char* dup = strdup(pubDir);
env->ReleaseStringUTFChars((jstring)strPathObject, pubDir);

GetStringUTFChars 関数で、const char* に変換して取得しています。 GetStringUTFChars 関数は、

UTF-8 文字の文字列配列を参照するポインタを返します。
この配列は、ReleaseStringUTFChars() によって解放されるまで有効です。 

とありますので、strdup 関数でコピーを作成して、解放しています。

DIRECTORY_DOWNLOADS では、

/storage/emulated/0/Download

を取得できました。

表記はこのようですが、ちゃんと内部ストレージの Download フォルダにアクセスできます。

▲ページ先頭へ
line
関連トピックス
line

JNI でスタティックな定数を取得する

JNI による、スタティックな定数の取得方法を記載しています。

JNI でウェブサイトを開く

JNI で、標準のブラウザで(あるいは1つを選択してもらって)ウェブサイトを開く方法について、記載しています。

line
その他のおすすめ
line

おすすめ記事はありません。

JavaScriptが無効です
▲ページ先頭へ


© 2017-2021 StraightApps.com 無断転載を禁じます。No reproduction without permission.