もくじ
文章データをDocumentディレクトリに保存するよ
保存するファイルの種類に合わせて保存先、保存処理を変更する必要があります
下に必要な部分だけまた書いています
アプリが保存されている場所以外のディレクトリにアクセスする場合は
アクセスするファイルっごとに設定が必要
ここかイマイチ冬月も理解しきれてないところでは有るのですが、
写真や動画などのメディアデータは
Movies/(動画データが格納されているディレクトリ)
Pictures/(画像データが格納されているディレクトリ)
この3つのディレクトリに保存するようになりました。
(それ以外のフォルダに保存する方法もあるとは思いますが、まだ調査中です)
ソースコードも
を使って読み書きするようです
音楽などのサウンドデータは
Alarms/(プレイリストやアルバム情報が格納されているディレクトリ)
このあたりに保存する必要があるようです。
ソースコードも
を使って読み書きするようです。
保存するファイルのタイプに合わせて保存するディレクトリ先やプログラムを変更する必要があります
今回はDocumentディレクトリかDownloadディレクトリに
保存することにしました
前には書いてなかったんですが、
テキストデータやPDFやWord、Excelなどの文章データは
Download/(ブラウザ等からダウンロードした物を格納するディレクトリ)
この2つのディレクトリに保存します。
ソースコードも
を使って保存していきます。
こちらはタイプを切り替えれば画像なども保存できるみたいです。
アプリのデータ関連や、会計データは文章データなので
こちらのディレクトリに保存しようと思います。
保存プログラムを書くよ
ネイティブコードで保存ダイアログを呼び出す
色んなサイトから参考にして書いてるので色々ゴッチャゴチャです
Javaコード!
package StudioCross.ReadWriteFiles;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import com.unity3d.player.UnityPlayer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ReadWriteFiles extends Fragment
{
private static final String TAG = “unimgpicker”;
/// ファイル生成時のコールバック判断 ///
private static final int CREATE_FILE = 1002;
/// ファイル生成時のコールバック判断 ///
private static final int OPEN_DOCUMENT_REQUEST = 1003;
/// 保存か読み込みかを振り分けるフラグ ///
public boolean _IsSave = false;
/// 保存するデータを一時的に保持する ///
public String _DataText = “”;
/// 保存名を一時的に保持する ///
public String _FileName = “”;
/// 保存状況を数値化したもの ///
public static int _StageNum = 0;
/// 読み込んだテキストデータ ///
public static String _LoadDataText = “”;
/**
* テキストファイルを保存する
* String fileName : 保存するファイル名
* String dataText : 保存するテキストデータ
**/
public static void CallSetCreateTextFile(String fileName, String dataText)
{
// 色々呼び出す
Activity unityActivity = UnityPlayer.currentActivity;
ReadWriteFiles readWriteFiles = new ReadWriteFiles();
readWriteFiles._StageNum = 0;
readWriteFiles._LoadDataText = “”;
readWriteFiles._DataText = dataText;
readWriteFiles._FileName = fileName;
readWriteFiles._IsSave = true;
FragmentTransaction transaction = unityActivity.getFragmentManager().beginTransaction();
transaction.add(readWriteFiles, TAG);
transaction.commit();
}
/**
* テキストファイルを読み込みする
* String fileName : 読み込みするファイル名
**/
public static void CallSetReadTextFile(String fileName)
{
// 色々呼び出す
Activity unityActivity = UnityPlayer.currentActivity;
ReadWriteFiles readWriteFiles = new ReadWriteFiles();
readWriteFiles._StageNum = 0;
readWriteFiles._LoadDataText = “”;
readWriteFiles._DataText = “”;
readWriteFiles._FileName = fileName;
readWriteFiles._IsSave = false;
FragmentTransaction transaction = unityActivity.getFragmentManager().beginTransaction();
transaction.add(readWriteFiles, TAG);
transaction.commit();
}
/**
* 呼び出せたら処理されるコールバック
**/
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if(_IsSave == true)
{
SetCreateTextFile(_FileName, _DataText);
}
else
{
SetReadTextFile(_FileName);
}
}
/**
* テキストファイルを保存する
* String fileName : 保存するファイル名
* String dataText : 保存するテキストデータ
**/
private void SetCreateTextFile(String fileName, String dataText)
{
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(“text/plain”);
intent.putExtra(Intent.EXTRA_TITLE, fileName);
startActivityForResult(intent, CREATE_FILE);
}
/**
* テキストファイルを読み込みする
* String fileName : 読み込みするファイル名
**/
private void SetReadTextFile(String fileName)
{
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType(“text/plain”);
if(fileName != “”)
{
intent.putExtra(Intent.EXTRA_TITLE, fileName);
}
startActivityForResult(intent, OPEN_DOCUMENT_REQUEST);
}
/**
* テキストファイルを保存状況を返却する
* return : 1:成功 0:保存中 -1:保存失敗
**/
public static int CallReadWriteComplete()
{
return _StageNum;
}
/**
* 保存されているテキストデータ
* return : 1:成功 0:保存中 -1:保存失敗
**/
public static String CallReadTextData()
{
return _LoadDataText;
}
/// ファイル作成後にデータを保存する際に呼び出される
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData)
{
super.onActivityResult(requestCode, resultCode,resultData );
// データ作成時
if(resultCode == Activity.RESULT_OK)
{
if(resultData.getData() == null)
{
/// 書き込みが失敗した
_StageNum = -1;
return;
}
Uri uri = resultData.getData();
Context context = getActivity().getApplicationContext();
if (requestCode == CREATE_FILE)
{
try(OutputStream outputStream = context.getContentResolver().openOutputStream(uri))
{
if(outputStream != null)
{
outputStream.write(_DataText.getBytes());
// 書き込みが成功した
_StageNum = 1;
return;
}
}
catch(Exception e)
{
/// 書き込みが失敗した
_StageNum = -1;
}
return;
}
else if (requestCode == OPEN_DOCUMENT_REQUEST)
{
// 読み込み
String str = “”;
StringBuffer buf = new StringBuffer();
try (InputStream inputStream = context.getContentResolver().openInputStream(uri))
{
if (inputStream != null)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
while ((str = reader.readLine()) != null)
{
buf.append(str + “\n” );
}
}
inputStream.close();
}
catch(Exception e)
{
/// 書き込みが失敗した
_StageNum = -1;
return;
}
_LoadDataText = buf.toString();
// 書き込みが成功した
_StageNum = 1;
return;
}
}
/// 書き込みが失敗した
_StageNum = -1;
}
}
なーんかこのコード表示のプラグイン、
インデントが無くなっちゃうんだよねぇ……
「XXXXXProject\Assets\Plugins\Android\ReadWriteFiles」に保存するとネイティブ側の対応は完了ね
ということで、コードのすべてを解説しちゃうととんでもない長さになるので要点だけ説明します。
Unityの場合、UnityPlayerを継承するけど今回はしませんでした
まずは
クラス定義ですがFragmentを継承します。
データの読み書きの際に、ファイルアクセス時にイベントが発行されるので何かしらのActivityを継承する必要があります。
で、Unityの場合はUnityPlayerを継承してクラスを作るのが一般的ですが他のプラグインを入れたときとか、アセットを適応したときに、そのプログラムがUnityPlayerを継承している時があり、競合が起こってしまうことがあります。
競合してしまうとその解決がとんでもなくめんどくさいので、
今回はFragmentを継承してイベント管理することにしました。
ファイルの書き込みはIntent(Intent.ACTION_CREATE_DOCUMENT)
で行います。
保存本体の処理は、
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(“text/plain”);
intent.putExtra(Intent.EXTRA_TITLE, fileName);
startActivityForResult(intent, CREATE_FILE);
ここになります。
最後の行がファイルが作成されたらイベントを起こすための処理です。
この部分の種類を変えることで
データの保存から読み込みに変更することができます。
今回はファイルを作成して保存の種類に設定しています。
ここで保存するファイルのタイプを設定しています。
現在はtext/plainとなっているように書類データとしています。
下に書き方が有るので任意のファイルに合わせて設定してください。
image/jpeg JPEGファイル(.jpg, .jpeg)
image/png PNGファイル
image/gif GIFファイル
image/bmp bmpファイル
にすると良いと思います
設定を間違えるとデータはテキストデータなのに、種類が画像データみたいな変なデータが出来上がっちゃうから気をつけてね
Qiita Content-Typeの一覧
https://qiita.com/AkihiroTakamura/items/b93fbe511465f52bffaa
データを読み込み終えたらイベントが発行されます
保存時は関数にデータを送るだけでいいのですが
読み込み時はデータを受け取る必要があるのでデータを読み込むまで待つ必要が出てきます。
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData)
{
super.onActivityResult(requestCode, resultCode,resultData );以下略
この関数はデータの保存や読み込みが完了時に呼び出される関数で
読み込み時にここデータの復元を行っています。
データの書き込みの完了タイミングをUnityで調べるため、
上の関数に管理するための変数を用意すると扱いやすいです
詳細の動きはソースコードを見てください。
Unityからネイティブコードを呼び出します
このあたりもそこまで難しくないわ
C#コード!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// データ保存周りの処理管理
/// </summary>
public class DataStorageProcessingManager : MonoBehaviour
{
/// <summary> インスタンス変数 </summary>
public static DataStorageProcessingManager _Instance;
[Header(“パネル設定”)]
/// <summary> 読み込みオブジェクト </summary>
public GameObject _LoadObject;
#if PLATFORM_ANDROID
/// <summary> Andorid操作 </summary>
private AndroidJavaClass _CallSAFAndroid;
#endif
/// <summary>
/// データの読み込みが完了したときに呼び出される関数
/// </summary>
/// <param name=”dataText”>読み込んだデータ</param>
public delegate void LoadCompleteProcessing(string dataText);
/// <summary> データの読み込みが完了したときに呼び出される関数 </summary>
private LoadCompleteProcessing _LoadCompleteProcessing;
/// <summary>
/// 起動処理
/// </summary>
private void Awake()
{
}
/// <summary>
/// 初期化処理
/// </summary>
void Start()
{
_Instance = this;
#if PLATFORM_ANDROID
_CallSAFAndroid = new AndroidJavaClass(“StudioCross.ReadWriteFiles.ReadWriteFiles”);
#elif PLATFORM_IOS
#else
#endif
_LoadObject.SetActive(false);
}
/// <summary>
/// 更新処理
/// </summary>
void Update()
{
}
/// <summary>
/// 書き込み処理
/// </summary>
/// <param name=”fileName”>ファイル名(拡張子もつける)</param>
/// <param name=”dataText”>保存するデータ</param>
public static void CallSetSave(string fileName, string dataText)
{
if(_Instance != null)
{
_Instance.SetSave(fileName, dataText);
}
}
/// <summary>
/// 読み込み処理
/// </summary>
/// <param name=”fileName”>ファイル名(拡張子もつける)</param>
/// <param name=”loadCompleteProcessing”> データの読み込みが完了したときに呼び出される関数 </param>
public static void CallSetLoad(string fileName, LoadCompleteProcessing loadCompleteProcessing)
{
if (_Instance != null)
{
_Instance.SetLoad(fileName, loadCompleteProcessing);
}
else
{
loadCompleteProcessing(“”);
}
}
#if PLATFORM_ANDROID
/// <summary>
/// 書き込み処理
/// </summary>
/// <param name=”fileName”>ファイル名(拡張子もつける)</param>
/// <param name=”dataText”>保存するデータ</param>
private void SetSave(string fileName, string dataText)
{
_LoadObject.SetActive(true);
_CallSAFAndroid.CallStatic(“CallSetCreateTextFile”, fileName, dataText);
Debug.Log(“書き込み開始”);
StartCoroutine(CallReadWriteComplete(true));
}
/// <summary>
/// 読み込み
/// </summary>
/// <param name=”fileName”>ファイル名(拡張子もつける)</param>
/// <param name=”loadCompleteProcessing”> データの読み込みが完了したときに呼び出される関数 </param>
private void SetLoad(string fileName, LoadCompleteProcessing loadCompleteProcessing)
{
_LoadObject.SetActive(true);
_CallSAFAndroid.CallStatic(“CallSetReadTextFile”, fileName);
_LoadCompleteProcessing = loadCompleteProcessing;
Debug.Log(“読み込み開始”);
StartCoroutine(CallReadWriteComplete(false));
}
/// <summary>
/// 読み込み完了まで待つ
/// </summary>
/// <returns></returns>
private IEnumerator CallReadWriteComplete(bool isSave)
{
int status = 0;
while (status == 0)
{
status = _CallSAFAndroid.CallStatic<int>(“CallReadWriteComplete”);
yield return null;
}
if (status == 1)
{
// 読み込み完了
if (isSave == false)
{
if (_LoadCompleteProcessing != null)
{
_LoadCompleteProcessing(_CallSAFAndroid.CallStatic<string>(“CallReadTextData”));
}
}
}
else
{
// エラーが発生
Debug.Log(“読み書きでエラーが発生”);
}
_LoadObject.SetActive(false);
Debug.Log(“読み書き完了”);
}
#elif PLATFORM_IOS
/// <summary>
/// 書き込み処理
/// </summary>
/// <param name=”fileName”>ファイル名(拡張子もつける)</param>
/// <param name=”dataText”>保存するデータ</param>
private void SetSave(string fileName, string dataText)
{
}
#else
/// <summary>
/// 読み込み
/// </summary>
/// <param name=”fileName”>ファイル名(拡張子もつける)</param>
/// <param name=”loadCompleteProcessing”> データの読み込みが完了したときに呼び出される関数 </param>
private void SetLoad(string fileName, LoadCompleteProcessing loadCompleteProcessing)
{
}
#endif
}
あ
実際に動かすとこんな感じです
あ