Android学习笔记之ColorMatrix、图像处理

2024-06-13 03:18

本文主要是介绍Android学习笔记之ColorMatrix、图像处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

飞呀

    • 1.色彩矩阵
      • 1.1改变偏移量
      • 1.2改变颜色系数
    • 2.使用ColorMatrix来改变图片的色光属性
      • 2.1 色调
      • 2.2 饱和度
      • 2.3 亮度
      • 2.4 效果混合postConcat
      • 2.5 示例
    • 3.使用颜色矩阵来改变图片
      • 3.1几种图片颜色矩阵处理效果
        • 3.1.1 灰度效果
        • 3.1.2 图像颜色反转
        • 3.1.3 怀旧效果
        • 3.1.4 去色效果
        • 3.1.5 高饱和度
    • 4.像素点分析
      • 4.1常用图像像素点处理效果
        • 4.1.1 底片效果
        • 4.1.2 浮雕效果

1.色彩矩阵

  图片是由点阵和颜色值组成的,点阵是一个包含像素的举证,每个元素对应着图片的一个像素。当我知道颜色和矩阵有关时比较惊讶,大学时学的线性代数在这里用上了。
  在Android中处理图像的色彩效果的类是颜色矩阵ColorMatrix。颜色矩阵是一个4x5的数字矩阵。对于每个像素点,都有一个颜色分量矩阵用来保存颜色的RGBA值。
A = { a b c d e f g h i j k l m n o p q r s t } C = { R G B A 1 } A= \left\{ \begin{matrix} a & b & c & d & e\\ f & g & h & i & j \\ k & l & m & n & o \\ p & q & r & s & t \end{matrix} \right\} C= \left\{ \begin{matrix} R \\ G \\ B \\ A \\ 1 \end{matrix} \right\} A=afkpbglqchmrdinsejotC=RGBA1
  矩阵A是一个4x5的颜色矩阵,在Android中使用一维数组的形式来存储[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t]。矩阵C是一个颜色矩阵分量。
  使用矩阵乘法运算AC来处理颜色分量矩阵,矩阵A、C,乘法运算过程,如下。
R = { R 1 G 1 B 1 A 1 } = A C { a b c d e f g h i j k l m n o p q r s t } { R G B A 1 } = { a R b G c B d A e f R g G h B i A j R k G l B m A n o R p G q B r A s t } = { R 1 G 1 B 1 A 1 } R =\left\{ \begin{matrix} R1\\G1 \\ B1 \\ A1 \end{matrix} \right\} =AC \\ \left\{ \begin{matrix} a & b & c & d & e\\ f & g & h & i & j \\ k & l & m & n & o \\ p & q & r & s & t \end{matrix} \right\} \left\{ \begin{matrix} R\\G \\ B \\ A \\1 \end{matrix} \right\} =\left\{ \begin{matrix} aR & bG & cB & dA & e\\ fR & gG & hB & iA & j \\ Rk & Gl & Bm & An & o \\ Rp & Gq & Br & As & t \end{matrix} \right\} =\left\{ \begin{matrix} R1\\G1 \\ B1 \\ A1 \end{matrix} \right\} R=R1G1B1A1=ACafkpbglqchmrdinsejotRGBA1=aRfRRkRpbGgGGlGqcBhBBmBrdAiAAnAsejot=R1G1B1A1
R1 = a x R + b x G + c x B + d x A + e;
G1 = f x R + g x G + h x B + i x A + j;
B1 = k x R + l x G + m x B + n x A +o;
A1 = p x R + q x G + r x B + s x A + t;
A = { a b c d e f g h i j k l m n o p q r s t } A= \left\{ \begin{matrix} a & b & c & d & e\\ f & g & h & i & j \\ k & l & m & n & o \\ p & q & r & s & t \end{matrix} \right\} A=afkpbglqchmrdinsejot

  • 第一行的abcde值用来决定新的颜色值中的R——红色
  • 第二行的fghij值用来决定新的颜色值中的G——绿色
  • 第三行的klmno值用来决定新的颜色值中的B——蓝色
  • 第四行的pqrst值用来决定新的颜色值中的A——透明色

第五列ejot值分别用来决定每个分量中的offset,即偏移量

R1 = a x R + b x G + c x B + d x A + e,令a=1,b、c、d、e都等于0,结果为R1 =R。构造一个矩阵,如下。
A = { 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 } A= \left\{ \begin{matrix} 1 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 \\ 0 &0 & 1& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} A=10000100001000010000
  如果把这个矩阵代入R1=AC,R1=R。这个矩阵通常被用来作为初始颜色矩阵来使用,不会对原有颜色值进行任何变化。
  改变颜色的两种方法:一是直接改变颜色的offset,二是改变对应RGBA的系数。

1.1改变偏移量

A = { 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 } A = { 1 0 0 0 100 0 1 0 0 100 0 0 1 0 0 0 0 0 1 0 } A= \left\{ \begin{matrix} 1 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 \\ 0 &0 & 1& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} A= \left\{ \begin{matrix} 1 & 0 & 0 & 0 & 100\\ 0 & 1 & 0 & 0 & 100 \\ 0 &0 & 1& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} A=10000100001000010000A=100001000010000110010000
  红色、绿色分量增加了100,红绿混合会得到黄色,最后图片颜色偏黄。

1.2改变颜色系数

A = { 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 } A = { 1 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 1 0 } A= \left\{ \begin{matrix} 1 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 \\ 0 &0 & 1& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} A= \left\{ \begin{matrix} 1 & 0 & 0 & 0 & 0\\ 0 & 2 & 0 & 0 & 0\\ 0 &0 & 1& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} A=10000100001000010000A=10000200001000010000
  改变的绿色,图片颜色偏绿,后面会有相应的展示。

2.使用ColorMatrix来改变图片的色光属性

2.1 色调

  setRotate(int axis, float degrees),axis使用0、1、2来代表R、G、B三种颜色。degrees处理的值。

        ColorMatrix hueMatrix = new ColorMatrix();hueMatrix.setRotate(0, hue);hueMatrix.setRotate(1, hue);hueMatrix.setRotate(2, hue);

2.2 饱和度

  setSaturation(float sat),当sat为0时,图片就变成黑白图片了。

        ColorMatrix saturationMatrix = new ColorMatrix();saturationMatrix.setSaturation(saturation);

2.3 亮度

  setScale(float rScale, float gScale, float bScale,float aScale),当三原色以相同比例进行混合时,就会显示出白色,系统正是使用这个原理来改变一个图像的亮度。当亮度为0时,图片就变为黑色了。

        ColorMatrix lumMatrix = new ColorMatrix();lumMatrix.setScale(lum, lum, lum, 1);

2.4 效果混合postConcat

  将上面三种ColorMatrix 叠加,需要通过ColorMatrix 的postConcat方法。

        ColorMatrix imageMatrix = new ColorMatrix();imageMatrix.postConcat(hueMatrix);imageMatrix.postConcat(saturationMatrix);imageMatrix.postConcat(lumMatrix);

2.5 示例

  通过三个seekbar来控制图片效果的变化。
seekbar的监听事件

//MID_VALUE =127        seekbar最大值255
// float mHue = 0;
// float mStauration = 1.0f;
// float mLum = 1.0f;	@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {int id = seekBar.getId();switch (id) {case R.id.sb_1:mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;break;case R.id.sb_2:mStauration = progress * 1.0F / MID_VALUE;break;case R.id.sb_3:mLum = progress * 1.0F / MID_VALUE;break;}iv.setImageBitmap(handleImageEffect(bm, mHue, mStauration, mLum));}

设置图像矩阵的代码

    public Bitmap handleImageEffect(Bitmap bm,float hue,float saturation,float lum) {Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bmp);Paint paint = new Paint();ColorMatrix hueMatrix = new ColorMatrix();hueMatrix.setRotate(0, hue);hueMatrix.setRotate(1, hue);hueMatrix.setRotate(2, hue);ColorMatrix saturationMatrix = new ColorMatrix();saturationMatrix.setSaturation(saturation);ColorMatrix lumMatrix = new ColorMatrix();lumMatrix.setScale(lum, lum, lum, 1);ColorMatrix imageMatrix = new ColorMatrix();imageMatrix.postConcat(hueMatrix);imageMatrix.postConcat(saturationMatrix);imageMatrix.postConcat(lumMatrix);paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));canvas.drawBitmap(bm, 0, 0, paint);return bmp;}

  设置好颜色矩阵后,需要使用paint类的setColorFilter方法,将imageMatrix构造的ColorMatrixColorFilter对象传递进去。
  Android系统不允许直接修改原图,必须通过原图创建一个同样大小的Bitmap,并将原图绘制到新的bitmap中。直接修改原图会报错java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor。
在这里插入图片描述

3.使用颜色矩阵来改变图片

  接下来我们通过直接修改矩阵中的值来改变图片的显示效果。
布局文件xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:id="@+id/mm_iv"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="2" /><GridLayoutandroid:id="@+id/mm_gl"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="3"android:columnCount="5"android:rowCount="4" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginStart="10dp"android:layout_marginEnd="10dp"android:orientation="horizontal"><Buttonandroid:id="@+id/mm_but_change"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="改变" /><Buttonandroid:id="@+id/mm_but_reset"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginStart="20dp"android:layout_weight="1"android:text="重置" /></LinearLayout></LinearLayout>

  动态创建edittext,添加到gridlayout

    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_mmactivity);mm_but_change = findViewById(R.id.mm_but_change);mm_but_reset = findViewById(R.id.mm_but_reset);mm_iv = findViewById(R.id.mm_iv);mm_gl = findViewById(R.id.mm_gl);bm = BitmapFactory.decodeResource(getResources(), R.mipmap.dgdfg);mm_iv.setImageBitmap(bm);mm_gl.post(new Runnable() {@Overridepublic void run() {etWidth = mm_gl.getWidth() / 5;etHeight = mm_gl.getHeight() / 4;addEts();initMatrix();}});}/*** 添加edittext*/private void addEts() {for (int i = 0; i < ets.length; i++) {EditText editText = new EditText(this);editText.setGravity(Gravity.CENTER);ets[i] = editText;mm_gl.addView(editText, etWidth, etHeight);}}/*** 设置edittext的值*/private void initMatrix() {for (int i = 0; i < ets.length; i++) {if (i % 6 == 0) {ets[i].setText(1 + "");} else {ets[i].setText(0 + "");}}}

  获得修改后的EditText值,将矩阵值设置给颜色矩阵。

    private void getMatrix() {for (int i = 0; i < 20; i++) {colorM[i] = Float.valueOf(ets[i].getText().toString());}}private void setImageMatrix() {Bitmap bitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);ColorMatrix colorMatrix = new ColorMatrix();colorMatrix.set(colorM);Canvas canvas = new Canvas(bitmap);Paint paint = new Paint();paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));canvas.drawBitmap(bm, 0, 0, paint);mm_iv.setImageBitmap(bitmap);}

  最后,设置点击事件,重置是将图片还原,改变是将现有的矩阵作用到图片。

        mm_but_change.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {getMatrix();setImageMatrix();}});mm_but_reset.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {initMatrix();getMatrix();setImageMatrix();}});

在这里插入图片描述

3.1几种图片颜色矩阵处理效果

3.1.1 灰度效果

{ 0.33 0.59 0.11 0 0 0.33 0.59 0.11 0 0 0.33 0.59 0.11 0 0 0 0 0 1 0 } \left\{ \begin{matrix} 0.33 & 0.59 & 0.11 & 0 & 0\\ 0.33 & 0.59 & 0.11 & 0 & 0 \\ 0.33 &0.59 & 0.11& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 0.330.330.3300.590.590.5900.110.110.11000010000
在这里插入图片描述

3.1.2 图像颜色反转

{ − 1 0 0 1 1 0 − 1 0 1 1 0 0 − 1 1 1 0 0 0 1 0 } \left\{ \begin{matrix} -1 & 0 & 0 & 1 & 1\\ 0 & -1 & 0 & 1 & 1 \\ 0 &0 & -1& 1& 1 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 10000100001011111110
在这里插入图片描述

3.1.3 怀旧效果

{ 0.393 0.769 0.189 0 0 0.349 0.686 0.168 0 0 0.272 0.534 0.131 0 0 0 0 0 1 0 } \left\{ \begin{matrix} 0.393 & 0.769 & 0.189 & 0 &0\\ 0.349 &0.686 & 0.168 & 0 & 0\\ 0.272 &0.534 & 0.131& 0& 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 0.3930.3490.27200.7690.6860.53400.1890.1680.131000010000
在这里插入图片描述

3.1.4 去色效果

{ 1.5 1.5 1.5 0 − 1 1.5 1.5 1.5 0 − 1 1.5 1.5 1.5 0 − 1 0 0 0 1 0 } \left\{ \begin{matrix} 1.5 & 1.5 & 1.5 & 0 &-1\\ 1.5 &1.5 & 1.5 & 0 & -1\\ 1.5 &1.5 & 1.5& 0& -1 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 1.51.51.501.51.51.501.51.51.5000011110
在这里插入图片描述

3.1.5 高饱和度

{ 1.438 − 0.112 − 0.016 0 − 0.03 − 0.062 1.378 − 0.016 0 0.05 − 0.062 − 0.122 1.483 0 − 0.02 0 0 0 1 0 } \left\{ \begin{matrix} 1.438 & -0.112 & -0.016 & 0 &-0.03\\ -0.062 &1.378 & -0.016 & 0 & 0.05\\ -0.062 &-0.122 & 1.483& 0& -0.02 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right\} 1.4380.0620.06200.1121.3780.12200.0160.0161.483000010.030.050.020
在这里插入图片描述

4.像素点分析

  通过改变每个像素点argb值,来达到修改图片。android系统提供了Bitmap.getPixels()方法来获取这个bitmap的像素点。

bm.getPixels(int[] pixels, int offset, int stride,int x, int y, int width, int height)
pixels:接收每个点的颜色
offset:写入到pixels中的第一个像素索引值
stride:pixels中的行间距
x:从位图中读取的第一个像素的X坐标值
y:从位图中读取的第一个像素的y坐标值
width:从每一行中读取的像素宽度
height:读取的行数

一般使用

bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);

具体代码

 public Bitmap handleImagePixelsOldPhoto(Bitmap bm) {//由于不能修改原始图片,需要创建一个图片的副本bmp Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(),Bitmap.Config.ARGB_8888);int width = bm.getWidth();int height = bm.getHeight();int color = 0;int r, g, b, a, r1, g1, b1;//初始化像素数组的大小int[] oldPx = new int[width * height];int[] newPx = new int[width * height];//获取像素点bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);for (int i = 0; i < width * height; i++) {color = oldPx[i];a = Color.alpha(color);r = Color.red(color);g = Color.green(color);b = Color.blue(color);//效果的公式,用来改变像素点的颜色r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b);g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b);b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b);//最大值255if (r1 > 255) {r1 = 255;}if (g1 > 255) {g1 = 255;}if (b1 > 255) {b1 = 255;}newPx[i] = Color.argb(a, r1, g1, b1);}bmp.setPixels(newPx, 0, width, 0, 0, width, height);return bmp;}

在这里插入图片描述

4.1常用图像像素点处理效果

4.1.1 底片效果

处理方法都是前辈们研究出来的,这里我们只是使用。贴出代码和效果图。

    public static Bitmap handleImageNegative(Bitmap bm) {int width = bm.getWidth();int height = bm.getHeight();int color;int r, g, b, a;Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);int[] oldPx = new int[width * height];int[] newPx = new int[width * height];bm.getPixels(oldPx, 0, width, 0, 0, width, height);for (int i = 0; i < width * height; i++) {color = oldPx[i];r = Color.red(color);g = Color.green(color);b = Color.blue(color);a = Color.alpha(color);r = 255 - r;g = 255 - g;b = 255 - b;if (r > 255) {r = 255;} else if (r < 0) {r = 0;}if (g > 255) {g = 255;} else if (g < 0) {g = 0;}if (b > 255) {b = 255;} else if (b < 0) {b = 0;}newPx[i] = Color.argb(a, r, g, b);}bmp.setPixels(newPx, 0, width, 0, 0, width, height);return bmp;}

在这里插入图片描述

4.1.2 浮雕效果

直接贴代码。

 public static Bitmap handleImagePixelsRelief(Bitmap bm) {Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(),Bitmap.Config.ARGB_8888);int width = bm.getWidth();int height = bm.getHeight();int color = 0, colorBefore = 0;int a, r, g, b;int r1, g1, b1;int[] oldPx = new int[width * height];int[] newPx = new int[width * height];bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);for (int i = 1; i < width * height; i++) {colorBefore = oldPx[i - 1];a = Color.alpha(colorBefore);r = Color.red(colorBefore);g = Color.green(colorBefore);b = Color.blue(colorBefore);color = oldPx[i];r1 = Color.red(color);g1 = Color.green(color);b1 = Color.blue(color);r = (r - r1 + 127);g = (g - g1 + 127);b = (b - b1 + 127);if (r > 255) {r = 255;}if (g > 255) {g = 255;}if (b > 255) {b = 255;}newPx[i] = Color.argb(a, r, g, b);}bmp.setPixels(newPx, 0, width, 0, 0, width, height);return bmp;}

在这里插入图片描述

这篇关于Android学习笔记之ColorMatrix、图像处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1056128

相关文章

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Android 实现一个隐私弹窗功能

《Android实现一个隐私弹窗功能》:本文主要介绍Android实现一个隐私弹窗功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 效果图如下:1. 设置同意、退出、点击用户协议、点击隐私协议的函数参数2. 《用户协议》、《隐私政策》设置成可点击的,且颜色要区分出来res/l

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel