phicdy devlog

Androidアプリ開発やその他技術系の記事をたまに書きます

AndroidとかiOSとかモバイル多め。その他技術的なことも書いていきます。

既存アプリのNavigation導入

Navigationを新規で入れるのは楽そうだなーと思いつつも現実的には既存アプリに入れることが多いと思う。自分の既存アプリに対して入れてみる。

developer.android.com

環境

起動ActivityをFragmentに移行する

一旦Toolbar等も含めて全部コピーしてFragmentに移してしまう。

メニューをFragmentに移す際は setHasOptionsMenu(true) を呼ばないとメニューが表示されない。 安直に移行すると差分が大きくなってGitの履歴が消えてしまう恐れがあるのでので一旦ファイル名&クラス名をFragmentに変えただけでコミットしておくと安心。

AndroidXになっていないFragmentをAndroidXに移行する

Navigationで使用するFragmentがAndroidXのものでないと遷移時にClassCastExcetpionでクラッシュする。 そんなことないだろと思うが私の場合は PrefrenceFragment がAndroidXになっていなかったので移行した。

Navigation導入

def navigation_version = '2.2.1'
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"

res/navigation/nav_graph.xml を作る。

  • app:startDestination で最初のFragmentを指定
  • fragmentにはlabelをつけておく
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.phicdy.navigationsample.MainFragment"
        android:label="main"
        tools:layout="@layout/fragment_main" />
</navigation>

Activityのレイアウト内の<fragment>NavHostFragment にする

    <fragment
        android:id="@+id/navHostFragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

遷移を指定

最初はUIから追加したが2個目からは直接XML更新してた

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.phicdy.navigationsample.MainFragment"
        android:label="main"
        tools:layout="@layout/fragment_main">
        <action
            android:id="@+id/action_mainFragment_to_secondFragment"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.phicdy.navigationsample.SecondFragment"
        android:label="second"
        tools:layout="@layout/fragment_second" />
</navigation>

おわりに

とりあえず最初の導入まではすっといけた。他にもdeep linkやらsafe argやら他の機能もあるのでキャッチアップしていく。マルチモジュールで一覧→詳細→一覧みたいに循環して移動できる場合どうなるのかも気になる。

Thinkpad X1 Carbon 7th(2019)を買いました

ある日Macbook Proの電源を入れると画面が映らなくなってしまいました... 外部ディスプレイに繋げば使えるのでiMacとして使えますがさすがに手元で使えないのは不便なので買い換えることにしました。 色々悩んだ結果、Thinkpad X1 Carbon 7th(2019)に乗り換えてWindowsUbuntuデュアルブート運用してみることにしました。

f:id:phicdy:20200204000232j:plain

f:id:phicdy:20200204000254j:plain

f:id:phicdy:20200204000320j:plain

f:id:phicdy:20200204000336j:plain

スペック

セールで価格で252,560円でした。

  • インテル Core i7-10710U プロセッサー (1.10GHz, 12MB)
  • Windows 10 Home 64bit - 日本語版
  • 16GB LPDDR3 2133MHz (オンボード)
  • 512GB ソリッドステートドライブ + 32GB インテル Optane メモリー (M.2 2280, PCIe-NVMe)
  • 14.0型UHD液晶 (3840x2160 IPS 500nit) 光沢あり
  • ブラック
  • ブラック・ペイント・カーボン
  • 内蔵グラフィックス
  • IR & 720p HDカメラ(マイクロフォン付)
  • インテル Wireless-AC 9560 (2x2) + Bluetooth (vPro対応)
  • WLANアンテナ
  • 指紋センサー
  • 日本語キーボード(バックライト、指紋センサー付) ブラック
  • TPMあり(TCG V2.0準拠,ハードウェアチップ搭載)
  • BIOS Absolute有効
  • 4セル リチウムイオンバッテリー (51Wh)
  • 45W ACアダプター (2ピン)(USB Type-C)
  • 14.0型UHD液晶 (3840x2160 IPS 500nit) 光沢あり、ThinkShutter付きIR&HDカメラ、マイク、HDR、ブラック
  • リテールパッケージ
  • 1年間 引き取り修理

使用感

やはり軽いというのは素晴らしいです。1.09kgしかないので片手でも楽々持てます。 ほぼ180度まで開くので膝において作業するときに作業しやすいです。 キーボードはやはりMacbookより打ちやすいです。Thinkpadおなじみの赤ポチも使いやすいです。 4Kディスプレイは自分の顔がわかるくらい反射して結構気になるのでアンチグレアフィルムを買いました。

https://www.amazon.co.jp/gp/product/B00CL6J246

以下はメモです。

Ubuntu導入手順

まずUbuntu 18.04はどうやってもWi-Fiがつながらなかったりスリープからの復帰ができず、なんとかできるようになったと思ったらWindowsが起動できなくなり、諦めるまで時間が掛かりました(おそらくカーネルのバージョンの問題?)。Ubuntu 19.10にしてからは今のところ問題なく動いています。

インストールUSB作成

https://www.ubuntulinux.jp/download/ja-remix からubuntu-ja-19.04-desktop-amd64.isoをダウンロード

Windows管理ツール → コンピュータの管理を管理者として実行 はどうやっても 記憶域 → ディスクの管理 → Windows(C:) → ボリュームの縮小 で縮小後のサイズが希望のサイズになるように調整

Secure bootを切る

再起動してLenovoロゴの画面でF1を押してBIOS設定へ 

推奨するBIOSの入り方 - ThinkPad, ThinkCentre, ThinkStation - MO

Security → Secure boot → Off

インストール

再起動してLenovoロゴの画面でF12を押してBoot設定へ 行きUSBから起動

USBドライブから起動する方法 - ThinkPad - IT

Try Ubuntu without Install Ubuntuを選択

gpartedを起動

縮小したできたパーティションを削除

編集→保留中の全ての操作を適用する

Ubuntu 19.04のインストールからインストール

512MBブート用に取って/bootに割り当てる。残りを本体用の/に割り当てる

BIOS → Config → Power → Sleep State → Linuxに変更

ソフトウェアの更新から19.10にアップデート

参考

linuxfan.info

www.abten.net

9時間ずれる問題

sudo timedatectl set-local-rtc true

ダウンロードフォルダなどの日本語フォルダを英語にする

LANG=C xdg-user-dirs-gtk-update

ファイアウォールを有効にする

apturl apt:chrome-gnome-shell

addons.mozilla.org

sicklylife.jp

時計を右に移動

https://extensions.gnome.org/extension/2/move-clock/

Google Drive

標準の設定からのログインだとアプリから使えないので https://github.com/odeke-em/drive を使う

Docker

https://docs.docker.com/install/linux/docker-ce/ubuntu/

19.10用のDockerがまだないので19.04のDockerをインストール

https://qiita.com/EilinenTuuli/items/cdd73c4b37ff710aab0f

Docker compose

https://docs.docker.com/compose/install/

Albert

sudo apt install curl
curl https://build.opensuse.org/projects/home:manuelschneid3r/public_key | sudo apt-key add -
sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/manuelschneid3r/xUbuntu_19.10/ /' > /etc/apt/sources.list.d/home:manuelschneid3r.list"
sudo apt update
sudo apt install albert

https://www.virment.com/how-to-setup-albert-ubuntu/

設定からAutostart on loginにチェックを入れて自動起動

tlp

https://matoken.org/blog/2019/08/10/try-the-power-management-software-tlp-gui-tlpui/

mcomix

aptからインストールすると起動エラーになるので下記から最新版をダウンロード

https://launchpad.net/ubuntu/+source/mcomix/1.2.1mcomix3+git20191129-1

python3 mcomix/mcomixstarter.py

LINE

https://qiita.com/north_redwing/items/357bf0cfbc17990a5276

Visual Studio Code

snapで入れるとバグがあり日本語が入力できない https://teratail.com/questions/121986

公式ページからdebファイルをダウンロードしてインストールする https://code.visualstudio.com/download

sudo dpkg -i code_1.41.1-1576681836_amd64.deb 

python環境 https://dev.classmethod.jp/tool/python-pyenv-vscode/

Android Studio

https://developer.android.com/studio/install?hl=ja#linux

sudo apt install openjdk-8-jdk    
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386

Chrome

フリーズ対策 https://freefielder.jp/blog/2019/10/ubuntu-chrome-freeze-settings.html

Firefox

Back spaceで戻る:about:configでbrowsudo apt install openjdk-8-jdkser.backspace_actionを0にする

2019年振り返り

2019年ももう終わりなので1年を振り返ろうかなと思います。

リニューアルプロジェクト

今年の1月〜4月は昨年から続くリニューアルプロジェクトをやっていました。今は7人いるAndroidエンジニアもこの時はまだ私ともう1人しかAndroidエンジニアがいなくてヒーヒー言ってた記憶があります。リリースにはこぎつけたもののだいぶスコープを削った結果なので結局10月辺りまで削った分の残件をやっていました。大規模な新機能開発がある中でのアーキテクチャの見直しやリファクタリングは良くないと諦めたこと、リニューアルによる大規模テストに合わせて溜まってたライブラリアップデートをできたのは良かったかなと思います。当時はKotlin Coroutines使い始めで0.xから1.0にアップデートするのが大変でした。

スペイン&フランス旅行

ゴールデンウィークには念願だったスペイン旅行に行ってきました。

f:id:phicdy:20191227195825j:plain

f:id:phicdy:20191227200244j:plain
パエリア最高

カンプ・ノウで一番いい席を取って試合を見れたのが最高でした。メッシが後半からでてきて決勝ゴールとなりバルセロナの優勝が決定した試合でした。優勝パレードも見れて大満足でした。

f:id:phicdy:20191227195812j:plain

f:id:phicdy:20191227200042j:plain

フランスは凱旋門とかよかったんですが、とにかく治安が悪くてあまりいい体験はなかったです...

f:id:phicdy:20191227200402j:plain

リードエンジニア

帰国して5月からはリードエンジニアとして働き始めました。社内の開発プロセスが新しいものになり、新しい働き方に慣れてなかった感じがしてます。何個もプロジェクトマネジメントしていると自分の実装時間がなくなったり、コードレビューがめっちゃ溜まったり、どうしたもんかなと悩んでた気がします。今はチームが2つに分かれてだいぶ負担は減ったものの、どうやっていくのが一番いいんだろうなと悩む毎日です。来年は引き続きエンジニアとして働きつつもプロジェクトマネジメント方面にも力を入れる割合を増やしていこうかなと思います。

今年やった取り組みとは会社ブログにまとめました。

buildersbox.corp-sansan.com

Fluxに向き合う

ちょうどチームが2つに分かれた辺りでチーム内でアーキテクチャについて話し合い、Fluxに向き合うようになりました。まず今年は基盤を作ってこういう風にやっていきましょうというのを出せたのがよかったかなと思います。当時は進捗が2週間くらい遅れており、100コメント超えのPRを3つマージすることでようやく基盤の初期案が完成しました。Fluxを横展開していく中でまだまだ課題はあり、徐々に改善していける段階まで来ているので来年はより横展開していき、基盤をよりよいものにできていけたらなと思います。

Flux移行の詳細は会社ブログに書きました。

buildersbox.corp-sansan.com

Kotlin Fest & Droidkaigi落ちる

今年は初めてカンファレンスにCfPを出しました。しかし両方とも落選...来年はもうちょっとネタやCfPの書き方を工夫して通るようにできたらなと思います。

Macbook Pro壊れる

12月の中頃、突然MacBook Pro (15-inch, 2016)のディスプレイがつかなくなりました。色々対処してみたものの直らず、外部ディスプレイに繋げるしか使う方法がなくなってしまいました。Apple Storeに持っていったら修理代見積もりは88000円でした... Macbook Pro 16-inch 2019を買うか悩みましたが高すぎるのでThinkpad X1 CarbonにUbuntuを入れてみるのに挑戦してみることにしました。

f:id:phicdy:20191227202012p:plain

年末に届くか微妙ですが楽しみです。

おわりに

こう振り返ってみると色んなことをした1年だったなと思います。また来年もAndroidエンジニアとして頑張りたいと思います。

Kotlin Coroutinesで並列にListの各項目を処理する

coroutineScope {
    list.map { async { updateListItem(it) } }
        .map {
          val updated = it.await()
          ... 
        }
}

最初のmapで処理を走らせてしまうと処理待ちになって逐次実行になる。そのためまず最初のmapですべてのListの項目の処理を遅延実行し、次のmapで各処理の結果を待つ。

ちなみに最初Flowでできないか試したが、Flowはイベントストリームのように逐次実行されるのでListの各項目を並列に処理するのには向いていない。

list.asFlow()
        .map {
            Timber.i("start async ${it.title} start")
            coroutineScope { async { updateListItem(it) } }
        }
        .map {
            Timber.i("start await ${it} start")
            it.await()
        }

とすると

2019-12-17 21:29:55.426 11681-11681/com.phicdy.sample I/Hoge$updateAll$$inlined$map: start async list1 start
2019-12-17 21:29:55.434 11681-11808/com.phicdy.sample I/Hoge$hoge: DefaultDispatcher-worker-3 list1 updateListItem start
2019-12-17 21:29:56.856 11681-11808/com.phicdy.sample I/Hoge$hoge: DefaultDispatcher-worker-3 list1 updateListItem done
2019-12-17 21:29:56.858 11681-11681/com.phicdy.sample I/Hoge$updateAll$$inlined$map: start await DeferredCoroutine{Completed}@d1d861 start
2019-12-17 21:29:56.863 11681-11681/com.phicdy.sample I/Hoge$updateAll$$inlined$map: start async list2 start
2019-12-17 21:29:56.881 11681-11808/com.phicdy.sample I/Hoge$hoge: DefaultDispatcher-worker-3 list2 updateListItem start
2019-12-17 21:29:58.045 11681-11807/com.phicdy.sample I/Hoge$hoge: DefaultDispatcher-worker-2 list2 updateListItem done
2019-12-17 21:29:58.046 11681-11681/com.phicdy.sample I/Hoge$updateAll$$inlined$map: start await DeferredCoroutine{Completed}@ef2999d start

と逐次実行になってしまう。

マルチモジュールのプロジェクトでdanger-android_lintを使う

Dangerでandroid lint結果をPR上でコメントしようと思うとdanger-android_lintを使うことになるがマルチモジュールに対応しておらずレポートを1つしか指定できない。

github.com

マルチモジュールに対応する1つの方法はレポートをマージする。

qiita.com

blog.bitrise.io

もう1つの方法が今回採用したやり方で、あらかじめlintを実行しておき、その後にdanger-android_lintではlintの実行をスキップして各レポートからDangerを実行する。こうすることでマージの手間が省けた。

android_lint.skip_gradle_task = true
android_lint.filtering = true
Dir["*/build/reports/lint-results-debug.xml"].each do |file|
  android_lint.report_file = file
  android_lint.lint(inline_mode: true)
end

MarkdownエディターをEvernoteからBoostoneに乗り換えてDropbox Paperに乗り換えた

tl;dr

Evernote Boostnote Notion Inkdrop Dropbox Paper
Markdown対応 ○(Markdown here使用)
無料もしくは買い切りの有料(サブスクでない) △(3台目からは要サブスク) ○(無料) ☓(サブスク) ☓(サブスク) ○(無料)
メモ内容で検索できる
PC・モバイルで同期できる ☓(3台目から有料) ○(Dropbox同期)
PC・モバイルで編集できる △(モバイルはMarkdownに戻して編集しないとフォーマットが崩れる ☓(モバイル開発凍結)

Dropbox Paperになった

背景

Evernoteの端末制限が厳しくなるまではMarkdown hereをブラウザに入れてブラウザ版のEvernoteでメモを書いていた。それをモバイルで見ていた訳だが端末制限で乗り換えを考え始めた。Markdown hereでフォーマットするとブラウザ上でMarkdownに戻して編集しないとフォーマットが崩れてしまうのもある。

markdown-here.com

乗り換え条件としては

  • Markdown対応
  • 無料もしくは買い切りの有料(サブスクでない)
  • メモ内容で検索できる
  • PC・モバイルで同期できる
  • PC・モバイルで編集できる

まず最初にBoostnoneを選んだ。

boostnote.io

BoostnoteはOSSDropbox同期が可能でMarkdownがリアルタイムにプレビューされるので体験が良かった。

他の候補としては、Notion はサブスクかつタイトルでしか検索できない(もう改善された?)、Inkdrop はサブスクなのでNG。

Boostnoteをしばらく使っていた訳だが、やはりモバイルで閲覧・編集したくなる機会が増えてきた。 しかしBoostnoteのモバイルは開発が現状止まっており、再度乗り換えを考え始めた。

github.com

結果、今日Dropbox Paperに行き着いた。

www.dropbox.com

Dropbox PaperはWebのDropbox上もしくはモバイルアプリ上からMarkdownでメモができる。Dropboxにファイルを置くのでそのまま同期できる。しいてだめな点をあげるとAndroidアプリから新規ファイルを作るとDropbox直下に置かれてしまうのでそれだけなんとかされたい。WebのDropboxでフォルダを分けて作ったファイルはAndroidアプリからも編集可能ではある。

GitHub ActionでAndroidのCIを設定してみた

ベータを申し込んだら1日で有効になったので使ってみた

help.github.com

設定

ひとまずPR時にUTとlintを並列で動かしてみる。

name: Android CI

on:
  pull_request:
    branches:
    - develop

jobs:
  unit_test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Decode google-services.json
      env:
        GOOGLE_SERVICE: ${{ secrets.GOOGLE_SERVICE}}
      run: echo $GOOGLE_SERVICE | base64 --decode > ./app/google-services.json
    - name: Unit Test
      env:
        TZ: Asia/Tokyo
      run: ./gradlew testDebugUnitTest
      
  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Decode google-services.json
      env:
        GOOGLE_SERVICE: ${{ secrets.GOOGLE_SERVICE}}
      run: echo $GOOGLE_SERVICE | base64 --decode > ./app/google-services.json
    - name: Lint
      run: ./gradlew lintDebug

を.github/workflows以下に置くだけ。 google-services.jsonは一旦base64のデコードでお茶を濁す。Secretsはリポジトリ -> Settings -> Secret -> Add a new secretで追加できる。 タイムゾーン環境変数TZを設定してあげればよい。

help.github.com

感想

まず無料でベータだから仕方ないかもしれないがCircleCIに比べて単純に遅い。無課金のCircleCIで ./gradlew testDebugUnitTest が1分10秒ほど、 ./gradlew lintDebug が1分30秒ほどだったのに対し、GitHub Actionでは ./gradlew testDebugUnitTest が3分20秒ほど、 ./gradlew lintDebug が3分40秒ほどかかった。個人プロジェクトなど時間にこだわらない用途であればいい選択肢だと思う。無料で1リポジトリ辺り20並列まで使えるのはCircleCIやBitriseより遥かに上を行っているし、この程度の設定なら30分くらいで設定できた。実際のプロダクトで使うかはCPUが強いプランが来てくれたらかなと思った。

また↓の記事では不安定だったというのもあるのでそのあたりも改善されたい。

diary.app.ssig33.com

あとGitHub Actionが出たことによってCircleCIやBitriseとの競争がより激しくなり、互いのサービスがよりよくなる流れがきてるのがいいなと思った。