软件测试 | 测试开发 | Android App 保活服务的配置与禁用

2023-05-16

Android应用保活是应用、系统、用户三个角色相互影响的产物。几乎每一款应用都希望自己能实现永久保活,并通过保活实现消息推送、信息上报等等交互行为;几乎所有的系统厂商都想把应用关在笼子里,通过控制应用的运行时间来避免过多的电量和性能的消耗,这样可以大大提高系统流畅度和手机使用时间;对于用户来说我们希望使用的时候应用可以更好的运行,比如音乐、导航、通信软件,但是我们又希望不使用时彻底关闭应用,但是大部分用户都不清楚如何彻底关闭一个应用并让它不再运行。那么本文介绍一下在Android系统里面是如何实现保活方案,如何启动或禁用应用的保活。

Android应用自启与白名单

Android应用的保活一般会从两个方面着手:一是如何提高应用的存活周期;二是如何唤醒关闭的应用。一般情况下会通过Android账户的自动同步机制和开机广播唤醒已关闭的应用;然后通过定时任务、前台服务、在屏幕上显示控件等方式提高应用的存活周期。在账户同步的服务和开机广播接收器中为应用开启一个前台Service就实现了应用保活的基本策略。下面分别介绍各个方式的实现。

Android应用自启与白名单

通过静态注册开机广播可以在系统启动时唤醒应用,应用被唤醒后可以检查并初始化前台服务等保活策略。

public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //检查并初始化前台服务等保活策略
    }
}
<receiver
    android:name=".receiver.BootReceiver"
    android:directBootAware="true"
    android:enabled="true"
    android:exported="true">
    <!--通过priority指定广播的优先级-->
    <intent-filter android:priority="2147483647">
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
    </intent-filter>
  </receiver>

账户同步机制

Android应用可以在运行时注册系统账户,并通过service与系统账户进行关联,当系统运行时会在特定时期同步账户,同步账户的时候会启动所关联的service,在关联service中可以检查保活方案,通过账户同步机制可以唤醒被关闭的应用。

在开始之前首先定义两常量,在文中通过{常量名}的方式方式指代:

accountType=“xxxxxx”

contentAuthority=“xxxx”

<?xml version="1.0" encoding="utf-8"?>

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="{accountType}"
    android:allowParallelSyncs="false"
    android:contentAuthority="{contentAuthority}"
    android:isAlwaysSyncable="true"
    android:supportsUploading="true"
    android:userVisible="true" />
<?xml version="1.0" encoding="utf-8"?>

<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="com.qihoo.qa.ticker.account"
    android:icon="@mipmap/ic_launcher" <!--在系统设置中显示的账户图标-->
    android:label="@string/app_name" /><!--在系统设置中显示的账户名称-->
public class AccountSyncProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
                      @Nullable String[] selectionArgs) {
        return 0;
    }
}
<provider
android:name=".account.AccountSyncProvider"
android:authorities="{contentAuthority}"
android:enabled="true"
android:exported="true" />
public class AuthenticationService extends Service {

    private AccountAuthenticator accountAuthenticator;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return accountAuthenticator.getIBinder();//返回binder对象供系统使用
    }

    @Override
    public void onCreate() {
        super.onCreate();
        accountAuthenticator = new AccountAuthenticator(this);
    }

    public static class AccountAuthenticator extends AbstractAccountAuthenticator {

        public AccountAuthenticator(Context context) {
            super(context);
        }

        @Override
        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
            return null;
        }

        @Override
        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType,
                                 String[] requiredFeatures, Bundle options) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
                                         Bundle options) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
                                   String authTokenType, Bundle options) throws NetworkErrorException {
            return null;
        }

        @Override
        public String getAuthTokenLabel(String authTokenType) {
            return null;
        }

        @Override
        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
                                        String authTokenType, Bundle options) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
                                  String[] features) throws NetworkErrorException {
            return null;
        }
    }
}
<service android:name=".account.AuthenticationService">
    <intent-filter>
        <action android:name="android.accounts.AccountAuthenticator" />
    </intent-filter>
    <meta-data
        android:name="android.accounts.AccountAuthenticator"
        android:resource="@xml/account_authenticator" /> <!--指定账户配置文件-->
</service>
public class AccountSyncService extends Service {

    private SyncAdapter mSyncAdapter;

    private static final String TAG = "SyncService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mSyncAdapter.getSyncAdapterBinder();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mSyncAdapter = new SyncAdapter(getApplicationContext(), true);
    }

    ;
    public static class SyncAdapter extends AbstractThreadedSyncAdapter {
        public SyncAdapter(Context context, boolean autoInitialize) {
            super(context, autoInitialize);
        }


        @Override
        public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
            //账户同步时回调此方法,在此处检测保活业务
        }
    }
}
<service
  android:name=".account.AccountSyncService"<!--指定service文件-->
  android:enabled="true"
  android:exported="true">
  <intent-filter>
      <action android:name="android.content.SyncAdapter" />
  </intent-filter>
  <meta-data
      android:name="android.content.SyncAdapter"
      android:resource="@xml/account_sync_adapter" /><!--指定配置文件,该配置文件需要手动添加-->
</service>
accountName="test"
accountPwd="pwd"
//添加账户
 AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
 Account account = new Account(accountName,  {accountType});
 accountManager.addAccountExplicitly(account, accountPwd, new Bundle());
 //设置账户同步
 Account account = new Account(accountName, {accountType});
// 下面三个都需要同一个权限  WRITE_SYNC_SETTINGS
// 设置同步
ContentResolver.setIsSyncable(account, {contentAuthority}, 1);
// 自动同步
ContentResolver.setSyncAutomatically(account, {contentAuthority}, true);
// 设置同步周期
ContentResolver.addPeriodicSync(account, {contentAuthority}, new Bundle(), 1);

Schedule定时任务

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class LiveJobService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        //执行任务时回调
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}
<service
    android:name=".service.LiveJobService"
    android:enabled="true"
    android:exported="true"
    android:permission="android.permission.BIND_JOB_SERVICE" /><!--指定服务权限-->
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
//setPersisted 在设备重启依然执行
JobInfo.Builder builder = new JobInfo.Builder(lastJobId+i, new ComponentName(context.getPackageName(),
LiveJobService.class.getName())).setPersisted(true);
// 50s后执行任务
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
    builder.setPeriodic(50000);
} else {
    // 延迟执行任务
    builder.setMinimumLatency(50000);
}
jobScheduler.schedule(builder.build());

前台服务

保活服务一般在Service中后台运行,而Android系统对后台服务有一些列的运行限制,所以把服务绑定为前台服务会提高服务的优先级,在系统资源紧张时可以更好的运行。

/**
 * @author walker
 * @date 2020/12/25.
 * @description 在应用后台处理数据
 */
public class NotificationService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 如果Service被终止
        // 当资源允许情况下,重启service
        //绑定前台通知
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            setForegroundService();
        }
        return START_STICKY;
    }

    /**
     * 通过通知启动服务
     */
    @androidx.annotation.RequiresApi(api = Build.VERSION_CODES.O)
    public void setForegroundService() {
        //设定的通知渠道名称
        String channelName = "slient_name";
        String CHANNEL_ID = "slient_id";
        //设置通知的重要程度
        int importance = NotificationManager.IMPORTANCE_LOW;
        //构建通知渠道
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, channelName, importance);
        channel.setDescription("test");
        //在创建的通知渠道上发送通知
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
        builder.setSmallIcon(R.drawable.ic_launcher) //设置通知图标
                .setContentTitle("通知")//设置通知标题
                .setContentText("前台服务")//设置通知内容
                .setAutoCancel(true) //用户触摸时,自动关闭
                .setOngoing(true);//设置处于运行状态
        //向系统注册通知渠道,注册后不能改变重要性以及其他通知行为
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.createNotificationChannel(channel);
        //将服务置于启动状态 NOTIFICATION_ID指的是创建的通知的ID
        startForeground(111, builder.build());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
<!--通过android:process标签指定独立进程名-->
<service
    android:name=".service.DataService"
    android:enabled="true"
    android:exported="false"
    android:process=":sync" />
  1. 在应用启动时开启服务
startService(new Intent(context,DataService.class));

如何禁用后台运行

我们在开发或配置应用保活相关功能时主要通过开机自启、后台运行、关联启动、账户同步几个方面入手,不同的手机设置入口有可能不一样,但是可以参考这几点进行设置,下面介绍一下华为荣耀20上的配置方式。

01

开机自启权限的处理

以华为系统为例,在【手机管家】app中找到【应用启动管理】,并在应用启动管理中找到对应的app,将对应app切换为【手动管理】,并为激活【允许自动启动】【允许关联启动】【允许后台活动】三个选项。

允许设备开机自启以及后台服务的配置:

禁止后台服务以及开机自启的设置:

02

账户同步服务的处理

我们在【设置】/【账户】下可以看到系统内所有的账户信息,并可以在这里管理同步服务

允许账户同步设置:

允许账户同步时系统会按既定策略回调注册同步Service,在Service内可以启动应用其他服务,但是部分机型上可能存在适配问题

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

软件测试 | 测试开发 | Android App 保活服务的配置与禁用 的相关文章

  • 使用 Android 前台服务为 MediaPlayer 创建通知

    问题就在这里 我目前正在开发一个应用程序 该应用程序必须提供 A 广播播放器 来自 URL 的 AAC 直播 还有一个播客播放器 来自 URL 的 MP3 流 该应用程序必须能够在后台运行 Android 服务 并通过以下方式向用户公开持续
  • GCM 向主题发送消息:TOO_MANY_TOPICS 错误

    以前 GCM 每个应用程序有 100 万个主题订阅的限制 我发现他们现在已经取消了这一限制 基于发布 订阅模型 主题消息支持 每个应用程序无限订阅 https developers google com cloud messaging to
  • 检测到设备正在振动?

    我使用下面的代码来振动设备 public void vibrator try Vibrator vibrator Vibrator getSystemService Context VIBRATOR SERVICE vibrator vib
  • Android第一次动画不流畅

    我正在尝试一个动画将 imageView 从屏幕底部滑动到屏幕中心 但是当我第一次执行此动画时 它不平滑 但当第二次执行动画时 它是正常且平滑的 我几乎尝试了所有方法 但无法解决我的问题 这是我的动画文件
  • 与 Admob 广告单元 ID 混淆

    我跟着tutorial https developers google com admob android quick start在我的应用程序中创建广告横幅 到目前为止 这有效 我可以看到测试广告 但是 本教程指示我在两个不同的位置使用两
  • Firebase Analytics 禁用受众国家/地区跟踪

    我正在开发一个严格不允许位置跟踪的应用程序 我想使用 Firebase Analytic 的其他功能 例如 PageTransitions 和 Crashalitics 但如果我无法禁用受众位置跟踪 我就无法使用其中任何功能 这是我在 An
  • (Ionic 2)尝试回退到 Cordova-lib 执行时发生错误:TypeError:无法读取未定义的属性“then”

    Edit 使用 ionic 2 时会发生这种情况 我知道它还不稳定 但我认为可能有一些解决方案 因为其他人似乎没有遇到这个问题 Edit end 由于某种原因 我在尝试使用 ionic build android 和 ionic build
  • 在 Google Analytics 中跟踪应用程序版本

    我正在使用谷歌分析模块 https marketplace appcelerator com apps 5081 2014113336 https marketplace appcelerator com apps 5081 2014113
  • TextView 之间有分隔线

    我正在尝试在 android studio 中创建以下布局 因为我对 android 东西还很陌生 所以我第一次尝试使用 LinearLayout 并认为这可能无法实现 现在我正在尝试使用RelativeLayout 我已经用颜色创建了这个
  • 如何在 sqlite 中将 2 列合并为新列

    我有一个包含 3 列的表 我必须将 2 列中的值按降序排列到一列中 A B C z 1 2 f 5 7 s 9 5 使用此示例 输出会将 B 列和 C 列中的值放入其中 如下所示 A B s 9 f 7 f 5 s 5 z 2 z 1 我当
  • 在我的Android中,当其他应用程序想要录制音频时如何停止录音?

    在我的应用程序中 服务通过 AudioRecord 持续录制音频 当我的应用程序运行时 其他与音频记录相关的应用程序 例如 Google 搜索 无法工作 如何知道何时有其他应用想要录制音频 以便我可以停止录制以释放资源 答案是MediaRe
  • 更新到材质 1.2.0 后,材质按钮上缺少圆角半径属性

    这是我的材质按钮代码
  • ROOM迁移过程中如何处理索引信息

    CODE Entity tableName UserRepo indices Index value id unique true public class GitHubRepo PrimaryKey autoGenerate true p
  • 如何创建像谷歌位置历史记录一样的Android时间轴视图?

    我想设计像谷歌位置历史这样的用户界面 我必须为我正在使用的应用程序复制此 UIRecyclerView 每行都是水平的LinearLayout其中包含右侧的图标 线条和视图 该线是一个FrameLayout具有圆形背景和半透明圆圈Views
  • 当手机旋转(方向改变)时如何最好地重新创建标记/折线

    背景 开发一个使用 Android Google Map v2 的本机 Android 应用程序 使用android support v4 app FragmentActivity 在 Android v2 2 上运行 客观的 在更改手机方
  • Android - 将 ImageView 保存到具有全分辨率图像的文件

    我将图像放入 ImageView 中 并实现了多点触控来调整 ImageView 中的图像大小和移动图像 现在我需要将调整大小的图像保存到图像文件中 我已经尝试过 getDrawingCache 但该图像具有 ImageView 的大小 我
  • 保护 APK 中的字符串

    我正在使用 Xamarin 的 Mono for Android 开发一个 Android 应用程序 我目前正在努力使用 Google Play API 添加应用内购买功能 为此 我需要从我的应用程序内向 Google 发送公共许可证密钥
  • SharedFlow 和 StateFlow 的主要区别

    两者有什么区别共享流 and 状态流 以及如何使用这些MVI建筑学 使用简单更好吗Flow或者这些作为状态和事件 Flow 是冷的 意味着它仅在收集数据时才发出数据 另外Flow不能保存数据 可以把它看成是水在里面流动的管道 Flow中的数
  • Android 如何聚焦当前位置

    您好 我有一个 Android 应用程序 可以在谷歌地图上找到您的位置 但是当我启动该应用程序时 它从非洲开始 而不是在我当前的城市 国家 位置等 我已经在developer android com上检查了信息与位置问题有关 但问题仍然存在
  • 在webview android中加载本地html文件

    我正在尝试在 android 的 webview 中加载 html 文件的内容 但是 它给了我 网页不可用错误 如果我尝试使用谷歌或雅虎等网站 它们就会起作用 html文件位于src gt main gt assests gt index

随机推荐

  • vue.js中使用v-for以及获取索引的方法介绍

    下面Vue js教程栏目带大家了解一下vue js中v for的使用及索引获取 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 2 x版本 xff1a v for 61 34 item index i
  • HTML网页自动跳转的5种方法

    xff08 推荐教程 xff1a html教程 xff09 在我们进行网站创建时经常会遇到需要进行网页跳转的情况 xff0c 本文就来为大家介绍五种网页自动跳转的方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望
  • 深入讨论JavaScript中Set对象如何让代码更快

    我确信有很多开发人员坚持使用基本的全局对象 xff1a 数字 xff0c 字符串 xff0c 对象 xff0c 数组和布尔值 对于许多用例 xff0c 这些都是需要的 但是如果想让你的代码尽可能快速和可扩展 xff0c 那么这些基本类型并不
  • ES6中的for ... of循环和可迭代对象

    推荐教程 xff1a JavaScript视频教程 本文将研究 ES6 的 for of 循环 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 旧方法 在过去 xff0c 有两种方法可以遍历 java
  • 8个编写JS代码的小技巧和窍门

    下面js教程栏目给大家介绍8个编写javascript代码的技巧和窍门 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 推荐教程 xff1a JavaScript视频教程 1 生成指定区间内的数字 有
  • 浅谈css z-index应用

    做过页面布局的同学对z index属性应该是很熟悉了 xff0c z index是针对网页显示中的一个特殊属性 因为显示器是显示的图案是一个二维平面 xff0c 拥有x轴和y轴来表示位置属性 为了表示三维立体的概念如显示元素的上下层的叠加顺
  • jquery中怎样将类数组对象转换为数组对象

    相关推荐 xff1a jQuery视频教程 类数组对象的定义 xff1a 所谓 34 类数组对象 34 就是一个常规的Object对象 xff0c 如 34 p 34 但它和数组对象非常相似 xff1a 具备length属性 xff0c 并
  • 理解对象原型和原型链

    本篇文章带大家介绍一下JavaScript中的对象原型和原型链 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 对象原型 相信大家都这样用过 map xff1a let arr 61 0 1 2 le
  • JavaScript中处理异步的几种方式

    在网站开发中 xff0c 异步事件是项目必然需要处理的一个环节 xff0c 也因为前端框架的兴起 xff0c 通过框架实现的 SPA 已经是快速建构网站的标配了 xff0c 一部获取数据也就成了不可或缺的一环 xff1b 本文来就讲一讲 J
  • 软件测试 | 测试开发 | 一种基于目标检测实现黑花屏分类任务的方案

    背景 视频帧的黑 花屏的检测是视频质量检测中比较重要的一部分 xff0c 传统做法是由测试人员通过肉眼来判断视频中是否有黑 花屏的现象 xff0c 这种方式不仅耗费人力且效率较低 为了进一步节省人力 提高效率 xff0c 一种自动的检测方法
  • VSCode使用Git进行版本控制

    相关推荐 xff1a vscode基础教程 Visual Studio Code 使用Git进行版本控制 本来认为此类教程 xff0c 肯定是满网飞了 今天首次使用VS Code的Git功能 xff0c 翻遍了 所有中文教程 xff0c 竟
  • 使用Llama Logs实时可视化Node错误

    本篇文章给大家介绍一下Node开发神器 Llama Logs xff0c 使用Llama Logs实时可视化Node错误 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a node
  • JS中的工厂函数和构造函数

    当谈到JavaScript语言与其他编程语言相比时 xff0c 你可能会听到一些令人困惑东西 xff0c 其中之一是工厂函数和构造函数 工厂函数 所谓工厂函数 xff0c 就是指这些内建函数都是类对象 xff0c 当你调用他们时 xff0c
  • 如何使用 map 代替纯 JavaScript 对象?

    JavaScript 普通对象 key 39 value 39 可用于保存结构化数据 但是我发现很烦人的一件事 xff1a 对象的键必须是字符串 xff08 或很少使用的符号 xff09 如果用数字作键会怎样 xff1f 在这种情况下没有错
  • 深入浅析Service Workers

    本篇文章给大家介绍一下JavaScript API Service Workers 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a 编程入门 service worker 是什么
  • javascript中的modules、import和export

    在互联网的洪荒时代 xff0c 网站主要用 HTML和 CSS 开发的 如果将 JavaScript 加载到页面中 xff0c 通常是以小片段的形式提供效果和交互 xff0c 一般会把所有的 JavaScript 代码全都写在一个文件中 x
  • 分享Atom入坑需要安装的一些插件

    本篇文章给大家推荐一些Atom入坑必备插件 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a atom使用教程 Atom作为Javascript CSS HTML等前端编辑器利器
  • angular中使用jsencrypt插件

    本篇文章给大家介绍一下angular中jsencrypt插件的使用方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a angular教程 angular使用jsencrypt插
  • 使用 Node 处理 I/O 密集型任务

    下面本篇文章给大家介绍一下使用 nodejs 多线程 处理高并发任务的方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a nodejs视频教程 摩尔定律 摩尔定律是由英特尔联合
  • 软件测试 | 测试开发 | Android App 保活服务的配置与禁用

    Android应用保活是应用 系统 用户三个角色相互影响的产物 几乎每一款应用都希望自己能实现永久保活 xff0c 并通过保活实现消息推送 信息上报等等交互行为 xff1b 几乎所有的系统厂商都想把应用关在笼子里 xff0c 通过控制应用的