2018年12月25日火曜日

PCX125 (JF56) のタイヤ交換

5か月ほど前から、うちのPCXの前後タイヤの空気が抜けるのが早く、2週間ぐらいで、2.0→1.5ぐらいになるようになった。
スリップサインに関しては、前はまだまだ、後ろはもうちょっとというところだったので、電動空気入れを購入していつでも対応できるようにしていた。

2週間ほど前、後タイヤのスリップサインが出たので(総走行距離数は15,000kmぐらい)、タイヤ交換することにした。
交換は後ろだけでも良かったが、空気の抜けがウザいのと、純正のIRCが雨の日に滑りやすくて怖いのでグリップに定評のあるミシュラン CITY GRIPへ総取換えにした。

メーカー側に在庫があったので、注文後、1週間掛からずに入荷の連絡があり、すぐにピットに持って行った。
交換作業は、後タイヤのベアリングがカバーに固着して取れずに難航したが、サービスの方の努力で1時間半ほどで終わった。
固着したところは、錆でダメになる一歩手前だったらしく、「もう少しで手遅れになるところですよ」と言われた。本当にラッキーなタイミングだったとしか言いようがない。
しかし面倒な作業となったので、ただただ、サービスの方に感謝するのみ。


交換したのタイヤの詳細
・MICHELIN CITY GRIP 【90/90-14 46P】7,560円
・MICHELIN CITY GRIP 【100/90-14 57P reinf】8,208円
・工賃 6,480円
・廃タイヤ代 500円
(計 22,748円)※相場よりちょっと高かったけど、近くて安心だし満足。

交換前と交換後の写真(左面 上:前、下:後)


交換前と交換後の写真(右面 上:前、下:後)


交換前と交換後の写真(前面 上:前、下:後)


交換前と交換後の写真(後面 上:前、下:後)


まだ、交換した直後で慣らし運転も終わってないが、地面に張り付いた感はいいですね。
見た目は、溝が一杯あるぶん、細く見えるなぁ。
個人的には太く見える方がいいけど、雨で滑りにくい方が優先なので、これでヨシ!



2018年4月8日日曜日

android の Widget は躊躇なく殺される。ならば、何度でも蘇ろう!

ここん所、android の Widget 作成を頑張っているんだけど、満足のいく安定性が得られない。
各OSバージョンの android エミューレータや android 6.0.1 のSH-M03 では問題なく動くんだけど、android 4.4.2 の SH-06F だと非常に不安定になる。
さまざまな処理に伴う高負荷に関しては、非同期にしたり、分散させたりで、何とか使えるレベルにきたんだけど、ActivityManager による下記の死の宣告から逃れられない。

I/ActivityManager: Killing 25548:jp.dpp.fmwatcher/u0a311 (adj 11): stop jp.dpp.fmwatcher

とか

I/ActivityManager: Process jp.dpp.fmwatcher (pid 26415) has died.

ですね。
OSに標準の殺し屋がいるので、目を付けられたら、プロセスごと逝ってしまう。こっちの都合でなく、OS側の都合なので、目を付けられないようにビクビクしながら生かすしかない。
具体的な困りごととしては、AppWidgetProvider のインスタンスから、アクティビティを呼び出して、そのアクティビティから親インスタンスに属す remoteview に変更を加えようとしたところ、親が死んでてエラーになるってケース。

...で、どうも限界だなと、発想を変えて「産みの親が死ぬんだから、里親を探すしかない」。里親探しのため、子供からブロードキャストで onUpdate を呼び出すことにした。
というのも、タブレットを回転させたときに Widget の表示が狂うので onAppWidgetOptionsChanged で、remoteview の再描画ルーチンを組み込んだ onUpdate を組み込んだら、偶然、新しい AppWidgetManager の里親を連れてきたことに気づいたから。
子供から直接 onUpdate を呼べないので、ブロードキャストで適当なアクション入れたインテントを送って、親の onReceive で受けて、onUpdate を実行させればOK。
onUpdate に必要な context は onReceive が引数で持っている。
AppWidgetManager は getInstance(context) 。
appWidgetId だけがどうしようないので、onUpdate の中で static な変数に入れるようにすれば(onAppWidgetOptionsChanged でも、上書き代入させる必要あり)、とりあえず調達できる。
骨の部分だけ記載すると、こんな感じ。

==================================================
親側
==================================================
public class XXXXXX extends AppWidgetProvider {

    public static int wid;  // appWidgetId格納変数

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {

...
        wid = appWidgetIds[0];
...
    }

    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);

        wid = appWidgetId;
        onUpdate(context, appWidgetManager, new int[]{appWidgetId});

    }


    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);

        switch (intent.getAction()) {
            case "適当なアクション":
                try {
                    onUpdate(context, AppWidgetManager.getInstance(context), new int[]{wid});
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}
==================================================


==================================================
子側
==================================================
            Intent intent01 = new Intent(this, XXXXXX.class);
            intent01.setAction(”適当なアクション”);
            sendBroadcast(intent01);
==================================================


これで、親が死んでも、子供が必要な時に新しい里親が現れる。
あとは、親の情報は基本 static にしておいて、どんな親が来ても同じ返事ができるようにしておくこと、子は子で、重複しても構わず、自力で必要な情報を確保できるよう独立した造りにすれば、何とかなるでしょう。

また、remoteview のボタンにこの「適当なアクション」を PendingIntent で発行させるようにすれば、「ウィジェットが表示されてるけど本当は死んでる」状態も自分のボタンで蘇ることができる。
もちろん、updatePeriodMillis を 0 にするなんてもったいない。最小30分ごとに自力で蘇えられるチャンスが訪れるのだから、使わない手はない(何らかのタイマー駆動処理使ってるなら、重複処理しない制御構文入れれば何の影響もないはずだし)。


とりあえず、これで死の恐怖から逃れることができた。
最大限「死なない親」にこだわる必要はあるけど、現実的に「親が死んでもなんとかなる」対策の方が重要ですな。

2018年3月7日水曜日

android 8.0 (oreo) のエミュレータで NotificationManager がシステムUIエラーのループ

結論から言うと、大した問題ではなかった。
8.0 (oreo) の実機を持ってないので、あくまでエミュレータの話になるけど....。

Notification.Builder を生成するとき、

.setSmallIcon(R.mipmap.xxxxx);

だと、システムUIエラーのループが発生する。
リソースの参照先を「mipmap」から「drawable」に変更して(もちろん実際のリソースファイルも移動)、

.setSmallIcon(R.drawable.xxxxx);

にすれば、普通に動く。
ただし、API27 やその他のシステムイメージではこんなこと起こらない。あくまで 8.0 (oreo) のシステムイメージに限った話のよう。

何が正しくて、何がおかしいかは不明だが、確実に起こってしまうのだから、エミュレータ使いは注意が必要だ。

【追記】
最近のシステムイメージのアップデートで、この症状は出なくなった。
さっきやったら、問題なく動いた。

2018年3月5日月曜日

android の JobService が思った間隔で動かない...!?

android 8.0 (oreo) から、ウィジェットの肝のバックグラウンドのサービスが特定の状況以外、禁止となっていた。
そんなスゴい端末を持っていないから全くのノーチェックだったが、こりゃイタい仕様変更だ。要は、これからウィジェットを作成するなら、Service からの Timer 起動 でなく、JobScheduler を使えという話ですなぁ。

勉強がてら実験してみると、JobInfo のメソッドでジョブの繰り返しミリ秒数を設定する setPeriodic() が説明どおりに動いてくれない。

.setPeriodic(60000);

としても、ログでは 11 分間隔で動いている。「おやっ、もしかして」って感じですね。
ちなみに

.setPeriodic(180000);

とすると、予想どおり 13 分間隔になった。
説明書きのどこを読んでも、「プラス 10 分」とは記載されていないようなので、何の確信もない。ただの実験結果であるが、クサいですなぁ。
一応、エミュレータ以外にも、実端末(SH-M03 v6.0.1)で試したが同様の結果だった。

ということは、

.setPeriodic(0);

ならどうなるかって感じですな。裏を読んで「『0』って繰り返さない意味かも」って考えちゃいますけどね。素直に 10 分間隔という話。

まぁ今は、「こんなこと言ってた人がいたなぁ」って思っててもらえれば、悩んだ数時間が報われる。
しかし、これが事実だと、わざわざ JobScheduler 使ってもウィジェットの情報更新間隔が 10 分以下にならないわけだ。ウィジェット本体が持つ updatePeriodMillis を使うと最小が 30 分間隔だから、まだちょっとはマシなんだろうなぁ。


※android 8.0 の場合は、

.setPeriodic(899000);

な感じで、15 分以下だと、

03-05 14:16:47.065 6980-6980/jp.dpp.fmwatch W/JobInfo: Specified interval for 1 is +14m59s0ms. Clamped to +15m0s0ms

こんなログが出力されるので、15 分以下は全部、15 分間隔にしちゃうらしい。
でもまだ実際には、一度も繰り返してくれてないので格闘中だ。

<追記>
どうも Oreo で setPeriodic が無効になったという噂を見かけた。情報元が Google ではないのであくまで噂だが、実際に動かないので信憑性は高い。
なので、これから使うなら、

firebase-jobdispatcher-android

がお勧め。
モジュール側の build.gradle の dependencies に一行

implementation 'com.firebase:firebase-jobdispatcher:0.8.5'

を加えるだけで使えるようになるライブラリ。
これだと JobScheduler に対応していない OS バージョンでも、持ってる機能だけで何とか「なんちゃって JobScheduler」を使えるようにしてくれるから便利。
こんなに悩むことになるなら、最初からこれを使っておけばよかった。マジで...。