整理したものはTech sheetsにあるよ

2017年8月5日土曜日

Builderscon 2017 Tokyoで代表が講演したよ

みんなー
Builderscon 2017 Tokyoというイベントが8月3日(前夜祭)から5日で開催されたよ♪運営のみなさま、参加してたみんな、おつかれさまでした!

CFPが通ったので、4日にだいひょーがAndroidアプリ開発アンチパターンというテーマで講演したよ♪
スライドはこちら!


今回は全体的にWebやインフラ・バックエンドの講演が多かったので、かなり異端な感じの講演になってたけど、現地で聞いてくれたみんな、ありがとー!

Buildersconは「知らなかった、を聞く」がテーマなので、普段Androidアプリ開発とは関係ない方に少しでも「知ってもらう」ことができたら、嬉しいなー

イベントそのものは、「現地で参加し、参加者どうしで交流する仕掛け」としての「しゃもじ」がとてもいい感じだったよ♪(注:しゃもじをGETするには個人スポンサーかスピーカーである必要がありました)

ボクがもらったのは、このしゃもじ!



こんな感じで、他の参加者と組み合わせておもしろフレーズが作れる仕組みは、よかったー



次回は、もっとみんなに楽しんでもらえる講演できるといいなー(とだいひょーが言ってた!

2017年6月9日金曜日

[Android]XMLでFragment in Fragment

みんなー、こんにちはー

今日は、AndroidのFragment in Fragmentに関する話題をお届け!

Android N(API 25)までは、Fragment用のレイアウトに<fragment>で子Fragmentを配置するとクラッシュするのは、みんな知ってるよね?

レイアウトは、こんな感じ。

activity_main.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    <fragment
        android:id="@+id/fragment_1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.mokelab.fragment.MainFragment"/>
</FrameLayout>


fragment_main.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    <fragment
        android:id="@+id/fragment_1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.mokelab.fragment.ChildFragment"/>
</FrameLayout>

Activity -> MainFragment -> ChildFragment という感じで貼り付けてあるね。N(API 25)までだと、クラッシュしちゃう。

でもこれをAndroid O(API 26?)で実行すると、問題なく表示されるようになったよ!
詳しくはI/O 2017の動画も見てねー♪

ちなみに、ActivityにMainFragmentを貼り付ける部分をJava(Kotlin)コードで動的に追加してあげると、N以前でもクラッシュせずに済むよー


2017年5月23日火曜日

Google I/O 2017 参加レポート!

みんなー、こんにちはー

なんと代表が2年連続でGoogle I/Oのチケットにあたったので、Google I/O 2017@Mountain Viewに参加してきたよー!

今年も去年と同様にサンフランシスコ市内に泊まっての参加!

個別の発表内容はいろんな方が紹介してるので、ボクは参加の様子をお伝えするね♪

0日目

今年も去年と同じく、前日に参加バッヂの受け取り!早く受付すると前のほうの席に座れる仕組みだよ。なので朝5時に起きて朝6時のCalTrainに乗ってMoutain Viewまで行ったよ!

無事にバッヂをゲット。今年はバッヂの受け取り時に他の日本人参加者もいたので、Google Storeまでお買い物♪

(この時、ここでのお買い物が悲劇をうむとは誰も想像できなかった)

1日目

1日目は基調講演(Keynote)からスタート!朝5時に起きて朝6時20分のシャトルバスに乗ってサンフランシスコからマウンテンビューに移動。7時半前についたけど、今年はもう朝ごはんの提供が始まってた!

今年はKeynoteが2部構成。最初は新しいサービスなどの発表がメインだったよ♪Google Lens、試してみたいねー

最初のKeynoteの動画は、ここー


お昼ご飯を1時間で食べて午後のKeynote第2部!こちらは開発者向けのKeynoteだよー。一番大きな発表はAndroidでKotlinが正式採用されたことだよね!

そして最後に、I/O参加者全員にGoogle Homeと$700分のGoogle Cloud Platformクレジットをプレゼント!Google Homeは日本でも発売決定!

今年のI/Oは、セッションを事前に予約できるシステムが導入されてたよ。予約がとれていたら、確実にセッションが聞ける仕組み!Google I/O 2016では「列に並んだけど入れない」みたいなのばっかりだったから、大きな改善!

2日目

2日目は朝からずっとセッションを聞いてたよ。最初のセッションが朝8時半から始まるので、結構大変><

今回のI/Oでは、Android Payを使った企画もあったよ。Android Payのポイントカード機能を使って、4ポイント貯めたらドロイド君をGET!端末は、こんな感じー

3日目

3日目はセッション数が減るけど、今年はKotlinの発表があったのでKotlinに関するセッションが2つ追加!

1つ目はIntroduction to Kotlin。Kotlinだとこんな感じにかけるよーというライブデモだったよー
2つ目はLife is Great and Everything Will Be Ok, Kotlin is Here。スピーカーがNon-Googlerという珍しいセッション。
  • PinterestのChristina Lee
  • SquareのJake Wharton
ButterKnifeをはじめとする著名ライブラリに関わっているJake WhartonはKotlinの面白い使い方を。Christina Leeは組織に新しいものを持ち込む難しさを紹介!

3日目はあまりにも暑い日だったので、一番大きな会場でのセッションが一部移動になったりとかもあったよ。日中はとても暑く、朝晩は冷えるのがこの地域の特徴><


3日目は他にも日本人Googlerがスピーカーというセッションもあったよー!


来年も、いけるといいなー。Google I/O!

2017年2月2日木曜日

[Android]BottomNavigationViewの選択状態を変更する

こんにちはー!

Androidアプリを書いてるみんなは、BottomNavigationViewを上手に使えてるかな?
今日はそのBottomNavigationViewに関するおはなしー!

BottomNavigationViewはidをつけても選択状態は保存してくれないんだ。(support v25.1.1で確認)
なので、Viewの再生成時は自分で選択状態を戻さないといけないね。

BottomNavigationViewの選択状態を変更するには、次の2通りがあるよ
  • setChecked(true)を使う方法
  • performClick()を使う方法

setChecked(true)を使う方法

こちらは選択状態「だけ」を変更するよ。Viewの状態復元にはこちらがいいかな?

Menu menu = bottomNavigationView.getMenu();
MenuItem menuItem = menu.getItem(position);
menuItem.setChecked(true);

performClick()を使う方法

こちらは項目をタップしたのと同じ動作になるよ。findViewById()でViewがとれるのはたまたまな気はするけど。。。

Menu menu = bottomNavigationView.getMenu();
MenuItem menuItem = menu.getItem(position);
bottomNavigationView.findViewById(menuItem.getItemId()).performClick();

今回のサンプルをgithubで公開したので、動かしてみてねー!

2016年12月12日月曜日

TypeScript 2.1.4でXMLHttpRequestのコールバックがコンパイルエラー

みんなー!

TypeScriptで、次のコードをコンパイルしてみよう!


var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://tech.mokelab.com', true);
xhr.onload = function() {
    console.log(this.response);
};
xhr.send();

onloadプロパティにセットするコールバック関数は、呼ばれる時、thisにXMLHttpRequestがセットされる。だから、アローではなくfunctionを使うんだったね!

でもこのコード、TypeScript 2.1.4でコンパイルすると次のようなエラーがでちゃう。


moke.ts(4,22): error TS2339: Property 'response' does not exist on type 'XMLHttpRequestEventTarget'.

responseっていうプロパティはXMLHttpRequestにあるはずなんだけど、なぜか型がXMLHttpRequestEventTargetになってて、「そんなプロパティは知らないよ」というコンパイルエラーが出てるね。

この動きを理解するために、TypeScript 2で導入された構文を確認しよう!TypeScript 2で、コールバック関数をセットするプロパティに、thisの型が指定できるようになったんだ。こんな感じで定義を書くよ。


interface Moke {
    onclick: (this: MyObject, ev: Event) => any;
}

第一引数名がthisで、型を「実際に呼ばれる時にthisにセットされている型」にすると、function()の中でも適切に型チェックしてくれる!

そして本題のXMLHttpRequestのコールバック部分。2.1.3までは次のようになっているよ(一部のみ抜粋)。

interface XMLHttpRequestEventTarget {
    // onloadのみ掲載
    onload: (this: this, ev: Event) => any;
}

これが、TypeScript 2.1.4だとこうなってた!

interface XMLHttpRequestEventTarget {
    // onloadのみ掲載
    onload: (this: XMLHttpRequestEventTarget, ev: Event) => any;
}

もちろん実際に呼ばれるときは、XMLHttpRequestEventTargetを実装しているXMLHttpRequestがthisにセットされるよ。 なので、いまひとつだけど次のようにキャストするしかないみたい。。。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://tech.mokelab.com', true);
xhr.onload = function() {
    console.log((<XMLHttpRequest>this).response);
};
xhr.send();

同じ問題でハマった人の助けになるといいなー

2016年10月18日火曜日

[Golang]Goの型付き比較をみてみよう

みんなー!モケラだよ

今日は、Go言語の型のおはなしだよ。

Goは、こんな風に知ってる型の別名を作れるよね!


type Moke int

このMokeという型はintの別名で、キャストするには次のように書くんだったね!

val :=  Moke(1)
val2 := int(val)

じゃあ、意味としては同じ1を次のようなmapにいれると、どうなるかな?

type Moke int
type Piyo int

func main() {
        hash := map[interface{}]string{
                Moke(1): "Moke",
                Piyo(1): "Piyo",
        }
        fmt.Printf("1 = %s\n", hash[1])
        fmt.Printf("Moke(1) = %s\n", hash[Moke(1)])
        fmt.Printf("Piyo(1) = %s\n", hash[Piyo(1)])
}

1が3個mapに入ってるね。実行結果はこちら!
$ go run main.go
1 =
Moke(1) = Moke
Piyo(1) = Piyo

比較は、型も考慮してやってくれるのが、Go言語!

2016年6月4日土曜日

[Android]FCM(Firebase Cloud Messaging)をいれようとして大ハマりしたおはなし

みんな、こんにちはー!

Google I/O 2016で、AndroidへのPush通知の仕組みがGCMからFCM(Firebase Cloud Messaging)に移行されるという発表があったね!

そこでモケラボも、既存のアプリを1つ、GCMからFCMに移行してみたよ!

とっても簡単かなと思ってたけど、世の中そんなに甘くないね! ちゃんとPushが届くのを確認するまでに1人日(1モケラ日?)かかっちゃった。

AndroidアプリへのFCMの導入は簡単。GCMの時よりもっと簡単になってたよ!これからおはなしする問題に遭遇しなければね!

詳細はTech sheets でまとめるので、ここでは概要だけ紹介するね。

FCMを導入しよう

FirebaseのWebコンソールでプロジェクトを作る

検索するとたくさんでてくると思うので、ここは省略するね。

FirebaseのWebコンソールで、Androidアプリを登録する

アプリのパッケージ名をいれよう。SHA-1は空っぽでもだいじょうぶ
アプリを追加すると、勝手にgoogle-services.jsonのダウンロードが始まるよ。

google-services.jsonをapp配下に配置する

これは、ちょっと前のGCMと同じだね!ちなみにProductFlavorを使ってる場合は、app/src/Flavor1/google-services.json のように、app直下じゃなくてFlavorの配下に置こう。

ルートのbuild.gradleを編集する

dependenciesのところを次のようにするよ。
dependencies {
    classpath 'com.android.tools.build:gradle:2.1.0'
    classpath 'com.google.gms:google-services:3.0.0'
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}

appのbuild.gradleを編集する

dependenciesに
compile 'com.google.firebase:firebase-messaging:9.0.1'
を追加しよう。そしてファイルの一番最後の行
apply plugin: 'com.google.gms.google-services'
を追加しよう。

これだけ!

実はこれだけでOKなんだ。初期化のプログラムを書く必要すらなくなってる!サンプルを動かすとちゃんとPushとどいたよ!

でも既存のアプリにいれたら動作しなかった!

ここからが本題。既存のアプリにいれたらなぜか動かなかったんだ。モケラボでいろいろ原因調査したので、その過程もセットでどうぞ!

トークンを取ろうとすると例外を投げる

FCMのトークンは
String token = FirebaseInstanceId.getInstance().getToken();
で取得できるよ。でもうまく動いていないときは、このメソッドが例外を投げるんだ。
java.lang.IllegalStateException: FirebaseApp with name [DEFAULT] doesn't exist.

このFirebaseAppというのがキーワードみたいだね。ということで次にFirebaseAppについて調査してみたんだ。すると。。

FirebaseAppが1つもみつからない

FirebaseAppというオブジェクトは、
List<FirebaseApp> apps = FirebaseApp.getApps(this); // thisはContext
で取得できるよ。ちゃんと動作していれば1つ以上返ってくるよ。

でも動かなかった時はこのメソッドが長さ0のリストを返していたんだ。さっきの例外はこれが原因だったのかな?

小さいプロジェクトだと1つ返ってきて、ある程度の規模のプロジェクトでなぜか1つもとれないのはなぜだろう。と、その前にこのFirebaseAppはどこで生成されてるんだろう?

Applicationクラスをこっそり差し替えたりするのかな?と思って、あえて独自のApplicationクラスをいれてみたけど、結果は同じ。ここではなさそう。

ログを眺めてみた

次に、うまくFirebaseAppが取得できた時と、失敗してる時のログをぼんやり眺めてみたよ。すると、うまくいっている時にはこんなのが見つかったよ!

06-04 07:10:09.180 9933-9933/com.mokelab.fbdemo I/FirebaseInitProvider: FirebaseApp initialization successful

「FirebaseAppの初期化に成功したよ!」というログだね。失敗してる時にはこれ無かったんだ。と、ここに新キャラの「FirebaseInitProvider」というのがいるね。Providerって名前がついてるから、きっとAndroidManifest.xmlにいるよね。

AndroidManifest.xmlを見てみた

サンプルではAndroidManifest.xmlを全く編集していないので、きっとビルド時に追加されてると思ってbuild/intermidates/manifests/の下に生成されているAndroidManifest.xmlを見てみたよ。

 <provider
   android:name="com.google.firebase.provider.FirebaseInitProvider"
   android:authorities="com.mokelab.fbdemo.firebaseinitprovider"
   android:exported="false"
   android:initOrder="100" />

やっぱりいたね!ということは、なぜかマージされないAndroidManifestが今回の原因だったみたい。マージされないのなら自分で書けばいいや!ということで、アプリ本体のAndroidManifest.xmlに次の項目を追加したらちゃんと動くようになったよ!一部アプリのパッケージ名があるのでそのままコピペして動きません!という苦情コメントは受け付けないよ!

<receiver
    android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

        <category android:name="com.mokelab.fbdemo" />
    </intent-filter>
</receiver>

<!--
     Internal (not exported) receiver used by the app to start its own exported services
     without risk of being spoofed.
-->

<receiver
    android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
    android:exported="false" />

<!--
     FirebaseInstanceIdService performs security checks at runtime,
     no need for explicit permissions despite exported="true"
-->

<service
    android:name="com.google.firebase.iid.FirebaseInstanceIdService"
    android:exported="true" >
    <intent-filter android:priority="-500" >
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
    </intent-filter>
</service>

<provider
    android:name="com.google.firebase.provider.FirebaseInitProvider"
    android:authorities="com.mokelab.fbdemo.firebaseinitprovider"
    android:exported="false"
    android:initOrder="100" />

残る謎

今回解決できていないのは、どのような条件の時に、ライブラリプロジェクトのAndroidManifestの項目がマージされないかということ。だれか詳しい人、教えて!