程序性能优化之耗电优化(四)下篇

Android 进阶——性能优化之电量优化全攻略及实战小结(一) 性能优化之电量优化之低电耗模式和应用待机模式概述 阅读详情

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680

本篇文章将最后从以下两个方面来介绍耗电优化:

  • 【JobScheduler】
  • 【WorkManager】

一、JobScheduler API的使用详细

1.1 简介

JobScheduler API是在Android5.0开始有的,使用场景:在稍后的某个时间点或者当满足某个特定的条件(连接电源,网络状态变化、手机是否空闲)时需要执行一个任务。

1.2 API函数

开发者主要通过三个类JobService和JobScheduler、JobInfo来使用JobSchedule API

而且你要知道一件事,一旦通过命令开启了任务,只要符合条件就会执行任务,除非应用程序死亡,所以比如设置周期运行任务的,会不断的启动任务

JobScheduler实现了任务的调度,相关函数如下

/*返回值:1表示执行成功,0代表失败*/
public abstract int schedule(JobInfo job);
/**通过指定的jobId取消Job任务*/
public abstract void cancel(int jobId);
 
/**取消所有的Job任务*/
public abstract void cancelAll();
 
/**获取所有的未执行的Job任务*/
public abstract @NonNull List<JobInfo> getAllPendingJobs();
 
/**获取指定的Job未执行的任务*/
public abstract @Nullable JobInfo getPendingJob(int jobId);

JobService是一个抽象类,是Service的继承类,我们需要实现两个个函数,来完成任务执行的开始和停止

/*开启任务*/
public abstract boolean onStartJob(JobParameters params);
/*停止任务方法*/
public abstract boolean onStopJob(JobParameters params);

JobInfo是为了规定任务启动的规则,通过JobScheduler的建造者模式来设置规则

// jobId每个Job任务的id
int jobId = 1;
// 指定你需要执行的JobService
ComponentName name = new ComponentName(getPackageName(), MyJobService.class.getName()));
JobInfo.Builder builder = new JobInfo.Bulider(jobId, name);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE); //设置需要的网络条件,默认NETWORK_TYPE_NONE
builder.setPeriodic(3000);//设置间隔时间,不断的触发任务的启动
builder.setMinimumLatency(3000);// 设置任务运行最少延迟时间,与setPeriodic相似,只是间隔时间不确定,不能与setPeriodic一起使用,
builder.setOverrideDeadline(50000);// 设置deadline,若到期还没有达到规定的条件则会开始执行  
builder.setRequiresCharging(true);// 设置是否充电的条件,默认false
builder.setRequiresDeviceIdle(false);// 设置手机是否空闲的条件,默认false
builder.setPersisted(true);//设备重启之后你的任务是否还要继续执行
JobInfo info = builder.build();

1.3 各种使用

(1)基础代码
首先我贴出一个最基本JobSheduler API的使用代码,后面的测试代码都是通过这个段代码稍微改动而来的,大家可以边看博客,自己试一试代码的效果

MyJobService就是显示一个Toast,之所以最后是通过Handler实现,是因为想要模仿耗时任务,毕竟这是一个后台Service,使用场景很多是耗时任务。

如果要模拟的不是耗时任务,那就去掉这个Handler,然后将onStartJob的返回值设置为false。因为在onStartJob里返回false代表你任务执行完成了,他会开始计时为下一次任务的执行做准备,如果返回true,代表任务还在执行,需要我们自己调用jobFinished(param,true)函数来告诉JobService任务执行完成了

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)//API需要在21及以上
public class MyJobService extends JobService {
 
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Toast.makeText(MyJobService.this, "MyJobService", Toast.LENGTH_SHORT).show();
            JobParameters param = (JobParameters) msg.obj;
            jobFinished(param, true);
 
            return true;
        }
    });
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }
 
    @Override
    public boolean onStartJob(JobParameters params) {
        Message m = Message.obtain();
        m.obj = params;
        handler.sendMessageDelayed(m, 2000);
        return true;
    }
 
    @Override
    public boolean onStopJob(JobParameters params) {
        handler.removeCallbacksAndMessages(null);
        return false;
    }
}

因为是Service还需要在AndroidManifest注册,只是注册时还需要设置一个特殊的权限

        <service
            android:name=".MyJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"></service>

activity就是设置一个按钮,点击触发任务的调度开启

public class MainActivity extends AppCompatActivity {
 
    Button btn;
    private JobScheduler mJobScheduler;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        btn = (Button) findViewById(R.id.btn);
        mJobScheduler = (JobScheduler) getSystemService( Context.JOB_SCHEDULER_SERVICE );
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    JobInfo.Builder builder = new JobInfo.Builder( 1,
                            new ComponentName( getPackageName(), MyJobService.class.getName() ) );
                    builder.setMinimumLatency(2000);
                    if( mJobScheduler.schedule( builder.build() ) <= 0 ) {
                        //If something goes wrong
                    }
                }
            }
        });
    }
}

(2)各种使用情况
setMinimumLatency
基础代码里通过setMinimumLatency(2000)设置任务最少执行延缓,虽然是最少延缓,但是在我的测试下,发现这个延缓时间比较随机,有时候能够在两秒后开始执行任务,不过一般的都是7、8秒开始执行任务。在任务执行完成后,他们会初始化延缓时间,开始计时,准备下一次任务的执行。

setPeriodic
在基础代码里,我把setMinimumLatency(2000)代码换成setPeriodic(2000)

这里要注意setMinimumLatency和setPeriodic不能一起设置,会报错,我想是因为他们都有周期设置任务启动的特性有关。

当我运行代码,应用却没有按照两秒周期的时间触发任务,然后我在控制台看到了如下log

 W/JobInfo: Specified interval for 1 is +2s0ms. Clamped to +15m0s0ms
 W/JobInfo: Specified flex for 1 is +2s0ms. Clamped to +5m0s0ms

就是说你这个间隔必须在15分钟以上,这个规定是在Android7.0开始有的,所以如果需要比较频繁的调用任务,还是需要使用setMinimumLatency来完成。
setRequireNetworkType
如果我把setRequireNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)替换setMinimumLatency(2000),运行程序会报错

java.lang.IllegalArgumentException: You're trying to build a job with no constraints, this is not allowed.

我得出结论setRequireNetworkType不算主约束条件,需要依附像setMinimumLatency类似的主约束条件,那好我添加一个setMinimumLatency(2000000),再来运行,之所以把最少延缓的时间设置的这么大,是因为只要满足设置的条件之一就会启动任务。
然后运行程序,当我们连接WIFI,就会触发任务,市面很多通知栏广告就是使用这个类型的网络类型触发的,因为JobInfo规定的网络类型还有几个,我就不试了,大家可以自己试试

    public static final int NETWORK_TYPE_ANY = 1;
    public static final int NETWORK_TYPE_METERED = 4;
    public static final int NETWORK_TYPE_NONE = 0;
    public static final int NETWORK_TYPE_NOT_ROAMING = 3;
    public static final int NETWORK_TYPE_UNMETERED = 2;

setRequiresChargin
setRequiresChargin不是主约束条件, 将setRequiresChargin(true)替换setRequireNetworkType(JobInfo.NETWORK_TYPE_UNMETERED),运行程序。
setRequiresChargin(true)指的是监听充电,如果给的false就不监听,所以默认就是false

setOverrideDeadline
如果jobInfo配置如下

                    JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName()))
                            .setPeriodic(2000)
                            .setOverrideDeadline(10000)
                            .build();

运行程序会报错

java.lang.IllegalArgumentException: Can't call setOverrideDeadline() on a periodic job.
如果我去掉setPeriodic(2000)这行代码,在运行程序,我发现点击一次按钮就会立即启动任务,也就是弹出Toast,但是后面会继续启动任务,只是间隔时间比10秒时间要长得多

现在JobInfo代码我写成如下

                    JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName()))
                            .setOverrideDeadline(5000)
                            .setMinimumLatency(3000)
                            .build();

程序运行正常,符合预期
setPersisted
setPersisted不是一个主约束条件,当JobInfo代码如下

                    JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName()))
                            //.setMinimumLatency(10000)
                            .setPersisted(true)
                            .build();

按下按钮,然后重启手机,没有效果。。。。。

setRequiresDeviceIdle
代码如下

                    JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName()))
                            .setRequiresDeviceIdle(true)
                            .build();

额,手机空闲状态,明明其他手机程序都退出来,半天等不到,

(3)JobSheduler任务调度
之前我们在基础代码里看到了以下代码,就是判断任务是否创建成功,但是这还不是任务调度的范围

                    if( mJobScheduler.schedule( builder.build() ) <= 0 ) {
                        //If something goes wrong
                    }

首先我们看看cancel函数的效果,activity的代码里加一个button,为了触发cancel函数

public class MainActivity extends AppCompatActivity {
 
 
    Button btn, btn_pause;
    private JobScheduler mJobScheduler;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        btn = (Button) findViewById(R.id.btn);
        btn_pause = (Button) findViewById(R.id.btn_pause) ;
        mJobScheduler = (JobScheduler) getSystemService( Context.JOB_SCHEDULER_SERVICE );
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    JobInfo.Builder builder = new JobInfo.Builder( 1,
                            new ComponentName( getPackageName(), MyJobService.class.getName() ) );
                    builder.setMinimumLatency(20000);
                    if( mJobScheduler.schedule( builder.build() ) <= 0 ) {
                        //If something goes wrong
                    }
                }
            }
        });
        btn_pause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    mJobScheduler.cancel(1);
                }
            }
        });
 
    }
 
 
}

运行程序,点击第一个按钮,然后再点击第二个按钮,确认任务没有被启动过,把cancel函数缓存cancelAll效果一样。
其实还有函数可以看到未执行任务的函数,比如getPendingJob,但是这一个需要手机系统是Android7.0以上的。

二、WorkManager 的使用及优势

commonj.work.WorkManager

2.1 描述:

工作管理器 (commonj.work) API 提供的接口允许应用程序在一个容器中并发地执行多个工作项。

实际上,此 API 就是 java.lang.Thread API 的容器管理替代方法。但是java.lang.Thread API 不适用于受管 Java EE 环境中承载的应用程序。在这样的环境中,WorkManager的效果更好,因为它允许容器全面查看和控制所有执行线程。
注意:
工作管理器 API 不提供故障转移和持久性机制。如果受管服务器环境出现故障或关闭,则当前所有工作都将丢失。

2.2 优点:

  1. 可以由容器来管理线程
  2. 可以协调多个线程共同工作

接口:
WorkManager - 此接口提供一组用于调度要执行工作的调度方法。
系统管理员可在服务器级别定义 WorkManager。WorkManager 实例通过执行 JNDI 查找来获取。受管的环境可以支持多个 WorkManager 实例。请参阅"WebLogic JNDI 编程"。您在部署期间将 WorkManager 配置为 resource-ref。。
Work - 通过此接口,您可以异步运行应用程序代码。通过创建实现此接口的类,您可以创建可通过调度在特定的时间或定义的间隔运行的代码块。换句话说,此为在工作管理器 API 中处理的"工作"。
WorkItem - 在将 Work 实例提交给 WorkManager 后,WorkManager 将返回一个 WorkItem。此 WorkItem 用于确定整个 Work 实例的状态。
WorkListener - WorkListener 接口是一个回调接口,它在 WorkManager 与 Work 实例中定义的调度工作之间建立通信。

WorkItem item = WorkManager.schedule(Work work, WorkListener wl);

可以使用 WorkListener 确定 Work 项的当前状态。
注意:
WorkListener 实例总与通过 WorkManager 调度 Work 的初始线程在同一个 JVM 中执行。

web.xml:
<resource-ref>
<res-ref-name>wm/ThreadsWorkManager</res-ref-name>
<res-type>commonj.work.WorkManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

**Java code: **
可以在Listener 或 servlet中用以下代码来获取WorkManager。
WorkManager wm =(WorkManager)ic.lookup("java:comp/env/wm/ThreadsWorkManager");
wm.schedule(new OpenWOWork());

public class OpenWOWork implements Work{ 
private boolean isRun = true; 
public void run() { 
     while(isRun ){ 
     //add code 
     } 
} 

public void release() { 
    isRun = false; // 
} 

  public boolean isDaemon() { 
     return true; 
  } 

} 

Work Manager具有两个无可比拟的优势:

  1. 使多个Work之间协作起来,例如使用监听器,当一个任务完成后再开始另一个任务。

  2. 线程由服务器统一管理起来,如果服务器的将该应用停掉,就会shut down 所有的work,当然你也可以选择不shut down. 但是它提供了这个的功能让用户在使用时可以保证不会因为多次部署而导致应用产生多个线程在跑。
    参考:https://www.iteye.com/blog/dana-wang-2160023
    https://blog.csdn.net/z979451341/article/details/80608607

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680

LVGL在STM32H743IIT6上的性能优化技巧:如何提升图形界面刷新速度 本文详细介绍了在STM32H743IIT6高性能MCU上优化LVGL图形界面刷新速度的实战技巧。通过利用芯片的DMA2D硬件加速模块替代低效的软件画点,将绘制缓冲区放入SDRAM并启用双缓冲机制,以及合理配置CPU缓存与MPU,有效解决了界面卡顿问题。文章还涵盖了LVGL引擎调优、内存架构优化等关键步骤,帮助开发者充分发挥STM32H743的硬件潜力,实现流畅的图形界面体验。 阅读详情

相关推荐

【NLP】第10章 使用基于 BERT 的 Transformer 进行语义角色标记

Transformers 在过去几年里取得的进步比上一代 NLP 还要多。标准 NLU 方法首先学习句法和词汇特征来解释句子的结构。在运行语义角色标签( SRL )之前,将训练以前的 NLP 模型理解语言的基本语法。Shi和Lin(2019)在他们的论文开始时询问是否可以跳过初步的句法和词汇训练。基于 BERT 的模型可以在不经过那些经典训练阶段的情况下执行 SRL 吗?答案是肯定的!Shi和Lin (2019) 建议 SRL 可以被视为序列标记并提供标准化的输入格式。他们基于 BERT 的模型产生了令人惊

sikh_0529的博客 7349

Android之任务调度WorkManager和JobSchedule的使用

学更好的别人,做更好的自己。——《微卡智享》本文长度为5347字,预计阅读11分钟前言调度任务也是最近产品中需要用的,定时与后台进行数据同步,研究了几种方法后,觉得还是JobSchedul...

Vaccae的博客 3798

API STD 667-2022 Plate-and-Frame Heat Exchangers.pdf

API STD 667-2022 Plate-and-Frame Heat Exchangers.pdf

Android开发笔记(一百十三)任务调度JobScheduler

任务调度 App除了通过屏幕向用户展示可交互的界面元素之外,还经常需要在后台做些背地里做的事情,比如说精密计算、文件下载、统计分析、数据导入、状态监控等等,这些用户看不到的事一般放在Service中处理。 然而有时候我们希望在特定情况下再启动事务,比如说延迟若干时间之后,或者等手机空闲了...

chuanao8829的博客 830

JobScheduler系列1-基本用法和注意事项

JobScheduler推出的背景 在android开发中,我们为了节约电池电量,经常要在稍后的某个时间点或者满足某个特定的条件时去执行某个任务,例如网络状态连接到wifi状态时执行某些网络请求,或者每隔24小时,进行一项特定的操作。 针对每隔一段时间执行特定操作的需求,我们可以使用AlarmManager来完成,示例代码如下: public void alarmTest(Context c){ ...

hyc1988107的博客 9096

程序性优化耗电优化)上篇

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 本篇文章将最后从以下两个方面来介绍耗电优化: 【Doze 和 StandBy模式】 【Battery Historian 使用】 一、 Doze 和 StandBy模式 1.1 Doze低电耗模式 Android 6.0(API 级别 23)引...

AndroidAlvin的博客 815

Android爆炸程序性优化耗电优化

概念 什么是耗电优化? 有些同学可能会疑惑,所谓的耗电优化不就是减少应用的耗电,增加用户的续航时间吗?但是落到实践中,如果我们的应用需要播放视频、需要获取 GPS 信息、需要拍照,这些耗电看起来是无法避免的。 如何判断哪些耗电是可以避免,或者是需要去优化的呢?你可以看下面这张图,当用户去看耗电排行榜的时候,发现“王者荣耀”使用了 7 个多小时,这时用户对“王者荣耀”的耗电是有预期的。 假设这个时...

1 517

手机耗电统计app_程序性优化耗电优化)上篇

11/12号文档资料已全面更新!;《【阿里P7】移动互联网架构师高级教程+BAT面试题》,点击下方链接前往领取:【阿里P7】Android高级教程+BAT面试题​shimo.im本篇文章将最后从以下两个方面来介绍耗电优化:【Doze 和 StandBy模式】【Battery Historian 使用】一、 Doze 和 StandBy模式1.1 Doze低电耗模式Android 6.0(API 级...

weixin_39850167的博客 741

字节大牛耗时八个月又一力作,Android性能调优秘籍:设计思想与代码质量优化+程序性优化+开发效率优化(全网疯传)(1)

再者,高效的算法以及节省内存的数据结构如果配合优秀的符合应用场景的设计模式,那么,这些就为高性能的代码提供了实现依据。不了解,得不到精髓没得关系,今天咱来鉴赏一下全网疯传的Android性能调优秘籍…事先声明:关于以下小编所介绍的【设计思想与代码质量优化+程序性优化+开发效率优化】Android性能调优秘籍架构笔记,原版文件都有整理,需要的朋友可免费分享给你,私信我【666】立即给你回复。

goodhighting的博客 804

字节大牛耗时八个月又一力作,Android性能调优秘籍:设计思想与代码质量优化+程序性优化+开发效率优化(全网疯传)

性能代码;对已经成型的代码通过工具检查代码的问题,通过检查到的问题来指导我们进行代码的删改,这个过程被称为调优。大部分开发者都知道,如何写出高性能的代码呢?首先需要具备深厚的代码功底,这就是代码的基础,如:数据结构达到可以根据应用场景写出符合当前场景的特殊结构,比如google针对Android平台特征研发了sparseArray替代HashMap。另外,对常用的算法有自己独到的见解,leetcode上有刷个上百道题,这样差不多可以达到精通最常用的排序查找动态规划等算法的能力。

2501_90433232的博客 403

Android程序性优化——ANR,设计思想与代码质量优化+程序性优化+开发效率优化

Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,就能够达到流畅的画面所需要的60fps。(为了能够实现60fps,这意味着程序的大多数操作都必须在16ms内完成) 如果你的某个操作花费时间是超过了16ms,系统在得到VSYNC信号的时候就无法进行正常渲染,这样就发生了丢帧现象。那么用户在当前帧看到的仍然是上一帧画面。 卡顿检测 blockcanary =========================================================.

m0_66264533的博客 2806

如何进行性能优化?这篇360°全方面性能调优(含一线大厂Android端性能优化方案),帮您分分钟解决APP卡顿

什么是性能调优? 性能调优就是对计算机硬件、操作系统和应用有相当深入的了解,调节三者之间的关系,实现整个系统(包括硬件、操作系统、应用)的性能最大化,并能不断的满足现有的业务需求。 性能优化的目的 流畅(解决:卡顿) 稳定(解决:内存溢出、崩溃) 低耗损(解决:耗电快、流量大、网络慢) 小安装包(解决:APK过大) 性能优化原则: 先优化瓶颈问题; 方案简单,尽量不引入更多复杂性,尽量不降低业务体验; 满足系统性能要求即可,不引入新的bug。 为什么需要性能调优? 为了获得更好的系统性能(就是你现

xiaokangss的博客 6480

程序性优化

启动速度与执行效率优化 冷启动和热启动解析 APP 启动黑白屏解决办法 APP 卡顿问题分析及解决方案 启动速度与执行效率优化之 StrictMode …… 布局检测与优化 布局层级优化 过度渲染 …… 内存优化 内存抖动和内存泄漏 内存大户 Bitmap 内存优化 Profile 内存监测工具 Mat 大对象与泄漏检测 耗电优化 网络传输与数据存储优化网络传输与数据存储优化 APK 大小优化 屏幕适配 …… 耗电优化 Doze&Standby Battery Historian JobSchedu

小汤圆 75

60帧的丝般顺畅 - QQ飞车手游优化点滴

作者:申江涛,腾讯互娱客户端工程师 商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。 原文链接:http://wetest.qq.com/lab/view/401.html WeTest 导读 加入项目组的这段时间主要是承担性能优化这块的工作,同时也会去实现一些场景材质、特效材质以及工具。今天就性能优化这块分享一下个人的经验。 设备等级划分 设备等级划分是一切...

wetest_tencent的博客 3505

Android性能优化——360°全方面性能调优指南(含内存优化、布局优化、实战解析等)

曾经,我的前前东家系统研发部门来了一位大神,为什么叫他大神? 因为在他来公司的一年时间里只做了一件事,就是把服务器的数量缩减到了原来的一半,系统的性能指标,反而还提升了。 好的系统性能调优不仅可以提高系统性能,还能为公司节省资源。这也是我们做性能调优的最直接的目的。

大模型学习路线 803
上一篇: 程序性能优化之耗电优化(四)上篇
下一篇: 程序性能优化之网络传输与数据存储优化(五)上
Python_林一
博客等级 码龄7年 740粉丝 510原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值