ネイティブアプリのAPI更新対応備忘録 〜Android編〜
概要
iOSに引き続き、Androidの方も修正の備忘録を記す。
AndroidのminSDKは元々14で、今回はminSDK19まで引き上げることにした。(本当は21まで上げれば色々と幸せになるが、もろもろ諸事情を天秤にかけた結果なので仕方なし)
対応時のAndroid Studioバージョン : 4.0
gradle更新
まずはこれをしないと始まらない。
バージョンが3.1.4だったので、4.0.0まで引き上げた。特別な対応はしておらず、Android Studioの指示に従って更新をかけた。
更新後はAndroid ManifestでminSDKバージョン記載するなと警告が表示されたので削除してGradleファイルの方に記載。
また、 .idea
フォルダに jarRepositories.xml
なるファイルが自動生成されたが、gitに上げるかどうかは調べた限り不要そうだったので gitignore に追加。
FacebookSDK更新
こちらも version 3 時代と相当古く、かつフレームワークがローカルフォルダ参照だったのでこれを削除し、最新の公式ドキュメントに沿って mavenCentral から動的にインポートする形式に変更。
また、更新によってコード内にて宣言されているimport文の名前空間が変わっていたので修正。
Before
import com.facebook.Settings; // 中略 if (Settings.isDebugEnabled()) { // hogehoge }
After
import com.facebook.FacebookSdk; // 中略 if (FacebookSdk.isDebugEnabled()) { // hogehoge }
またSDKの初期化方法も変化していたので修正。
具体的には AppEventsLogger.activateApp()
を onResume()
から onCreate()
で呼び出すように変更し、AppEventsLogger.deactivateApp()
は呼び出す必要が無くなったので削除。
Before
@Override protected void onResume() { AppEventsLogger.activateApp(this.getApplicationContext()); AppEventsLogger.deactivateApp(this); }
After
@Override public void onCreate(final Bundle savedInstanceState) { AppEventsLogger.activateApp(getApplication()); }
Facebook ダッシュボードの設定更新
Facebookのダッシュボードもv3で設定したっきりで、色々と警告が表示されていた。(主にセキュリティ面)
いきなり本番アプリの設定をいじるのはやばいので、サンボックス版で設定を更新して検証。
Facebookログイン設定において「リダイレクトURIに制限モードを使用」がOFFになったのでONにしたところ、OFFに戻すことができなくなってしまった。
どうやら現在では強制的にONになるらしく、当プロジェクトは単純に設定が古すぎたのが原因。
気をつけなければいけないのは、ONにしたら「有効なOAuthリダイレクトURI」という項目にURI設定を記載しないといけなくなる点。
これが設定されてないとFacebookでのアカウント連携や引き継ぎ等はエラーでできなくなるので注意。
AndroidXへ移行
すでにサポートが終了しているAndroidサポートライブラリをAndroidXへ移行させた。
Android Studioには自動で移行してくれる機能があるのでそれを利用した。
プロジェクトによっては破壊的変更もあるだろうが、当プロジェクトではそんなつ使っておらず、影響は微小だった。
傾き探知
端末の傾きを探知するAPIが deprecated だったので修正。
いろいろ調べたが、正直よく分わからんまず地磁気センサと加速度センサの値から、回転行列を作成。
その値をシステムに合った座標軸系へ行列変換させ(下図では outRotationMatrix に変換後の値が渡る)、最後の getOrientation メソッドで傾き情報を取得という方法で対処。
Before
private SensorManager sensorManager; private Sensor sensor; @Override public void onCreate(final Bundle savedInstanceState) { sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL); } /** * センサーの値が変化すると呼ばれる * @param event センサー値 */ public void onSensorChanged(SensorEvent event) { int type = event.sensor.getType(); if (type == Sensor.TYPE_ORIENTATION) { float[] mOrientationData = new float[3]; mOrientationData = event.values.clone(); float pitch = mOrientationData[2]; } // pitchで傾き判定処理 }
After
private SensorManager sensorManager; // 回転行列 private float[] rotationMatrix = new float[9]; private float[] outRotationMatrix = new float[9]; private float[] gravity = new float[3]; private float[] geomagnetic = new float[3]; private float[] attitude = new float[3]; @Override public void onCreate(final Bundle savedInstanceState) { sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); } @Override public void onCreate(final Bundle savedInstanceState) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (accelerometer != null) { sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } Sensor magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); if (magneticField != null) { sensorManager.registerListener(this, magneticField, SensorManager.SENSOR_DELAY_NORMAL, SensorManager.SENSOR_DELAY_UI); } } else { sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI); sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_UI); } } /** * センサーの値が変化すると呼ばれる * @param event センサー値 */ public void onSensorChanged(SensorEvent event) { switch(event.sensor.getType()){ case Sensor.TYPE_MAGNETIC_FIELD: geomagnetic = event.values.clone(); break; case Sensor.TYPE_ACCELEROMETER: gravity = event.values.clone(); break; } // 端末画面の向きを計算する if (geomagnetic != null && gravity != null) { SensorManager.getRotationMatrix( rotationMatrix, null, gravity, geomagnetic); // アプリケーション座標に変換 SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_Y, SensorManager.AXIS_X, outRotationMatrix); SensorManager.getOrientation( outRotationMatrix, attitude); int pitch = radianToDegree(attitude[1]); //Debug.out("方位角: " + attitude[0] + ", 傾斜角: " + attitude[1] + ", 回転角: " + attitude[2] + ", pitch: " + pitch); // pitchで傾き判定処理 } } /** * ラジアンを度に変換する * @param rad ラジアン * @return 度 */ int radianToDegree(float rad){ return (int) Math.floor( Math.toDegrees(rad) ) ; }
参考サイト:
- 位置センサー | Android デベロッパー | Android Developers
- 端末の向きと傾きを取得する方法 - 加速度センサーと地磁気センサーの利用 - Androidプログラミングの基礎 - Android 開発入門
- sd-tech: Androidでセンサーから方位を取得する
- Androidアプリ開発メモ029:モーションセンサー その3 SensorManager#getOrientation(): ぷ~ろぐ
Handler派生クラスのリーク警告修正
Handlerクラスで、以下のような警告が表示されていたので修正。
This Handler class should be static or leaks might occur.
コード内容は正確ではないが、だいたい以下のような作りを修正しました。
Before
public class TestActivity { Handler handler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { Debug.out("check hoge"); } }; private void hoge { Message msg = new Message(); Bundle b = new Bundle(); b.putString("o___O nothing", "to say"); msg.setData(b); handler.sendMessage(msg); } }
After
import java.lang.ref.WeakReference; public class TestActivity { private final static int MSG_POST_LOGIN = 1; private TestHandler handler = new TestHandler(this); private static class TestHandler extends Handler { private final WeakReference<TestActivity> mTestActivity; TestHandler(TestActivity testActivity) { mTestActivity = new WeakReference<>(testActivity); } @Override public void handleMessage(@NonNull Message msg) { TestActivity mTest = mTestActivity.get(); if (mTest != null) { if (msg.what == MSG_POST_LOGIN) { Debug.out("check hoge"); } } } } private void hoge { handler.sendEmptyMessage(MSG_POST_LOGIN); } }
参考サイト:
Handlerのリーク警告を解決するには - outcesticide
ハードウェアID取得の廃止
TelephomyManagerクラスで取れるハードウェア周りのAPI自体は結構前にdeprecatedになっていたが、Android 10 (targetSDK = 29)でいよいよ完全に取得できなくなった。
具体的には下記テーブルを参照してほしいが、アプリ側のtargetSDKが29以上の場合、ハードウェア系のAPIを呼び出すと SecurityException を吐くようになる。
(まぁ、アプリ側のターゲットビルドが28以下でもインストールした側が29以上だと null を返すのでアレなのは変わりない…)
API | targetSDK >= 29 | targetSDK < 29 |
---|---|---|
getImei | SecurityException | null |
getSubscriberId | SecurityException | null |
getDeviceId | SecurityException | null |
getMeid | SecurityException | null |
getSimSerialNumber | SecurityException | null |
ストア側のtargetSDKは現時点でまだ28だが、おそらく例年通りなら11月あたりに29になるので、それまでに対応しないといけない。
自プロジェクトではハードウェアIDをキーとしてUUIDを生成していたので、この方法がいよいよ完全に使えなくなってしまうので、その代替案としてFirebaseのインスタンスIDで対応することにした。
この方法はGoogleも推奨しているので大丈夫だろう。
ただ、このインスタンスIDはハードウェアIDと違ってアプリをアンインストールすると変わってしまうため、認証処理周りは作り変える必要がある。
参考サイト:
Android ロボットは、Google が作成および提供している作品から複製または変更したものであり、クリエイティブ・コモンズ表示 3.0 ライセンスに記載された条件に従って使用しています