2015年9月9日水曜日

Google App Engine / Go で Google Cloud Storage に画像をアップロードする

How to upload image to Google Cloud Storage with Google App Engine / Go.

この情報は2015年9月9日時点のものです。appengine.Context, context.Context 問題は過渡期のようなので今後インタフェースが変わる可能性があります。

File API が終了になったので調べたけどあんまり情報がなく苦労したのでメモとして残しておく

storage#NewWriter に渡す Context は appengine#Context ではなく context#Context なので次のようにするとエラーになります。

import ( "appengine" "google.golang.org/cloud/storage" ... ) func uploadImage(w http.ResponseWriter, r *http.Request) { ... c := appengine.NewContext(r) wc := storage.NewWriter(c, bucketName, fileName) wc.ContentType = "image/jpg" if _, err := wc.Write(data); err != nil { ... } ... } google.golang.org/appengine の NewContext は context#Context を返すため、こちらを利用します。このワークアラウンドは https://github.com/golang/oauth2/#app-engine を参考にしています。 import ( "appengine" "fmt" "io/ioutil" "net/http" "strings" "golang.org/x/net/context" "golang.org/x/oauth2" "golang.org/x/oauth2/google" newappengine "google.golang.org/appengine" newurlfetch "google.golang.org/appengine/urlfetch" "google.golang.org/cloud" "google.golang.org/cloud/storage" ) func uploadImage(w http.ResponseWriter, r *http.Request) { file, fileHeader, err := r.FormFile("image_file") if err != nil { fmt.Fprint(w, "no image") // no image return } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { fmt.Fprint(w, "cloud not upload image") return } var mimeType string if strings.HasSuffix(filename, ".png") { mimeType = "image/png" } else if strings.HasSuffix(filename, ".jpeg") { mimeType = "image/jpg" } else { mimeType = "image/jpg" } bucketName := "mybucketname" fileName := fileHeader.Filename c := appengine.NewContext(r) ctx := newappengine.NewContext(r) hc := &http.Client{ Transport: &oauth2.Transport{ Source: google.AppEngineTokenSource(ctx, storage.ScopeFullControl), Base: &newurlfetch.Transport{Context: ctx}, }, } ctx2 := cloud.NewContext(appengine.AppID(c), hc) wc := storage.NewWriter(ctx2, bucketName, fileName) wc.ContentType = mimeType if _, err := wc.Write(data); err != nil { fmt.Fprint(w, "cloud not upload image") return } if err := wc.Close(); err != nil { return }
google.golang.org/cloud/storage などは GOPAH を設定して $ go get -u google.golang.org/cloud/storage でインストールしておきます


参考


2015年9月7日月曜日

FragmentArgs 使ってみた

FragmentArgs

Fragment 生成時に値を渡すには、引数付きのコンストラクタを用意するのではなく、Fragment.setArguments() で値を渡すのがベストプラクティスです。

FragmentArgs はアノテーションを付けたフィールドからFragmentのビルダークラスを生成してくれるライブラリです。

以下の内容は2015年9月7日時点の状態をもとにしています。



設定

Project の build.gradle buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.3.1' // apt を使うので以下が必要 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.7' } } allprojects { repositories { jcenter() } } app の build.gradle apply plugin: 'com.android.application' // apt を使うので以下が必要 apply plugin: 'com.neenbedankt.android-apt' android { ... } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // 以下が必要 compile 'com.hannesdorfmann.fragmentargs:annotation:2.1.2' apt 'com.hannesdorfmann.fragmentargs:processor:2.1.2' }

使い方 public class MainFragment extends Fragment { @Arg int userId; @Arg String name; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // @Arg を付けたフィールドに getArguments() から値が代入される FragmentArgs.inject(this); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Toast.makeText(getActivity(), "userId = " + userId + ", name = " + name, Toast.LENGTH_SHORT).show(); } } public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final MainFragment f = new MainFragmentBuilder("Android", 100) .build(); // 以下のメソッドも生成される // final MainFragment f = MainFragmentBuilder.newMainFragment("Android", 100); getSupportFragmentManager() .beginTransaction() .replace(android.R.id.content, f) .commit(); } }

Library Project での使い方

Library Project の build.gradle apply plugin: 'com.android.library' // apt を使うので以下が必要 apply plugin: 'com.neenbedankt.android-apt' // Library Project では以下を追加しないとコンパイルエラーになる apt { arguments { fragmentArgsLib true } } android { ... } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // 以下が必要 compile 'com.hannesdorfmann.fragmentargs:annotation:2.1.2' apt 'com.hannesdorfmann.fragmentargs:processor:2.1.2' } 2015年9月7日時点の FragmentArgs の README だと fragmentArgsLib = true のように = がついてますが、android-apt:1.7 ではメソッドになったので = が必要ないです。

public class MainLibraryFragment extends Fragment { @Arg int userId; @Arg String name; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Library Project のときは生成されたビルダークラスの injectArguments() を利用する必要がある MainLibraryFragmentBuilder.injectArguments(this); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Toast.makeText(getActivity(), "userId = " + userId + ", name = " + name, Toast.LENGTH_SHORT).show(); } }


2015年8月3日月曜日

Android JUnit 4 形式のテストにする

InstrumentationTestRunner は JUnit 3 しかサポートしていませんが、Android Testing Support Library に含まれる AndroidJUnitRunner を使うと JUnit 4-compatible なテストを実行することができます。
(JUnit 3 と JUnit 4.10 までの JUnit 4 互換)

Android 2.2 (API Level 8) 以上が必要です。

▪️ Setup dependencies { androidTestCompile 'com.android.support.test:runner:0.3' // to use JUnit 4 rules androidTestCompile 'com.android.support.test:rules:0.3' } android { defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } }

▪️ Usage

- JUnit 4 で実装されたテストクラスには @RunWith(AndroidJUnit4.class) をつける
- テスト対象メソッドには @Test をつける
- setUp() を Override し、public に変更し @Before をつける
- tearDown() を Override し、public に変更し @After をつける
- 必要に応じて AndroidTestCase.setContext(InstrumentationRegistry.getContext()) や InstrumentationTestCase.injectInstrumentation(InstrumentationRegistry.getInstrumentation()) を setUp() で行う

@RunWith(AndroidJUnit4.class) public class SimpleTest extends AndroidTestCase { @Before public void setUp() throws Exception { super.setUp(); setContext(InstrumentationRegistry.getContext()); } @Test public void testSample() { assertNotNull(getContext()); } @After public void tearDown() throws Exception { super.tearDown(); } } - InstrumentationRegistryでテスト実行時の情報を取得できる
- InstrumentationRegistry.getContext() : instrumentation のパッケージContext
- InstrumentationRegistry.getTargetContext() : テスト対象のアプリケーションのContext
- InstrumentationRegistry.getInstrumentation() : 実行中の instrumentation


▪️ Test filtering

- @RequiresDevice : 実機でのみ実行(エミュレータでは実行しない)
- @SdkSuppress : 指定された API Level より低い場合実行しない、@SdkSuppress(minSdkVersion=21) など
- @SmallTest, @MediumTest, @LargeTest : テストにどのくらいかかるかのクラス分け


▪️ 参考

- http://developer.android.com/tools/testing-support-library/index.html