Reputation: 4129
I have an Android project consisting of two modules: An 'SDK' and a 'demonstration Android app'.
Currently the SDK lives in /path/to/work/dir/sdk
and the app in /path/to/work/dir/demo-app
. Developers open '/path/to/work/dir/demo-app' in Android Studio and can see + edit the source code for both modules at the same time.
We have another Android app, but that includes the SDK as a compiled .aar file and it can't see or edit the "live" source, only the decompiled/debug-symbol-ed one, if that makes sense?
TL;DR I would like to make the SDK into a plain-Java library so that it can be used in non-Android apps, and yet still have it fully editable inside Android Studio as part of the demo app 'project'. How do I do this?
The SDK's job is to control some hardware devices on the other end of a socket. It is almost entirely plain Java, but has a few incidental uses of the Android library:
android.util.Log
, associated TAG
constants in classes, and runtime checks to disable log messages (BuildConfig.DEBUG
)
Annotations for static analysis:
@Nullable
, @AnyThread
, @NonNull
,
@Nullable
, @UiThread
, @WorkerThread
, @IntRange
,
@Size
, @SuppressLint
Incidental use of libraries where Java stdlib was lacking:
android.text.TextUtils
android.support.v4.util.Pair
Its test suite are all "test". There's a single, useless "androidTest" which can just be deleted.
The Gradle build environment is still tailored towards Android, as is the directory structure. i.e. the repo dir looks like this:
/path/to/work/dir/sdk
├───.git
│ └───...etc...
├───.gradle
│ └───...etc...
├───app
│ ├───app.iml
│ ├───Mobile-SDK-Android.iml
│ ├───proguard-rules.pro
│ ├───build
│ │ ├───generated
│ │ │ └───...etc...
│ │ ├───intermediates
│ │ │ └───...etc...
│ │ ├───outputs
│ │ │ └───aar
│ │ └───tmp
│ │ ├─...
│ ├───docs
│ ├───libs
│ └───src
│ ├───androidTest
│ │ └───java
│ │ └───com
│ │ └───...etc...
│ ├───main
│ │ ├───java
│ │ │ └───com
│ │ │ └───...etc...
│ │ └───res
│ │ └───values
│ │ └───strings.xml
│ └───test
│ ├───java
│ │ └───com
│ │ └───...etc...
│ └───resources
├───build
│ ├───android-profile
│ ├───generated
│ └───intermediates
└───gradle
└───wrapper
and build.gradle
is as follows:
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "0.7"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable false
}
}
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/ASL2.0'
}
productFlavors {
}
publishNonDefault true
}
repositories {
... etc ...
}
dependencies {
// For @Nullable/@NonNull
compile 'com.android.support:support-annotations:25.3.1'
testCompile 'junit:junit:4.12'
... etc ...
}
The 'release build' instructions contains the following:
$ ./gradlew check
$ ./gradlew assembleRelease
# Then copies and packages app/build/outputs/aar/app-release.aar
The 'Android app' module appears to be normal Android app, e.g. it's using Activity classes and the Android libraries and Android Studio deploys it to devices just fine. There's these gradle scritps:
/path/to/work/dir/demo-app/build.gradle (Project: Demo-App)
/path/to/work/dir/demo-app/settings.gradle (Project Settings)
/path/to/work/dir/demo-app/app/build.gradle (Module: app)
settings.gradle
in it's entirety:
include ':app', ':Mobile-SDK-Android'
project(':Mobile-SDK-Android').projectDir = new File('/path/to/work/dir/sdk/app')
build.gradle
Doesn't contain anything relevant, that I can see?
app/build.gradle
containing these relevant bits:
apply plugin: 'com.android.application'
android {
... etc
}
dependencies {
compile project(path: ':Mobile-SDK-Android', configuration: 'debug')
... etc
}
What is an appropriate sequence of steps to convert the SDK into a "normal" and idiomatic Java library?
e.g.:
android.util.Log
to use e.g. SLF4J
TextUtils
and Pair
?minSdkVersion
?Upvotes: 2
Views: 261
Reputation: 14755
It is easier to create an additional third new non-android-lib porject and move all classes that have no android dependencies from skd-lib-android to sdk-lib-common. sdk-lib-common can be used outside android.
for the other android dependent classes
For further reference see
Recommended ways to produce app portable between Android and "other platforms"
Upvotes: 1
Reputation: 28267
Android is a massive API, and has a lot of alternatives. As a result of this, and I want to be clear, there are no fixed rules for changing the library. Most of the code needs to be rewritten from scratch. A large amount of the code is basically "useless". Log.x, the class for printing is gone. This can be replaced either by a library, Logger
or just System.out
.
As for annotations, this depends on the source.
Library-annotations usually work on both desktop and Android, for an instance Lombok. Android specific annotations (@TargetApi
) are gone. minSdk
cannot be used (as it isn't a part of the API in Java). I hope you get the idea here.
And if you didn't: You can't just convert a Java library to Android or the other way around. There's too much code written for the given platform that isn't there on a different platform.
The solution is to create it from scratch. The Android framework runs an entirely different system compared to desktop Java making a lot of the code unusable. And the Activity system removes any of that code, then there are .xml resources. If you create a .xml parser for String resources you can continue using Strings.xml. Layouts would require far too much to create a processor. dp
, the unit, is also gone.
So to answer the title:
What steps need to be performed to convert an Android Java module into a normal Java one?
Once you have the library running (or an idea) you need to rewrite it. In addition to the desktop Java (what you call normal java) being different in language, it also has different systems. There are no .xml resources (JavaFX uses .fxml for layouts, but JavaFX is an entirely different system) that come as default. You need to parse any files and handle locales for Strings yourself. Layouts, JavaFX.
What is an appropriate sequence of steps to convert the SDK into a "normal" and idiomatic Java library?
Start at the core, like with any other project. Create the main classes, and then expand out.
You provided a list of annotations.
@Nullable
Works fine
@AnyThread
Android
@NonNull
Works fine
@UiThread
Android
@WorkerThread
Android
@IntRange
Android
@Size
@SuppressLint
SuppressLint doesn't exist in desktop Java. They have SuppressWarnings though (unrelated issue, but it explains what the annotation is)
If that wasn't clear, annotations marked as Android
above doesn't work using desktop Java, meaning they are written for Android
So the output is .jar instead of .aar and isn't called app-release.aar
When you use a Java library, it ends up as .jar.
Is it still possible to have things like minSdkVersion ?
Nope
Is it possible to have an 'android flavour' of this jar file, incase we need to put android specific code in the SDK in future?
Nope
As for an order to resolve the situation, it isn't possible to make one. You can't, as I hope I have made clear by now, just convert code between Java and Android. There are too many differences, meaning you have to start from scratch. Therefore, how you set up the list is up to you. But you need to rewrite more or less the entire thing. There are places you can copy-paste, but there are few of those.
I have converted an Android project to Java (moved from native Android to LibGDX) and I had to rewrite the entire code. There were only some segments that were copy-pastable and that was mostly things like constructors and some fields, basic operations and so on. There is nothing in the way of copy-pasting basic Java, the problem is that any project doesn't have that much basic java (like constructors, fields, math operations and so on)
Upvotes: 2