I am writing an Android library. The vast majority of the interface in the lbirary supports Android API level 10 or above. Some functionality, though, requires a higher API level. For instance, part of the library requires API 18 for Bluetooth Low Energy.
For the sake of concreteness, let's say that the library produces three classes ClassA, ClassB and ClassC. ClassA uses functionality available in API 10, ClassB uses functionality available in API 14 and ClassC uses functionality available in API 18.
I want to be able to trigger a lint issue (warning/error) whenever someone uses a class from my library without having the required API level in their project (unless they suppress the warning with the appropriate annotation), similar to the already built-in NewApi issue used by lint.
After searching, I have found the following possible solutions:
1) This solution isn't along the lines of lint: Split the library into three .jar files, say lib_10.jar that includes all classes using functionality available in API 10 (ClassA in the example), lib_14.jar that includes all the classes using functionality available in API 14 (ClassB in the example) and lib_18.jar that includes all classes using functionality available in API 18 (ClassC in the example). This solution allows portability but would complicate the later maintainability of the codebase and would potentially require some code duplication as well.
2) Create my own annotation (say, #RequireAndroidApi(API_LEVEL) indicating the minimum API level required by the annotated class/method/etc...) and use the lint-api.jar (http://tools.android.com/tips/lint-custom-rules) to create a custom lint rules that check the usage of any annotated classes/methods/etc... with a lower API than required. Something that would later look like this:
#RequireAndroidApi(10)
Class ClassA {
}
#RequireAndroidApi(14)
Class ClassB {
}
#RequireAndroidApi(18)
Class ClassC {
}
The problem is that I couldn't find good documentation for the lint API and it seems that this is reinventing the wheel for a functionality that lint already supports (lint already checks for the "NewApi" issue).
3) Finally, I succeeded to edit <SDK>/platform-tools/api/api-versions.xml in order to indicate the API level required by each class as follows:
<api version="1">
...
<class name="package/path/ClassA" since="10">
<extends name="java/lang/Object" />
<method name="<init>()V" />
</class>
<class name="package/path/ClassB" since="14">
<extends name="java/lang/Object" />
<method name="<init>()V" />
</class>
<class name="package/path/ClassC" since="18">
<extends name="java/lang/Object" />
<method name="<init>()V" />
</class>
</api>
Which caused lint to trigger the NewApi issue in the same manner as it would with the Android APIs. I like this type of solution because it doesn't reinvent the wheel and further any errors thrown this way would utilize the suggested solutions programmed in Eclipse or Android Studio to deal with the problem (i.e. "quick fixes" in Eclipse). The problem with this solution is that it requires editing api-versions.xml that ships with the Android SDK, which makes this solution not very portable for releasing the library for several reasons including: a) the api-versions.xml file is not local to a project and changes the behavior of lint for all android projects, including the ones that do not use the library; and b) api-versions.xml will be overwritten every time the SDK is updated from the Android SDK manager which would overwrite any changes made.
I was wondering if there is a simpler solution to achieve this "minimum API errors/warnings" or if there is a way to write a separate file similar to api-versions.xml that can be placed in the project directory which can be read by lint whenever lint is ran on the project in question (something similar to lint.xml).
Thanks for bearing with me during this long description of the problem and I appreciate any help in advance.
There is no need to create your own annotation, the android support library's #RequiresApi annotation is what you are looking for. For example:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public void someMethod() {}
This annotation tells lint to warn if someMethod() is used in a context that may have a lower API level.
Note that #TargetApi is different: It's used to assure the linter that the annotated method will only be called with the targeted API, in a situation where it would otherwise warn you not to use that method. So #TargetApi can be used to silence the lint warning triggered by #RequiresApi.
I have recently done this on a custom view class, which needed special constructor for some api levels.
I have done it with the #TargetApi annotation.
If a method is available only since api level 16:
#TargetApi(16)
public void someMethod () {}
This should do the trick, including lint errors.
Related
The version 27.0.0 of the Android Support Library introduces the class FragmentCompat.PermissionCompatDelegate. I wondered what it was exactly and how it should be used (reading the docs without any example didn't help much).
I dived a bit deeper, and discovered FragmentCompat.OnRequestPermissionsResultCallback, introduced back in 24.1.0, and also noticed that these two nested interfaces have ActivityCompat variants, introduced in the same Support Library versions.
Do these interfaces finally allow any class to manage permissions requests and grant results without having to do ugly hacks like are done in most of these libraries that often required to extend a "BaseActivity" or "BaseFragment"?
How to use them? If you answer with code, Kotlin snippets are appreciated, but Java ones are accepted too!
I am working on a huge android project with tons of classes. Until now we are compiling with Android 3.2 and giving support to Android 2.3.
Now I am testing to compile with Android 5.1.1, but still giving support to Android 2.3. My surprise was that a lot of code is deprecated now... (getWith(), getDrawable(), setBackgroundDrawable(), HttpRequest, ActivityGroup, etc...). Those deprecated functions does not give compile error, but it gives deprecated warning.
I know that I can duplicate code and make special calls to deprecated methods if SDK_INT is lower than XX and calls to new methods if SDK_INT is newer than XX, but this is a huge patch, that will need a lot of duplicate code in a lot of functions in a ton of classes and packages, so, if it is possible, we prefeer to wait until we don't need to give support to oldest versions of android. It means to continue using deprecated methods until for example we only need to give support from 4.4, that will means munch less duplicated code and functions will be needed.
The question is: If i compile with Android 5.1.1 but still use those deprecated functions, this huge project will continue to work on all devices (from 2.3 to 5.1.1)? Now it's working compiling with Android 3.2.
I understand that Deprecated means that they are likely to be removed in a future version of the platform and so you should begin looking at replacing their use in your code, but if it is still supported and does not give a compile error, it will work well as until now. It is what I understand of deprecation. It is right?
Thanks.
Deprecated means two things:
There is another (maybe better) solution
It isn't fully support anymore
That means that your code could be run fine. But Google/Android don't guarantee that.
According to Java documentation the #Deprecated annotation says:
#Deprecated annotation indicates that the marked element is deprecated and should no longer be used. The compiler generates a warning whenever a program uses a method, class, or field with the #Deprecated annotation. [...]
So please stop using deprecated methods. :)
Have you looked or do you know that there are support-libraries to help you with backward compatibility?
Yes it should work fine. But you really should use the latest methods instead of deprecated ones and use support libraries to make it compatible to the previous versions.
All the deprecated methods that I have used have worked fine, but Google does not guarantee that they will.
You can continue using deprecated methods, they should still work as intended. But you have to be careful, because they could be removed in future versions.
As mentioned in the documention over developer.android.com
A program element annotated #Deprecated is one that programmers are
discouraged from using, typically because it is dangerous, or because
a better alternative exists. Compilers warn when a deprecated program
element is used or overridden in non-deprecated code.
It may stop working with new version.
Example: LocationManager.addNmeaListener(GpsStatus.NmeaListener listener)
Deprecated from API 24.
Stop Working in API 29 (Android 10). No implementation inside (returns false).
In documentation: No-op method to keep backward-compatibility.
Had trouble to find why old code stop working.
I like to use the findAll/ findAllAsync method in android.webkit.WebView. findAll is deprecated and Google suggests to use findAllAsync which requires Jelly Bean or higher. However, I like my application to support 2.2+. I tried to the following, but I get warning for findAll (deprecation) and error for findAllAysnc (need to increment minimum SDK version):
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
myWebView.findAll(query);
else
{
myWebView.findAllAsync(query);
}
What's the best way to deal with this? Should I just use findAll and ignore the deprecation warning?
I believe the answer goes in several ways:
What are you setting as min SDK version and target SDK version in the manifest ?
Same question, but in Eclipse (or whatever IDE you're using) for Android build API properties ?
(I'm answering as if your question is "how do I get rid of the android lint warning", rather than "how do I fix the warning correctly" .. )
Use findAll and if the warning is really too annoying add a #SuppressWarning("deprecation") annotation to suppress it explicitly.
One problem with this is that when you use this annotation on your method you might miss other deprecated calls as it will apply to the whole method.
There's a very interesting and powerful way of doing what you want, and it is called reflection. From the Oracle's Java documentation:
Reflection is commonly used by programs which require the ability to
examine or modify the runtime behavior of applications running in the
Java virtual machine. This is a relatively advanced feature and should
be used only by developers who have a strong grasp of the fundamentals
of the language.
In short, reflection allows you to find out if class is defined, you can find out its methods and properties, and invoke a class' functionality... all at runtime!
I have an app that needs to handle the audio focus on Android 2.1 devices but the Audio Focus is not available for those versions, so I use this technique. It is, by the way, a bumpy road. I would suggest reading carefully the documentation and try to follow some examples.
I'm calling a function through reflection which is available since froyo. How can I mark that this call requires froyo?
There is the annotation #TargetApi(Build.VERSION_CODES.FROYO) but this does just prevent warnings I want to generate a warning if this version does not match.
If there is no way from the SDK site is it possible to write such an annotation? I'm not familiar with that.
Annotations are just metadata and need tools to actually process them, there's no lint check to do the reverse of what you want: indicating that something requires a certain API according to http://tools.android.com/tips/lint-checks.
If you are providing an API, it would be best to document the requirement in the Javadoc of the function so that a caller is aware that that particular call requires Froyo. In the function you should probably throw an exception if this pre condition is not satisfied or handle it in some other way.
Another option, if you are creating a library, is to set the minSdk version of that project to 8 or up.
You also could write your own lint rule that would do a custom check I guess. More details on writing lint rules are here: http://tools.android.com/tips/lint-custom-rules. The downside is that users of your api have to perform some custom setup to activate the rule.
I totally forgot to mention this, but this annotation #RequiresApi was actually introduced in Support Lib v24 back in 2016, after raising a bugreport.
I see quite a few good old useful methods or even entire classes being "deprecated and obsolete".
But code that used to call those methods continues to work. So, what does this mean to me, as an Android applications developer?
Continue using this method as long as I want, because newer SDKs
will always remain backward compatible.
It will work as long as I build for older targets (e.g. API 8), but
if I build from API 14 up, the compiler will refuse to complete
the build.
Both (1) and (2)
Other?
This is especially confusing when no alternatives are provided, as in the case of WebView.PictureListener.html#onNewPicture.
It usually means that there's either a better way of doing things or that the deprecated functionality had some irreparable flaw and should be avoided. You can usually keep using deprecated methods, but you are advised to either switch to some new API (in the first case) or find some other way of doing what you want (in the second).
Regarding onNewPicture in particular, the entire PictureListener interface is deprecated. There's no sign of what, if anything, is supposed to replace it. A comment by #CommonsWare in this thread is food for thought:
It is conceivable that upstream changes in WebKit are driving the deprecation and that support for PictureListener might be totally lost in some future release.
I would go with 4:
It will basically tell you that the use of the method or class is discouraged; it is NOT 100% that they will keep backward compatibility (they can decide to not include that method in future releases), so you should try to use the replacement of the method or class. This is sometimes not possible to use the new methods (for instance, if you want to support devices running older versions).
Some other times it is actually possible. For instance, the showDialog method is now deprecated and they recommend to use DialogFragment class. You can achieve that even in older versions of Android by using the compatibility library.
Deprecated methods are not guaranteed to remain backwards compatible. They might remain in there for a few more releases just to give everyone a chance to migrate away from them before the developers remove them. The fact that they're deprecated means that the developers think that there's an easier, faster, neater, or otherwise better way to do whatever that class or method does.
It's probably better to change your code to use a non-deprecated interface now, since if you wait and it does get removed, your users will see crashes and errors.
Even when they are deprecated, they may compile but not work. Google has decided to delete various functionality at the low OS level.
Case in point. Google, at android release 2.3 deprecated many but not all method API's that allowed call recording. They compile OK but do not function since Android 2.3 and forward on any android phone device, or tablet with phone capabilities.
As an example for a deprecated interface that has been removed in a later API level, consider the org.apache.http package: It has been deprecated in API level 22 and removed in API level 23.
Of course on actual Android devices, the classes contained in that package will still be available in the system libraries (otherwise, applications targeting an older Android release would no longer run on that device).
They are however not available in the SDK anymore, so compilation will fail unless you either change the target/build SDK to an older version (or manually include the deprecated classes).
If Google were really determined to discourage use of those libraries, they could modify the implementation so that the affected classes check the target API version of the running application and complain and/or throw a runtime exception.