conta's diary

思ったこと、やったことを書いてます。 twitter: @conta_

Flaskでpostされたjsonを受け取る

Flaskのrequestでデータを受け取るには

  • request.data
  • request.form
  • request.json

等がある。
それぞれ、Content-Typeで受け取り方が切り替わるらしい。
request.jsonはContent-Typeapplication/jsonに設定していれば
自動的にオブジェクトへパースしてデータを返してくれる。
POSTするデータはJSON.stringifyしてないと400エラーが出て怒られる。

なので、Javascript側ではajaxの設定を下記のようにする。

  • Content-Typeapplication/json
  • データはJSON.stringifyで文字列に

例)

$.ajax(
    {
      url:'/api/test',
      type:'POST',
      data:JSON.stringify(data),
      dataType: 'json',
      contentType: 'application/json'
    })
    .done(function(data, textStatus, jqXHR ){
            console.log("done");
            console.log(data);
          })
    .fail(function(jqXHR, textStatus, errorThrown){
            console.log("failed");
          });;


Python側では念の為にヘッダーのチェックを行う。

@app.route('/api/test', methods=['POST'])
def face_info():
    if request.headers['Content-Type'] != 'application/json':
        print(request.headers['Content-Type'])
        return flask.jsonify(res='error'), 400

    print request.json

    return flask.jsonify(res='ok')


参考API — Flask 0.9dev documentation

Android string.xmlの分割と多言語化

多言語化はフォルダ名をvalues-XX(XXは国コード"ja"とか"cn"とか)にして、同じようなリソースを置くことでAndroidが勝手に入れ替えてくれる。

また、言語ファイルをよく記述しているres/values/string.xmlは、他の名前で定義しても利用可能。
なので、string.xmlが大きくなりそうになったらファイルを分割するとスッキリする。
例えば、string_test.xml内で"hello"という文字列を定義すると、R.string.helloでアクセスできるようになる。

下記はサンプル。

res/values/string_test.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
  <string name="hello">hello!</string>
</resources>


res/values-ja/string_test.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
  <string name="hello">こんにちわ!</string>
</resources>

利用例)

Log.d("debug", getString(R.string.hello));

これで言語が日本語の際は"こんにちは!"、それ以外は"hello"と出力される。

AndroidのLogCat出力用テンプレート(メソッド名とか行番号とか表示したいよ!)

メソッド名とか行番号とかLogCatに出力したいなーと、ぐぐってみたらここを読んで、おぉー(・・)と思い作ってみました。
Eclipseのテンプレ設定にすると非常に便利かも!
下記コード。

private static final boolean ENABLE_LOG = true;

private static void logd(String msg) {
    if(ENABLE_LOG) {
        Log.d(getTag(), msg);
    }
}

private static final String getTag() {  
    StackTraceElement ste = Thread.currentThread().getStackTrace()[4];  
    String className = ste.getClassName();  
    className = className.substring(className.lastIndexOf(".") + 1);  
    String methodName = ste.getMethodName();  
    int lineNum = ste.getLineNumber();  
    return className + "." + methodName + ":" + lineNum;  
}  

logd()を使うと、
HogeActivity.foobar:11
のようなタグが出力されます。


これをEclipseの設定で下記のようにコピペすると、Classを作成した時にテンプレ付きで作成されます。

f:id:contaconta:20121211194431p:plain

テンプレにすることの難点は、すべてのクラスにコレがくっついてくることです(´・ω・`)
まぁ消せばOKなんですけどね。
やっぱりコピペツールに登録しようかなー

なにかいい方法があれば教えて下さい

iOSで通信する際のUser-Agentを組み立てる

こういうフォーマットのUAがつくりたい!
<アプリ名>/ ( <デバイス名>; ; iOS; <言語設定>)
例)
MyApp/1.0 (iPhone5,2; 6.0; iOS; ja)

//format: MyApp/1.0 (iPhone5,2; 6.0; iOS; ja)
- (NSString*)buildUserAgent
{
  // モデル名取得
  NSString *modelname = [self platformName];
  
  // osのバージョン取得
  float iOSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];

  // 言語設定取得
  NSArray *langs = [NSLocale preferredLanguages];
  //取得した配列から先頭の文字列を取得(先頭が現在の設定言語)
  NSString *currentLanguage = [langs objectAtIndex:0];
  NSString* userAgent
  = [NSString stringWithFormat:@"MyApp/1.0 (%@; %0.2f; iOS; %@)",
     modelname, iOSVersion, currentLanguage];
  return userAgent;
}

// デバイスの情報を返す
- (NSString *)platformName
{
  size_t size;
  sysctlbyname("hw.machine", NULL, &size, NULL, 0);
  char *machine = malloc(size);
  sysctlbyname("hw.machine", machine, &size, NULL, 0);
  NSString *platformName = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
  free(machine);
  return platformName;
}

ちょっと決め打ち多いね^^;
あとはこの文字列をNSURLConnectionなりAFHTTPClientなりにセットしよう!

Macで"dyld: Library not loaded:"が出たら。。。

dyld: Library not loaded: lib/.....
  Referenced from: /Users/......./hoge
  Reason: image not found

これを.zshrcに書いとこう。

export DYLD_LIBRARY_PATH=/path/to/lib:$DYLD_LIBRARY_PATH

これで動くはず。

Xcodeでビルドした時の”duplicate symbols”エラーを回避する

FacebookSDKやAWSiOSSDKなど、いろんなライブラリをブチ込みまくると

ld: - duplicate symbols for architecture ---

と言うエラーがよく出る。
重複して使われているライブラリ(SBJsonとか)が出るとこのようなエラーがでてしまう。

くっそ!

そしてググりまくって回避方法を見つけました(`・ω・´)

今回はFacebookとAWSの.frameworkの衝突を回避する例を紹介します。


まず普通にビルドすると、

duplicate symbol _OBJC_CLASS_$_SBJsonParser in:
    .../Build/Products/Debug-iphoneos/libFacebook SDK.a(SBJsonParser.o)
.
.
.
.
.
XcodeWorkspace/aws-ios-sdk-1.4.3/AWSiOSSDK.framework/AWSiOSSDK(SBJsonWriter.o)
ld: 6 duplicate symbols for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

お、おぅ、となります。
○SBJsonParser.o
○SBJsonWriter.o
これらがFacebookSDKとAWSのSDKで使われてるから怒られる。たぶん。

なので、AWSiOSSDKの方からこれらを消してあげます。

まず下記コマンドで、どのアーキテクチャ用になってるかチェック。

$lipo -info AWSiOSSDK
Architectures in the fat file: AWSiOSSDK are: armv7 (cputype (12) cpusubtype (11)) i386 

armv7とi386アーキテクチャ用にビルドされてるそうです。

下記コマンドで分解。

$lipo -thin armv7 AWSiOSSDK -output AWSiOSSDK_armv7
$lipo -thin i386 AWSiOSSDK -output AWSiOSSDK_i386

.oファイル検索

$ar -t AWSiOSSDK_armv7|grep SBJson                                                                                               

SBJsonParser.o
SBJsonStreamParser.o
SBJsonStreamParserAccumulator.o
SBJsonStreamParserAdapter.o
SBJsonStreamParserState.o
SBJsonStreamWriter.o
SBJsonStreamWriterAccumulator.o
SBJsonStreamWriterState.o
SBJsonTokeniser.o
SBJsonUTF8Stream.o
SBJsonWriter.o

あったあった。

目的のファイル削除。

ar -dv AWSiOSSDK_armv7 SBJsonParser.o SBJsonWriter.o
ar -dv AWSiOSSDK_i386 SBJsonParser.o SBJsonWriter.o 

合体!

lipo -create AWSiOSSDK_armv7 AWSiOSSDK_i386  -output AWSiOSSDK

これでビルドすると。。。。
Building...... (´・ω・`)

Succeed!いぇーす!おめでとう!

*追記

armv7sのライブラリ抽出は下記で可能。

xcrun -sdk iphoneos lipo -thin armv7s AWSiOSSDK -output AWSiOSSDK_armv7s

AndroidでBitmap画像を保存

よく忘れるのでメモ書き。
ギャラリーに"/MyPhoto/"フォルダができてその中に保存されます。
最後のContentResolverの部分で保存したファイルパスを登録しておかないと、
デバイスを再起動するまでギャラリーが更新されない罠が準備されてます。

public void saveBitmap(Bitmap saveImage) throws IOException {

    final String SAVE_DIR = "/MyPhoto/";
    File file = new File(Environment.getExternalStorageDirectory().getPath() + SAVE_DIR);
    try{
        if(!file.exists()){
            file.mkdir();
        }
    }catch(SecurityException e){
        e.printStackTrace();
        throw e;
    }

    Date mDate = new Date();
    SimpleDateFormat fileNameDate = new SimpleDateFormat("yyyyMMdd_HHmmss");
    String fileName = fileNameDate.format(mDate) + ".jpg";
    String AttachName = file.getAbsolutePath() + "/" + fileName;

    try {
        FileOutputStream out = new FileOutputStream(AttachName);
        saveImage.compress(CompressFormat.JPEG, 100, out);
        out.flush();
        out.close();
    } catch(IOException e) {
        e.printStackTrace();
        throw e;
    }
    
        // save index
    ContentValues values = new ContentValues();
    ContentResolver contentResolver = getContentResolver();
    values.put(Images.Media.MIME_TYPE, "image/jpeg");
    values.put(Images.Media.TITLE, fileName); 
    values.put("_data", AttachName);
    contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}