#android #accessibilityservice #android-accessibility #accessibility-api
#Android #accessibilityservice #Специальные возможности #доступность-api
Вопрос:
Я обнаружил, что Android 9 теперь показывает информацию, если служба специальных возможностей перестала работать.
Это всегда было проблемой для разработчиков, которые пытаются использовать accessibility API.
-
Доступность выглядит включенной, но служба остановлена. И чтобы вернуть ее к работе, требуется отключить и снова включить доступность.
-
Я был бы рад, если Google исправит это полностью, но теперь они просто показывают подсказку, что это полезно отключить-включить вручную.
Не самый лучший материал, но хоть что-то.
-
Итак, я попытался выяснить, как система узнает, произошел сбой службы. Случайно оказался класс с именем AccessibilityUtil, и он содержит
hasServiceCrashed
метод. -
К сожалению, он проверяет скрытое поле
crashed
из AccessibilityNodeInfo, которое недоступно сторонним разработчикам (из-за отказа в отражении), а также в предыдущих версиях Android.
Итак, мне интересно, есть ли альтернативный способ получить информацию из системы, которая разъясняет, что моя служба специальных возможностей разбита / перестала работать и требуется действие пользователя. Начиная с Lollipop. Подсказки приветствуются.
Комментарии:
1. Вы решили это? Правильный ли ответ Линча Чена, если основной причиной является «когда-либо сбой»? спасибо
Ответ №1:
Мне пришла в голову идея использовать статическое логическое значение, указывающее статус службы специальных возможностей, и сравнить его с Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
. Я тестировал на нескольких устройствах, не обнаружил никаких проблем с этим методом.
1. Объявите статическое логическое значение в службе специальных возможностей.
private static boolean bServiceRunning = false;
2.In Служба специальных возможностей, установите логическое значение в onServiceConnected
и onUnbind
@Override
protected void onServiceConnected() {
super.onServiceConnected();
bServiceRunning = true; //put this at the very beginning to reduce time gap
}
@Override
public boolean onUnbind(Intent intent) {
bServiceRunning = false;
return super.onUnbind(intent);
}
3. Создайте статическую функцию в службе специальных возможностей
public static boolean bGetServiceStatus(){
return bServiceRunning;
}
С помощью логического флага я могу узнать, запущена ли служба специальных возможностей в желаемом состоянии. Когда служба принудительно останавливается, onUnbind
вызывается, поэтому логическое значение превращается в false.
4. Мы используем Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
для получения статуса переключения службы специальных возможностей
public static boolean bIsAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
String strServicesSettingResult = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (strServicesSettingResult == null){
return false;
}
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
colonSplitter.setString(strServicesSettingResult);
while (colonSplitter.hasNext()) {
String strComponentName = colonSplitter.next();
ComponentName enabledService = ComponentName.unflattenFromString(strComponentName);
if (enabledService != null amp;amp; enabledService.equals(expectedComponentName))
return true;
}
return false;
}
5. И это то, что мы хотим, мы проверяем с помощью двух вышеупомянутых методов определение реального состояния службы специальных возможностей.
public static int intIsAccessibilityServiceEnabled_WithCrashCheck(Context context, Class<?> accessibilityService){
//return 0 if Accessibility Service enabled and running
//return -1 if Accessibility Service disabled and not running
//return -2 if Accessibility Service enabled but stopped working or crashed
//first check Accessibility Service boolean
if(bGetServiceStatus()){
//service is running
return 0;
}else{
//service not running, now double check with Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
boolean bResult = bIsAccessibilityServiceEnabled(context, accessibilityService);
if(!bResult){
//Accessibility Service is disabled
return -1;
}else{
//Accessibility Service is enabled, but service is not actually running, Accessibility Service is crashed
return -2;
}
}
}
Использование «AccessibilityManager» также работает так же, но я предпочитаю более «облегченную» версию со статическим логическим значением для лучшей производительности.
Примечание: Использование Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
без повторной проверки приведет к ошибке. Значение не синхронизировано. Результат не всегда отражает реальное состояние службы. Следующие шаги могут создать такой случай :
- Запустите службу специальных возможностей, затем перейдите в настройки приложения, принудительно остановите приложение.
- Теперь вы можете видеть, что переключатель службы специальных возможностей отключен. (На данный момент кажется достаточным, но следующий шаг создаст проблему)
- Запустите службу специальных возможностей снова, на устройствах Android 9.0 вы обнаружите, что переключатель включен, но служба фактически не запущена, и теперь она отображает «Не работает. Нажмите для получения информации.»
2022/11/29 Редактировать: Эта ошибка исправлена согласно this issue tracker. Я больше не могу воспроизвести эту ошибку на своих новых устройствах Android 12 и 13. Однако на устройствах со старой прошивкой Android эта ошибка сохраняется. (Исправление также применяется к новейшим изображениям AVD. Чтобы протестировать эту ошибку с AVD, теперь вы должны загрузить старые версии изображений AVD.)
Ответ №2:
Android обычно предотвращает запуск приложений при повторном сбое. Очевидно, что такое поведение службы специальных возможностей может повлиять на пользователей, которые зависят от службы, но поскольку эти службы могут эффективно управлять пользовательским интерфейсом, повторяющийся сбой также может привести к непригодности устройства.
Мне и в голову не приходило, что кого-то еще заинтересует поле «сбой» в AccessibilityServiceInfo. К сожалению, я заполнил это поле, используя данные, доступные только системе. Я сравниваю список служб, которые включены, со списком тех, которые привязаны.
Если вам интересно, не заблокирован ли запуск вашей службы, вы, вероятно, могли бы сделать что-то подобное, отслеживая, когда вызываются onBind и onUnbind, и просматривая список включенных служб из AccessibilityManager.
Комментарии:
1. Спасибо за ответ и за идею с несвязанным событием. Попытаюсь реализовать. Время от времени происходит сбой каждого приложения, и когда служба специальных возможностей запускается в том же процессе, что и остальные приложения, даже какая-то редкая ошибка пользовательского интерфейса приложения может остановить доступность. И мое приложение предназначено для управления устройством, которое пользователь не должен иметь возможности отключить. На устройствах Samsung Knox очень помогает, но для других Android я использую множество хитростей, чтобы пользователи не могли взломать приложение. Служба специальных возможностей похожа на самую слабую часть, поскольку мое приложение перезапускается после завершения работы, но не служба специальных возможностей.
2. Привет, @Bringoff, у тебя был какой-нибудь успех с решением привязки / отмены привязки?
3. @VasiliFedotov не совсем. Мы улучшили частоту сбоев и сократили количество пользовательских отчетов о потерянных возможностях. Кроме того, я начал проверять статус доступности в фоновом режиме с помощью настройки ENABLED_ACCESSIBILITY_SERVICES. Это произошло не сразу, но для наших нужд было достаточно.
4. @VasiliFedotov Я опубликовал решение, связанное с отменой привязки.
Ответ №3:
Я не знаю, является ли это решением. Но я обнаружил, что это не работает: если я использую «dumpsys accessibility«, часть служб пуста, выглядит как:
User state[attributes:{id=0,
currentUser=true,
touchExplorationEnabled=false,
displayMagnificationEnabled=false,
navBarMagnificationEnabled=false,
autoclickEnabled=false}
services:{}]
Возможно, вы можете проверить, не пусты ли службы.