化石Androidプロジェクトをアップデートする
9年前にアップロードしたきり、放置していたテスト用Androidアプリをアップデートしてみる。
機能性皆無でほぼ何もできないアプリだが、devアカウントが削除されるらしいのでその対策でもある。
実際は、ただの興味本位。
9年前のプロジェクトをどの程度改変すれば再アップロードに耐えうるのか?
新規テンプレートに移し替えた方が楽なのはわかってるが、必要最小限の変更にとどめたいので、あえて古いプロジェクトからスタートしてる。
Gradle系のアップデート
まず、Android Studioでひらけない。開けるがsyncすらできない。
原因はgradleが古いから。
(antじゃなくてgradleなのは助かったw)
gradleとAGPをアップデートしていく。
@@ -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にしとく。
@@ -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は古すぎるせいか、動かなかったが、普通はこれを使うべき。
Support Library -> AndroidX
非推奨になってから久しいsupport libraryを使っていたので、migration toolsを使ってmigrateした。
まだtoolを残してくれているのが、偉い。ありがとう。
Refactor -> Migrate to AndroidXを実行し、そのままdo refactorした。
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;
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をオフに
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
- AndroidXへのmigrationで
enableJetifier
とuseAndroidX
の二つ追加された。 - AGPのいつからか(8.0なら最近か?)、resource idがfinalとして生成されなくなったため、別のところでwarningが出ていた。
- https://stackoverflow.com/questions/75137125/avoid-using-resource-ids-in-const-fields-agp-8-0
- 抑制するために
android.nonFinalResIds
も指定する必要があった。 - butterknifeのannotationの引数にすると
Attribute value must be constant
と怒られたため。 - これはそのうちdeprecatedになるかもしれない。
- switch case文でも怒られたが、こちらはwarningを抑制すれば良いのかもしれない。
- butterknifeがGradle 8(というかjdk 17)だと動かないので回避用のjvmオプション
- jdkのclassをopenにするオプション
# 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系は去年避けてきたのでキャッチアップが遅れたのかもしれない。
Butterknife
上でbutterknifeを更新したが、java側の変更も必要だった。
inject -> bindの違いのみ。量が多いと大変かもしれないが、置換すれば問題なさそう。
+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);
Android Manifest
- packageの削除
- 起動activityへのexportedの追加(確かandroid 12くらいから)
@@ -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" />
その他
- crashlyticsの削除
- そもそもfirebase買収前のFabricを使っていた。
- 見ていないので削除。
- Realm関連の更新
- 0.80とか使っていた。mongo買収前。。
-
チュートリアルを参考に、コードの更新
- plugin形式に変更
- getInstance -> getDefaultInstance
- clear -> delete
- findAllSorted -> findAll().sort()
- scheme自体は変更不要だったが、configの問題か、schemeが設定されていない的なcrashは出たが、気づいたら治った。
- migrationは、できない気がするので諦めた。これもneed migration的なcrashが起きたが気づいたら治った。
- アップデート時にはクラッシュするかもしれない。。が今回は無視した。
- AsyncTaskの削除
機能が皆無なのもあるが、全然ライブラリを使っていなかった。
感想
複数のつまりポイントとかあったが、既知のものが多くて、作業自体は簡単だった。
むしろスクラップを描く方が時間かかった。
難易度はアプリの規模によるが、コード変更が必要なものは新規作成しても楽ではない印象になった。
去年あたりの動き
Gradle/AGPは去年8系が正式リリースされているが、いくつかクリティカルな変更があったような気がする。(前から予告あったような気もする)
また、JDK 17への対応が入ったのも地味に大きい印象。
レガシーライブラリに依存していると一気にアップデート難易度が上がる。
ただ、Android StudioやGradle等はしばらくは古いままでもいけそうな気はする。
変わるもの・変わらないもの
あとは、時代の移り変わりを感じた。
有名ライブラリでも作者がgoogleに入社したり、運営元が買収によって交代したり廃止されたり。
と同時にあまり変化していなさにびっくりもした。
Javaアプリでは10年前のActivity中心 + RxJavaがベストプラクティスな流れはそのまま変化せず、別流でJetpack ComposeやKotlin周辺が進化していったように思える。
Kotlinは素晴らしいが、依存が膨らむ分、追従コストは上がる印象が強まった。
今回は非kotlinのおかげで救われた。
おそらく初期kotlinを採用していたりすると、ライブラリ同士の依存にも引っ張られ、少し面倒なことにはなったと思う。
あと、IDE等でも意外とレガシーのサポートも継続されているので、古き良きアプリの方が寿命は長いのかもしれない。
アップデート時に負債となるので、結局作り替えられるとは思うし、よくJetpack composeへのリプレイスの話は聞くが、リプレイス後にさらにflutterで作り替えたりしてて、アプリやサービスの本質とは別なのかもしれないとも感じる。
メモとして
おそらく、もっと面倒なプロジェクトで自分が必要になりそうなので、メモっておいた。
だが、こうしてみると本当に自分にしか役に立たなそう。。