6.6 编写第一个Chrome应用

优质
小牛编辑
132浏览
2023-12-01

在编写Chrome应用时请时刻记住,这已经不是单纯地开发浏览器扩展了,现在要编写的是一款真正的桌面程序,而Chrome只是类似CLR和Java的环境而已。

下面我们来一起编写一个计算机性能监视器。

首先来创建Manifest文件:

{
    "app": {
        "background": {
            "scripts": ["background.js"]
        }
    },
    "manifest_version": 2,
    "name": "Performance Monitor",
    "version": "1.0",
    "description": "A performance monitor to show cpu and memory status.",
    "icons": {
        "16": "images/icon16.png",
        "48": "images/icon48.png",
        "128": "images/icon128.png"
    },
    "permissions": [
        "system.cpu",
        "system.memory"
    ]
}

下面编写background.js脚本,根据6.5的内容可以直接写出如下代码:

chrome.app.runtime.onLaunched.addListener(function() {
    chrome.app.window.create('main.html', {
        'id': 'main',
        'bounds': {
            'width': 542,
            'height': 360
        },
        'resizable': false,
        'frame': 'none'
    });
});

同理,main.html中的自定义菜单我们也用上一节提到的代码,但取消最大化按钮。为绘制曲线和饼图,本例使用了一个JS图表库,Chart.js,有关Chart.js的详细内容可以通过http://www.bootcss.com/p/chart.js/查看。

下面是main.html的代码:

<html>
<head>
<title>Performance Monitor</title>
<style>
body {
    margin: 0;
    padding: 0;
    border: #EEE 1px solid;
}

#title_bar {
    -webkit-app-region: drag;
    height: 22px;
    line-height: 22px;
    font-size: 16px;
    background: #EEE;
    padding: 0 10px;
    box-sizing: border-box;
}

#title_bar a {
    -webkit-app-region: no-drag;
    display: inline-block;
    float: right;
    height: 12px;
    width: 12px;
    margin: 5px;
    border: gray 1px solid;
    box-sizing: border-box;
    border-radius: 6px;
}

#title_bar a:hover {
    background: gray;
}

#box_body {
    padding: 20px;
}

.chart {
    margin-bottom: 20px;
    font-size: 0;
}

.usage_item {
    color: gray;
    padding: 2px 0;
    font-size: 14px;
    border-bottom: #EEE 1px solid;
    margin-bottom: 4px;
}
</style>
</head>
<body>
<div id="title_bar">Performance Monitor
    <a id="close" href="#"></a>
    <a id="minimize" href="#"></a>
</div>
<div id="box_body">
<div class="usage_item">CPU Usage</div>
<div class="chart">
<canvas id="cpu_total" width="100" height="100"></canvas>
<canvas id="cpu_history" width="400" height="100"></canvas>
</div>
<div class="usage_item">Memory Usage</div>
<div class="chart">
<canvas id="mem_total" width="100" height="100"></canvas>
<canvas id="mem_history" width="400" height="100"></canvas>
</div>
</div>
<script src="control.js"></script>
<script src="Chart.js"></script>
<script src="main.js"></script>
</body>
</html>

其中的canvas用来展示数据。

control.js的代码:

var current_window = chrome.app.window.current();

document.getElementById('minimize').onclick = function(){
    current_window.minimize();
}

document.getElementById('close').onclick = function(){
    current_window.close();
}

下面来编写main.js,这个脚本用来定时获取数据并进行展示。

function getCpuUsage(callback){
    chrome.system.cpu.getInfo(function(info){
        var total = 0;
        var user = 0;
        var kernel = 0;
        for(var i=0; i<info.processors.length; i++){
            total += info.processors[i].usage.total - cpu_history.last_total[i];
            cpu_history.last_total[i] = info.processors[i].usage.total;
            user += info.processors[i].usage.user - cpu_history.last_user[i];
            cpu_history.last_user[i] = info.processors[i].usage.user;
            kernel += info.processors[i].usage.kernel - cpu_history.last_kernel[i];
            cpu_history.last_kernel[i] = info.processors[i].usage.kernel;
        }
        user = Math.round(user/total*100);
        kernel = Math.round(kernel/total*100);
        callback({user:user,kernel:kernel,total:user+kernel});
    });
}

function getMemUsage(callback){
    chrome.system.memory.getInfo(function(info){
        callback(info);
    });
}

function updateCpuHistory(){
    getCpuUsage(function(usage){
        cpu_history.user.shift();
        cpu_history.user.push(usage.user);
        cpu_history.kernel.shift();
        cpu_history.kernel.push(usage.kernel);
        cpu_history.total.shift();
        cpu_history.total.push(usage.total);
        showCpu();
    });
}

function updateMemHistory(){
    getMemUsage(function(usage){
        mem_history.used.shift();
        mem_history.used.push(Math.round((usage.capacity-usage.availableCapacity)/usage.capacity*100));
        showMem();
    });
}

function updateData(){
    updateCpuHistory();
    updateMemHistory();
}

function showCpu(){
    var history = {
        labels : (function(){for(var i=0,labels=[];i<ponits_num;labels.push(''),i++);return labels;})(),
        datasets : [
            {
                fillColor : "rgba(220,220,220,0.5)",
                data : cpu_history.total
            },
            {
                fillColor : "rgba(90,140,255,0.5)",
                data : cpu_history.kernel
            },
            {
                fillColor : "rgba(255,90,90,0.5)",
                data : cpu_history.user
            }
        ]
    };

    var now = [
        {
            value: cpu_history.total[ponits_num-1],
            color:"rgba(220,220,220,0.7)"
        },
        {
            value : 100-cpu_history.total[ponits_num-1],
            color : "rgba(220,220,220,0.3)"
        }            
    ];
    var his_ctx = document.getElementById('cpu_history').getContext("2d");
    var now_ctx = document.getElementById("cpu_total").getContext("2d");
    new Chart(his_ctx).Line(history, {scaleFontSize:4,pointDot:false,animation:false});
    new Chart(now_ctx).Pie(now, {segmentShowStroke:false,animation:false});
}

function showMem(){
    var history = {
        labels : (function(){for(var i=0,labels=[];i<ponits_num;labels.push(''),i++);return labels;})(),
        datasets : [
            {
                fillColor : "rgba(220,220,220,0.5)",
                data : mem_history.used
            }
        ]
    };

    var now = [
        {
            value: mem_history.used[ponits_num-1],
            color:"rgba(220,220,220,0.7)"
        },
        {
            value : 100-mem_history.used[ponits_num-1],
            color : "rgba(220,220,220,0.3)"
        }            
    ];
    var his_ctx = document.getElementById('mem_history').getContext("2d");
    var now_ctx = document.getElementById("mem_total").getContext("2d");
    new Chart(his_ctx).Line(history, {scaleFontSize:4,pointDot:false,animation:false});
    new Chart(now_ctx).Pie(now, {segmentShowStroke:false,animation:false});
}

function init(){
    cpu_history = {
        user: [],
        kernel: [],
        total: [],
        last_user: [],
        last_kernel: [],
        last_total: []
    };
    mem_history = {
        used: []
    };
    init_cpu_history();
}

function init_cpu_history(){
    for(var i=0; i<ponits_num; i++){
        cpu_history.user.push(0);
        cpu_history.kernel.push(0);
        cpu_history.total.push(0);
    }
    chrome.system.cpu.getInfo(function(info){
        for(var i=0; i<info.processors.length; i++){
            cpu_history.last_total.push(info.processors[i].usage.total);
            cpu_history.last_user.push(info.processors[i].usage.user);
            cpu_history.last_kernel.push(info.processors[i].usage.kernel);
        }
        init_mem_history();
    });
}

function init_mem_history(){
    for(var i=0; i<ponits_num; i++){
        mem_history.used.push(0);
    }
    updateData();
    setInterval(updateData, 1000);
}

var cpu_history, mem_history, ponits_num=20;

init();

其中getCpuUsagegetMemUsage函数分别用于获取CPU和内存的使用量。值得注意的是,Chrome返回的CPU使用量是累计使用时间,并不是获取瞬间的CPU占用量,所以需要对前后两个时间点获得的结果做差。showCpushowMem方法将获取的数据显示成图表,两个函数都是参照Chart.js文档编写的,感兴趣的读者可以自行查阅。

另外Chart.js本身有new Function声明函数的部分,由于之前介绍的CSP规则,这在Chrome应用中是不被允许的,所以本例中的Chart.js是编者改写后的,具体差异读者可以下载后自行对照。在以后编写Chrome应用引用现成的库时,可能会经常遇到由于CSP的限制而无法直接运行的情况,这需要读者拥有自行更改库代码的能力。

本例应用运行的截图如下所示:

enter image description here
Performance Monitor运行截图

本节中讲解的实例的源代码可以通过https://github.com/sneezry/chrome_extensions_and_apps_programming/tree/master/performance%20monitor下载到。