技術はメシのタネ

底辺プログラマで技術の向上を目指しているけれどやりたい事が無くて困っている

Swiftのクロージャが説明からして難しすぎる件について

The Swift Programming Languageや他の解説サイトでクロージャの説明を読んでみたのだが、文法からしてわからん。
試行錯誤しつつ実際に使って見ると、引数付きの場合のstrnums -> [Int] inがちょっと特殊だった。
てか例文をなんでもっと簡潔に書いてくれないんだろう。

func test() {

    // これがクロージャ
    let moge: ([String]) -> ([Int]) = {
        strnums -> [Int] in
        var nums: [Int] = []
        for str: String in strnums {
            nums.append(Int(str)!)
        }
        return nums
    }

    // クロージャを使ってみる
    let bar = moge(["5", "4", "3"])

    // 結果を確認
    for number: Int in bar {
        print("\(number)")    // => 5, 4, 3
    }
}

むしろ関数がクロージャの1種って言われてもなあ。

Swiftの他の機能もそうだけど、iOSアプリを作る中でむしろそれをどう使うのかがいまいちわからん。

ちょっとメモ。

public class LocalStorageManager {
    private static final int mPrivateMode = Context.MODE_PRIVATE;
    private static final int mPublicMode  = Context.MODE_WORLD_WRITEABLE | Context.MODE_WORLD_READABLE;
    private static final String prefix = "app_";
    
    /**
    * /data/data/パッケージ名を取得する
    * @param context
    * @return
    * @throws FileNotFoundException 
    */
    public static final File getPackageNameDirectory (Context context) throws FileNotFoundException {
        String temporary = "hoge";
        StringBuilder builder = new StringBuilder();
        builder.append(prefix).append(temporary);
        
        File root = new File(context.getDir(temporary, mPrivateMode).getAbsolutePath().replace(builder.toString(), ""));
        if (!root.exists()) {
            throw new FileNotFoundException();
        }
        return root;
    }

    /**
    * /data/data/パッケージ名/直下にpathフォルダがあるかどうか
    * @param context
    * @param path app_"path"ディレクトリ名 
    * @return
    */
    public static final File findLocalDirectory (Context context, final String path) {
        try {
            File find = new File(getPackageNameDirectory(context), path);
            if (find.exists()) {
                return find;
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

play video from android internal storage

Androidの話。
自アプリ内のコンテンツの再生を、暗黙的インテントで他アプリに依頼するとします。

本当は、getFilesDir()を使って/data/data/パッケージ名/files下に保存して、ファイルプロバイダーで依頼するのがいいと思います。

本当は。

しかしですね、諸事情で/data/data/パッケージ名/app_a/b/c/d/e/にコンテンツを保存せざるを得ないとします。
主に仕様とか仕様とか仕様とか。

しかも、コンテンツがクソ重くてコピーもままならず、このままインテントで再生を依頼するしかないと仮定します。


この場合、コードとしてはこんな感じになります。

play video from android internal storage

(以下引用)

Uri fileUri = Uri.fromFile(new File(String file_path));
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(fileUri, URLConnection.guessContentTypeFromName(fileUri.toString()));
startActivity(intent);

このURLConnection#guessContentTypeFromName、優秀です。
この呪文1つ唱えるだけでテキストでも動画でも画像でも再生できます。
拡張子で場合分けする必要はありません。
(再生に対応するアプリが存在すればね)

さて……。
これで再生できれば、もうここから先を読む必要はありません。

どうです?
再生できました?


ここからは、再生できなかった人向けです。
しかも、こういうケースです。

  • 暗黙的インテントで対応するアプリを選ぶダイアログが起動する
  • 再生するアプリで再生を行うと「この動画は再生できません」的メッセージ
  • Logcatでログを確認するとAwesomePlayerとかからpermission deniedって言われてる
  • 他にも(1, -2147483648)とか


こういう場合、ログに素直に従って再生コンテンツのパーミッションを確認しましょう。

adbコマンドで/data/下にアクセスできるかどうかがポイントですね。
lspermission deniedと言われても、run-as パッケージ名でアクセスできる可能性があります。

コンテンツの保存されているディレクトリへ向けて、1つずつパーミッションを確認していきましょう。
run-asができるかどうかは機種依存らしいので、ダメなら別の方法で確認します。

コンテンツを一旦/data/data/パッケージ名/app_aに移動させて、暗黙的インテントで再生できるか確認してみてください。
app_agetDir()で作成するのが一般的と思いますが、この時にファイルアクセスを設定できます。
Context.MODE_WORLD_READABLEとか付ければいいんじゃないですかね、たぶん。

さて、どうでした?
途中のディレクトリも含めて他アプリから読み取り可能なパーミッションでしたか?
/data/data/パッケージ名/app_a直下であれば再生可能でしたか?

自分の場合はrun-asで確認できまして、途中のディレクトリのパーミッションの設定が他アプリから読み取り可能ではありませんでした。

原因は、/data/data/パッケージ名/app_aディレクトリ以下をmkdirs()で作ったことだったっぽいです。
mkdir()だと親ディレクトリのパーミッションを引き継ぐとどこかで読んだ気がするのですが、mkdirs()は違うんですかね?

対処法ですが、File#setReadable(boolean executable, boolean ownerOnly)とかでmkdir()したディレクトリを読み取り可能にしてあげれば良いです。

int mode = Context.MODE_WORLD_WRITABLE | CONTEXT.MODE_WORLD_READABLE;
// /data/data/パッケージ名/直下にフォルダを作るとapp_がくっつきます
File root = getDir("a", mode);
File second = new File(root, "b");
if (!second.exists()) {
    second.mkdir();
    second.setExecutable(true, false);
    second.setWritable(true, false);
    second.setReadable(true, false);
}
...

過剰にパーミッションを盛りましたが、適度に調節してください。
あ、ファイル自体が再生不可能な場合はここの説明の対象外です。
悪しからず。

以上。