CN

Android Service: Building Your Own Notification Center (1) - Introduction to ...

Word count: 1.3kReading time: 7 min
2014/03/17
loading

1. Introduction to Accessibility Service

Updated 2026-10-13: This article was originally written in 2014 and covers accessibility service configuration available from Android 4.0 (API 14). Accessibility services remain supported, but using them as a general notification-center API is outdated. Android 4.3 (API 18) introduced NotificationListenerService for that purpose. The examples below correct service declarations and explain the node-recycling change in Android 13 (API 33).

Accessibility services help people with disabilities, or people temporarily unable to interact with a device, use applications. They can read interface text aloud or provide haptic feedback. This section covers service creation, event handling, and inspecting accessibility nodes.

The system binds an AccessibilityService after the user enables it; it is not started like an ordinary background task service. The examples give you a service skeleton and a few methods. To use them in your app, you’ll still need to add text-to-speech, a settings interface, and application resources. See the service lifecycle and configuration reference.

2. Creating Your Own Accessibility Service

2.1 Inheriting from AccessibilityService

An accessibility service can be bundled with an existing application or provided as a standalone application. Create a class extending AccessibilityService and implement the two required callbacks:

1
2
3
4
5
6
7
8
9
10
11
12
13
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;

public class MyAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}

@Override
public void onInterrupt() {
// Stop any accessibility feedback currently being delivered.
}
}

onAccessibilityEvent() receives events matching the configuration. onInterrupt() asks the service to interrupt feedback, such as speech; it does not mean the service has disconnected.

2.2 Declaring the Service in Manifest

Add the declaration inside <application> in AndroidManifest.xml. The service’s android:permission attribute requires BIND_ACCESSIBILITY_SERVICE, restricting binding to the system. Explicitly declaring android:exported also meets the component requirement for intent filters when targeting Android 12 (API 31) or higher.

1
2
3
4
5
6
7
8
9
<!-- Inside the existing <application> element. -->
<service
android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>

The action identifies the service to Android. Event selection comes from the configuration below. The complete declaration also needs metadata pointing to that XML resource.

2.3 Configuring the Service

A service does not require a launcher Activity. Keep one if the application needs to explain authorization or provide settings. Then decide what the service should respond to: which events, from all apps or only selected packages, and with what kind of feedback? These settings belong in AccessibilityServiceInfo.

To update runtime settings, use setServiceInfo(). Place this method in the service class and import android.accessibilityservice.AccessibilityServiceInfo. Starting with the existing configuration retains other XML settings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

@Override
protected void onServiceConnected() {
AccessibilityServiceInfo info = getServiceInfo();
if (info == null) {
return;
}
info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
AccessibilityEvent.TYPE_VIEW_FOCUSED;
info.packageNames = new String[]
{"com.example.android.myFirstApp", "com.example.android.mySecondApp"};
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
info.flags = AccessibilityServiceInfo.DEFAULT;
info.notificationTimeout = 100;
this.setServiceInfo(info);
}

Replace the example package names with the applications the service assists. notificationTimeout is an event-notification throttling interval in milliseconds, not a limit on how long the service runs.

From Android 4.0 (API 14), configuration can also be supplied in res/xml/serviceconfig.xml. Capabilities such as canRetrieveWindowContent must be declared in XML. This configuration selects the same event types:

1
2
3
4
5
6
7
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>

feedbackSpoken declares a feedback type; it does not implement speech. If a settingsActivity is specified, it must name an actual Activity declared by the application.

Complete the previous manifest declaration by adding the XML metadata:

1
2
3
4
5
6
7
8
9
10
<service
android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="@xml/serviceconfig" />
</service>

The resource name must match the XML filename. See AccessibilityServiceInfo for the available settings.

2.4 Responding to AccessibilityEvents

Use getEventType() to distinguish events. This method excerpt reads the content description and falls back to the event text when the description is absent:

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
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
final int eventType = event.getEventType();
String eventText;
switch(eventType) {
case AccessibilityEvent.TYPE_VIEW_CLICKED:
eventText = "Clicked: ";
break;
case AccessibilityEvent.TYPE_VIEW_FOCUSED:
eventText = "Focused: ";
break;
default:
return;
}

CharSequence label = event.getContentDescription();
if (label == null && !event.getText().isEmpty()) {
label = event.getText().get(0);
}
if (label == null) {
return;
}
eventText = eventText + label;
// Pass eventText to the app's initialized text-to-speech implementation.
}

The callback runs on the service’s main thread, so expensive work needs separate scheduling. The speech implementation indicated by the comment must handle initialization and interruption.

For additional context, an accessibility service can inspect the accessibility node tree, available from Android 4.0 (API 14). Enable this attribute in the XML configuration above:

1
android:canRetrieveWindowContent="true"

event.getSource() can return a node or null. Event type, service capabilities, and whether the node remains available all affect the result; an active window alone does not guarantee a node. The accessibility tree also need not map one-to-one to the View hierarchy. See getSource().

The following alternative callback assumes a known layout: the event source’s parent has a label as its first child and a checkbox as its second. Real applications must locate nodes using their accessibility semantics instead of applying these indexes to arbitrary interfaces. This excerpt requires imports for AccessibilityNodeInfo and android.os.Build, and compileSdk 33 or higher:

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
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
AccessibilityNodeInfo rowNode = null;
AccessibilityNodeInfo labelNode = null;
AccessibilityNodeInfo completeNode = null;
try {
if (source == null) return;
rowNode = source.getParent();
if (rowNode == null || rowNode.getChildCount() < 2) return;
labelNode = rowNode.getChild(0);
completeNode = rowNode.getChild(1);
if (labelNode == null || completeNode == null
|| !completeNode.isCheckable()) return;
CharSequence label = labelNode.getText();
if (label == null) return;
String report = label + (completeNode.isChecked() ? " (checked)" : " (not checked)");
// Deliver report through the app's feedback implementation.
} finally {
recycleBeforeAndroid13(completeNode);
recycleBeforeAndroid13(labelNode);
recycleBeforeAndroid13(rowNode);
recycleBeforeAndroid13(source);
}
}

@SuppressWarnings("deprecation")
private static void recycleBeforeAndroid13(AccessibilityNodeInfo node) {
if (node != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
node.recycle();
}
}

Android 13 (API 33) discontinued node object pooling. recycle() is deprecated and has no effect on those versions. On older systems, recycle acquired nodes when finished; finally handles early returns as well. See AccessibilityNodeInfo.recycle().

After installation, the user must enable the service in Settings → Accessibility. Authorization, package selection, and event configuration must all allow the event. A manifest declaration alone does not grant access.

About Me && Blog

Below is my personal intro and related links. I look forward to exchanging ideas with fellow professionals. “When three walk together, one can always be my teacher!”

  1. Blogger Intro
  2. Blog Content Navigation: A guide for my blog content.
  3. Curated Excellent Blog Articles - Android Performance Optimization Must-Knows
  4. Android Performance Optimization Knowledge Planet: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.

One walks faster alone, but a group walks further together.

Scan WeChat QR Code

CATALOG
  1. 1. 1. Introduction to Accessibility Service
  2. 2. 2. Creating Your Own Accessibility Service
    1. 2.1. 2.1 Inheriting from AccessibilityService
    2. 2.2. 2.2 Declaring the Service in Manifest
    3. 2.3. 2.3 Configuring the Service
    4. 2.4. 2.4 Responding to AccessibilityEvents
  3. 3. About Me && Blog