This post is part of another post dealing with how to let Android Studio work with a custom android framework where modifications have been made in the AOSP tree.
What is framework.jar
I do not have the full grasp of this jar
file. BTW, a jar
file is a Java archive format which may contain whatever resources. In the case for framework.jar
, it would be a huge set of classes.
After an AOSP full image build, a framework.jar
file can be found in the /system/framework
directory in the output files. However, in my case the framework.jar
was an empty file that contained nothing. This can be checked with the jar tf [jar file]
command like the example below:
$ jar tf framework.jar META-INF/ META-INF/MANIFEST.MF
I am not sure if other AOSP builds might create a framework.jar
file that may actually contain something. If so, then I believe you do not have to read the rest of this section.
Clearly, the /system/framework.jar
is not the framework.jar
file that I am looking for. After many googling, one result pointed me to checkout the intermediary files that are saved in the /out
directory. I found a jar
file that looked promising and it was /out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
.
listing the contents of this jar
file proved that it indeed contained the class that had been custom modified.
$ jar tf classes.jar ... android/speech/tts/TextToSpeech$Engine.class ...
One questions that may rise is why is it not named framework.jar
but classes.jar
? According to the google result, the classes.jar
file is the file which will be renamed/transformed to framework.jar
. There may be some minor changes applied to the classes.jar
but it is not critical in which the android studio will not be able to understand.
Adding it to Android Studio
An android studio project has been created with the following tree structure.
$ tree . -d -L 2 . ├── app │ ├── build │ ├── libs │ └── src ├── build │ ├── android-profile │ ├── generated │ └── intermediates ├── framework ├── gradle │ └── wrapper └── META-INF
Adding it to app/libs
The classes.jar
file has been copied to app/libs
directory and renamed to framework.jar
.
Adding it to dependencies
In Android Studio after opening the project, go to File - Project Structure
. In the pop-up window, go to Dependencies
tab.
Click the +
icon on the right to add a new dependency. Click Jar Dependency
. Navigate and select app/libs/framework.jar
file.
In the newly added libs/framework.jar
entry, change the Scope to provided
.
Click OK
to save the changes.
The procedure above will allow the project to recognize the custom framework.jar
. Then the question is which one will the project reference? Will it reference our custom framework.jar
or will it reference to the official default SDK? Unfortunately, the project will still refer to the default SDK. The next step will take care of this problem.
Adding app.iml
modifying gradle script
The main problem we now face is telling the project to reference framework.jar
first instead of the default SDK. The hierarchy of referencing is written in app.iml
file located in /app
directory. Take a look and one can recognize a group of orderEntry
items at the bottom of this file. The order of these orderEntry
s defines which one to refer first over the other. We can see that the framework.jar
entry is placed below the Android SDK
entry.
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK"/> ... <orderEntry type="library" exported="" name="framework" level="project"/>
We need to make sure that the framework
entry is placed above the Android SDK
entry. A gradle script can be added to the module’s build.gradle
to make sure of this. In the module’s build.gradle
, please add the following snippet:
preBuild { doLast { def imlFile = file( project.name + ".iml") println 'Change ' + project.name + '.iml order' try { def parsedXml = (new XmlParser()).parse(imlFile) def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' } parsedXml.component[1].remove(jdkNode) def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform" println 'what' + sdkString new Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK']) groovy.xml.XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile)) } catch (FileNotFoundException e) { // nop, iml not found println "no iml found" } } }
Based on observation, a gradle sync or typical build will call preBuild
directive. This will execute the snipped above (probably at the last moment due to the doLast
command). The code will read the app.iml
, save the orderEntry that contains the Android SDK, removes it from the iml file, add a new orderEntry at the last which is identical to the temporarily saved Android SDK orderEntry to the iml file. Eventually, the Android SDK orderEntry will be placed at the very last, thereby guaranteeing the framework.jar
to be referenced first than the Android SDK.
Force to refer framework.jar
during Java compile
A few more lines need to be added to the build.gradle
file. The following code(not the whole but the part after “please add the following”) will be read during gradle’s configuration phase. What it means is, for the projects including subprojects where this build.gradle
resides, all the tasks that are type of JavaCompile
will be executed with an additional compiler argument -Xbootclasspath/p:app/libs/framework.jar
. The reason why this code is necessary is self explanatory.
allprojects { repositories { jcenter() } // please add the following! gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.compilerArgs.add('-Xbootclasspath/p:app/libs/framework.jar') } } }
Result
After gradle sync, the “cannot resolve method” error has vanished.
And although I do not have a screenshot, the build of .apk
file is successful too.
there’re some mistakes …
options.compilerArgs.add(‘-Xbootclasspath/p:app/libs/framework.jar ‘)
LikeLike
which are in this line is incorrect?
LikeLike
I meant “which part in that line is incorrect”
LikeLike
After doing this, not able to build APK
LikeLike
Hi, currently i’m also unable to build APK after following the above mentioned procedure. Were you able to resolve this issue?
LikeLike
java.lang.ArrayIndexOutOfBoundsException: 65535
LikeLike
make sure that you have given dependency scope compileOnly/provided not implementation.
LikeLike
After applying this tutorial, I was also not able to build an APK due to the following error :
“reference to findViewById is ambiguous”
I solved this by removing Activity.class from the archive
LikeLike
My project don’t have .iml files.
How can I solve it ?
This is gradle logs after I build success
> Task :app:preBuild
Change app.iml order
no iml found
LikeLike