「Realm Report」にみるモバイル開発のトレンド

Realm Report Q4 2017

モバイル開発者が利用している言語は?

モダンな言語に移行が進んでいる

今現在, モバイルプラットフォームでは7つの主要言語がありますが割合は急速に変化しています. iOS開発者においては, Swift が登場しましたが, 今では Objective-C を超えています.

Androidでは, Javaがピークに達しており Kotlin のAndroidプラットフォーム獲得が進んでいます.

 

世界で一番モバイル開発の進んでいるのは?

優秀な開発者はどこにでもいる

Realm を利用した開発者は基本的なデフォルトの選択を超えており, 193カ国からコードがプッシュされていますが, 上位10カ国は世界中に均等に分布しています.

現在, 開発者が注目している技術やプラットフォームの興味深い違いをこの分析に見ることができます.

 

iOSは西欧を拠点としている

西欧の開発者は iOS 11 beta にすばやく注目しており, 全開発者の10%以上が, ドイツとアメリカにおいて iOS11beta を利用しています.

 

西欧の開発者は iOS の新技術に素早く対応している

西欧諸国の開発者は新しい Realm の iOS バージョンに非常に早くアップグレードしています.

 

西欧の開発者はiOS8のような古いバージョンは素早く切り捨てています

新しい技術に対応することは, 古いバージョンやデバイスのサポートをやめることになることになることがあります. 西欧諸国では, そのトレードオフのほうを好んでいます,

 

iOSに関しては, ドイツとアメリカが常に上位 (イギリスが3位)

iOS11対応やRealm対応, 古いiOSバージョンを切り捨てるスピードでは3カ国の開発者は他を圧倒しています. イギリスは開発者数では12番目ですが, 対応の速さでは常にドイツとアメリカに続いています.

 

Androidではアジアがリードしている

iOS では, アプリビルド数ではアメリカが上位ですが, Android Oreo 対応では低迷しており, ロシアや日本が上位となっています.

 

Android版Realmの対応割合の上位ははっきりしない

Android版Realmの対応状況はアジア諸国の開発者は新技術を追うことに対してより保守的であることを示しています.

 

Kotlin 対応はアメリカがさらに加速していくかも知れない

Android に Kotlin が新しい風を起こしており, アメリカ開発者のプラットフォームの将来を動かすかもしれない.

 

モバイル開発全体ではドイツが明快にトップ

ドイツのモバイル開発が両プラットフォームでのトップで Android とiOS の新バージョンや技術の対応も最も素早い.

 

Kotlin が今 Android 生態系を変えようとしている

Google I/O 2017 以降, Kotlin の割合は劇的に世界中で増加しています.

 

まとめ?

iPhoneXは4Kテレビと同じにおいを感じるんですよ。「技術としてスゴい」ことと「便利」なことって似ているようで違いますよね。んで、iPhoneXは、技術を追求するために便利さを捨てている気がするんですよ。

ホリエモン×ひろゆきが分析するiPhoneXの新機能「技術を追求するために便利さを捨てている」 - IT・テクノロジー - ニュース|週プレNEWS[週刊プレイボーイのニュースサイト]

アジアでありながら西欧諸国のようにiPhoneの販売台数割合は大きい日本ですが, 今後, 「古いOSやデバイスを考慮しない」という考え方でそれらを切り捨てていくという西欧スタイルの考え方は今後もさらに浸透していくのでしょうか.

そして, 新しい技術への対応や古いOSを切り捨てることに対してのより保守的なアジアスタイルの考え方はやは今後も縮小していくのでしょうか.

Realm Report Q4 2017


Dagger に馴染めない人のためのいくつかの原則

Keeping the Daggers Sharp ⚔️ – Square Corner Blog – Medium

Dagger2 は 素晴らしい Dependency Injection ライブラリですが, なかなか上手に使いこなせません.

分かりやすくするための考え方や実装方法をいくつか見てみましょう.

フィールドよりコンストラクタのインジェクションを使う

フィールドインジェクションは, finalでなく, privateでないフィールドに使います.


// BAD
class CardConverter {

  @Inject PublicKeyManager publicKeyManager;

  @Inject public CardConverter() {}

}

フィールドに @Inject を忘れると NullPointerException の原因となります.


// BAD
class CardConverter {

  @Inject PublicKeyManager publicKeyManager;
  Analytics analytics; // Oops, forgot to @Inject

  @Inject public CardConverter() {}

}

コンストラクタインジェクションはイミュータブルですので, 局所的な状態を持ちませんのでスレッドセーフにつながります.


// GOOD
class CardConverter {

  private final PublicKeyManager publicKeyManager;

  @Inject public CardConverter(PublicKeyManager publicKeyManager) {
    this.publicKeyManager = publicKeyManager;
  }

}

Kotlinでは, さらに簡素化してくれます.


class CardConverter

@Inject constructor(
  private val publicKeyManager: PublicKeyManager)

それでも, フィールドインジェクションを使いたい場合は以下のようになります.


public class MainActivity extends Activity {

  public interface Component {
    void inject(MainActivity activity);
  }

  @Inject ToastFactory toastFactory;

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Component component = SquareApplication.component(this);
    component.inject(this);
  }

}

Singleton はあまり使う必要はない

ミュータブルな状態に対して多くのアクセスが必要な場合は Singleton は便利です.


// GOOD
@Singleton
public class BadgeCounter {

  public final Observable<Integer> badgeCount;

  @Inject public BadgeCounter(...) {
     badgeCount = ...
  }

}

しかし, 変化しない状態に対しては Singleton にする必要はありません.


// BAD, should not be a singleton!
@Singleton
class RealToastFactory implements ToastFactory {

  private final Application context;

  @Inject public RealToastFactory(Application context) {
    this.context = context;
  }

  @Override public Toast makeText(int resId, int duration) {
    return Toast.makeText(context, resId, duration);
  }

}

まれに, 作成のコストがかかるキャッシュインスタンスの走査に使うことがあります. そうすることで, 繰り返し作成して破棄することを避けることができます.

@Provides でなく @Inject を使う

@Provides はコンストラクタを複製すべきではありません.
関係する部分を一つにするとコードがわかりやすくなります.


@Module
class ToastModule {

  // BAD, remove this binding and add @Inject to RealToastFactory
  @Provides RealToastFactory realToastFactory(Application context) {
    return new RealToastFactory(context);
  }

}

このことは, 特に singleton においては重要です. そのクラスを読むときの重要な実装の内容一覧となります. 一部分をみればすべての内容が把握できます.


// GOOD, I have all the details I need in one place.
@Singleton
public class BadgeCounter {

  @Inject public BadgeCounter(...) {}

}

static @Provides を使う

@Provides は static にすることができます.


@Module
class ToastModule {

  @Provides
  static ToastFactory toastFactory(RealToastFactory factory) {
    return factory;
  }

}

モジュールインスタンスを作成する代わりに, 直接メソッドを実行することができます. このときそのメソッドの呼び出しはコンパイラによってインライン化されています.


@Generated
public final class DaggerAppComponent extends AppComponent {

  // ...
  @Override public ToastFactory toastFactory() {
    return ToastModule.toastFactory(realToastFactoryProvider.get())
  }

}

一つだけのメソッドを static にしてもあまり変化はないですが, すべてを static にすると, かなりのパフォーマンスが向上します.

また, モジュールを abstract にすると, static でない @provides メソッドが ひとつでもあるとコンパイルに失敗します.

@Provides よりも @Binds を使う

あるタイプを他にマッピングするときは @Provides でなく @Binds を使う.


@Module
abstract class ToastModule {

  @Binds
  abstract ToastFactory toastFactory(RealToastFactory factory);

}

このメソッドは abstract でなければなりません. @Generated コードは実装内容をそのまま使おうとします.


@Generated
public final class DaggerAppComponent extends AppComponent {

  // ...
  private DaggerAppComponent() {
    // ...
    this.toastFactoryProvider = (Provider) realToastFactoryProvider;
  }

  @Override public ToastFactory toastFactory() {
    return toastFactoryProvider.get();
  }

}

@Singleton の interface binding は避ける

Statefulness is an implementation detail

集中するアクセスがミュータブルな状態にアクセスする必要があるかは実装のみが知っていますので, 実装をインターフェースにバインドさせるとき, アノテーションをつけるべきではありません.


@Module
abstract class ToastModule {

  // BAD, remove @Singleton
  @Binds @Singleton

  abstract ToastFactory toastFactory(RealToastFactory factory);

}

error-prone を使おう

一般的な Dagger のエラーはこれを使うことで分かりやすく検出できます.

👉 MVVM で Hilt のパターン化 💉  hatena-bookmark


Android 8.0 Oreo「Picture in Picture」を実際に確認する

YouTube で使うには YouTube Red のサブスクリプションが必要.

YouTube's Picture-in-Picture mode in Oreo is only available to paying Red customers in five countries, and that's a problem

よって, 日本国内 YouTube では, 現在, アプリ・ブラウザ共に使えない模様.

Chrome上でのYouTube以外の動画 や GoogleMap のナビゲーションモード では国内でも使える.

設定からインストールしている*実装上は*対応しているアプリを把握できる.

[設定]
↓
[アプリと通知]
↓
[特別なアプリアクセス]
↓
[PinP]

動画を表示するアプリを中心に対応が進んでいくのでしょうね.

WhatsApp update includes picture-in-picture calls for Android 8.0 Oreo | Tech | Life & Style | Express.co.uk


開発者が見ておくべき /r/androiddev にみるAndroid環境の王道

それなりに王道の本筋ではないかと思えますがどう思います?

/r/androiddev survey time! : androiddev

以下, 箇条書きに.

端末は Google Nexus シリーズ.

Root化やROMカスタマイズはしない.

Linux はラップトップで Ubuntu (Unity).

Windows はデスクトップで10.

Mac はラップトップで 10.12 Sierra.

主携帯は Android, タブレットは持っていない.

開発OS は OSX Intl Core i7, SSD, メモリ16G.

アプリにタブレット向けデザインは作成しない.

minSdkVersion は 16 (4.1.x).

アーキテクチャは MVP.

DIライブラリは Dagger 2.

Rx ライブラリは RxAndroid + RxJava2.

代替のフレームワークは Cordova.

データのシリアライズは Gson.

データ永続化は Realm.

ネットワーク処理は Retrofit.

画像読込みは Picasso.

Viewバインドは ButterKnife.

思ったより RxJava2 の躍進が早いなあと思いました.


LINEbot でオウム返し 2017 PHP版

やり方調べるのになぜか時間がかかる.

なんだかつらたん.

ただ, オウム返ししたいだけなのに!

しくみ

LINEサーバを介して自分のサーバ内のスクリプトで受け取ります.

スクリプト作成の前に LINE管理画面で,

- アクセストークンをもらう
- サーバー内スクリプトまでのURLを教えておく

スクリプト

LINE側 API はわかりやすい仕様で作られています.


<?php

$access_token = /* your access token */;

$url = 'https://api.line.me/v2/bot/message/reply';

// receive json data from line webhook
$raw = file_get_contents('php://input');
$receive = json_decode($raw, true);

// parse received events
$event = $receive['events'][0];
$reply_token  = $event['replyToken'];
$message_text = $event['message']['text'];


// build request headers
$headers = array('Content-Type: application/json',
                 'Authorization: Bearer ' . $access_token);

// build request body
$message = array('type' => 'text',
                 'text' => $message_text);

$body = json_encode(array('replyToken' => $reply_token,
                          'messages'   => array($message)));


// post json with curl
$options = array(CURLOPT_URL            => $url,
                 CURLOPT_CUSTOMREQUEST  => 'POST',
                 CURLOPT_RETURNTRANSFER => true,
                 CURLOPT_HTTPHEADER     => $headers,
                 CURLOPT_POSTFIELDS     => $body);

$curl = curl_init();
curl_setopt_array($curl, $options);
curl_exec($curl);
curl_close($curl);

結果

どんだけつらたんなのか!