惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
O
OpenAI News
V
V2EX
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
小众软件
小众软件
L
LINUX DO - 热门话题
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - Franky
Security Latest
Security Latest
S
SegmentFault 最新的问题
Project Zero
Project Zero
Spread Privacy
Spread Privacy
K
Kaspersky official blog
J
Java Code Geeks
V
Vulnerabilities – Threatpost
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
月光博客
月光博客
T
The Exploit Database - CXSecurity.com
L
Lohrmann on Cybersecurity
人人都是产品经理
人人都是产品经理
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
WordPress大学
WordPress大学
量子位
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Simon Willison's Weblog
Simon Willison's Weblog
S
Secure Thoughts
博客园 - 【当耐特】
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
Visual Studio Blog
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
腾讯CDC
Cyberwarzone
Cyberwarzone
IT之家
IT之家
GbyAI
GbyAI
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
云风的 BLOG
云风的 BLOG
T
Troy Hunt's Blog
D
Docker

Android Performance

SmartPerfetto 架构文章 Q&A:8 个深度技术问答 从 Trace 到洞察:SmartPerfetto AI Agent 的 Harness Engineering 实战 OpenClaw 常见问题解答:Token 消耗、能干什么、本地模型、隐私安全、使用体验 我把 OpenClaw 跑在本地三周后,发现它根本不是聊天机器人 Android Perfetto 系列 10 - Binder 调度与锁竞争 Android Perfetto 系列 9 - CPU 信息解读 Android Perfetto 系列 8:深入理解 Vsync 机制与性能分析 Android Perfetto 系列 7 - MainThread 和 RenderThread 解读 Android Perfetto 系列 6:为什么是 120Hz?高刷新率的优势与挑战 Android Perfetto 系列 5:Android App 基于 Choreographer 的渲染流程 Android Perfetto 系列 4:使用命令行在本地打开超大 Trace Android ANR 系列 3 :ANR 案例分享 Android ANR 系列 2 :ANR 分析套路和关键 Log 介绍 Android ANR 系列 1 :理解 Android ANR 设计思想 Android Perfetto 系列 3:熟悉 Perfetto View Android Perfetto 系列 2:Perfetto Trace 抓取 Android Perfetto 系列 1:Perfetto 工具简介 Android Perfetto 系列目录 2023 年的方方面面 关于 The Android Performance 知识星球介绍 The Performance Design Of OS OS 设计之性能设计 当 App 有了系统权限,真的可以为所欲为? The Performance 星球茶话会 - 第一期 Systrace 线程 CPU 运行状态分析技巧 - Sleep 和 Uninterruptible Sleep 篇 Systrace 线程 CPU 运行状态分析技巧 - Running 篇 Systrace 线程 CPU 运行状态分析技巧 - Runnable 篇 Android 性能优化的术、道、器 Techniques, Philosophy, and Tools for Android Performance Optimization 回顾 2021 一本讲 Android 流畅性的书,应该有什么内容? Android 系统开发系列(1):Android 12 源代码下载、编译和刷机 Android Systrace 响应速度实战 3 :响应速度延伸知识 Android Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例 Android Systrace 响应速度实战 1 :了解响应速度原理 Android Systrace 流畅性实战 3 :卡顿分析过程中的一些疑问 Android Systrace 流畅性实战 2 :案例分析 - MIUI 桌面滑动卡顿分析 Android Systrace 流畅性实战 1 :了解卡顿原理 华为手机刷微博体验更好?技术角度的一些分析和思考 一个「闰」字引发的事故 - 三星系统重启分析 Android App 链式唤醒分析 Android Systrace 基础知识 - SurfaceFlinger 解读 Android 开发者学习路线(2020 版本) 我的 2020 年读书单 Android Systrace 基础知识 - CPU Info 解读 Android Systrace 基础知识 - Triple Buffer 解读 Android Systrace 基础知识 - Binder 和锁竞争解读 「置顶」博客文章目录 Android Systrace 基础知识 - Vsync 解读 Android App 启动优化全记录 Android Systrace 基础知识 - MainThread 和 RenderThread 解读 Android Systrace 基础知识 - Input 解读 Android 中的“后台无效动画“行为分析 Android 基于 Choreographer 的渲染机制详解 Android 中的卡顿丢帧原因概述 - 低内存篇 Android 桌面被杀问题分析案例 Android 中的卡顿丢帧原因概述 - 应用篇 Android 中的卡顿丢帧原因概述 - 系统篇 Android 中的卡顿丢帧原因概述 - 方法论 Android 中的 Activity Launch Mode 详解 Android 中的 Hardware Layer 详解 Android Systrace 基础知识 -- 分析 Systrace 预备知识 Android Systrace 基础知识 - SystemServer 解读 Android Systrace 基础知识 -- Systrace 简介 Android Systrace 基础知识 -- Why 60 fps ? Android Systrace -- 系列文章目录 Android 新的流畅体验,90Hz 漫谈 利器 - 高效工具推荐 Android 无障碍服务导致的整机卡顿案例分析 2018 年度好物推荐 - 给辛勤工作的自己一点奖励 Android 系统开发源码环境搭建 陆奇:除了好代码,工程师怎样才算优秀? 程序员的修炼-08-阅读之美 程序员的修炼-07-游戏与编程 程序员的修炼-06-互联网那些事 程序员的修炼-05-了解你的用户 程序员的修炼-04-关于测试的一些思考 程序员的修炼-03-Web 设计原则 程序员的修炼-02-编程之道 程序员的修炼-01-绝地反击之术 Android 系统不释放内存吗? 关于 Android 系统流畅性的一些思考 知乎 救救你的 StartingWindow 「置顶」Android 性能优化必知必会 2017 年度好物推荐 - 给辛勤工作的自己一点奖励 2017 Android Bottom navigation 规范二:样式、行为与规格 Android Bottom Navigation 规范一:使用方法 Android 中如何计算 App 的启动时间? Android 应用启动优化:一种 DelayLoad 的实现和原理(下篇) Android 应用启动优化 - 一种 DelayLoad 的实现和原理(上篇) Android hwui 中 RenderThread 工作流程 Java7 HashMap 源码分析 Android 代码内存优化建议 - OnTrimMemory 优化 Android 代码内存优化建议 - Android 资源篇 Android 代码内存优化建议 - Android 官方篇 Android 代码内存优化建议 - Java 官方篇 Nexus6 with Android M 开启多窗口模式 细说 Java 单例模式 Android 性能优化典范 - Profile GPU Rendering
Android Service:开发自己的通知中心(2):辅助性服务实战
Gracker · 2014-04-01 · via Android Performance

1.辅助性服务实战介绍

上一篇文章介绍了什么是Accessibility以及简单的使用,这一篇文章就来讲讲如何使用Accessibility服务来创建一个简单的Android通知中心。Android中通知中心是一个系统层面的服务,负责显示应用和系统发来的通知(Notification,比如USB插入、选择输入法、未接来电、截图、天气信息、新闻推送等等)。在android4.3之前,一般的第三方应用是无法获取Notification list的(在Android4.3之后,有了一个新的接口,NotificationListenerService.getActiveNotifications(),可以获取当前的Notification)。但是利用Accessibility服务可以监听到各种事件的特性,可以开发一个第三方的通知中心,实现与系统通知栏类似的功能。

下面就来介绍如何开发自己的通知中心。

2.开发第三方通知中心

2.1继承AccessibilitySerivce

按照上一篇辅助性服务的介绍,一个辅助性服务可以被捆绑到一个标准的应用程序上,或者以一个单独的安卓工程被创建,我们这里建立一个服务,继承AccessibilitySerivce

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
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Parcelable;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityRecord;
import android.widget.Toast;

public class NotificationFetcherService extends AccessibilityService {

private static final String TAG = \"NotificationFetcherService: \";

public void onAccessibilityEvent(AccessibilityEvent event) {
if (!(event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) ){
return;
}

Notification localNotification = (Notification)event.getParcelableData();

if (localNotification != null) {
Intent intent=new Intent();
intent.putExtra(\"NotifyData\", localNotification);
intent.setAction(\".NotificationFetcherService\");
sendBroadcast(intent);
}

}

@Override
protected void onServiceConnected() {

// Define it in both xml file and here, for compatibility with pre-ICS devices
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED |
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED |
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;

info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
setServiceInfo(info);
}

@Override
public void onInterrupt() {
System.out.println(\"onInterrupt\");
}

}

继承AccessibilitySerivce必须要重写几个重要的方法:

  1. onServiceConnected方法负责在服务和Activity绑定的时候,进行初始化数据,这里新建了一个AccessibilityServiceInfo对象,并将TYPE_NOTIFICATION_STATE_CHANGED、TYPE_WINDOW_STATE_CHANGED、TYPE_WINDOW_CONTENT_CHANGED纳入监听范围,TYPE_NOTIFICATION_STATE_CHANGED表示这个服务可以监听Notification的变化,我们正是使用这个特性来实现第三方的通知中心功能。
  2. onInterrupt是服务断开时调用的函数
  3. onAccessibilityEvent是最重要的,它负责监听所注册的eventTypes(在onServiceConnected中注册的)的事件。从上面的代码中我们可以得到一个Notification对象:
1
Notification localNotification = (Notification)event.getParcelableData();

得到Notification对象之后,就可以进行自己的操作,我这里是通过广播的形式,将收到的Notification发送给Activity进行处理。

这里也会碰到一个小问题:当一个Notification对象太大时(比如截图、未接来电等,Notification.contentView就很大,通过广播传播会出现data过大无法传输的问题),这时可以把Notification.contentView对象暂时保存在Application中,然后再置为null,Activity中接收到数据后,再进行赋值。

2.2在Manifest中注册service

1
2
3
4
5
6
7
8
9
10
11
<service
android:name=\".NotificationFetcherService\"
android:permission=\"android.permission.BIND_ACCESSIBILITY_SERVICE\" >
<intent-filter>
<action android:name=\"android.accessibilityservice.AccessibilityService\" />
</intent-filter>

<meta-data
android:name=\"android.accessibilityservice\"
android:resource=\"@xml/accessibilityserviceconfig />
</service>

这里就是普通的service注册,注意 <mate-data>标签中的xml文件:从Android 4.0版本开始,有另外一种方法:使用XML文件来配置这类服务。如果你以XML的形式来定义你的服务,某些像canRetrieveWindowContent可配置的选项就可用了。和service一样的配置,使用XML来定义。如果你要使用XML路径,要在你的mainfest文件中指定它,在你的服务声明中添加<meta-data>标签,并指向这个XML资源文件。比如上面的代码,我们在res/xml/中建立accessibilityaseviceconfig.xml,内容如下:

1
2
3
4
5
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<accessibility-service xmlns:android=\"http://schemas.android.com/apk/res/android\"
android:accessibilityEventTypes=\"typeWindowStateChanged|typeNotificationStateChanged|typeWindowContentChanged\"
android:accessibilityFeedbackType=\"feedbackGeneric\"
/>

服务这里就配置好了。

2.3 接受并处理Notification

下面的Activity中就可以接受这个数据,然后怎么处理就看自己了,这里只是简单地显示出来。

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import android.app.Activity;
import android.app.Application;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.os.Process;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;

public class NotificaitonActivity extends Activity {
private static final int NOTIFY_DATA_FLAG = 1;
private static final String NOTIFY_DATA_ID_STR= \"NotifyData\";

private NotifyDataReceiver receiver;
private TextView textView;
private LinearLayout rootLayout;
private Button button;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.notify_test_textview);
textView.setMovementMethod(ScrollingMovementMethod.getInstance());
rootLayout = (LinearLayout) findViewById(R.id.root_layout);

registerBroadcast();

button = (Button) findViewById(R.id.test_button);
button.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
Button b = new Button(NotificaitonActivity.this);
b.setText("Tthis");
rootLayout.addView(b);
}
});
}

private void registerBroadcast() {
receiver = new NotifyDataReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(".NotificationFetcherService");
this.registerReceiver(receiver, filter);
Log.e("Dx:", "Broadcast registered.........");
}

private void addToUi(RemoteViews remoteView) {
rootLayout.addView(remoteView);
}

private void showNotify(String notiString) {
textView.setText(textView.getText() + \"n\" + notiString);
}

private class NotifyDataReceiver extends BroadcastReceiver{

@Override
public void onReceive(Context context, Intent intent) {
Log.e(\"Dx:\", \"Receiver got msg in onReceive()...\");

Parcelable notifyParcelable = intent.getParcelableExtra(\"NotifyData\");

if (notifyParcelable != null) {

Notification notification = (Notification) notifyParcelable;
showNotify(\"tickerText: \" + notification.tickerText);
showNotify(\"toString: \" + (String)(notification.toString()));

RemoteViews remoteV = notification.contentView;
if (remoteV==null) {
showNotify(\"remoteView is: null\" );
} else {
showNotify(\"remoteView is: not null\" );

addToUi(remoteV);
}

PendingIntent pendIntent = notification.contentIntent;
if (pendIntent==null) {
showNotify(\"pendIntent is: null\" );
} else {
showNotify(\"pendIntent is: not null\" );
}

showNotify(\"**************************\" );
showNotify(\" \" );

}

}
}

}

注:这里有很重要的一点,由于AccessibilityService的特殊性,用户必须手动到设置-辅助功能中,打开对应的服务,我们才可以通过AccessibilityService获得对应的数据,这一点非常重要。

上面的Activity只是简单地显示Notification,关于更多Notification的操作,可以参考Notification这个类,其中重要的属性有:contentView,flags。要模拟真正的通知中心,还是要费一番功夫的。这里由于公司项目的保密,暂不提供对应的实现代码(其实得到Notification就已经成功了一半了),有兴趣的同学可以私下和我交流。

3.总结和问题

AccessibilityService的实战就讲到这里,这一篇博文也是拖了一段时间才写完的,也算是为前一段时间的项目做个了结。

项目中目前还存在的问题:

  1. 无法获取安装这个应用之前的系统的Notification
  2. 得到的Notification对象没法保存在本地,所以这个服务被杀掉之后,所有的数据都会丢失。(试过用db4o这种对象数据库来进行存储,发现行不通)
  3. 对Android系统的Notification对象的行为模仿不够(有些系统的事件监听不到,比如usb的插拔、usb调试的开关等)

关于我 && 博客

下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!

  1. 博主个人介绍 :里面有个人的微信和微信群链接。
  2. 本博客内容导航 :个人博客内容的一个导航。
  3. 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
  4. Android性能优化知识星球 : 欢迎加入,多谢支持~

一个人可以走的更快 , 一群人可以走的更远

微信扫一扫