Closed8

化石Androidプロジェクトをアップデートする

A AA A

9年前にアップロードしたきり、放置していたテスト用Androidアプリをアップデートしてみる。
機能性皆無でほぼ何もできないアプリだが、devアカウントが削除されるらしいのでその対策でもある。

実際は、ただの興味本位。
9年前のプロジェクトをどの程度改変すれば再アップロードに耐えうるのか?

新規テンプレートに移し替えた方が楽なのはわかってるが、必要最小限の変更にとどめたいので、あえて古いプロジェクトからスタートしてる。

A AA A

Gradle系のアップデート

まず、Android Studioでひらけない。開けるがsyncすらできない。
原因はgradleが古いから。
(antじゃなくてgradleなのは助かったw)

gradleとAGPをアップデートしていく。

gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Mar 19 12:50:44 JST 2024
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip

よく見たら2013年なので11年前だw
mavenはrepositoryはcloseされたjcenterのみなので、mavenCentral + googleにしとく。

build.gradle
@@ -2,10 +2,11 @@
 
 buildscript {
     repositories {
-        jcenter()
+        google()
+        mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.2.3'
+        classpath 'com.android.tools.build:gradle:8.2.2'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -14,6 +15,7 @@ buildscript {
 
 allprojects {
     repositories {
-        jcenter()
+        google()
+        mavenCentral()
     }
 }

pluginRepository形式とかにわざわざ変えない。settings.gradleは編集不要だった。
upgrade toolsは古すぎるせいか、動かなかったが、普通はこれを使うべき。
https://developer.android.com/build/agp-upgrade-assistant?hl=ja

A AA A

Support Library -> AndroidX

非推奨になってから久しいsupport libraryを使っていたので、migration toolsを使ってmigrateした。
まだtoolを残してくれているのが、偉い。ありがとう。
Refactor -> Migrate to AndroidXを実行し、そのままdo refactorした。
https://developer.android.com/jetpack/androidx/migrate?hl=ja

xmlの自動置換

laytout.xml
-    <android.support.v7.widget.RecyclerView
+    <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/recyclerView"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
 
-    <android.support.v7.widget.GridLayout
+    <androidx.gridlayout.widget.GridLayout
             android:id="@+id/gridLayout"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
 
-        <android.support.v7.widget.AppCompatButton
+        <androidx.appcompat.widget.AppCompatButton
                 android:id="@+id/button7"
                 android:layout_width="0dp"
                 android:layout_height="0dp"
 
-    </android.support.v7.widget.GridLayout>
+    </androidx.gridlayout.widget.GridLayout>

 
-    <android.support.v7.widget.CardView
+    <androidx.cardview.widget.CardView
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="center"
 
-    </android.support.v7.widget.CardView>
+    </androidx.cardview.widget.CardView>

Javaの自動置換

基本的にimportの置換だけで済んだ。
まあ、最近はAndroidXのライブラリも特にアップデートされていないのだろう。

-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;

----
 
-import android.support.v7.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView;

---
 
-import android.support.v7.app.AppCompatActivity;
+import androidx.appcompat.app.AppCompatActivity;
A AA A

gradleの設定変更

上のmigrationツールでも適用されたが、app/build.gradleやgradle.propertiesの更新も発生した。
butterknife(懐かしい。。)を使っていたので、ついでに最新に上げておいた。
(当時はannotation processorではなくreflectionだったぽい。知らなかった。)

app/build.gradle

  • support library -> androidx
  • compile -> implementation
  • butterknifeを最新に
  • compileSdkやtargetSdkをvup
  • namespaceの追加
    • ついでにmanifestからpackageを削除した。
  • buildConfigの生成がされなかったのでオプション追加
  • NonConstantResourceId のwarningをオフに
build.gradle
 android {
-    compileSdkVersion 22
-    buildToolsVersion "22.0.1"
+    compileSdk 34
+    namespace 'my.application.id'
 
     defaultConfig {
         applicationId "my.application.id"
         minSdkVersion 16
-        targetSdkVersion 22
-        versionCode 1
-        versionName "1.0.0"
+        targetSdkVersion 34
+        versionCode 2
+        versionName "1.1.0"
     }
     buildTypes {
         release {
@@ -37,20 +38,26 @@ android {
     }
+    lintOptions {
+        disable 'NonConstantResourceId'
+    }
+    buildFeatures {
+        buildConfig true
     }
 
 }
 
 dependencies {
-    compile fileTree(dir: 'libs', include: ['*.jar'])
-    compile 'com.android.support:appcompat-v7:22.2.0'
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation 'androidx.appcompat:appcompat:1.6.1'
-    compile 'com.android.support:gridlayout-v7:22.2.0'
-    compile 'com.jakewharton:butterknife:5.1.1'
-    compile 'com.android.support:recyclerview-v7:22.2.0'
-    compile 'com.android.support:cardview-v7:22.2.0'
-
-    compile 'io.realm:realm-android:0.80.3'
+    implementation 'androidx.gridlayout:gridlayout:1.0.0'
+    implementation 'com.jakewharton:butterknife:10.2.3'
+    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
+    implementation 'androidx.recyclerview:recyclerview:1.3.2'
+    implementation 'androidx.cardview:cardview:1.0.0'
 }

gradle.properties

gradle.properties
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More etails, visit
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 # org.gradle.parallel=true
+android.enableJetifier=true
+android.useAndroidX=true
+android.nonFinalResIds=false
+org.gradle.jvmargs=-Xmx2560m \
+--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
+--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \
+--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED

感想

JDK 17対応は他のプロジェクトでも詰まったので、慣れていたが、buildconfigの生成がデフォルトオフになっていたり、resourceidがfinalじゃなくなっていたのは知らなかった。。
gradle 8/AGP 8系は去年避けてきたのでキャッチアップが遅れたのかもしれない。

A AA A

Butterknife

上でbutterknifeを更新したが、java側の変更も必要だった。
inject -> bindの違いのみ。量が多いと大変かもしれないが、置換すれば問題なさそう。

MainActivity.java
+import butterknife.BindView;
 import butterknife.ButterKnife;
-import butterknife.InjectView;
 import butterknife.OnClick;
 
@@ -25,35 +26,36 @@ 
 
-    @InjectView(R.id.gridLayout)
+    @BindView(R.id.gridLayout)
     public View gridLayout;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
-        ButterKnife.inject(this);
+        ButterKnife.bind(this);
 
A AA A

Android Manifest

  • packageの削除
  • 起動activityへのexportedの追加(確かandroid 12くらいから)
AndroidManifest.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="my.namespace" >
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <uses-permission android:name="android.permission.INTERNET" />
 
@@ -12,7 +11,8 @@
         <activity
             android:name=".MainActivity"
             android:label="@string/app_name"
-            android:screenOrientation="portrait" >
+            android:screenOrientation="portrait"
+            android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
A AA A

その他

  • crashlyticsの削除
    • そもそもfirebase買収前のFabricを使っていた。
    • 見ていないので削除。
  • Realm関連の更新
    • 0.80とか使っていた。mongo買収前。。
    • チュートリアルを参考に、コードの更新
      • plugin形式に変更
      • getInstance -> getDefaultInstance
      • clear -> delete
      • findAllSorted -> findAll().sort()
    • scheme自体は変更不要だったが、configの問題か、schemeが設定されていない的なcrashは出たが、気づいたら治った。
    • migrationは、できない気がするので諦めた。これもneed migration的なcrashが起きたが気づいたら治った。
    • アップデート時にはクラッシュするかもしれない。。が今回は無視した。
  • AsyncTaskの削除

機能が皆無なのもあるが、全然ライブラリを使っていなかった。

A AA A

感想

複数のつまりポイントとかあったが、既知のものが多くて、作業自体は簡単だった。
むしろスクラップを描く方が時間かかった。
難易度はアプリの規模によるが、コード変更が必要なものは新規作成しても楽ではない印象になった。

去年あたりの動き

Gradle/AGPは去年8系が正式リリースされているが、いくつかクリティカルな変更があったような気がする。(前から予告あったような気もする)
また、JDK 17への対応が入ったのも地味に大きい印象。
レガシーライブラリに依存していると一気にアップデート難易度が上がる。
ただ、Android StudioやGradle等はしばらくは古いままでもいけそうな気はする。

変わるもの・変わらないもの

あとは、時代の移り変わりを感じた。
有名ライブラリでも作者がgoogleに入社したり、運営元が買収によって交代したり廃止されたり。

と同時にあまり変化していなさにびっくりもした。
Javaアプリでは10年前のActivity中心 + RxJavaがベストプラクティスな流れはそのまま変化せず、別流でJetpack ComposeやKotlin周辺が進化していったように思える。

Kotlinは素晴らしいが、依存が膨らむ分、追従コストは上がる印象が強まった。
今回は非kotlinのおかげで救われた。
おそらく初期kotlinを採用していたりすると、ライブラリ同士の依存にも引っ張られ、少し面倒なことにはなったと思う。

あと、IDE等でも意外とレガシーのサポートも継続されているので、古き良きアプリの方が寿命は長いのかもしれない。
アップデート時に負債となるので、結局作り替えられるとは思うし、よくJetpack composeへのリプレイスの話は聞くが、リプレイス後にさらにflutterで作り替えたりしてて、アプリやサービスの本質とは別なのかもしれないとも感じる。

メモとして

おそらく、もっと面倒なプロジェクトで自分が必要になりそうなので、メモっておいた。
だが、こうしてみると本当に自分にしか役に立たなそう。。

このスクラップは2ヶ月前にクローズされました