Background
I'm working on an app that has become very popular, so much that a part of it is supposed to become an SDK (which would be available for developers), and the app will split to 2 apps (both use the SDK).
According to what I know, there are multiple ways to create an SDK module (previously called "project" on Eclipse) :
Completely open sourced (Android library) - all sources and resources are open sourced and can be modified. An example might be Facebook's SDK and a lot of Github repos.
a single Jar file, which can be closed sourced.
The problem
Sadly, I can't make the SDK open sourced, and it should relatively be protected vs prying eyes (obfuscated etc...).
The issue here is, the SDK needs to use some resources of its own (drawables, strings,...), and so far (because I didn't have a lot of experience with creating SDKs) I've found 2 ways to handle resources for SDKs :
use reflection and/or "context.getResources().getIdentifier" . This is quite messy, as I lose the whole "R" usage of the code. Also, it has issues with "styleable" , as I've written here. It also makes it hard to find unused resources.
even worse ways: put resources in assets folder, put files in a wacky way inside the jar file, ...
Note that a part of the SDK includes custom views (for example, classes that extend from TextView), so even if I do split the SDk into 2 modules- resources and java files, both might have issues of dependencies (each uses the other one).
The question
Is it possible to somehow solve this issue?
Is it possible for the code part of the SDK to remain closed sourced, reach the "R" file as usual, and make it easy for both me and whoever use the SDK ?
How would I then generate the jar file as being obfuscated via Android Studio? and is it possible to prepare it to to be used via gradle afterwards?
Can I maybe make the Android-library of the SDK into an obfuscated jar file and not worry about the "R" file ? I ask this because this way I could enjoy both worlds: for our apps, it would remain open sourced, and for third party apps it would be closed sourced.
EDIT: seeing that this is supposed to be easy, I've tried it myself. I've created a totally new POC project which has an Android library module called "sdkmodule", and added this class to it:
public class SdkClass
{
public String doIt(Context context)
{
return context.getResources().getString(R.string.app_name);
}
}
Then, I've made the app's module to use this one, and I wrote this code in it:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SdkClass sdkClass=new SdkClass();
Log.d("AppLog","string from SDK:"+sdkClass.doIt(this));
Log.d("AppLog","string with same ID name from app:"+getResources().getString(R.string.app_name));
}
What I expected is that the first log would print the string that's in the SDK module, and the second to show the string of the current project, but instead I got an exception:
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/example/user/sdkmodule/R$string;
On another try, I've got the same string that's used on the app itself (the one that uses the SDK module). And, on another run, the SDK produced the needed string as I've wanted.
How could it be? What should I do ?
In addition, I've tried to make a second activity in the SDK itself, and I've created a resource there that has the same resource name (used for a textView in its layout) as of the app itself, yet with a different value, yet when I've reached this activity, I've seen the one used by the app.
Here's the project, zipped (ignore the name of the folder, I wanted to try flavors too) :
https://drive.google.com/file/d/0B-PZZGk2vPohX25WUDNKTmotUTg/view?usp=sharing
The answer to your problem is to package and distribute your library as an AAR bundle
This format allows you to provide an obfuscated SDK jar and with its resources and the R mapping file.
This format is a standard and fully supported by maven-android-plugin (actually it's the replacement of the old APKLib format which supports only the distribution of source files).
Of course it's also supported by Gradle and Android Studio.
The Android Archive (AAR) format does what you want. It's like an Android-specific JAR file and contains compiled code, but includes its own resources and manifest. You can also include obfuscation as part of the build process. By default, the current version of Android Studio (1.2) and Gradle automatically build .AAR files for all library modules you create in your project.
You can change an app module into a library project that will publish an AAR file just by changing apply plugin: 'com.android.application' into apply plugin: 'com.android.library' in your module's Gradle file. The .AAR file will be placed in your MODULENAME/build/outputs/aar folder after each build. Some more information is available here.
Edit 1, after question updated:
The resources in your AAR get consolidated with the app module when the final APK gets compiled. The app resources will override the library's. This is probably by design, to allow people using a 3rd party library to customize its resources when creating their app, without having to rebuild the library. I think the easiest way to solve your resource conflict issue would just be to name your sdkmodule resources something more unique. Why not make the string key R.string.com_example_sdk_name or something?
No, the AAR libraries don't get obfuscated by default, but you can set up ProGuard in the Gradle build file for your AAR library to take care of this. Other tools are also available.
Related
This question already has answers here:
How to make a .jar out from an Android Studio project
(11 answers)
Closed 5 years ago.
I want to create some library that i will use in the future beside my current project.
I can't find a way to create library in android studio.
How to do it on android ?
A library module is useful in the following situations:
When you're building multiple apps that use some of the same components, such as activities, services, or UI layouts.
When you're building an app that exists in multiple APK variations, such as a free and paid version and you need the same core components in both.
In either case, simply move the files you want to reuse into a library module then add the library as a dependency for each app module.
To create a new library module in your project, proceed as follows:
Click File > New > New Module.
In the Create New Module window that appears, click Android Library, then click Next.
There's also an option to create a Java Library, which builds a traditional JAR file.
Give your library a name and select a minimum SDK version for the code in the library, then click Finish.
Once the Gradle project sync completes, the library module appears in the Project panel on the left.
If you don't see the new module folder, make sure it's displaying the Android view.
Visit https://developer.android.com/studio/projects/android-library.html
As introduction I would suggest you to peek into this conceptually simple tutorial. Basically you can start your own library module when you chose your project, without adding any Activity. Then you create your Java Class, usually with a View. When your library is ready, with all its business logic, you can glue everything inserting in the top level build gradle the instruction you are using a library, basically before you remove the following line, that is not needed for a library:
applicationId
(This line in your gradle file is a unique application ID that looks like a Java package name, that identifies your app to the device you are running and in google play)
and then you change this line:
apply plugin: 'com.android.application'
to:
apply plugin: 'com.android.name_library'
When you have a more structured project you can follow the official documentation
As it is well explained the difference between a normal Application and a Library is:
An Android library is structurally the same as an Android app module. It can include everything needed to build an app, including source code, resource files, and an Android manifest. However, instead of compiling into an APK that runs on a device, an Android library compiles into an Android Archive (AAR) file that you can use as a dependency for an Android app module.
I would not encourage you to use solutions like web services that do it on your behalf, namely just copying/pasting your existing code. In fact a library often needs specific architectural choices, so is important to consider and learn different factors, is not just writing some business logic is quite complex to explain, but you can imagine that also the choice of what the user can see and modify can be crucial. Also should be as much as possible bug free, because once it is adopted could cause problems to the users. I remand you to a famous post, superbly written where you can find some solution to this aspect.
you can use https://jitpack.io/ is very easy publish an android library. just upload your code to github/bitbucket and paste the repository link on jitpack website. that's all
I have a mature app that needs to have an SDK brought in that wraps the camera and makes it do some spiffy processing while it's running. The SDK has come to me in the form of some aar files but my app still lives in Eclipse. Because of my massive, steaming pile of a branding structure and deadlines for this integration the uncertain timeline required to fully migrate to Android Studio will not work (for now) so I'm going for converting the aars and using them in my app via Eclipse.
The problem I'm having is that I need to kick off the activity in the library but even though I fixed up all my build time reference problems, when running the app once I get to the point that is supposed to kick off the activity I get this error.
I've read through and double checked dozens of how-tos explaining how to consume the aar files and I think I've followed every step including:
Unzip the aar files and dress them up as individual library projects, including the file structure with the resources, the manifest, and the .jar
Make the project that needs the libraries add them as such
Add the .jars contained in the library projects to the build path (this step was not listed in most articles, and wasn't necessary for the project to build, but nevertheless it did not help my problem)
Declare the activity in your AndroidManifest.xml that the library brings in and declares in its AndroidManifest.xml
As I've said, everything looks good at build time, so I'm not sure what else to check. Because I'm reading that Gradle and Android Studio mashes manifests together really well, I have a hunch that it's something I'm supposed to regulate between the manifests but I don't know what it could be if that is it. I've wondered if I'm declaring the 3rd party's activity properly, but I'm not sure how to test it because the only way I can think to test it is to provide gibberish for the namespace but even then there are no complaints. I've also tried dissecting the .apk to look at the .dex file but I could not decipher anything useful.
My guess is that the library may not be building properly in eclipse - even before it's being added as a dependency to the application project.
Try looking at out/classes/* and making sure you have a .class file for the activity in question. I think the .class should actually be in the library as well as end up in the application project's out/ dir also.
If there are any native files (x.so) (as I would imagine there might be for spiffy camera stuff), you can look for the x.so files being included in the out/ dirs of both the library and application projects as well as the library.jar file.
Another option to maybe consider for this use case: https://github.com/ksoichiro/gradle-eclipse-aar-plugin
It seems that our app had a bad version of the appcompat-v7 support library. The .jars in it were different sizes than the one that comes with the SDK and several resources were missing. I have no idea how we ended up that way or where this bad version came from. Once I replaced it, things went great.
Later, I did encounter the need to drop in the .so files into the libraries I made that came out of the .aar files as Stad Kurdziel said in his answer, but that was causing a different error (the exception explicitly states that the .so is missing) and I arrived at the solution independently.
I have seen this question, and have some more doubts regarding creating a jar file which I can distribute and can be used in any android applications.
What my requirement is
As I said, I want to build and distribute a closed source library. I
don't want the source code to be visible.
In that library I don't want to use any assets, layouts, resources
etc. But I want to use some android specific codes, like getting
android device id.
The most popular answer in the above linked SO question tells to create a regular java project and import android.jar in it. I tried to do that, but I don't know how to add android.jar to any java project. I would like to get some clarification on that too.
Moreover I would like to know if there are any other methods using android sdk itself (without using java project) create a closed source library jar file.
I think what I want is possible, since google analytics for android native apps seems to have done it. I am sure in the .jar file they distribute they are using android specific codes, since there seems no other way for them to get the device information to display in the analytics viewer.
EDIT : CAN SOMEONE CLARIFY THIS??
I think I have made some progress. This is what I have done
Created a regular android project (not library project, the "is
Library" checkmark is unchecked)
In the project I have coded out my logic. It uses some android
specific classes like SharedPreference, UUID, PackageManager. But
nothing related with assets, layouts also no class extending
Activity. Just a java class extending java.lang.object
Exported the project using Project->rightclick->export->Java->JAR
file. In the next screen I unchecked the checkbox near
AndroidManifest.xml. Set a destination directory to export and
clicked next thrice with keeping the default settings. Then I clicked
Finish, and got a lovely libMyLibraryName.jar at my desktop.
Then I created another android project, added this libMyLibraryName.jar to new project using project->rightclick->properties->java build path -> libraries->add external jar.
And I tried to use my class in the library, in my new project
MyLibraryClass objClass = new MyLibraryClass(this);
And I was able to compile and run successfully. I even sent the library to one of my co worker who was able to use the library in his on machine (Just making sure library project in my workspace wont influence the project using it).
Now I have 2 questions.
1) My first question is , what they meant by the term "true library" in the below given documentation ? Is it any non android java project which can be exported to a JAR file?
However, a library project differs from an standard Android
application project in that you cannot compile it directly to its own
.apk and run it on an Android device. Similarly, you cannot export
the library project to a self-contained JAR file, as you would do
for a true library. Instead, you must compile the library indirectly,
by referencing the library in the dependent application and building
that application.
Well this portion is taken from documentation under title "Library Projects".
2) My second question is, anything wrong with the way I have created the JAR file? Any possible pitfalls which might bite me back later? I would like to make sure I am not doing something terribly wrong, before using it in my important projects.
I might add that I didn't try the method of creating a JAVA project and importing android.jar. I am ready to try that one, if what I have done currently is wrong.
The android.jar will be located where you installed your Android SDK. Under the platforms directory there should be a number of other directories named android-<version>. The android.jar will be there. Choose the one specific to the minimum android version you are targeting.
Once you have that, copy it into your project. If you're using eclipse I think you can just cut and paste jars straight into your project, right click and add it to build path. If you're not using eclipse or any other IDE, you just need to ensure that the android.jar is on the classpath when building your jar file.
After that your newly built android library can be dropped into any Android project.
In answer to your additional questions:
What they mean by a true library is a jar file as opposed to an Android library project.
I don't think there's anything wrong with the way you created the jar file. I would have made it using the android.jar as I mentioned above but your way should also work. To verify this I would examine the jar contents and make sure all you have in there is .class files.
I am currently working on a Android Project where we are expected to merge our App with 2 more apps from vendors who wouldn't be sharing their code.So just wanted to know Is there any way we could just include there Source code as JAR Files in our project and then include their resources and point to them(I did do it using getResources().getIdentifier("splash", "layout", getPackageName()) But Its still not working ?? I think I have tried all possible methods so hoping you guys could help me with this.
To quote CommonsWare from this question:
Bear in mind that your packaged classes cannot reference resources (at
least not via R. syntax), and that JARs cannot package resources,
manifest entries, etc. If all you are trying to JAR up is pure Java
code (perhaps using Android APIs), then you should be fine.
Basically, you can only use JARs that contain pure java as libraries in your app, not entire other projects.
The Activities can be compiled into a jar and added to the main Android project and we need to add their project's resources into your Project. The only we could make it work is using the getResources().getIdentifier("splash", "layout", getPackageName()). Even the Widgets like TextView, Button and all those should be referred to using the getResources() method. Like, for example, If you want to perform a action on particular button then we need to identify them by getResources().getIdentifier("Button" /*id in the XML File*/, "id"/*type*/, getPackageName()).
One more thing: you need to specify all the Activities in your Main Android Project's AndroidManifest.xml file with their package name. I hope this solves something.
In order to support faster build times, the r16 tools are creating their own jar files inside of Android Library Projects. You can use this feature to solve this issue. If a vendor would like to release their Android Library Project but without source code, they can make a copy of that Android Library Project that contains everything except for the source code tree. Next, include the jar file from the original Android Library Porject (the one that the r16 tools built.) This will allow you to have a component you can distribute that does not require source code. The consumer of this new Android Library Project will need to manually add any necessary meta data to their own project's AndroidManifest.xml file as needed (Activities, Providers, Permissions, etc).
I have an Android code base which uses APIs with settings to get different data for several apps. All apps use the same code base but with one or two design tweaks. So how do I re-use the main code base without having to copy the whole Android project each time?
iPhone uses multiple targets in the same project which works well. If android cant do this do I need to compile binaries of the code base in one project and then import into each new app project? If so how? I'm using Eclipse and am an intermediate Java developer.
Any help much appreciated!
Doug
Check out "Working With Library Projects" from the Android documentation. This should be just what you're looking for: http://developer.android.com/tools/projects/projects-eclipse.html#SettingUpLibraryProject
The current way to approach this issue if you are using Android Studio with Gradle is by using Gradle, Build Type + Product Flavor
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
Build Variants
One goal of the new build system is to enable creating different versions of the same application.
There are two main use cases:
Different versions of the same application
For instance, a free/demo version vs the “pro” paid application.
Same application packaged differently for multi-apk in Google Play Store.
This new concept is designed to help when the differences are very minimum. If the answer to “Is this the same application?” is yes, then this is probably the way to go over Library Projects.
Note: This answer is basically obsolete now that one can create .aar libraries with resources. It still works, though, and there may be times when the portability of a .jar is desirable, so I'm leaving it here.
Blumer's answer is a good one, and you should definitely look into Android's idea of library projects. However, there is another alternative. If you have a module that contains only Java code, but no resources of any kind (images, layouts, etc.), you can compile it to a .jar file separately and use the resulting .jar in multiple application projects. It works like this:
Create a new Java project in Eclipse (not an Android project) and move the source code of your module there.
If your module references any classes from the SDK, you'll need to add the Android SDK .jar to the project's classpath (Project > Properties > Java Build Path > Libraries > Add JAR).
When your module is ready to use, bundle up its .class files into a .jar. You can do this manually, or you can look around to figure out how to get Eclipse to do it for you.
Copy your module .jar file into the "libs" directory of your app's main project.
Add the module .jar to the project's classpath (again, Project > Properties > Java Build Path > Libraries > Add JAR).
Now you should be able to build multiple apps using the same .jar, while maintaining only one copy of the module's source code.
Depending on your particular situation, this may or may not work any better for you than the standard Android library mechanism. But it's worth considering as an alternative.
The Android documentation recommends another approach if there aren't too many "different APIs" used.
The idea is to use reflection instead of making direction references to the code. Make sure to use optimized reflection instead of lookups every time.
References
http://developer.android.com/training/multiple-apks/api.html
http://developer.android.com/google/play/publishing/multiple-apks.html#ApiLevelOptions
You might want to consider using a source control system like Subversion (or GIT). Keep your most feature complete version in the trunk, and make branches for your separate versions that use different data sources or require minor layout changes, etc.