Форум программистов, компьютерный форум, киберфорум
Программирование Android
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.83/6: Рейтинг темы: голосов - 6, средняя оценка - 4.83
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70

Артефакт при осуществлении первой попытки обратного вызова для метода onAccessibilityStateChanged

20.03.2017, 13:27. Показов 1373. Ответов 12

Студворк — интернет-сервис помощи студентам
Я использую в своем приложении функциональность, связанную с дополнительными возможностями, представляемыми через использование служб Accessibility Services для операционной системы Андроид.

Созданная для моего приложения служба Accessibility Service первоначально находится в отключенном состоянии.

При запуске своего приложения я проверяю включена ли для моего приложения моя служба Accessibility Service с заданным именем.

Если соответствующая служба не включена, я вызываю Активити с системными настройками Андроида, где пользователь может вручную включить службу Accessibility Service для моего приложения.

После того, как пользователь нажмёт на кнопку включения моей службы в Настройках, мне необходимо отследить событие соответствующее тому, что произошло изменение состояния моей службы.

Т.е. сразу же после включения в Настройках моей службы Accessibility Service, необходимо осуществить проверку того, что эта служба оказалась включенной.

Для этого я использую подход, связанный с использованием обратного вызова метода, который отрабатывает AccessibilityStateChangeListener для созданного мной экземпляра AccessibilityManager.

Ниже будет приведен фрагмент кода из файла MainActivity.java моего приложения, который отвечает за регистрацию обратного вызова метода onAccessibilityStateChanged, проверяющего состояние того, включена ли моя служба Accessibility Service.

По какой-то непонятной причине при первом вызове этого метода моя служба не появляется в списке включенных служб, получаемых при помощи вызова метода getEnabledAccessibilityServiceList().

При последующем нажатии кнопки "Выкл.", а затем "Вкл." для службы Accessibility Service моего приложения вызов метода onAccessibilityStateChanged() начинает корректно отображать состояние включенности моей службы.

1. Скажите, по какой причине метод getEnabledAccessibilityServiceList() при первоначальном отработке события обратного вызова onAccessibilityStateChanged() возвращает некорректные сведения о состоянии включенности моей службы?

Таким образом, чтобы обойти этот артефакт я предполагаю осуществить вызов события обратного вызова метода onAccessibilityStateChanged() ВРУЧНУЮ.

Только я не понимаю, как это можно сделать.

2. Подскажите, пожалуйста, каким образом можно ВРУЧНУЮ (т.е. программным образом) осуществить вызов метода onAccessibilityStateChanged() в самом конце метода onCreate() класса MainActivity моего приложения?

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package com.comp.serviceaccessibility;
 
import java.util.List;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
 
public class MainActivity extends Activity {
 
      final static String LOG_TAG = "myLogs";
      
      public static String AccessibilityServiceId = "com.comp.serviceaccessibility/.AppAccessibilityService";     
      public static Boolean AcessibilityServiceIsEnabled;     
      public static AccessibilityManager accessibilityManager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        // Add Accessibility Service change listener        
        accessibilityManager = (AccessibilityManager)getSystemService(Context.ACCESSIBILITY_SERVICE);                
        accessibilityManager.addAccessibilityStateChangeListener(new AccessibilityManager.AccessibilityStateChangeListener() 
        {
            @Override
            public void onAccessibilityStateChanged(boolean b) 
            {
                Log.d(LOG_TAG, "onAccessibilityStateChanged");
                AcessibilityServiceIsEnabled = false;
                
                List<AccessibilityServiceInfo> runningServices = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
                                
                for (AccessibilityServiceInfo service : runningServices) 
                {
                    if (AccessibilityServiceId.equals(service.getId())) 
                    {
                        AcessibilityServiceIsEnabled = true;
                    }
                }
                
                Toast.makeText(MainActivity.this, "AcessibilityServiceIsEnabled: "+ AcessibilityServiceIsEnabled.toString(), Toast.LENGTH_SHORT).show();
            }
        });            
        
        // ??? What code should I insert here to call onAccessibilityStateChanged() method MANUALLY?
    }
        
    public void onStartAccessibilitySettingsActivity(View v) 
    {
        Log.d(LOG_TAG, "Turn On Service In Settings");
        
        // Start Accessibility Settings Activity
        Intent intentAccessibilitySettings = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);      
        PackageManager packageManager = getPackageManager(); 
        ComponentName componentName = intentAccessibilitySettings.resolveActivity(packageManager);
        if (componentName != null) {
            try {
                startActivity(intentAccessibilitySettings);    
            } catch (ActivityNotFoundException ex) {
                Log.d(LOG_TAG, "Accessibility Settings Activity is not found");       
            }
        } 
      }    
}
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
20.03.2017, 13:27
Ответы с готовыми решениями:

Не удалось найти значение для обратного вызова или не был реализован ICallbakEventHandler
Здравствуйте форумчане. Нужна ваша помощь. Работаю с компонентами DevExpress и мне необходимо на странице разместить ASPxDropDownEdit, а...

При попытке вызова метода ничего не происходит. Код не отрабатывает.
Здравствуйте Решил разобраться с классами На нескольких сайтах нашёл пример кода с классами Но при попытке вывода значения функции...

Выбор метода для вызова с varags параметрами
Какой результат попытки компиляции и запуска программы: public class MainClass { public static void test(int ...a) { ...

12
Модератор
 Аватар для vxg
3407 / 2178 / 354
Регистрация: 13.01.2012
Сообщений: 8,448
20.03.2017, 13:35
InessaSuper, при включении службы вызывается какой-либо метод самой службы (риторический вопрос) - почему бы просто не ловить ее состояние в этом методе вместо того что бы цеплять слушатели?
0
2884 / 2296 / 769
Регистрация: 12.05.2014
Сообщений: 7,978
20.03.2017, 14:19
Цитата Сообщение от InessaSuper Посмотреть сообщение
После того, как пользователь нажмёт на кнопку включения моей службы в Настройках, мне необходимо отследить событие соответствующее тому, что произошло изменение состояния моей службы.
в сервисе вызывается onServiceConnected

Добавлено через 31 минуту
а вообще если делать через слушатель, как в коде выше то там прилетает переменная onAccessibilityStateChanged(boolean b)
которая показывает включили сервис или нет

и я не понимаю зачем там этот дикий цикл с перебором
1
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
20.03.2017, 16:49  [ТС]
Первоначально я тоже предполагала использовать обработчик событий onServiceConnected в запущенной мною службе.

При этом в обработчике события onServiceConnected в классе своей службы AppAccessibilityService я планировала изменять значение соответствующего флага isEnabled в true, индицирующего таким образом, что служба запущена.

Но я столкнулась с тем, что в документации по API для Accessibility Services не определен обработчик события onServiceDisconnected как для обычных служб.

Таким образом, у меня нет возможности изменить значение флага isEnabled в false для созданной моим приложением службы Accessibility Service в том случае, когда пользователь отключает разрешение на её использование в Настройках.

Я пыталась использовать унаследованный от обычной службы обработчик события onDestroy(), но для служб Accessibility Services это событие отрабатывает, когда служба удаляется вместе с деинсталляцией использующего её приложения.

А когда же я просто в Настройках отключаю свою службу Accessibility Service, то управление не передаётся обработчику события onDestroy().

И нет никакого другого обработчика события отключения службы Accessibility Service, в котором я могла бы перевести значение соответствующего флага isEnabled в fasle.

Поэтому мне всё равно необходимо было бы использовать слушатель AccessibilityStateChangeListener для того, чтобы проверять значение флага isEnabled в ссылке на экземпляр класса своей службы AppAccessibilityService.

Таким образом, мне в любом случае необходимо использовать обработчик onAccessibilityStateChanged() для слушателя AccessibilityStateChangeListener, т.к. я не могу передавать поток управления ходом выполнения операций из своего приложения куда-либо (в том числе и в запущенной мною службу, которая "живёт своей жизнью").

Каким образом я осуществляю проверку "запущенности" моей службы Accessibility Service в своём приложении - это вопрос вторичный.

Цикл с перебором для всех запущенных служб с помощью вызова метода getEnabledAccessibilityServiceList() в обработчике события onAccessibilityStateChanged используется мной потому, что обработчик onAccessibilityStateChanged отрабатывает при изменении ЛЮБОЙ из установленных на мобильном устройстве служб Accessibility Service, но не конкретизирует какая именно служба изменила своё состояние.

Главный мой вопрос заключается в том, каким образом я могу вызвать обработчик onAccessibilityStateChanged() вручную программным образом, чтобы исключить влияние описанного мною выше артефакта?
0
2884 / 2296 / 769
Регистрация: 12.05.2014
Сообщений: 7,978
20.03.2017, 16:55
Цитата Сообщение от InessaSuper Посмотреть сообщение
у меня нет возможности изменить значение флага isEnabled в false для созданной моим приложением службы Accessibility Service в том случае, когда пользователь отключает разрешение на её использование в Настройках.
я когда проверял, то onUnbind() в сервисе срабатывал каждый раз когда с выключал службу

Добавлено через 1 минуту
Цитата Сообщение от InessaSuper Посмотреть сообщение
но не конкретизирует какая именно служба изменила своё состояние.
поэтому я и предложил следить за onServiceConnected() в сервисе
вместо стрёмных статических флагов оттуда лучше кидать интент, который будет ловить активити или сервис - смотря как надо реагировать
0
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
20.03.2017, 17:58  [ТС]
Мне кажется я нашла причину возникновения описанного мною артефакта, но не могу понять как мне с ним бороться.

Я обратила внимание, что в протоколе LogCat при первоначальном включении моей службы происходит сначала запуск обработчика метода onAccessibilityStateChanged() для слушателя AccessibilityStateChangeListener из класса MainActivity, а затем уж отрабатывает обработчик метода onServiceConnected() из класса моей службы AppAccessibilityService.

Поэтому какой бы подход я не использовала для определения того, запущена ли моя служба: во время её первого запуска, по какой-то причине, всегда отрабатывает "НЕПРАВИЛЬНАЯ" последовательность событий:

Последовательность 1

- MainActivity.accessibilityManager.Access ibilityStateChangeListener.onAccessibili tyStateChanged()
- AppAccessibilityService.onServiceConnect ed()

А вот уже затем после выключения и последующего включения моей службы всё отрабатывает ПРАВИЛЬНО и как надо в следующей последовательности:

Последовательность 2

- AppAccessibilityService.onServiceConnect ed()
- MainActivity.accessibilityManager.Access ibilityStateChangeListener.onAccessibili tyStateChanged()

Вопрос заключается в следующем.

Какова причина возникновения описанного мною артефакта и как можно сделать так, чтобы запуск необходимых мне обработчиков запускался сразу в соответствии со 2-й последовательностью?
0
2884 / 2296 / 769
Регистрация: 12.05.2014
Сообщений: 7,978
20.03.2017, 18:24
Цитата Сообщение от InessaSuper Посмотреть сообщение
- MainActivity.accessibilityManager.Access ibilityStateChangeListener.onAccessibili tyStateChanged()
убрать вообще

зачем вообще в MainActivity вешается этот слушатель?
есть сервис - пусть он кидает интенты при старте/останове и на них уже реагирует активити
0
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
20.03.2017, 18:59  [ТС]
Я завела этот слушатель в MainActivity для того, чтобы в потоке выполнения приложения сделать как бы "остановку", пока пользователь не включит мою службу AppAccessibilityService.

Сразу же после того, как пользователь "включает" мою службу, я в обработчике слушателя передаю выполнение исполняемого потока своего приложения далее.

Пока моя служба не включена, я не могу передавать поток управления своего приложения далее, т.к. "включаемая" мною служба мониторит активити других приложений, запускаемых в дальнейшем в свою очередь из моего приложения.

Подскажите, пожалуйста, что имется ввиду под высказыванием "есть сервис - пусть он кидает интенты при старте/останове и на них уже реагирует активити"?

Я ещё не волшебница, а только учусь...

Ниже приведен мой класс AppAccessibilityService, который отслеживает activityName в запускаемых приложениях на Андроид устройстве.

Скажите, каким образом необходимо изменить классы AppAccessibilityService и MainActivity, чтобы реализовать рекомендуемый подход с "киданием интентов при старте/останове и реагировании на них активити"?

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.comp.serviceaccessibility;
 
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
 
public class AppAccessibilityService extends AccessibilityService {
 
    public static String activityName;
 
    @Override
    public void onServiceConnected() {      
        Log.i("***", "onServiceConnected ");
        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
        info.notificationTimeout = 100;
        info.feedbackType = AccessibilityEvent.TYPES_ALL_MASK;
        setServiceInfo(info);
    }
        
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.i("***", "onAccessibilityEvent ");
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 
            activityName = String.valueOf(event.getClassName());
            Log.i("***", "onAccessibilityEvent " + activityName);
            //Toast.makeText(getApplicationContext(), activityName, Toast.LENGTH_LONG).show();            
        }
    }
 
    @Override
    public void onInterrupt() {
        Log.i("***", "onInterrupt ");
    }
}
0
2884 / 2296 / 769
Регистрация: 12.05.2014
Сообщений: 7,978
20.03.2017, 19:59
ну вот например есть сервис, я оттуда все лишнее вытер, оставил строки, которые надо перенести в свой сервис
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyAccessibilityService extends AccessibilityService {
 
    static final String ACTION_SERVICE_STARTED = "ACTION_SERVICE_STARTED";
    static final String ACTION_SERVICE_STOPPED = "ACTION_SERVICE_STOPPED";
 
    @Override
    public boolean onUnbind(Intent intent) {
        sendMessageToActivity(false);
        return super.onUnbind(intent);
    }
 
    @Override
    protected void onServiceConnected() {
        sendMessageToActivity(true);
    }
 
    private void sendMessageToActivity(boolean isStarted) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.setAction(isStarted ? ACTION_SERVICE_STARTED : ACTION_SERVICE_STOPPED);
        startActivity(intent);
    }
}
и минимальный код в активити
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        Intent intent = new Intent(MainActivity.this, MyAccessibilityService.class);
        startService(intent);
 
        if (getIntent() != null) {
            doSomething();
        }
    }
 
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    }
 
    private void doSomething() {
        String action = getIntent().getAction();
        if (MyAccessibilityService.ACTION_SERVICE_STARTED.equals(action)) {
            Log.i("***", "onNewIntent: SERVICE STARTED");
        } else if (MyAccessibilityService.ACTION_SERVICE_STOPPED.equals(action)) {
            Log.i("***", "onNewIntent: SERVICE STOPPED");
        }
    }
}
1
97 / 4 / 0
Регистрация: 09.05.2015
Сообщений: 70
21.03.2017, 10:53  [ТС]
Приведенный выше код отлично работает.

Для того, чтобы его запустить мне понадобилось добавить ещё одну строчку кода в метод sendMessageToActivity() класса MyAccessibilityService (intent.setFlags(Intent.FLAG_ACTIVITY_NE W_TASK), чтобы избежать следующей ошибки возникшей при компиляции:

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

Теперь метод sendMessageToActivity() выглядит следующим образом:

Java
1
2
3
4
5
6
7
    private void sendMessageToActivity(boolean isStarted) 
    {
        Intent intent = new Intent(this, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction(isStarted ? ACTION_SERVICE_STARTED : ACTION_SERVICE_STOPPED);
        startActivity(intent);
    }
В процессе поиска этого решения на форуме stackoverflow я обнаружила следующий комментарий к найденному решению:

Adding Intent.FLAG_ACTIVITY_NEW_TASK will solve your error, but make sure if you need this flag or not as it will trigger the activity as new task which you may not want in your scenario.

Start new Activity outside the Activity context (см. 3-й ответ)
http://stackoverflow.com/quest... ty-context


Я хочу понять, что критичного в том, что в моём случае MainActivity будет запускаться как новый таск?

Какие проблемы может за собой повлечь использование этого подхода в дальнейшей работе моего приложения?
0
Модератор
 Аватар для vxg
3407 / 2178 / 354
Регистрация: 13.01.2012
Сообщений: 8,448
22.03.2017, 08:53
InessaSuper, новый таск это к примеру у вас уже запущена активити со своим стеком возвратов и тут бац ещё одна такая же на пустом месте
0
22.03.2017, 10:39

Не по теме:

да там на самом деле проще просто прочитать внимательно что написано в подсказке по Ctrl+Q для каждого из вариантов (и на флагах и в манифесте), а не ждать пока на форуме кто-то потрудится настрочить пол страницы текста

0
22.03.2017, 11:00

Не по теме:

Цитата Сообщение от Паблито Посмотреть сообщение
да там на самом деле проще просто прочитать внимательно
прочитать /попробовать /офигеть /напиться /еще раз прочитать /еще раз попробовать /еще раз офигеть и в конце концов просто опытным путем выяснить нечто необъяснимое в комбинации этих казалось бы понятных флажков удовлетворяющее вашим требованиям)

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.03.2017, 11:00
Помогаю со студенческими работами здесь

Ошибка вызова метода для блока с параметром
source_file.rb:18:in `&lt;main&gt;': undefined method `power' for main:Object (NoMethodError) #протоколирование def log s, someProc ...

Процедура обратного вызова
Немного запутался в процедурах обратного вызова, в процедурных типах. Есть DLL и описание к ней. ... type TFullCardInfo =...

Функция обратного вызова
Приветствую всех. Создаю свой класс и при определенном событии в экземпляре этого класса должна выполниться функция, которая будет указана...

Интерфейсы обратного вызова
Как часто ли Вы используете интерфейсы обратного вызова? Если да, то для чего обычно? :)

Функция обратного вызова
Доброго времени суток уважаемые гуру программирования! Нужна помощь! В описании к DLL есть функция: function...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Новые блоги и статьи
http://iceja.net/ математические сервисы
iceja 20.01.2026
Обновила свой сайт http:/ / iceja. net/ , приделала Fast Fourier Transform экстраполяцию сигналов. Однако предсказывает далеко не каждый сигнал (см ограничения http:/ / iceja. net/ fourier/ docs ). Также. . .
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит переходные токи и напряжения на элементах схемы. . . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru