开发方法介绍
实现自动滑动屏幕有两种常用的方式:使用ADB(Android Debug Bridge)工具和安卓辅助服务。
ADB
ADB(Android Debug Bridge)是一个用于在计算机和安卓设备之间进行通信和调试的工具。它提供了一组命令行工具,可以执行各种与安卓设备相关的操作,包括自动滑动屏幕。
实现自动滑动屏幕需要理解两个关键概念:屏幕分辨率和输入事件。
屏幕分辨率:屏幕分辨率是指屏幕中可显示的像素数量。在安卓设备上,通过ADB可以使用
adb shell wm size
命令获取屏幕的分辨率。知道屏幕分辨率可以帮助我们确定滑动屏幕的相对位置,以便向设备发送合适的输入事件。输入事件:在安卓设备上,用户的触摸和滑动操作被转换为特定的输入事件。ADB提供了
input
命令来模拟这些输入事件。其中,adb shell input swipe
命令可以发送滑动事件到设备,从而自动滑动屏幕。该命令需要指定起始点坐标、终止点坐标和滑动时间。ADB将这些信息发送到设备,设备则会模拟用户的滑动操作。
结合以上概念,我们可以通过ADB实现自动滑动屏幕的原因如下:
ADB提供了与安卓设备进行通信和控制的功能,使我们可以通过命令行工具来发送指令到设备上。
ADB提供了获取屏幕分辨率的命令,帮助我们确定滑动屏幕的相对位置。
ADB的
input
命令可以模拟输入事件,包括滑动事件。使用adb shell input swipe
命令,我们可以向设备发送滑动事件,从而实现自动滑动屏幕的效果。
尽管ADB可以实现自动滑动屏幕的功能,但它依赖于USB连接, ADB需要通过USB连接计算机和安卓设备。这意味着需要确保设备已启用开发者选项并启用了USB调试模式。此外,如果设备的USB连接出现问题或被断开,自动滑动的操作将无法进行。
安卓辅助服务
另一种实现自动滑动屏幕的方法是使用安卓辅助服务。
安卓辅助服务(Android Accessibility Service)是一种特殊的后台服务,旨在帮助用户具备视觉、听觉或运动能力上的障碍更好地使用安卓设备。它主要通过提供额外的功能和改进用户界面访问性来实现这一目的。
安卓辅助服务的原理是利用系统级别的API,监测和接收来自用户界面的事件,并提供一些额外的功能来解决可访问性问题。它可以捕捉应用程序的界面内容、监听用户输入、模拟用户点击等。通过这种方式,辅助服务可以对用户交互进行监听和干预,进而实现一系列的辅助功能。
安卓辅助服务的主要作用是使有障碍的用户更加方便地使用安卓设备。它可以提供以下一些常见的功能:
- 文字转语音:将屏幕上的文字内容转换为语音,帮助视觉障碍用户理解应用程序的界面。
- 语音转文字:将用户的语音输入转换为文字,以便于视觉障碍用户进行文本输入。
- 手势控制:通过定义特定的手势来替代常规的触摸操作,帮助运动能力有限的用户进行操作。
- 增强可见性:调整应用程序的颜色、字体大小等,以满足视觉障碍用户的需求。
- 自动化任务:根据设定的规则,自动执行一些特定的任务,帮助用户完成常见操作(例如自动抢红包,自动点赞,自动回复,自动刷视频, 自动签到等)
安卓辅助服务的优点具有以下优点:
- 安卓辅助服务的优点是可以在后台运行,不依赖USB连接。但使用辅助服务需要用户提供相应的权限,并确保所使用的应用程序支持辅助功能。
- 多设备兼容性:将自动滑动屏幕功能开发成apk文件后,在支持安卓辅助服务的设备上都可以使用。无论是不同型号的手机还是平板电脑,只要设备支持安卓辅助服务,就可以安装并使用该apk文件。
- 便捷性:将自动滑动屏幕功能封装成apk文件后,用户只需下载并安装该应用程序,即可轻松实现自动滑动屏幕的功能。无需手动配置ADB和USB连接,也不需要进行复杂的设置。
- 独立性:将功能开发成apk文件后,可以独立运行在设备上,并不需要依赖计算机或其他外部设备。这使得使用更加方便,特别是在没有计算机时,仍然可以使用自动滑动屏幕功能。
在教程中,我们将详细介绍如何基于安卓辅助服务全过程开发一款自动滑动的APK。这个APK将具备自动滑动屏幕的功能,用户可以通过它来模拟滑动操作,实现自动浏览、阅读或刷视频的效果。
创建主程序
1. 开发前准备
新建项目
点击File
→new
→project
,
第一次没有安装过AndroidSDK
,会提示你下载安装,点击Install SDK
,一路下一步就会开始下载并自动安装。
选择项目模板
安装完成后就进入选择模板步骤,我们选择Empty Activity
创建一个空白项目模板。
修改项目信息,应用名改为AutoSwipe
。
2. 了解Android项目结构
在使用IntelliJ IDEA创建安卓应用时,项目结构通常遵循标准的Android项目结构。主要有以下:
app
模块:这是主要的应用程序模块,包含了你的代码和资源文件。gradle
目录:包含了与Gradle构建系统相关的脚本文件,如wrapper
目录用于存放Gradle Wrapper文件。build.gradle
文件:该文件位于项目根目录下,用于配置构建设置和依赖项。有两个重要的build.gradle
文件:- 项目级别的
build.gradle
:配置整个项目的构建设置,如Gradle版本、应用程序的最小SDK版本等。 - 模块级别的
build.gradle
:(位于app
模块中)配置特定模块的构建设置,如应用程序ID、依赖库等。
- 项目级别的
.gitignore
文件:用于指定Git版本控制系统忽略的文件或目录。
app
模块
首先app
模块是Android应用程序的主要模块,包含了应用程序的代码和资源文件。以下是app
模块中常见子目录:
src
目录:存放应用程序的源代码文件。main
目录:包含了主要的应用程序逻辑。java
目录:用于存放Java代码文件,按照包的结构组织。这里通常包含了应用程序的活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)等组件的类文件。res
目录:用于存放应用程序的资源文件,如布局文件、字符串资源、图像资源等。
androidTest
目录:包含了针对Android设备的测试文件。java
目录:用于存放Android设备测试的Java代码文件。这里通常包含了对应用程序功能进行测试的测试类文件。
test
目录:包含了单元测试文件。java
目录:用于存放单元测试的Java代码文件。这里通常包含了对应用程序各个模块进行单元测试的测试类文件。
build.gradle
文件:app
模块的构建配置文件,用于配置特定模块的构建设置和依赖项。该文件包括了以下重要的配置:android
块:配置与Android相关的构建设置,如应用程序的最小SDK版本、目标SDK版本、签名配置等。dependencies
块:声明依赖项,如支持库(Support Library)、第三方库等。
app模块是开发Android应用程序的核心,其中的java
目录存放了应用程序的Java代码文件,很多重要的组件和逻辑都在这里定义和实现。res
目录存放了应用程序所需的各种资源文件,用于展示和处理用户界面、提供本地化字符串等。build.gradle
文件则是配置构建设置和依赖项的关键文件,可以通过它来指定应用程序的特定要求和功能。
res
目录
其中res
目录是Android应用程序中存放资源文件的目录,用于定义应用程序的用户界面、字符串、图像等。以下是res
目录中常见子目录:
drawable
目录:存放图像资源文件。drawable
目录:存放一般的位图图像资源文件(如.png
,.jpg
等)。drawable-v24
目录:存放适用于Android 7.0及更高版本的位图图像资源文件。mipmap
目录:存放应用程序的启动图标和其他图标资源文件。
layout
目录:存放布局文件,用于定义应用程序的用户界面。activity_*.xml
文件:定义活动(Activity)的界面布局。fragment_*.xml
文件:定义片段(Fragment)的界面布局。item_*.xml
文件:定义列表项的布局。include_*.xml
文件:定义可重用的布局片段,可以在其他布局文件中进行引用。
values
目录:存放字符串资源、颜色值等。strings.xml
文件:定义应用程序中的字符串资源,如提示信息、按钮文本等。colors.xml
文件:定义应用程序中的颜色值。dimens.xml
文件:定义应用程序中的尺寸值,如边距、字体大小等。styles.xml
文件:定义应用程序中的样式,可以设置控件的外观和行为。arrays.xml
文件:定义应用程序中的数组资源。integers.xml
文件:定义应用程序中的整数资源。
menu
目录:存放菜单资源文件,用于定义应用程序的菜单界面。*.xml
文件:定义应用程序中的菜单布局。
anim
目录:存放动画资源文件,用于定义应用程序的动画效果。raw
目录:存放原始资源文件,如音频、视频等。xml
目录:存放其他XML文件,如网络请求配置文件、权限配置文件等。
res
目录是Android应用程序中非常重要的目录之一,存放了应用程序所需的各种资源文件。在开发过程中,我们可以根据需要在不同的子目录下创建相应的文件,以实现应用程序的用户界面、文本、图像和其他功能。这些资源文件在代码中可以通过资源ID进行访问和使用,帮助我们实现丰富多样的应用程序功能和界面效果。
MainActivity
MainActivity
是 Android 应用程序中的一个主要组件,它是应用程序的入口点,并控制着用户界面的展示和交互。详细了解下 MainActivity
:
继承关系:
MainActivity
通常是一个具体的类,并直接或间接继承自Activity
类或其子类。Activity
类是 Android 提供的 UI 组件,用于管理用户界面和处理用户交互。
生命周期回调方法:
MainActivity
可以重写一系列生命周期回调方法,以响应应用程序的不同状态和事件。常用的生命周期方法包括:onCreate()
,onStart()
,onResume()
,onPause()
,onStop()
,onRestart()
,onDestroy()
等。- 在这些回调方法中,开发者可以执行各种操作,如设置布局、初始化界面元素、处理用户输入、启动其他活动等。
布局文件:
MainActivity
通常与一个或多个布局文件关联,用于定义界面的结构和外观。布局文件使用 XML 格式编写,可以包含各种 UI 元素(如按钮、文本框、图像等)和布局容器(如线性布局、相对布局等)。- 在
MainActivity
的onCreate()
方法中,通常会通过调用setContentView()
方法将布局文件与MainActivity
关联起来,以便在应用程序启动时显示相应的界面。
用户交互和事件处理:
MainActivity
可以通过注册和实现监听器(如按钮点击监听器、列表选择监听器等)来处理用户的交互行为。监听器通常在onCreate()
方法中进行初始化,并在需要时执行相应的操作。- 通过监听器,可以监听并处理用户对界面元素(如按钮)的点击、滑动、长按等操作,从而触发相应的逻辑和功能。
其他功能:
MainActivity
还可以包含其他功能代码,如网络请求、数据库操作、调用系统服务、启动其他活动等。- 在应用程序开发中,
MainActivity
通常会与其他组件(如片段、服务、广播接收器等)进行交互,以实现更复杂的应用程序逻辑和功能。
MainActivity
是 Android 应用程序中一个重要的组件,负责处理用户界面显示和交互。通过重写生命周期回调方法和使用布局文件、监听器和其他功能代码,开发者可以实现丰富多样的应用程序界面和交互效果。
AndroidManifest.xml
AndroidManifest.xml
是Android应用程序的清单文件,它包含了关于应用程序的重要信息和配置。以下是AndroidManifest.xml
文件的一些常见配置:
package
属性:指定应用程序的包名,用于唯一标识应用程序。uses-sdk
标签:指定应用程序对Android SDK的最低版本要求和目标版本,以及是否支持不同的屏幕密度和配置。permission
标签:声明应用程序需要的权限,如访问网络、读取设备状态等。application
标签:指定应用程序的主要配置和组件,包括:activity
标签:定义应用程序的活动(Activity),即用户界面的一个窗口。intent-filter
标签:定义活动可以响应的意图(Intent),如启动应用程序的主要入口活动。
service
标签:定义应用程序的服务(Service),即在后台执行某种操作的组件。receiver
标签:定义应用程序的广播接收器(Broadcast Receiver),即用于接收和处理系统或其他应用程序发出的广播消息的组件。provider
标签:定义应用程序的内容提供器(Content Provider),即用于与其他应用程序共享数据的组件。meta-data
标签:定义应用程序相关的元数据信息。
uses-permission
标签:声明应用程序需要的权限,如访问网络、读取设备状态等。其他标签和属性:还有其他一些标签和属性可以用于定义应用程序的配置,如
application
,activity-alias
,supports-screens
,uses-feature
等。
通过AndroidManifest.xml
文件,我们可以确定应用程序的主要配置和组件,并声明所需的权限。这个清单文件在应用程序的开发过程中扮演着非常重要的角色,Android系统根据该文件中的信息来识别和管理应用程序的各个组件,并确保应用程序在安装和运行时具有所需的权限和功能。
3. 创建全局变量类Variable
在项目中定义一个全局变量类有以下几个用途:
全局可访问:全局变量类的成员变量和成员函数可以在项目的任何代码文件中访问,不需要特定的对象实例。这样可以方便地共享数据和方法,避免了传递参数的麻烦。
数据共享:全局变量类可以用来在不同的模块或组件之间共享数据。当多个模块需要访问相同的数据时,可以将这些数据作为全局变量保存在全局变量类中,方便各个模块之间的数据传递和共享。
状态管理:全局变量类可以用于管理项目的状态信息。通过在全局变量类中定义和更新状态变量,可以在整个项目中实时跟踪和控制状态的变化,简化项目的状态管理和调试过程。
以下是全局变量类Variable的代码:
package com.example.autoswipe.Global;
import android.content.Context;
import android.widget.TextView;
public class Variable {
// 悬浮按钮展示的文字
public static TextView btnTextView = null;
// 上下文
public static Context context = null;
// 屏幕宽高
public static int mWidth;
public static int mHeight;
// 是否自动滑动
public static boolean continueSwipe = true;
}
除了使用全局变量类以外,我们后面还会使用Intent方法可以实现不同模块或组件之间的消息和数据的共享,原理大致是通过将数据封装在Intent中,可以在Activity、Service、BroadcastReceiver之间传递数据。可以使用putExtra()方法将数据添加到Intent中,在目标组件中使用getExtra()方法获取数据。
4. 首次开发MainActivity
onCreate()
方法是 MainActivity
类中的一个生命周期回调方法,它在 MainActivity
第一次创建时被调用。MainActivity第一次创建时主要做了以下工作:
- 参数:
Bundle savedInstanceState
onCreate()
方法的参数Bundle savedInstanceState
是一个包含先前保存的实例状态的对象。它可以用于在应用程序重新创建时恢复之前的状态。- 如果应用程序启动时没有需要恢复的状态,则
savedInstanceState
参数为null
。
- 调用父类方法:
- 在
onCreate()
方法的开始处,通常会调用父类的onCreate()
方法,以确保执行父类的初始化逻辑。在MainActivity
中,通常会调用super.onCreate(savedInstanceState)
。
- 在
- 设置布局:
- 通过调用
setContentView(R.layout.activity_main)
方法,将与MainActivity
关联的布局文件设置为当前活动的内容视图。 R.layout.activity_main
是一个自动生成的资源标识符,表示应用程序中的一个布局文件。它指定了应该在MainActivity
中显示的界面结构和外观。
- 通过调用
- 初始化界面元素和事件处理:
- 在
onCreate()
方法中,我们将可以进行自己的初始化操作,首先是获取屏幕的宽高,赋值给全局变量以便调用,同时我们也将当前应用程序的上下文(Context)赋值给Variable
中的静态成员变量context
。(eg.上下文是一个 Android 应用程序的全局信息对象,它提供了对系统资源和服务的访问权限)。这样做的好处是,可以在全局变量Variable
中方便地使用这些资源和服务,而无需每次都通过参数传递上下文。
- 在
package com.example.autoswipe;
import android.util.DisplayMetrics;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.autoswipe.Global.Variable;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDisplay();
Variable.context = this.getApplicationContext();
}
public void initDisplay() {
DisplayMetrics dm = new DisplayMetrics();//屏幕度量
getWindowManager().getDefaultDisplay().getMetrics(dm);
Variable.mWidth = dm.widthPixels;//宽度
Variable.mHeight = dm.heightPixels;//高度
}
}
5. 给主页面增加控件
布局控件
我们使用控件(Widget)的目的是为了在应用程序中和界面展示出来的信息进行直接交互。
我们首先创建 ListView
控件来展示多条信息,点击每条信息响应不同的事件. ListView
控件的每条信息我们使用 TextView
控件来展示,同时赋予每个 TextView
点击事件。
下面是使用到的控件的作用:
ListView
控件:ListView
是一个用于显示垂直滚动列表的常见控件。它可以展示多个列表项,并支持用户滚动、点击等交互操作。在我们的例子中,通过将适配器加载到ListView
中,可以实现将数据源中的内容以列表项的形式展示出来,方便进行选择和查看。TextView
控件:TextView
是用于显示文本内容的常见控件。在我们的例子中,每个列表项都是由TextView
控件来显示的。通过获取被点击项的TextView
对象,并调用getText().toString()
方法,可以获取到该项对应的文本内容,以供后续处理或显示。
我们创建控件的方法是在MainActivity
对应的布局文件中定义一个名为 list_view
的 ListView
控件,并通过 findViewById()
方法找到该控件。在 Android 中,findViewById()
方法可以通过控件的唯一标识符查找对应的视图对象。
初始的MainActivity
对应的布局文件位于res/layout/activity_main.xml
中,可以看到有一个TextView
文本,内容为"曾续缘".
下面我们给它增添一个ListView
控件:
在上述代码中,有以下几个参数:
android:id="@+id/list_view"
:这是ListView
控件的唯一标识符,用于在代码中通过findViewById()
方法找到该控件。在这个例子中,该标识符被设置为list_view
。android:layout_width="match_parent"
:这个参数指定了ListView
控件的宽度,使用match_parent
表示它会根据父容器的宽度来自动匹配。android:layout_height="0dp"
:这个参数指定了ListView
控件的高度。在这个例子中,将其设为0dp
,是为了配合约束布局使用,让其根据约束条件自动调整高度。app:layout_constraintLeft_toLeftOf="parent"
:这是约束布局中的一个参数,它指定了ListView
控件的左边缘与父容器的左边缘对齐。app:layout_constraintRight_toRightOf="parent"
:同样是约束布局中的一个参数,它指定了ListView
控件的右边缘与父容器的右边缘对齐。app:layout_constraintTop_toTopOf="parent"
:这个参数指定了ListView
控件的顶部与父容器的顶部对齐。
通过以上参数的设置,我们可以实现以下效果:
ListView
控件的宽度会自动匹配父容器的宽度;ListView
控件的高度会根据约束布局的约束条件自动调整;ListView
控件的左、右边缘与父容器的左、右边缘对齐;ListView
控件的顶部与父容器的顶部对齐。
这样,我们就可以将 ListView
控件放置在布局中的适当位置,并根据需要进行进一步的布局调整和样式设置。
使用适配器连接数据和控件
我们需要构建一个适配器(Adapter),并将其应用到 ListView
控件上。
具体步骤如下:
绑定控件:
- 通过调用
findViewById(R.id.list_view)
,找到布局文件中的ListView
控件,并将其赋值给listView
变量,以便后续使用。
- 通过调用
准备数据:
- 在这个我们的应用中,提供了一个字符串数组
data
,其中包含几个字符串元素作为数据源。 - 这些数据将被适配器加载到
ListView
中,以供显示。
- 在这个我们的应用中,提供了一个字符串数组
创建适配器:
- 使用
ArrayAdapter
类创建一个适配器对象adapter
,它是数据源和ListView
之间的桥梁。 - 参数 1:当前的上下文环境(
MainActivity.this
)。 - 参数 2:当前列表项所加载的布局文件(
android.R.layout.simple_list_item_1
)。 - 参数 3:数据源(
data
数组)。
- 使用
将适配器加载到控件中:
- 调用
listView.setAdapter(adapter)
将适配器对象adapter
应用于ListView
控件。 - 这样,适配器会将数据源中的每个元素逐个加载到
ListView
中,以供进行选择和显示。
- 调用
添加点击事件监听器:
- 通过调用
listView.setOnItemClickListener()
方法,为ListView
中选中的项添加一个单击响应事件。 - 当我们点击列表项时,会触发回调函数,并将选中项的参数传递给该函数。
- 在这个例子中,通过
((TextView) view).getText().toString()
获取到被点击项的文本内容,并将其作为参数传递给switchResult()
方法。
- 通过调用
private void buildAdapter() {
//1、绑定控件
ListView listView = findViewById(R.id.list_view);
//2、准备数据
String[] data = {
"跳转到设置无障碍页面",
"开启悬浮窗服务",
"结束悬浮窗服务",
};
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, data);
//3、将适配器加载到控件中
listView.setAdapter(adapter);
//4、为列表中选中的项添加单击响应事件
listView.setOnItemClickListener((parent, view, i, l) -> {
String result = ((TextView) view).getText().toString();
switchResult(result);
});
}
private void switchResult(String result) {
// 跳转无障碍页面
if (result.equals("跳转到设置无障碍页面")) {
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
return;
}
if (result.equals("开启悬浮窗服务")) {
}
if (result.equals("结束悬浮窗服务")) {
}
}
上面的代码中,startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
是一个 Android 中用于启动一个新的活动(Activity)的代码,它通过一个意图(Intent)来指定要启动的活动。
我们使用 new Intent()
创建一个新的意图对象。意图中包含了一个额外的参数 Settings.ACTION_ACCESSIBILITY_SETTINGS
,该参数是一个字符串常量,表示要启动的活动的具体操作。在这种情况下,使用 Settings.ACTION_ACCESSIBILITY_SETTINGS
表示要跳转到系统的无障碍设置页面。
通过调用 startActivity()
方法,并传入这个意图对象作为参数,可以实现从当前活动跳转到指定的无障碍设置页面的功能。
现在已经包含了跳转到无障碍页面的配置。如果要在应用程序中实现悬浮窗服务,我们还需要进行以下配置:
- 在 AndroidManifest.xml 文件中声明悬浮窗权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- 在 AndroidManifest.xml 文件中声明注入模拟用户输入事件权限:
<uses-permission android:name="android.permission.INJECT_EVENTS" tools:ignore="ProtectedPermissions"/>
这样在我们开启悬浮窗后,就可以通过点击按钮来控制自动滑动手势的开始和停止了.
我们首先实现跳转无障碍页面的功能,到后面我们开发了悬浮窗相应的类才来实现悬浮窗服务.
最后,我们让主程序初始化时绑定控件.到这里我们的主程序就开发完了!
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDisplay();
Variable.context = this.getApplicationContext();
buildAdapter();
}
开发悬浮窗服务
前面我们已经实现了跳转到无障碍页面的配置。但是并没有开启和结束悬浮窗服务的功能.
现在让我们进入实现悬浮窗服务的部分吧.
1. 创建继承Service的悬浮窗类
创建一个 Service 类来处理悬浮窗服务。这个类需要继承自 Service
并实现相关的悬浮窗功能。
2. 实现悬浮按钮和点击事件,并加入窗口管理器中
我们首先需要创建一个按钮 TextView,并设置按钮的文本和样式,最后为按钮设置了点击事件监听器。
具体步骤和解释如下:
创建按钮 TextView:通过
new TextView(Variable.context)
语句创建了一个按钮 TextView,并将其赋值给变量Variable.btnTextView
。设置按钮文本和样式:
ViewGroup.LayoutParams
是一个用于设置视图布局参数的类。在这里,创建了一个新的txtParameters
对象,该对象的宽度和高度都设为MATCH_PARENT
,表示填充父容器。Variable.btnTextView.setText("开始")
:设置按钮的文本为"开始"。Variable.btnTextView.setTextSize(15)
:设置按钮的文本大小为15sp。Variable.btnTextView.setGravity(Gravity.CENTER)
:设置按钮的文本居中对齐。Variable.btnTextView.setTextColor(Color.argb(150, 10, 250, 0))
:设置按钮的文本颜色为透明度150、红色10、绿色250、蓝色0的颜色。Variable.btnTextView.setBackgroundColor(Color.argb(180, 0, 0, 0))
:设置按钮的背景颜色为透明度180、红色0、绿色0、蓝色0的颜色。Variable.btnTextView.setPadding(3, 3, 3, 3)
:设置按钮的内边距为3个像素。Variable.btnTextView.setLayoutParams(txtParameters)
:将按钮的布局参数设置为txtParameters
,即宽度和高度都填充父容器。
设置点击事件监听器:通过给
Variable.btnTextView
设置 OnClickListener 来实现点击事件的监听。使用 lambda 表达式v -> btnClick()
来定义点击事件的处理函数,在点击按钮时调用btnClick()
函数。
以上对应代码实现了创建一个样式为按钮的 TextView,并设置了按钮的文本、文本样式、背景颜色和点击事件监听器。最终的效果是创建了一个可点击的按钮,点击按钮时会触发 btnClick()
函数中的逻辑。
接下来是设置悬浮窗的布局参数和位置,最后将按钮 TextView 添加到 WindowManager 中显示。
具体步骤如下:
创建悬浮窗布局参数:通过
new WindowManager.LayoutParams()
创建了一个WindowManager.LayoutParams
对象,并将其赋值给变量parameters
。参数依次为:ViewGroup.LayoutParams.WRAP_CONTENT
:设置悬浮窗的宽度为自适应内容。ViewGroup.LayoutParams.WRAP_CONTENT
:设置悬浮窗的高度为自适应内容。WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
:设置悬浮窗的类型为应用程序悬浮窗,该类型允许悬浮窗显示在其他应用程序之上。WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
:设置悬浮窗不可获取焦点,确保用户点击屏幕其他区域时不会影响悬浮窗。PixelFormat.TRANSLUCENT
:设置悬浮窗的像素格式为透明,以实现悬浮窗的半透明效果。
设置悬浮窗位置:
parameters.x = 20
:设置悬浮窗相对于屏幕左边的横向偏移量为20个像素。parameters.y = 200
:设置悬浮窗相对于屏幕顶部的纵向偏移量为200个像素。parameters.gravity = Gravity.LEFT | Gravity.TOP
:设置悬浮窗的对齐方式为左上角对齐。
将按钮 TextView 添加到悬浮窗中显示:通过
wm.addView(Variable.btnTextView, parameters)
将按钮 TextView 添加到 WindowManager 中,并使用之前设置的布局参数parameters
来确定按钮 TextView 的位置和样式。
以上对应实现了设置悬浮窗的布局参数和位置,并将按钮 TextView 添加到悬浮窗中进行显示。最终的效果是创建了一个位于屏幕左上角偏移量为 (20, 200) 处的悬浮窗,悬浮窗中显示了具有特定样式和点击事件的按钮。悬浮窗可以显示在其他应用程序之上,并且不会影响用户点击屏幕其他区域的操作。
在btnClick()
方法中,我们根据按钮文本的不同来控制变量Variable.continueSwipe
的值,从而控制自动滑动操作的开始和结束。
在onDestroy()
方法中,我们需要移除悬浮按钮,即将其从WindowManager中移除。
对于自动滑动的代码逻辑,我们需要借助系统的辅助服务功能来进行手势模拟,需要新建一个继承自 AccessibilityService
的辅助服务类,这个我们稍后实现.
实现代码如下:
package com.example.autoswipe.Services;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.provider.Settings;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.TextView;
import com.example.autoswipe.Global.Variable;
public class FloatingButton extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
private WindowManager wm;
public void onCreate() {
super.onCreate();
// 动态请求悬浮窗权限,打开设备设置界面,授予悬浮窗权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(Variable.context)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + Variable.context.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
stopSelf(); // 停止服务
return;
}
// 初始化 WindowManager
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
// 创建一个按钮 TextView
Variable.btnTextView = new TextView(Variable.context);
// 设置按钮文本和样式
ViewGroup.LayoutParams txtParameters = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
Variable.btnTextView.setText("开始");
Variable.btnTextView.setTextSize(15);
Variable.btnTextView.setGravity(Gravity.CENTER); // 文字居中
Variable.btnTextView.setTextColor(Color.argb(150, 10, 250, 0));
Variable.btnTextView.setBackgroundColor(Color.argb(180, 0, 0, 0));
Variable.btnTextView.setPadding(3, 3, 3, 3);
Variable.btnTextView.setLayoutParams(txtParameters);
// 设置点击事件监听器
Variable.btnTextView.setOnClickListener(v -> btnClick());
// 设置悬浮窗的布局参数
WindowManager.LayoutParams parameters = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
// 设置悬浮窗位置
parameters.x = 20;
parameters.y = 200;
parameters.gravity = Gravity.LEFT | Gravity.TOP;
wm.addView(Variable.btnTextView, parameters);
}
private void btnClick() {
if ("开始".contentEquals(Variable.btnTextView.getText())) {
Variable.continueSwipe = true;
// start_SwipeService(); // 稍后实现
Variable.btnTextView.setText("结束");
} else if ("结束".contentEquals(Variable.btnTextView.getText())) {
Variable.continueSwipe = false;
Variable.btnTextView.setText("开始");
}
}
@Override
public void onDestroy() {
super.onDestroy();
// 移除悬浮按钮
if (wm != null && Variable.btnTextView != null && Variable.btnTextView.getParent() != null) {
Variable.continueSwipe = false;
wm.removeView(Variable.btnTextView);
}
}
}
3. 在清单文件中注册服务
别忘了要使该服务有效工作,还需要在 AndroidManifest.xml
文件中注册服务
<service android:name=".Services.FloatingButton" />
4. 总结
我们实现了一个悬浮按钮的服务,通过创建一个 TextView 作为悬浮按钮,并监听其点击事件来实现开始和结束自动滑动的功能。悬浮按钮可以显示在其他应用程序之上,并且监听的点击事件可以根据按钮文本的不同进行相应的操作。
具体功能如下:
实现了
onCreate()
方法,在该方法中进行初始化操作,包括:- 判断当前 Android 版本是否大于等于 Marshmallow(Android 6.0),以及是否有悬浮窗权限。如果没有权限,则会启动设备设置界面让用户授予悬浮窗权限。
- 初始化 WindowManager 对象
wm
,用于管理悬浮窗。 - 创建一个 TextView 对象
Variable.btnTextView
,作为悬浮按钮。 - 设置悬浮按钮的文本、样式和点击事件。
实现了
btnClick()
方法,用于处理悬浮按钮的点击事件。当按钮文本为 "开始" 时,设置变量Variable.continueSwipe
为 true,并修改按钮文本为 "结束"。当按钮文本为 "结束" 时,设置变量Variable.continueSwipe
为 false,并修改按钮文本为 "开始"。在
onDestroy()
方法中,移除悬浮按钮,即从 WindowManager 中移除并销毁Variable.btnTextView
。自动滑动的功能需要借助辅助服务类
AccessibilityService
,我们将在下一节中实现它!
实现效果图:
![]() | ![]() |
---|
启动辅助服务
1. 继承AccessibilityService类
我们建一个新的类SwipeService
,并实现onAccessibilityEvent()
和onInterrupt()
。这两个方法是必须实现的,如果不实现这些方法,SwipeService
将无法正常工作。
onAccessibilityEvent(AccessibilityEvent accessibilityEvent)
: 这个方法是一个抽象方法,必须在子类中实现。它负责接收和处理来自系统的无障碍事件。由于我们只需要实现自动滑动,因此不需要接收无障碍事件.onInterrupt()
: 这个方法是AccessibilityService
中的另一个抽象方法,同样也必须在子类中实现。当服务被中断时(例如被禁用或者被系统终止),系统会调用这个方法。如果不实现这个方法,服务可能无法正确地进行清理操作,可能导致未预期的行为或资源泄露。
2. 配置辅助功能服务
接下来我们需要在res
文件夹下创建一个名为xml
的文件夹,并在该文件夹下创建一个名为accessibility_service_config.xml
的配置文件,用于定义AccessibilityService的行为和设置。
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:canPerformGestures="true"
/>
我们的这段XML代码实现了一个辅助功能服务(Accessibility Service),它具有以下特性:
xmlns:android
:指定 XML 命名空间为 Android 命名空间,用于引用 Android 的命名空间。<accessibility-service>
:定义了一个辅助功能服务的根元素。android:canPerformGestures="true"
:设置辅助功能服务可以执行手势操作。通过将此属性设置为 true,该服务可以模拟用户的手势操作,例如滑动、点击等。
其他属性没有指定,因此该辅助功能服务不会监听任何辅助功能事件(没有指定 android:accessibilityEventTypes
属性),也没有指定反馈类型和其他功能。
总之,我们只实现了一个只需要执行手势操作的辅助功能服务。
3. 在清单文件中添加辅助服务
我们需要在 AndroidManifest.xml
文件中声明了一个服务(Service)组件,并设置相关属性和元数据。以下是对每个部分的作用的详细说明:
<service>
元素:
android:name=".Services.SwipeService"
:指定服务的类名为.Services.SwipeService
,表示该服务的实现代码位于.Services.SwipeService
类中。android:enabled="true"
:将服务设置为启用状态,使其可以被系统调用和响应。android:exported="true"
:将服务设置为可以被其他应用程序组件调用和访问。android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
:设置需要的权限,即BIND_ACCESSIBILITY_SERVICE
权限,确保只有具有此权限的应用程序能够绑定到此辅助功能服务。
<intent-filter>
元素:
<action android:name="android.accessibilityservice.AccessibilityService" />
:指定了解析此服务的 intent 的操作名称。在此例中,是为了让系统能够正确地识别并使用辅助功能服务。
<meta-data>
元素:
android:name="android.accessibilityservice"
:指定元数据的名称为android.accessibilityservice
,用于定义与此服务相关的附加信息或配置。android:resource="@xml/accessibility_service_config"
:指定了元数据的资源位置,此处为@xml/accessibility_service_config
,表示使用res/xml/
目录下的accessibility_service_config.xml
文件作为元数据。
实现代码如下:
<service
android:name=".Services.SwipeService"
android:enabled="true"
android:exported="true"
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_config" />
</service>
综上,这段代码的作用是在 AndroidManifest.xml
文件中声明了一个辅助功能服务组件。该服务被设置为启用和导出,并且需要 BIND_ACCESSIBILITY_SERVICE
权限。通过指定的 intent-filter
和元数据,系统可以正确识别和使用此服务,同时 accessibility_service_config.xml
文件中的配置可以帮助定义与该服务相关的其他信息或行为。
4. 实现辅助服务的核心方法
我们首先定义一个静态变量AccessibilityHelper
和一个构造函数SwipeService()
。
静态变量AccessibilityHelper
用来存储一个AccessibilityService
对象。该变量被标记为@SuppressLint("StaticFieldLeak")
,表示忽略对静态变量引起内存泄漏的警告。
使用构造函数SwipeService()
实例化一个SwipeService
对象,并将其赋值给静态变量AccessibilityHelper
。这样做的目的是为了在其他地方能够方便地访问AccessibilityService
对象。由于AccessibilityHelper
是一个静态变量,它可以在整个应用程序的生命周期内保持不变,并且可以通过类名直接访问。这样一来,在其他类中就可以直接使用AccessibilityHelper
来调用SwipeService
中的方法或属性,而不需要创建新的实例。这种设计方式可以简化代码,提高代码的可读性和可维护性。
@SuppressLint("StaticFieldLeak")
public static AccessibilityService AccessibilityHelper;
public SwipeService() {
AccessibilityHelper = this;
}
接着我们实现一个滑动手势的方法swipe()
,它接收起点坐标(x1, y1)
、终点坐标(x2, y2)
和持续时间duration
作为参数。这个方法使用了Android提供的Path
、GestureDescription
和AccessibilityService
类来构建和发送滑动手势。
代码的具体作用如下:
@RequiresApi(24)
:这是一个注解,用于指定代码需要运行在Android API Level 24及以上的设备上。它可以在运行时检查API级别,确保代码在兼容的设备上运行。- 创建一个
Path
对象,并根据起点和终点计算出随机偏移量后设置起点(randomX1, randomY1)
和终点(randomX2, randomY2)
。 - 创建一个
GestureDescription.Builder
对象,用于构建手势描述。 - 使用手势描述构建器的
addStroke()
方法添加一个手势路径,该路径使用前面创建的Path
对象,并设置开始时间为0,持续时间为传入的参数duration
。 - 调用手势描述构建器的
build()
方法,构建出最终的手势描述对象gestureDescription
。 - 使用静态变量
AccessibilityHelper
的dispatchGesture()
方法发送手势事件,并将手势描述和回调参数传入。 - 返回手势事件的执行结果,即是否成功发送手势。
另外,这段代码还包含一个私有方法getRandomOffset()
,它用于生成一个随机的偏移量,范围在-5到5之间。这个偏移量可以在设置起点和终点时加上,以产生一定的随机性。这样可以模拟真实的手势操作,增加应用的稳定性和可靠性。
@RequiresApi(24)
public static Boolean swipe(float x1, float y1, float x2, float y2, long duration) {
// 创建路径对象,并设置起点和终点
Path path = new Path();
float randomX1 = x1 + getRandomOffset();
float randomY1 = y1 + getRandomOffset();
float randomX2 = x2 + getRandomOffset();
float randomY2 = y2 + getRandomOffset();
path.moveTo(randomX1, randomY1);
path.lineTo(randomX2, randomY2);
// 创建手势描述构建器,并添加手势路径和持续时间
GestureDescription.Builder builder = new GestureDescription.Builder();
GestureDescription gestureDescription = builder
.addStroke(new GestureDescription.StrokeDescription(path, 0, duration))
.build();
// 发送手势事件,并设置回调
return AccessibilityHelper.dispatchGesture(gestureDescription, null, null);
}
private static float getRandomOffset() {
// 返回一个随机偏移量,范围在-5到5之间
return new Random().nextInt(10) - 5;
}
接下来我们定义一个供悬浮窗调用的自动滑动的函数,我们使用全局变量Variable.continueSwipe
来控制是否继续滑动,而Variable.continueSwipe
由悬浮窗控制,这样就能控制自动滑动的开始和停止了.
代码逻辑如下:
main(int total)
方法:滑动服务的主要逻辑方法。接收一个整数参数total
,表示需要执行滑动的总次数。- 首先定义了一个等待时间为2秒的变量
waitTwoSecond
。 - 然后根据屏幕宽高计算出起始位置和结束位置的坐标:
- x 坐标为屏幕宽度的一半,通过
Variable.mWidth / 2
计算得到。 - y1 和 y2 分别为屏幕高度的四分之三和四分之一,通过
Variable.mHeight / 4
和y2 * 3
计算得到。
- x 坐标为屏幕宽度的一半,通过
- 接下来通过一个循环,执行指定次数的滑动操作:
- 在每次循环开始时,首先检查是否需要继续滑动,如果不需要,则跳出循环。
- 然后使用
timeSleep()
方法使线程休眠一个随机时间。 - 判断当前设备的 Android 版本是否大于等于 Android N(Build.VERSION_CODES.N),如果是,则调用
swipe()
方法进行滑动操作。 - 最后再次调用
timeSleep()
方法使线程休眠2秒。
- 首先定义了一个等待时间为2秒的变量
timeSleep(int time)
方法:使线程休眠指定时间的方法。接收一个整数参数time
,表示休眠的时间(单位为毫秒)。- 在方法内部,通过调用
Thread.sleep(time)
来使线程休眠相应时间。 - 如果在休眠过程中发生中断异常(
InterruptedException
),则打印异常堆栈信息,然后抛出一个异常来中断程序执行, 并进行除以零的操作,导致抛出算术异常,直接让程序结束。
- 在方法内部,通过调用
以上完整代码如下:
package com.example.autoswipe.Services;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.annotation.SuppressLint;
import android.graphics.Path;
import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.RequiresApi;
import com.example.autoswipe.Global.Variable;
import java.util.Random;
public class SwipeService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
}
@Override
public void onInterrupt() {
}
@SuppressLint("StaticFieldLeak")
public static AccessibilityService AccessibilityHelper;
public SwipeService() {
AccessibilityHelper = this;
}
@RequiresApi(24)
public static Boolean swipe(float x1, float y1, float x2, float y2, long duration) {
// 创建路径对象,并设置起点和终点
Path path = new Path();
float randomX1 = x1 + getRandomOffset();
float randomY1 = y1 + getRandomOffset();
float randomX2 = x2 + getRandomOffset();
float randomY2 = y2 + getRandomOffset();
path.moveTo(randomX1, randomY1);
path.lineTo(randomX2, randomY2);
// 创建手势描述构建器,并添加手势路径和持续时间
GestureDescription.Builder builder = new GestureDescription.Builder();
GestureDescription gestureDescription = builder
.addStroke(new GestureDescription.StrokeDescription(path, 0, duration))
.build();
// 发送手势事件,并设置回调
return AccessibilityHelper.dispatchGesture(gestureDescription, null, null);
}
private static float getRandomOffset() {
// 返回一个随机偏移量,范围在-5到5之间
return new Random().nextInt(10) - 5;
}
public static void main(int total) {
int waitTwoSecond = 2000;
float x = Variable.mWidth / 2;
float y2 = Variable.mHeight / 4;
float y1 = y2 * 3;
for (int i = 0; i < total; i++) {
if (!Variable.continueSwipe) {
break;
}
timeSleep(new Random().nextInt(waitTwoSecond));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
swipe(x, y1, x, y2, 420);
}
timeSleep(waitTwoSecond);
}
}
public static void timeSleep(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
int i = 1 / 0;
}
}
}
5. 补充悬浮窗服务自动滑动的逻辑
我们补充start_SwipeService()
的法的代码逻辑,在一个新的线程中调用SwipeService
的main()
方法来开始滑动操作, 传入的参数控制滑动的次数。这样可以避免在主线程中执行耗时的滑动操作,保持应用的响应性能。
当我们点击文本为"结束"的按钮时,将设置变量Variable.continueSwipe
为false
,表示停止执行滑动操作。
private void btnClick() {
if ("开始".contentEquals(Variable.btnTextView.getText())) {
Variable.continueSwipe = true;
start_SwipeService();
Variable.btnTextView.setText("结束");
} else if ("结束".contentEquals(Variable.btnTextView.getText())) {
Variable.continueSwipe = false;
Variable.btnTextView.setText("开始");
}
}
private void start_SwipeService() {
new Thread(() -> {
try {
SwipeService.main(100);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
6. 回到最初的主程序启动悬浮窗
我们先来实现一个用于检查服务是否正在运行的辅助方法isServiceRunning
。它接受一个Class<?>
类型的参数serviceClass
,代表目标服务的类。
具体功能如下:
- 首先,通过
getSystemService()
方法获取系统级的ActivityManager
对象,用于管理应用程序的活动和服务。 - 然后,使用
getRunningServices()
方法获取所有正在运行的服务的信息列表,返回一个List<ActivityManager.RunningServiceInfo>
。 - 接下来,通过遍历这个列表,逐个比较每个服务的类名是否与传入的
serviceClass
的类名相同。 - 如果找到匹配的服务类名,则返回
true
,表示目标服务正在运行。 - 如果遍历完整个列表都没有找到匹配的服务类名,则返回
false
,表示目标服务未在运行。
该方法可以在需要判断某个特定服务是否正在运行时使用。在给定服务类的名称后,它将遍历所有正在运行的服务列表,并检查每个服务的类名是否与给定的服务类名相同。这样可以确定目标服务是否正在运行。
回到之前的switchResult
方法, 该方法根据传入的参数result
的不同值来执行相应的操作。
switchResult(String result)
方法:根据给定的字符串参数result
进行不同的操作判断。- 如果
result
等于"跳转到设置无障碍页面",我们之前已经实现了,这会跳转到手机设置中的无障碍页面,我们需要在页面中找到我们自己的辅助服务然后开启。 - 如果
result
的值等于 "开启悬浮窗服务",则调用checkFloatingButton()
方法来检查是否具有绘制悬浮窗的权限,并且如果有权限,我们创建一个Intent
对象,使用当前Activity
作为上下文和FloatingButton.class
作为目标服务,然后调用startService()
方法启动名为FloatingButton
的服务。。 - 如果
result
等于"结束悬浮窗服务",我们首先调用isServiceRunning()
方法检查名为FloatingButton
的服务是否正在运行。如果服务正在运行,则创建一个Intent
对象,并使用FloatingButton.class
作为目标服务,然后调用stopService()
方法停止该服务。
- 如果
checkFloatingButton(Activity activity)
方法:用于检查是否具有绘制悬浮窗的权限。首先检查设备的 Android 版本是否大于等于 Marshmallow(API级别23),以及是否具有绘制悬浮窗的权限。如果没有权限,会显示一个 Toast 提示用户进行授权,并且启动一个授权界面的 Intent 来请求权限。返回值为布尔类型,表示是否具有权限。isServiceRunning(Class<?> serviceClass)
方法:用于检查指定的服务类是否正在运行。它通过获取系统的 ActivityManager,并遍历正在运行的服务的列表来判断指定的服务类是否存在于列表中。如果存在,则返回true
,否则返回false
。
这样我们就可以在应用中实现跳转到无障碍页面、开启悬浮窗服务和结束悬浮窗服务的功能。
private void switchResult(String result) {
// 跳转无障碍页面
if (result.equals("跳转到设置无障碍页面")) {
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
return;
}
if (result.equals("开启悬浮窗服务")) {
if (checkFloatingButton(this)){
startService(new Intent(this, FloatingButton.class));
}
}
if (result.equals("结束悬浮窗服务")) {
if (isServiceRunning(FloatingButton.class)) {
stopService(new Intent(this, FloatingButton.class));
}
}
}
public boolean checkFloatingButton(Activity activity){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !Settings.canDrawOverlays(activity)) {
Toast.makeText(activity, "当前无权限,请授权", Toast.LENGTH_SHORT).show();
activity.startActivityForResult(
new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + activity.getPackageName())), 0);
return false;
}
return true;
}
// 检查服务是否正在运行
private boolean isServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
7. 运行效果
首先点击第一项进入无障碍页面
开启我们的无障碍服务AutoSwipe
![]() | ![]() | ![]() |
---|
回到App界面,开启悬浮窗服务后,出现开始
按钮,点击开始,即可进行自动滑动操作,执行100次自动停止
我们点击结束
按钮,将结束当前的自动滑动操作.
![]() | ![]() |
---|
打包成APK
介绍
做完一个AutoSwipe
项目之后,我们需要将自己的程序打包成Android安装包文件–APK(AndroidPackage)
,其后缀名为".apk
"。将APK文件直接上传到Android模拟器或Android手机中执行即可进行安装。Android系统要求具有其开发者签名的私人密钥的应用程序才能够被安装。生成数字签名以及打包项目成APK都可以采用命令行的方式,但是通过IDEA中的向导我们会更加方便地完成整个流程,打包发布的过程非常简单。下面以前面开发的AutoSwipe
为例,演示如何生成APK。
步骤
点击Build
→Generate Signed Bundle / APK
点击APK
→Next
选择新建keystore
路径,输入名称AutoSwipe
jks
代表Java KeyStore
,它是Java平台上用于存储密钥和数字证书的一种密钥库格式。在Android应用程序开发中,JDK文件被用来对APK文件进行签名,以确保其完整性和真实性。
JKS文件是一个二进制文件,可以包含一个或多个非对称(公钥/私钥)密钥对、对称密钥和X.509数字证书。这些密钥和证书用于身份验证、数据加密和数字签名等安全操作。
在打包APK时,使用JKS文件对应用进行数字签名是非常重要的。数字签名是一种用于验证应用的方式,它确保应用在发布过程中未被篡改或伪造。当用户下载和安装APK文件时,Android系统会检查APK的数字签名,并与应用商店中的签名信息进行比对。如果签名不匹配,系统将警告用户可能存在的安全风险。
为了生成JKS文件,可以使用Java的keytool工具。该工具允许生成密钥对、导入证书以及管理密钥库等操作。在生成JKS文件时,需要指定一个密码以保护其访问权限,这个密码应该被妥善保存,因为它将用于后续的签名和验证过程。
一旦生成了JKS文件,就可以在打包APK的过程中使用它。通过将JKS文件与应用进行数字签名,APK将包含一个数字证书。安装APK时,Android系统将使用JKS文件中的密钥对来验证APK的数字签名,并确保应用的完整性和真实性。
输入Password
和Cengxuyuan
, 继续
在打包安卓应用程序时,release
和 debug
是两种不同的构建类型,它们有以下区别:
目标用户:
release
构建用于发布到应用商店或供最终用户安装使用的版本,而debug
构建用于开发和测试阶段。release
构建通常是经过优化、去除调试信息和其他不必要的资源,以提高应用程序的性能和安全性。可调试性:
debug
构建会保留调试信息和符号表,以便在开发和测试期间进行调试。这使得开发人员可以通过调试器在运行应用程序时查看变量的值、执行代码跟踪等操作。而release
构建会剔除调试信息,无法进行完整的调试。性能和大小:
release
构建会对应用进行额外的优化,以提高应用程序的性能和响应速度。这可能包括代码压缩、混淆和优化,以减少应用程序的大小和资源使用。而debug
构建不会进行这些优化,以便开发人员能够更轻松地进行调试和分析。签名:
release
构建需要进行应用程序签名,以确保应用在发布时的身份验证和完整性。签名是应用程序的数字指纹,用于验证应用程序的来源和完整性,防止被篡改。而debug
构建通常使用调试密钥库(debug keystore),它不提供应用的身份验证和完整性保护,只是用于开发和测试。
在这里,我们选择release
版本
生成!
安装包位于根项目的release
文件夹下
完整资料和反馈
关注公众号:曾续缘,回复
自动滑动
领取完整源码和安装包如果您对开发过程有任何疑问或需要进一步的帮助,请随时在公众号后台留言给我们反馈。我将尽力解答您的问题并提供所需的支持。感谢您的理解和支持!