Android原生限制后台进程使用CPU机制(AndroidU)

一:背景

  系统资源是有限的,后台应用如果长时间使用CPU,可能会影响前台应用,造成前台应用卡顿(抢占CPU资源)。如何合理的分配系统资源、限制后台应用、把资源留给前台应用是个必须解决的事情。最近梳理代码,发现Google做了一部分这个事情,在此记录一下。

二:功能实现

2.1 ActivityManagerService.finishBooting

  系统开机完成,ActivitymanagerService.finishBooting函数会被调用(具体调用逻辑这里就不详细列出来了,有兴趣的可以跟下调用栈),在finishBooting会发送CHECK_EXCESSIVE_POWER_USE_MSG message来开始监听之旅。在接收到该message消息时,会检查应用的CPU使用情况,并以5分钟的时间间隔来不断循环检查。

    final void finishBooting() {
        ...
        synchronized (this) {
            ...
            // Start looking for apps that are abusing wake locks.
            Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
            mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
            ...
        }
    }

    final class MainHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            ...
            case CHECK_EXCESSIVE_POWER_USE_MSG: {
                //检查应用的CPU使用情况
                checkExcessivePowerUsage();
                removeMessages(CHECK_EXCESSIVE_POWER_USE_MSG);
                Message nmsg = obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
                //以POWER_CHECK_INTERVAL时间间隔轮询
                sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
            } break;
            ...
        }
    }

2.2 ActivityManagerService.checkExcessivePowerUsage

遍历LRU list所有进程,针对优先级低于PROCESS_STATE_HOME的进程,根据进程状态改变距离现在的时间,设置不同的限制阈值,然后调用updateAppProcessCpuTimeLPr继续检查流程。

    private void checkExcessivePowerUsage() {
        //更新CPU统计信息
        updateCpuStatsNow();

        ...
        synchronized (mProcLock) {
            //mLastPowerCheckUptime为上一次检查时间,第一次检查时,该值为0
            final boolean doCpuKills = mLastPowerCheckUptime != 0;
            final long curUptime = SystemClock.uptimeMillis();
            //从上次检查到现在的时间
            final long uptimeSince = curUptime - mLastPowerCheckUptime;
            //把当前时间赋值给mLastPowerCheckUptime
            mLastPowerCheckUptime = curUptime;
            //遍历LRU list中所有进程
            mProcessList.forEachLruProcessesLOSP(false, app -> {
                //如果是空进程,直接return
                if (app.getThread() == null) {
                    return;
                }
                //优先级低于PROCESS_STATE_HOME的进程
                if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
                    //检查阈值
                    int cpuLimit;
                    //进程开始变成非重要进程到现在的时间
                    long checkDur = curUptime - app.mState.getWhenUnimportant();
                    //根据进程状态改变的时间,设置不同的阈值
                    if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {//5min
                        cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;//25
                    } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 2)
                            || app.mState.getSetProcState() <= ActivityManager.PROCESS_STATE_HOME) {//10min
                        cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2;//25
                    } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL * 3)) {//15min
                        cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3;//10
                    } else {
                        cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4;//2
                    }

                    //继续检查流程
                    updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
                    ...
                }
            });
        }
    }

2.3 ActivityManagerService.updateAppProcessCpuTimeLPr

通过ProcessProfileRecord,计算出进程已经使用CPU的时间,调用checkExcessivePowerUsageLPr函数来检查是否超过限制阈值,如果超过则杀进程

    private void updateAppProcessCpuTimeLPr(final long uptimeSince, final boolean doCpuKills,
            final long checkDur, final int cpuLimit, final ProcessRecord app) {
        synchronized (mAppProfiler.mProfilerLock) {
            final ProcessProfileRecord profile = app.mProfile;
            //通过ProcessProfileRecord获取当前CPU使用时间
            final long curCpuTime = profile.mCurCpuTime.get();
            //通过ProcessProfileRecord获取上次CPU使用时间
            final long lastCpuTime = profile.mLastCpuTime.get();
            if (lastCpuTime > 0) {
                //该进程已经使用CPU的时间
                final long cpuTimeUsed = curCpuTime - lastCpuTime;
                //检查进程CPU使用时间是否超过阈值
                if (checkExcessivePowerUsageLPr(uptimeSince, doCpuKills, cpuTimeUsed,
                            app.processName, app.toShortString(), cpuLimit, app)) {
                    mHandler.post(() -> {
                        synchronized (ActivityManagerService.this) {
                            //如果是空进程或者优先级高于PROCESS_STATE_HOME的进程
                            if (app.getThread() == null
                               || app.mState.getSetProcState() < ActivityManager.PROCESS_STATE_HOME) {
                                   return;
                            }
                            //杀进程
                            app.killLocked("excessive cpu " + cpuTimeUsed + " during "
                                    + uptimeSince + " dur=" + checkDur + " limit=" + cpuLimit,
                                    ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
                                    ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
                                    true);
                        }
                    });
                    profile.reportExcessiveCpu();
                }
            }

            profile.mLastCpuTime.set(curCpuTime);
        }
    }

2.4 ActivityManagerService.checkExcessivePowerUsageLPr

检查CPU使用时间除以距离上次检查的时间的百分比是否超过阈值

private boolean checkExcessivePowerUsageLPr(final long uptimeSince, boolean doCpuKills,
            final long cputimeUsed, final String processName, final String description,
            final int cpuLimit, final ProcessRecord app) {
        ...
        // If the process has used too much CPU over the last duration, the
        // user probably doesn't want this, so kill!
        if (doCpuKills && uptimeSince > 0) {
            //如果CPU使用时间超过阈值(CPU使用时间除以距离上次检查的时间,cpuLimit可以看做百分比)
            if (((cputimeUsed * 100) / uptimeSince) >= cpuLimit) {
                mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName,
                        uptimeSince, cputimeUsed);
                app.getPkgList().forEachPackageProcessStats(holder -> {
                    final ProcessState state = holder.state;
                    FrameworkStatsLog.write(
                            FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED,
                            app.info.uid,
                            processName,
                            state != null ? state.getPackage() : app.info.packageName,
                            holder.appVersion);
                });
                return true;
            }
        }
        return false;
    }

三:总结

1 该功能只针对优先级低于PROCESS_STATE_HOME的非空进程做限制,对其他进程没有任何限制。

2. 进程越早变成低优先级进程(优先级低于PROCESS_STATE_HOME),限制阈值就越小,也就是进程越容易被杀。

3. 限制措施是kill进程,这种查杀方式在进程被杀后,允许进程通过service等手段自启。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在JavaScript中调用Android原生方法需要使用JavaScript与原生Android之间的交互技术。以下是一些方法: 1. 使用WebView执行JavaScript代码 可以使用WebView在Android应用程序中执行JavaScript代码。WebView对象提供了一个方法,可以通过该方法将JavaScript代码传递给WebView并执行它。在JavaScript代码中,可以使用window.prompt来调用Android方法。例如: ``` window.prompt("methodName:param1:param2", ""); ``` 在Android应用程序中,可以在WebViewClient的shouldOverrideUrlLoading方法中截取这个请求,解析参数,并调用相应的方法。例如: ```java public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("javascript:")) { String[] data = url.substring("javascript:".length()).split(":"); String methodName = data[0]; String param1 = data[1]; String param2 = data[2]; // call the Android method with the given name and parameters callMethod(methodName, param1, param2); return true; } return false; } ``` 2. 使用JavaScriptInterface接口 可以在Android应用程序中使用JavaScriptInterface接口来让JavaScript代码直接调用Android方法。首先,需要在WebView对象上启用JavaScript。然后,需要创建一个Java对象来处理JavaScript传递的调用请求。在Java对象中,需要使用@JavascriptInterface注解来标记要暴露给JavaScript调用的方法。例如: ```java public class MyJavaScriptInterface { private Context mContext; public MyJavaScriptInterface(Context context) { mContext = context; } @JavascriptInterface public void callNativeMethod(String methodName, String param1, String param2) { // call the Android method with the given name and parameters callMethod(methodName, param1, param2); } } ``` 然后,在Android应用程序中,需要将这个Java对象添加到WebView对象中,并使用addJavascriptInterface方法将它暴露给JavaScript代码。例如: ```java MyJavaScriptInterface jsInterface = new MyJavaScriptInterface(this); mWebView.addJavascriptInterface(jsInterface, "MyApp"); String javascript = "function callNativeMethod(methodName, param1, param2) {" + " MyApp.callNativeMethod(methodName, param1, param2);" + "}"; mWebView.loadUrl("javascript:" + javascript); ``` 以上是两种在JavaScript中调用Android原生方法的方法。当然,具体的实现方案需要根据具体的需求和场景进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值