写点什么

Android App 保活服务的配置与禁用

  • 2021-01-20
  • 本文字数:6589 字

    阅读完需:约 22 分钟

Android App 保活服务的配置与禁用

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


Android 应用自启与白名单


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


Android 应用自启与白名单


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


  1. 首先我们需要实现 BroadcastReceiver 的子类作为开机广播的接收器,并在 onReceive 方法中处理业务逻辑。

public class BootReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        //检查并初始化前台服务等保活策略    }}
复制代码


2. 然后我们将开机广播接收器注册到 AndroidManifest.xml 中,并增加开机动作过滤器。

<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”


  1. 在项目 res/xml 中添加账户配置文件,指定文件名为 account_sync_adapter.xml


<?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" />
复制代码


2. 在项目 res/xml 中配置账户显示信息,命名为 account_authenticator.xml


<?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" /><!--在系统设置中显示的账户名称-->
复制代码


3. 实现 ContentProvider 的子类,并在 AndroidManifest.xml 中注册

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; }}
复制代码


注册服务


<providerandroid:name=".account.AccountSyncProvider"android:authorities="{contentAuthority}"android:enabled="true"android:exported="true" />
复制代码


4. 实现账户的认证 service,系统会调用该 service 认证账户,由于是用于保活的空账户,所以不需要关注具体业务


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; }
复制代码


在 AndroidManifest.xml 中注册账户认证服务


<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>
复制代码


5. 编写并配置账户同步服务,系统自动同步账户时回调此服务


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) { //账户同步时回调此方法,在此处检测保活业务 } }}
复制代码


在 AndroidManifest.xml 中注册账户同步服务


<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>
复制代码


6. 添加账户并设置自动同步


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 定时任务

1. 实现 JobService 的子类,用于执行任务时回调


@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; }}
复制代码


2. 在 AndroidManifest 中配置任务 service


<service    android:name=".service.LiveJobService"    android:enabled="true"    android:exported="true"    android:permission="android.permission.BIND_JOB_SERVICE" /><!--指定服务权限-->
复制代码


3. 设置定时任务


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 系统对后台服务有一些列的运行限制,所以把服务绑定为前台服务会提高服务的优先级,在系统资源紧张时可以更好的运行。

1. 实现 Service 子类 NotificationService,并在在 onStartCommand 方法中开启常驻通知


/** * @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(); }}
复制代码


2. 在 AndroidManifest 中配置服务


<!--通过android:process标签指定独立进程名--><service    android:name=".service.DataService"    android:enabled="true"    android:exported="false"    android:process=":sync" />
复制代码


3. 在应用启动时开启服务


startService(new Intent(context,DataService.class));
复制代码


如何禁用后台运行


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


01 开机自启权限的处理

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


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



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


02 账户同步服务的处理

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


允许账户同步设置:


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



禁用所有账户同步功能:



禁用某一应用的账户同步功能:



本文转载自:360 技术(ID:qihoo_tech)

原文链接:Android App 保活服务的配置与禁用

2021-01-20 14:002588

评论

发布
暂无评论
发现更多内容

【我和openGauss的故事】在Kylin桌面版操作系统中配置openGauss的ODBC数据源

daydayup

【我和openGauss的故事】Navicat连接openGauss_5.0.0 企业版数据库

daydayup

2023-08-10:景区里有m个项目,也就是项目数组为int[][] game,这是一个m*2的二维数组 景区的第i个项目有如下两个参数: game[i] = { Ki, Bi } Ki一定是负数,

福大大架构师每日一题

左程云 福大大架构师每日一题

C++友元函数和友元类的使用

芯动大师

c++ 友元

新利好带动 POSE 持续上扬,月内几近翻倍

鳄鱼视界

从推动到拉动:研发效能提升的第一性原理

CODING DevOps

【我和openGauss的故事】openGauss5.0特性:CM支持VIP仲裁特性

daydayup

openGauss-graph 0.2.0版本正式上线

daydayup

ToolLLM:能够使用 16000 种真实 API 的 LLM

Zilliz

AIGC Towhee LLM

新利好带动 POSE 持续上扬,月内几近翻倍

BlockChain先知

【我和openGauss的故事】原生openGauss与Oracle数据库函数兼容性对比验证测试

daydayup

AIGC 浪潮下,鹅厂新一代前端人的真实工作感受

CODING DevOps

可视化数据分析工具是什么?可以用来做什么?

巷子

javascript之webAPIs(1)

timerring

JavaScript

怎么快速更新Python的第三方库?教你一招,小白也能学会

程序员晚枫

Python 编程 计算机 第三方库

面部表情识别技术在安全领域的应用

来自四九城儿

ARTS 打卡第 1 周 鸿蒙入门开发教程:一文带你详解工具箱元服务的开发流程

坚果

ARTS 打卡计划 HarmonyOS OpenHarmony 元服务 ArkTS

【我和openGauss的故事】openGauss5.0在某省医保局实时数仓应用

daydayup

【NodeJS】GLIBC_2.28 not found CentOS7不兼容Node高版本

ReturnTmp

node.js centos

Spring 架构

小万哥

Java spring 后端 springboot SpringCloud

中国信通院高质量数字化转型产品及服务全景图发布,合合信息多项AI产品入选

合合技术团队

人工智能 数字化转型

openGauss数据库源码解析系列文章——安全管理源码解析(三)

daydayup

go-zero 是如何实现令牌桶限流的?

AlwaysBeta

Go 源码 微服务 go-zero 限流器

作为网络报表分析工具的Quick BI,其功能如何

夜雨微澜

通义千问开源模型部署使用

WizInfo

通义千问

【我和openGauss的故事】一文帮你理解openGauss的高可用方案

daydayup

openGauss数据库源码解析系列文章——安全管理源码解析(四)

daydayup

【Linux】云服务器自动化部署VuePress博客(Jenkins)

ReturnTmp

jenkins vuepress

以商业大数据技术助力数据合规流通体系建立,合合信息参编《数据经纪从业人员评价规范

合合技术团队

人工智能 大数据 商业化

新利好带动 POSE 持续上扬,月内几近翻倍

威廉META

【我和openGauss的故事】openGauss 主备架构及同步复制模式理论学习与验证测试

daydayup

Android App 保活服务的配置与禁用_移动_360技术_InfoQ精选文章