TargetApi не учитывается
В одном из наших методов мы используем smoothScrolling в виде списка. Поскольку этот метод недоступен до уровня API 8 (FROYO), мы использовали аннотацию TargetApi, чтобы предотвратить вызов метода в предыдущих версиях SDK.
Как вы можете видеть, мы используем аннотацию TargetApi как в определении класса, так и в операторах, которые используют объекты класса. Это больше, чем нужно.
Наша проблема заключается в том, что аннотация TargetApi не учитывается и вызывает сбой нашего эмулятора в версии ECLAIR (SDK 7). Прослеживая, мы просто понимаем, что код, который должен выполняться только в версиях 8+, также выполняется в версии 7.
Мы что-то упускаем?
Этот код находится в слушателе:
@TargetApi(8)
private final class MyOnMenuExpandListener implements OnMenuExpandListener {
@Override
public void onMenuExpanded( int position ) {
doScrollIfNeeded( position );
}
@Override
public void onMenuCollapsed( int position ) {
doScrollIfNeeded( position );
}
protected void doScrollIfNeeded( int position ) {
if ( mListViewDocuments.getLastVisiblePosition() - 2 < position ) {
mListViewDocuments.smoothScrollToPosition( position + 1 );
}
}
}
И слушатель регистрируется следующим образом:
@TargetApi(8)
private void allowSmothScrollIfSupported() {
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ) {
//This if should not be necessary with annotation but it is not taken into account by emulator
Log.d( LOG_TAG, "Smooth scroll support installed." );
folderContentAdapter.setOnMenuExpandListener( new MyOnMenuExpandListener() );
}
}
Кстати, мы запускаем код в режиме отладки, поэтому проблема не связана с аннулированием обфускации.
Ответы
Ответ 1
@TargetApi
не предотвращает запуск какого-либо кода, это просто для аннотирования кода и предотвращения ошибок компилятора для новых API, как только вы знаете, что вы вызываете их только условно.
Вам все равно нужно добавить что-то вдоль строк
if (Build.VERSION.SDK_INT > 7){
//...
}
Ответ 2
С почти одним годом больше думая об этом, я хотел бы добавить крошечное дополнение к ответу @Guykun:
@TargetApi будет использоваться только инструментами для разработчиков: "Эй, не используйте этот метод ниже XXX android SDK". Обычно линт.
Итак, если вы создаете такой метод, как:
if (Build.VERSION.SDK_INT > 7){
//...
}
то вы должны добавить @TargetApi (7) к вашей сигнатуре метода.
НО, если вы добавляете оператор else и предоставляете альтернативу, которая заставляет его работать для всех версий Android, например:
if (Build.VERSION.SDK_INT > 7){
//...
} else {
//...
}
тогда вы должны не добавить @TargetApi (7) в свою подпись метода. В противном случае другие разработчики подумают, что они не могут использовать ваш метод belw api level 7, но, действительно, это сработает и для них.
Таким образом, эта аннотация должна использоваться для статического анализа для указания минимального уровня api, поддерживаемого этим методом. Как в:
@TargetApi( 7 )
public void foo() {
if (Build.VERSION.SDK_INT > 7){
//...
else if (Build.VERSION.SDK_INT > 10){
//...
}
}
и еще лучше, используйте константы, определенные в android.Build.VERSION_CODES.*
.
Кстати, вы бы заметили, что это бесполезно для частных методов, за исключением получения более чистого кода и содействия продвижению этого метода в будущем.
Ответ 3
Чтобы принудительно использовать ошибку lint при использовании метода, направленного на более высокий уровень Api, вы можете использовать RequiresApi
вместо TargetApi
и всякий раз, когда вы попытаетесь использовать метод без проверки кода версии, вы получите ошибку компиляции.
Вот что документация говорит о RequiresApi
Это похоже на старую аннотацию @TargetApi, но более четко выражает, что это требование для вызывающего, а не использоваться для "подавления" предупреждений в рамках метода, который превышают minSdkVersion.