Android辅助功能实现自动抢红包(附源码)
一、描述
最近看到同事有用抢红包的软件,就想看看抢红包的具体实现是如何的,所以了解了一下,有用辅助功能实现的,所以在下面的示例中会展示一个抢红包的小Demo,附带源码抢红包源码。
二、效果图
在桌面收到红包进行抢
在聊天页面收到口令红包
三、AccessibilityService使用
创建辅助服务类,继承AccessibilityService,实现两个接口,接收系统的事件
public class MyService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
辅助服务的配置文件,配置事件,在 res/xml下创建accessibility_service_info.xml
//具体属性的说明在第5点有说明
<?xml version="1.0" encoding="utf-8"?
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:packageNames="top.cokernut.sample"
android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity" /
注册Service辅助服务,并且为Service附加上第二步创建的xml,看清除下面的一些属性,必须要加,如果有的没加的话是没效果的
<service
android:name=".MyService"
android:label="辅助功能"
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/accessibility_service_info" /
</service
4 清单文件中添加权限
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /
辅助服务配置文件xml属性说明:
//是否可以检索整个层级下的内容
android:canRetrieveWindowContent="true"级下的信息
//事件通知触发点,比如窗口打开,滑动,焦点变化,长按等。
android:accessibilityEventTypes="typeAllMask"
#TYPES_ALL_MASK:所有类型
#TYPE_VIEW_CLICKED :单击
#TYPE_VIEW_LONG_CLICKED :长按
#TYPE_VIEW_SELECTED :选中
#TYPE_VIEW_FOCUSED :获取焦点
#TYPE_VIEW_TEXT_CHANGED :文字改变
#TYPE_WINDOW_STATE_CHANGED :窗口状态改变
//表示反馈方式,比如是语音播放,还是震动
android:accessibilityFeedbackType="feedbackGeneric"
//接受事件的时间间隔,通常将其设置为100即可.
android:notificationTimeout="100"
//表示该服务是用来单独监听哪个应用的产生的事件,其他的都会过滤,如果不填就是对所有的应用进行监听,填入包名即可。
android:packageNames="top.cokernut.sample"
//在代码中我们就可以通过node节点来getViewIdResourceName()获取对应的节点的id
android:accessibilityFlags="flagDefault"
提供一个AccessibilityService的基类,集成了一些常用方法:
public class BaseAccessibilityService extends AccessibilityService {
private AccessibilityManager mAccessibilityManager;
private Context mContext;
private static BaseAccessibilityService mInstance;
public void init(Context context) {
mContext = context.getApplicationContext();
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
public static BaseAccessibilityService getInstance() {
if (mInstance == null) {
mInstance = new BaseAccessibilityService();
}
return mInstance;
}
/**
* Check当前辅助服务是否启用
*
* @param serviceName serviceName
* @return 是否启用
*/
private boolean checkAccessibilityEnabled(String serviceName) {
List<AccessibilityServiceInfo accessibilityServices =
mAccessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
for (AccessibilityServiceInfo info : accessibilityServices) {
if (info.getId().equals(serviceName)) {
return true;
}
}
return false;
}
/**
* 前往开启辅助服务界面
*/
public void goAccess() {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
/**
* 模拟点击事件
*
* @param nodeInfo nodeInfo
*/
public void performViewClick(AccessibilityNodeInfo nodeInfo) {
if (nodeInfo == null) {
return;
}
while (nodeInfo != null) {
if (nodeInfo.isClickable()) {
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
break;
}
nodeInfo = nodeInfo.getParent();
}
}
/**
* 模拟返回操作
*/
public void performBackClick() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
performGlobalAction(GLOBAL_ACTION_BACK);
}
/**
* 模拟下滑操作
*/
public void performScrollBackward() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
/**
* 模拟上滑操作
*/
public void performScrollForward() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
performGlobalAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
/**
* 查找对应文本的View
*
* @param text text
* @return View
*/
public AccessibilityNodeInfo findViewByText(String text) {
return findViewByText(text, false);
}
/**
* 查找对应文本的View
*
* @param text text
* @param clickable 该View是否可以点击
* @return View
*/
public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
return null;
}
List<AccessibilityNodeInfo nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
return nodeInfo;
}
}
}
return null;
}
/**
* 查找对应ID的View
*
* @param id id
* @return View
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public AccessibilityNodeInfo findViewByID(String id) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
return null;
}
List<AccessibilityNodeInfo nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null) {
return nodeInfo;
}
}
}
return null;
}
public void clickTextViewByText(String text) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
return;
}
List<AccessibilityNodeInfo nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null) {
performViewClick(nodeInfo);
break;
}
}
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public void clickTextViewByID(String id) {
AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
if (accessibilityNodeInfo == null) {
return;
}
List<AccessibilityNodeInfo nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
if (nodeInfo != null) {
performViewClick(nodeInfo);
break;
}
}
}
}
/**
* 模拟输入
*
* @param nodeInfo nodeInfo
* @param text text
*/
public void inputText(AccessibilityNodeInfo nodeInfo, String text) {
if (Build.VERSION.SDK_INT = Build.VERSION_CODES.LOLLIPOP) {
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
} else if (Build.VERSION.SDK_INT = Build.VERSION_CODES.JELLY_BEAN_MR2) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", text);
clipboard.setPrimaryClip(clip);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
}
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
四、QQ抢红包
(一)抢红包流程:
- 通知栏收到QQ的消息,发现是QQ红包,模拟点击消息进入聊天页面
- 检索页面上的所有元素,发现有包含“点击拆开”的字眼,就模拟点击打开红包窗口
- 一两秒后执行Back操作,关闭红包窗口。
- 继续等待消息来到。
(二)实现功能:
- 锁屏抢红包(不可以有密码或者图案之类的锁屏)
- 口令红包,自动输入口令并且发送
- 抢完红包后,自动回复感谢语,可在红包设置里自行设置内容
- 其他的功能就没继续往下做了,知道方法,其他都可能慢慢研究出来。
(三)抢红包辅助功能类,注释都写好了,很好理解,类中有用到QQConstant类,在第四点贴出了代码
/**
* 描述:QQ抢红包服务
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/6 上午9:25
*/
public class EnvelopeService extends BaseAccessibilityService {
//锁屏、解锁相关
private KeyguardManager.KeyguardLock kl;
//唤醒屏幕相关
private PowerManager.WakeLock wl = null;
private long delayTime = 0;//延迟抢的时间
/**
* 描述:所有事件响应的时候会回调
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/6 上午9:26
*/
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
//验证抢红包的开关
if (!invalidEnable()) {
return;
}
//事件类型
int eventType = event.getEventType();
//获取包名
CharSequence packageName = event.getPackageName();
if (TextUtils.isEmpty(packageName)) {
return;
}
switch (eventType) {
//状态栏变化
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
if (QQConstant.QQ_PACKAGE_NAME.equals(packageName)) {
//处理状态栏上QQ的消息,如果是红包就跳转过去
progressQQStatusBar(event);
}
break;
//窗口切换的时候回调
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
if (QQConstant.QQ_PACKAGE_NAME.equals(packageName)) {
//处理正在QQ聊天窗口页面,有其他群或者人有新的红包提醒,跳转过去。
progressNewMessage(event);
//处理聊天页面的红包
progressQQChat(event);
}
break;
}
}
/**
* 描述:处理新消息
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/3 下午11:21
*/
private void progressNewMessage(AccessibilityEvent event) {
if (event == null) {
return;
}
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
//根据event的source里的text,来判断这个消息是否包含[QQ红包]的字眼,有的话就跳转过去
CharSequence text = source.getText();
if (!TextUtils.isEmpty(text) && text.toString().contains(QQConstant.QQ_ENVELOPE_KEYWORD)) {
performViewClick(source);
}
}
/**
* 描述:验证抢红包是否开启
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/3 下午4:57
*/
private boolean invalidEnable() {
return SettingConfig.getInstance().getReEnable();
}
/**
* 描述:处理QQ状态栏
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/1 下午1:49
*/
public void progressQQStatusBar(AccessibilityEvent event) {
List<CharSequence text = event.getText();
//开始检索界面上是否有QQ红包的文本,并且他是通知栏的信息
if (text != null && text.size() 0) {
for (CharSequence charSequence : text) {
if (charSequence.toString().contains(QQConstant.QQ_ENVELOPE_KEYWORD)) {
//说明存在红包弹窗,马上进去
if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
Notification notification = (Notification) event.getParcelableData();
if (notification == null) {
return;
}
PendingIntent pendingIntent = notification.contentIntent;
if (pendingIntent == null) {
return;
}
try {
//要跳转之前,先进行解锁屏幕,然后再跳转,有可能你现在屏幕是锁屏状态,先进行解锁,然后打开页面,有密码的可能就不行了
wakeUpAndUnlock(MyApp.context);
//跳转
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* 描述:处理QQ聊天红包
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/1 下午1:56
*/
public void progressQQChat(AccessibilityEvent event) {
if (TextUtils.isEmpty(event.getClassName())) {
return;
//如果当前页面是聊天页面或者当前的描述信息是"返回消息界面",就肯定是对话页面
}
//验证当前事件是否符合查询页面上的红包
if (!invalidEnvelopeUi(event)) {
return;
}
//延迟点击红包,防止被检测到开了抢红包,不过感觉还是感觉会被检测到,应该有的效果吧...
try {
Thread.sleep(delayTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
//普通红包,检索点击拆开的字眼。
List<AccessibilityNodeInfo envelope = findViewListByText(QQConstant.QQ_CLICK_TAKE_APART, false);
//处理普通红包
progressNormal(envelope);
//口令红包,检索口令红包的字眼。
List<AccessibilityNodeInfo passwordList = findViewListByText(QQConstant.QQ_CLICK_PASSWORD_DIALOG, false);
//处理口令红包
progressPassword(passwordList);
}
/**
* 描述:验证是否现在是在聊天页面,可以进行抢红包处理
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/3 上午11:52
*
* @param event
*/
public boolean invalidEnvelopeUi(AccessibilityEvent event) {
//判断类名是否是聊天页面
if (!QQConstant.QQ_IM_CHAT_ACTIVITY.equals(event.getClassName().toString())) {
return true;
}
//判断页面中的元素是否有点击拆开的文本,有就返回可以进行查询了
int recordCount = event.getRecordCount();
if (recordCount 0) {
for (int i = 0; i < recordCount; i++) {
AccessibilityRecord record = event.getRecord(i);
if (record == null) {
break;
}
List<CharSequence text = record.getText();
if (text != null && text.size() 0 && text.contains(QQConstant.QQ_CLICK_TAKE_APART)) {
//如果文本中有点击拆开的字眼,就返回可以进行查询了
return true;
}
}
}
return false;
}
/**
* 回到系统桌面
*/
private void back2Home(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
}
/**
* 描述:处理普通红包
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/1 下午5:02
*/
public void progressNormal(List<AccessibilityNodeInfo passwordList) {
if (passwordList != null && passwordList.size() 0) {
for (AccessibilityNodeInfo accessibilityNodeInfo : passwordList) {
if (accessibilityNodeInfo != null && !TextUtils.isEmpty(accessibilityNodeInfo.getText()) && QQConstant.QQ_CLICK_TAKE_APART.equals(accessibilityNodeInfo.getText().toString())) {
//点击拆开红包
performViewClick(accessibilityNodeInfo);
//回复感谢信息,根据配置文件中配置的回复信息回复
String reReplyMessage = SettingConfig.getInstance().getReReplyMessage();
if (!TextUtils.isEmpty(reReplyMessage)) {
replyMessage(reReplyMessage);
}
}
}
//最后延迟事件触发返回事件,关闭红包页面
performBackClick(1200);
}
}
/**
* 描述:处理口令红包
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/1 下午4:58
*
* @param passwordList
*/
public void progressPassword(List<AccessibilityNodeInfo passwordList) {
if (passwordList != null && passwordList.size() 0) {
for (AccessibilityNodeInfo accessibilityNodeInfo : passwordList) {
if (accessibilityNodeInfo != null && !TextUtils.isEmpty(accessibilityNodeInfo.getText()) && QQConstant.QQ_CLICK_PASSWORD_DIALOG.equals(accessibilityNodeInfo.getText().toString())) {
//如果口令红包存在,就在输入框中进行输入,然后发送
AccessibilityNodeInfo parent = accessibilityNodeInfo.getParent();
if (parent != null) {
CharSequence contentDescription = parent.getContentDescription();
if (!TextUtils.isEmpty(contentDescription)) {
//1. 获取口令
String key = (String) contentDescription;
if (key.contains(",") && key.contains("口令:")) {
key = key.substring(key.indexOf("口令:") + 3, key.lastIndexOf(","));
}
Log.e("口令", key);
//2. 填写口令到编辑框上然后进行发送
replyMessage(key);
//返回,关闭红包页面
performBackClick(1200);
}
}
}
}
}
}
/**
* 唤醒屏幕并解锁权限
* <uses-permission android:name="android.permission.WAKE_LOCK" /
*/
@SuppressLint("Wakelock")
@SuppressWarnings("deprecation")
public void wakeUpAndUnlock(Context context) {
// 点亮屏幕
wl.acquire();
// 释放
wl.release();
// 解锁
kl.disableKeyguard();
}
/**
* 描述:回复消息,无延迟
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/3 下午5:10
*/
public void replyMessage(String key) {
replyMessage(key, 0);
}
/**
* 描述:回复消息
* 作者:卜俊文
* 邮箱:344176791@qq.com
* 日期:2017/11/3 下午5:10
*/
public void replyMessage(String key, int time) {
//延迟
if (time 0) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//获取QQ聊天页面输入框
AccessibilityNodeInfo chat_edit = findViewByID(QQConstant.QQ_CHAT_MESSAGE_INPUT);
if (chat_edit != null) {
//把口令粘贴到输入框中
pastaText(chat_edit, MyApp.context, key);
//获取QQ聊天页面发送消息按钮
AccessibilityNodeInfo sendMessage = findViewByID(QQConstant.QQ_CHAT_MESSAGE_SEND);
//然后就按下发送按钮
if (sendMessage != null && Button.class.getName().equals(sendMessage.getClassName())) {
performViewClick(sendMessage);
}
}
}
@Override
public void onInterrupt() {
}
@Override
protected void onServiceConnected() {
super.onServiceConnected();
// 获取电源管理器对象
PowerManager pm = (PowerManager) MyApp.context
.getSystemService(Context.POWER_SERVICE);
// 获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
wl = pm.newWakeLock(
PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
KeyguardManager km = (KeyguardManager) MyApp.context.getSystemService(Context.KEYGUARD_SERVICE);
kl = km.newKeyguardLock("unLock");
//初始化屏幕的监听
ScreenListener screenListener = new ScreenListener(MyApp.context);
screenListener.begin(new ScreenListener.ScreenStateListener() {
@Override
public void onScreenOn() {
Log.e("ScreenListener", "屏幕打开了");
}
@Override
public void onScreenOff() {
//在屏幕关闭的时候,进行锁屏,不执行的话,锁屏就失效了,因为要实现锁屏状态下也可以进行抢红包。
Log.e("ScreenListener", "屏幕关闭了");
if (kl != null) {
kl.disableKeyguard();
kl.reenableKeyguard();
}
}
@Override
public void onUserPresent() {
Log.e("ScreenListener", "解锁了");
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
(四)QQ辅助服务里有用到的常量
public class QQConstant {
//QQ的应用包名
public static final String QQ_PACKAGE_NAME = "com.tencent.mobileqq";
//状态栏红包关键字
public static final String QQ_ENVELOPE_KEYWORD = "[QQ红包]";
//QQ聊天页面
public static final String QQ_IM_CHAT_ACTIVITY = "com.tencent.mobileqq.activity.SplashActivity";
//点击拆开
public static final String QQ_CLICK_TAKE_APART = "点击拆开";
//口令红包
public static final String QQ_CLICK_PASSWORD_DIALOG = "口令红包";
//聊天页面,输入框ID
public static final String QQ_CHAT_MESSAGE_INPUT = "com.tencent.mobileqq:id/input";
//聊天页面,发送按钮
public static final String QQ_CHAT_MESSAGE_SEND = "com.tencent.mobileqq:id/fun_btn";
}
五、红包问题
用的时候偶尔会被QQ检测到用了红包插件,可能是因为抢的速度太快,导致数据不符合正常的点击时间,我有加入一个延迟时间,不知道有没有效果,如果有知道的也可以留言,谢谢。
在QQ的主页面上,收到消息的时候通知栏是不会通知的,所以这里不能进行解析通知栏跳转聊天页面,没有找到什么元素可以告诉我怎么进入红包的聊天页面,如果有知道的可以留言,谢谢。
这种辅助服务的方式抢红包,进入聊天页面后,他检索字段只会检索当前页面可视的元素,某些红包要是在聊天记录上面看不见的,需要滑动上去才可以触发解析红包,不过一般不会一次性10个红包都发出来吧,嘿嘿。
六、总结
学习制作了这个项目,也了解了辅助功能的使用,感觉这个还是可以做很多东西的,上面已经贴出了核心代码
以上就是本文的全部内容,希望对大家的学习有所帮助。
- python基础随笔
- Mysql+Keepalived双主热备高可用操作记录
- Mysql双主热备+LVS+Keepalived高可用操作记录
- 被曝大裁员!曲德君坚称万达网科没有倒下、目标决心不变
- Linux下smokeping网络监控环境部署记录
- Linux下的rsyslog系统日志梳理(用户操作记录审计)
- 数据结构之数组封装
- Centos下内网NDS主从环境部署记录
- 一搜解决,微信的这个功能厉害了!
- Saltstack自动化操作记录(2)-配置使用
- Saltstack自动化操作记录(1)-环境部署
- CentOS源码编译安装Nginx和tcp_proxy module
- 介绍一个MonoTouch开发的伦敦官方城市指南应用
- 虾说区块链-55-《精通比特币》笔记十
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PathClassLoader加载与查找类
- springboot maven项目引入并打包本地JAR
- 移植lua5.2和luasocket库到嵌入式linux,使能强大的lua脚本和网络功能
- Go或者C中调用Lua业务脚本,实现终端应用的热更新机制
- 多线程基础(八):ReentrantLock的使用及与synchronized的区别
- 在java中notify和notifyAll的区别
- 我在近期求职中遇到的前端面试问题及其解法
- 腾讯云 云开发 部署 Blazor网站
- 最新基准测试:Kafka、Pulsar 和 RabbitMQ 哪个最快?
- 基于飞桨实现高光谱反演:通过遥感数据获取土壤某物质含量
- 飞桨Tracking目标跟踪库开源!涵盖业界主流的VOT算法,精准检测动态目标轨迹
- 基于react的组件库主题设计方案
- Xilinx MPSoC PS/PL之间的数据交互和外设设计
- 基于Res-Unet网络实现肝脏肿瘤分割任务
- golang判断map中key是否存在的方法