Android平台如何获取CPU占用率和电池电量信息

技术背景

我们在做Android平台GB28181设备接入模块、轻量级RTSP服务模块和RTMP推流模块的时候,遇到这样的技术诉求,开发者希望把实时CPU占用、电池信息等叠加在视频界面。

获取CPU占用率

Android平台获取CPU占用情况,可以读取/proc/stat文件,解析出各个 CPU 时间参数,然后计算出 CPU 的使用率,示例代码如下:

java 复制代码
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.util.Log;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class MainActivity extends Activity {

    private Handler handler = new Handler(Looper.getMainLooper());
    private Runnable runnable;
    private long prevTotalCpuTime = 0;
    private long prevIdleCpuTime = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    // 读取 /proc/stat 文件获取 CPU 时间信息
                    BufferedReader reader = new BufferedReader(new FileReader("/proc/stat"));
                    String line = reader.readLine();
                    reader.close();

                    String[] tokens = line.split("\\s+");
                    long user = Long.parseLong(tokens[2]);
                    long nice = Long.parseLong(tokens[3]);
                    long system = Long.parseLong(tokens[4]);
                    long idle = Long.parseLong(tokens[5]);
                    long iowait = Long.parseLong(tokens[6]);
                    long irq = Long.parseLong(tokens[7]);
                    long softirq = Long.parseLong(tokens[8]);

                    long totalCpuTime = user + nice + system + idle + iowait + irq + softirq;
                    long idleCpuTime = idle;

                    // 计算 CPU 使用率
                    if (prevTotalCpuTime!= 0 && prevIdleCpuTime!= 0) {
                        long diffTotalCpuTime = totalCpuTime - prevTotalCpuTime;
                        long diffIdleCpuTime = idleCpuTime - prevIdleCpuTime;
                        float cpuUsage = ((diffTotalCpuTime - diffIdleCpuTime) / (float) diffTotalCpuTime) * 100;
                        Log.d("CPU_USAGE", "CPU Usage: " + cpuUsage + "%");
                    }

                    prevTotalCpuTime = totalCpuTime;
                    prevIdleCpuTime = idleCpuTime;

                } catch (IOException e) {
                    e.printStackTrace();
                }

                // 延迟一段时间后再次执行
                handler.postDelayed(this, 1000);
            }
        };

        // 启动获取 CPU 使用率的任务
        handler.post(runnable);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 停止任务
        handler.removeCallbacks(runnable);
    }
}

使用BatteryManager类获取电池电量

在 Android 中,可以使用BatteryManager类来获取电池电量信息。以下是具体步骤:

一、注册广播接收器

在你的 Android 组件(如 Activity 或 Service)中注册一个广播接收器来监听电池状态变化的广播。可以在onCreate方法中进行注册。

java 复制代码
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(batteryReceiver, filter);

这里创建了一个IntentFilter来指定要监听的广播为电池状态变化广播(Intent.ACTION_BATTERY_CHANGED),然后使用registerReceiver方法注册广播接收器。

二、创建广播接收器

创建一个广播接收器类来处理电池状态变化的广播,如下所示:

java 复制代码
private BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        int batteryPercentage = (level / (float)scale) * 100;
        // 这里可以根据获取到的电池电量信息进行相应的处理
    }
};

在广播接收器的onReceive方法中,可以从广播意图中获取电池电量的级别(BatteryManager.EXTRA_LEVEL)和总刻度(BatteryManager.EXTRA_SCALE),然后计算出电池电量的百分比。

三、取消注册广播接收器

在适当的时候,如组件的onDestroy方法中,取消注册广播接收器,以避免资源泄漏:

java 复制代码
unregisterReceiver(batteryReceiver);

这样,当电池状态发生变化时,你的广播接收器将会收到通知,并可以获取到电池电量信息进行相应的处理。

需要注意的是,获取电池电量信息可能需要相应的权限。在 AndroidManifest.xml 文件中添加以下权限声明:

java 复制代码
<uses-permission android:name="android.permission.BATTERY_STATS" />

场景应用

以大牛直播SDK的GB28181设备接入模块为例,摄像头实时视频采集,可以叠加上实时CPU占用和实时电量信息,亦或实时时间信息,会对场景带来很大益处,比如CPU占用率或电量信息,简单来说可以通过bitmap把文字读取下来,投递到底层比如jni层即可,非常方便。下面就文字和图片水印做个简短的技术普及。

文字水印

文字水印不再赘述,主要注意的是文字的大小、颜色、位置。

java 复制代码
private int postText1Layer(int index, int left, int top) {
    Bitmap text_bitmap = makeTextBitmap("文本水印一", getFontSize()+8,
            Color.argb(255, 200, 250, 0),
            false, 0,false);

    if (null == text_bitmap)
        return 0;

    ByteBuffer buffer = ByteBuffer.allocateDirect(text_bitmap.getByteCount());
    text_bitmap.copyPixelsToBuffer(buffer);

    libPublisher.PostLayerImageRGBA8888ByteBuffer(handle_, index, left, top, buffer, 0,
            text_bitmap.getRowBytes(), text_bitmap.getWidth(), text_bitmap.getHeight(),
            0, 0, 0, 0, 0,0);

    int ret = text_bitmap.getHeight();

    text_bitmap.recycle();

    return ret;
}

png水印

png水印,除了常规的位置需要注意之外,还涉及到logo水印的大小问题,为此,我们添加了缩放效果,可以缩放后,再贴到图层,确保以更合适的比例展示在图层期望位置。

java 复制代码
private int postPictureLayer(int index, int left, int top) {
    Bitmap bitmap = getAssetsBitmap();
    if (null == bitmap) {
        Log.e(TAG, "postPitcureLayer getAssetsBitmap is null");
        return 0;
    }

    if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
        Log.e(TAG, "postPitcureLayer config is not ARGB_8888, config:" + Bitmap.Config.ARGB_8888);
        return 0;
    }

    ByteBuffer buffer = ByteBuffer.allocateDirect(bitmap.getByteCount());
    bitmap.copyPixelsToBuffer(buffer);

    final int w = bitmap.getWidth();
    final int h = bitmap.getHeight();
    if ( w < 2 || h < 2 )
        return 0;

    int scale_w = 0, scale_h = 0, scale_filter_mode = 0;

    final float r_w = width_ - left; // 有可能负数
    final float r_h = height_ - top; // 有可能负数

    if (w > r_w || h > r_h) {
        float s_w = w;
        float s_h = h;

        // 0.85的10次方是0.19687, 缩放到0.2倍差不多了
        for ( int i = 0; i < 10; ++i)  {
            s_w *= 0.85f;
            s_h *= 0.85f;

            if (s_w < r_w && s_h < r_h )
                break;
        }

        if (s_w > r_w || s_h > r_h)
            return 0;

        // 如果小于16就算了,太小看也看不见
        if (s_w < 16.0f || s_h < 16.0f)
            return  0;

        scale_w = align((int)(s_w + 0.5f), 2);
        scale_h = align( (int)(s_h + 0.5f), 2);
        scale_filter_mode = 3;
    }

    /*
    if ( scale_w > 0 && scale_h > 0)
        Log.i(TAG, "postTextLayer scale_w:" + scale_w + ", scale_h:" + scale_h + " w:" + w + ", h:" + h) ; */

    libPublisher.PostLayerImageRGBA8888ByteBuffer(handle_, index, left, top, buffer, 0, bitmap.getRowBytes(), w, h,
            0, 0, scale_w, scale_h, scale_filter_mode,0);

    int ret = scale_h > 0 ? scale_h : bitmap.getHeight();

    bitmap.recycle();

    return ret;
}
相关推荐
此去正年少18 小时前
编写adb脚本工具对Android设备上的闪退问题进行监控分析
android·adb·logcat·ndk·日志监控
落羽凉笙18 小时前
Python基础(4)| 玩转循环结构:for、while与嵌套循环全解析(附源码)
android·开发语言·python
十幺卜入19 小时前
Unity3d C# 基于安卓真机调试日志抓取拓展包(Android Logcat)
android·c#·unity 安卓调试·unity 安卓模拟·unity排查问题
frontend_frank19 小时前
脱离 Electron autoUpdater:uni-app跨端更新:Windows+Android统一实现方案
android·前端·javascript·electron·uni-app
薛晓刚19 小时前
MySQL的replace使用分析
android·adb
DengDongQi20 小时前
Jetpack Compose 滚轮选择器
android
stevenzqzq20 小时前
Android Studio Logcat 基础认知
android·ide·android studio·日志
代码不停20 小时前
MySQL事务
android·数据库·mysql
朝花不迟暮20 小时前
使用Android Studio生成apk,卡在Running Gradle task ‘assembleDebug...解决方法
android·ide·android studio
yngsqq20 小时前
使用VS(.NET MAUI)开发第一个安卓APP
android·.net