I was wondering whether you could use a trick of the compiler to include different functions for a free and paid version of the app. For instance:
public static final boolean paid = false;
if (paid){
runPaidMethod();
}
else {
runFreeMethod();
}
The compiler will look at that and say that it doesn't need the first branch of the if statement so it won't compile it. Furthermore, it should look at the program and see that runPaidMethod() is no longer referenced from anywhere and remove it.
Therefore the question is: is it feasible to just have this flag, compile it once for free, swap the flag then compile it again for paid?
Using a final boolean variable is good because the Java compiler is smart enough to see that your condition is always false. If you decompile the compiled class (you can try it, with the javap -c command) you will see that your code :
public static final boolean paid = false;
if (paid) {
runPaidMethod();
}
else {
runFreeMethod();
}
will be compiled to a single call to :
runFreeMethod();
The compiler removes any unreachable code, so nobody will be able to reverse engineer your app. But be careful, you have to declare runPaidMethod() as a private method, or its content will still appear in the compiled class.
However from a maintenance point of view, it is still better to use Library Projects to handle multiple app versions.
The concept you are trying to express is known as conditional compilation. In a language like C or C++ you would accomplish this with a combination of preprocessor directives and compiler flags. A rather crude example:
#ifdef PAID
runPaidMethod();
#else
runFreeMethod();
#endif
Good, bad or indifferent, this sort of conditional compilation does not exist in Java. But thats not too say what you are trying to do cannot be accomplished, you just need to think in a more object oriented fashion. One such way of implementing what you are seeking would be to define your major service providers as interfaces, and provide implementations for the paid and free versions. Something like:
public interface UsefulService {
public void someMethod();
public void otherMethod();
}
public class BaseUsefulService {
// Common functionality here
public void otherMethod() {
}
}
public class FreeUsefulService {
public void someMethod() {
}
}
public class PaidUsefulService {
public void someMethod() {
}
}
With this kind of breakdown you can actually build the paid version of the application into an entirely separate application (by putting all its service providers in a separate project).
Related
I would like to now whether it is safe or not to use #SuppressLint("RestrictedApi") annotation. I am pretty sure that the answer is NO, so I want to ask why as well.
I guess that the development team wanted to hide such restricted code from the API users. Probably due to changes in the future or because the code is intended to work as internal functionality
Example code with androidx.preference:preference:1.1.1:
public abstract class CustomAdapterPreferenceFragment extends PreferenceFragmentCompat {
#Override
protected PreferenceGroupAdapter onCreateAdapter(PreferenceScreen preferenceScreen) {
// this annotation removes warning that says PreferenceGroupAdapter can only be called from package names that start with androidx.preference
#SuppressLint("RestrictedApi")
final PreferenceGroupAdapter adapter = new PreferenceGroupAdapter(preferenceScreen) {
#Override
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
}
};
return adapter;
}
Link to annotation that restricts code usage in this example: AOSP AndroidX
You're correct: this is not safe. Symbols marked with #RestrictTo are not considered public API, and may change behavior or signature arbitrarily between releases. The only guarantee is that they won't break behavior that other AndroidX libraries rely on internally, and what that behavior is is not defined.
This is especially true for symbols that restrict to a single library or a library group that requires all libraries within the group to be pinned to the same version, as there is no need to maintain compatibility with different versions of other AndroidX libraries.
I guess that the development team wanted to hide such restricted code from the API users. Probably due to changes in the future or because the code is intended to work as internal functionality
This is exactly correct. There is sometimes a need to expose functionality internally that would not make sense as public API, though we try to avoid it in general, because it makes it harder to copy a part of the code out and customize your own version of it. This is especially true with Java code, which doesn't have Kotlin's internal modifier to expose classes to the entire library (package-private doesn't really cut it).
I'm kinda new to Android development so my question might be weird or not even possible. I wouldn't know!
Anyway, I'm building multiple apps that will have a lot of shared elements, so I decided to build a library with those components and use it in all of the apps, rather than stupid copying and pasting code.
For example, the library handles the welcome screen and login/signup flow activities, among other things. So here are the problems this approach might cause:
While the behavior is the same across the apps, but the logo that I show at the welcome screen is different. Right now I populate it with an image resource from the library resources (R class) which will be the same for all apps and is obviously not correct.
The login/signup process is based on Firebase, which will require the app to have a key to be able to use them. Right now I also populate it with a dummy string resource from the library resources.
So my question really boils down to 3 parts:
Is there anyway I could pass this info from the app to the library? can I somehow modify the R class of the library? Or can I use the app's R class from the library? I can also call this part of the library as a function passing the parameters I need. But the first solution looks maybe more clean to me?
Whatever the answer to Q1 is. Where would I do this and how? The library has the welcome activity itself which is supposed to be the first activity in the app. How and where do I do this once the app starts and before the first activity starts?
If what I'm doing is wrong or impossible, is there any other way to achieve it?
Is there anyway I could pass this info from the app to the library?
can I somehow modify the R class of the library? Or can I use the
app's R class from the library? I can also call this part of the
library as a function passing the parameters I need. But the first
solution looks maybe more clean to me?
You don't need to modify the R class because you can override the resource file by creating a file with the same name. But it's not a clean solution because you constantly need to ensure your project and library resources name are the same.
Whatever the answer to Q1 is. Where would I do this and how? The
library has the welcome activity itself which is supposed to be the
first activity in the app. How and where do I do this once the app
starts and before the first activity starts?
Instead of overriding the resources name, you're better to modify your library to receive a configuration as a contract to use the library. Here the sample:
First, create the class for holding the configuration:
public class Configuration {
private int welcomeImageDrawableId;
private int logoDrawableId;
// constructor
public Configuration(int welcomeImageDrawableId, int logoDrawableId) {
this.welcomeImageDrawableId = welcomeImageDrawableId;
this.logoDrawableId = logoDrawableId;
}
// setter and getter.
public int getLogoDrawableId() {
return logoDrawableId;
}
}
Second, use the configuration class for the library by creating a Singleton class which will be used internally by the library:
public class MyLibrary {
private static MyLibrary myLibrary;
private Configuration configuration;
private MyLibrary(){}
private MyLibrary(Configuration configuration) {
this.configuration = configuration;
}
public static MyLibrary getInstance() {
if(myLibrary == null) {
throw new RuntimeException("Need call createInstanceWith method first!!");
}
return myLibrary;
}
public static MyLibrary createInstanceWith(Configuration configuration) {
if(myLibrary == null) {
synchronized(MyLibrary.class) {
if (myLibrary == null) {
myLibrary = new MyLibrary(configuration);
}
}
}
return test;
}
public Configuration getConfiguration() {
return configuration;
}
}
Third, use the configuration class in your library via the singleton class. something like this:
// assume imvLogo is an existing ImageView
Configuration configuration = MyLibrary.getInstance().getConfiguration();
imvLogo.setImageResource(configuration.getLogoDrawableId());
Last, register the contract when the library is used with:
Configuration configuration = new Configuration(R.drawable.welcome, R.drawable.logo);
MyLibrary.createInstanceWith(configuration);
Note: all the code isn't tested yet, error is to be expected.
Apart from the solution above, I also found another way to achieve this whole thing without having to initialize libraries and whatnot.
I think the correct way to do this is to use productFlavors in the library. This allows the library to share the one main set of source code, one main set of resources, then an extra set of resource per app/flavors. This is very sufficient for my purposes.
For more info about build variants and flavors:
https://developer.android.com/studio/build/build-variants
The Android ART runtime is an ahead-of-time (aot) compiler, contrary to Dalvik and Hotspot which do just-in-time (jit) compiling.
One advantage of jit compilation is that it can inline methods that are not provably the right target. In code like this:
public class Foo {
public int doOperation(int a, int b) { return a + b; }
}
public class Bar {
public int doIt(int a, long repeat, Foo foo) {
for (long i = 0; i < repeat; i++) {
a = foo.doOperation(a, 1);
}
return a;
}
}
it is not generally possible to inline the doOperation into doIt, because one could create a derived class DerivedFoo that overrides the doOperation method. In the case of Java, such a DerivedFoo class need not be present at compile time but can be present at runtime or (on HotSpot) even be dynamically generated at runtime.
One advantage of jit compilation is that the compiler can specialize for the exact code and types that are being used, and e.g. HotSpot would be able to inline the doOperation call in the code above, and dynamically de-optimize if it turned out that the passed in foo was not a Foo as was assumed.
Android ART does ahead-of-time compilation, so it can not make such an optimization, unless it assumes that all code is present at compile time and sees that no other classes derive from Foo.
Does ART make such an assumption, and is it able to make use of that during compilation and e.g. inline the doOperation into doIt in the above code? If not, would it inline doOperation if it were marked final?
Is there a way to retrieve Robolectric's version at runtime? My environment is under controlled library versioning and would like to make sure I run a certain test only if Robolectric has an specific version (for example, to avoid runtime known issues with it). For example:
#Test
public void should_do_x() {
if (Robolectric.version >= supported_version) {
//do something
//validate something
}
}
I have a JNI library that I've written to capture sensor (mostly accelerometer and gyro) data and do some feature detection algorithms on said data. The features detected are configurable via a few configuration files. When the features are detected, the JNI uses a callback to notify the java side of the application. All this works great.
Now I want to be able to have multiple instances of the same JNI library running simultaneously (so I can recognize features from multiple configuration files at once). To do this, I wrote a 'wrapper' class that implements the callbacks for the JNI library and takes care of all the initialization of the library as well. I was planning on simply instantiating this class and using each instance separately. What I've found is that while each wrapper instance is distinct, the library is reused across instances almost like it was statically declared. When I try to initialize the library from the second instance of the Wrapper class, I find it has already been initialized.
Here is a wrapper class similar to the code I've written:
public class JNIWrapper {
public native int initializeJNI(String configPath);
public native void endProcessing();
public native int getInstanceIdFromJNI();
public JNIWrapper(){
try {
System.loadLibrary("libjnicode.so");
}
catch (Exception e) {
Log.e("JNI", "WARNING: Could not load libjnicode.so: " + e.getMessage());
}
}
public int initialize(String configPath){
return initializeJNI(configPath);
}
public void stop(){
endProcessing();
}
public void callbackFromJNI(int output, int instanceId){
//notify the subscribed application(s) of the feature detection
//via message passing.
}
}
Does anyone know how I can instantiate multiple copies of a JNI library?
Thanks!
You can't do that. The dynamic linker will only load a given .so file into a process once.
Ideally you would modify the library to give it a light object-oriented style, allowing you to create instances and initialize those (rather than process-level static state) from your configuration files or calls. This isn't necessarily as complicated as it seems - basically put all your state in a struct and pass the pointer to it through all your calls. You'll have one marathon editor session resulting in a tired "paste" finger, and then some mistake cleanup. Fortunately once you remove the static variables you'll get compile errors on all remaining attempts to use them.
A very hacky alternative might be to declare some remote-process services in your AndroidManifest.xml and load the library into each of those. Or, really breaking the android model (and at theoretical risk of random killing), load the library into multiple created-on-demand native executables.