如何在TrafficStats中获取正确的发送和接收字节数?

12

我的应用程序试图计算通过WiFi / LAN和移动数据连接发送和接收的字节数。为此,我在某个时间点获取TrafficStats计数器的值,并将其从下次检查时的值中减去。

// get current values of counters
long currentMobileTxBytes = TrafficStats.getMobileTxBytes();
long currentMobileRxBytes = TrafficStats.getMobileRxBytes();
long totalTxBytes = TrafficStats.getTotalTxBytes();
long totalRxBytes = TrafficStats.getTotalRxBytes();

// to get mobile data count, subtract old from current
long currentMobileSent = currentMobileTxBytes - oldMobileTxBytes;
long currentMobileReceived = currentMobileRxBytes - oldMobileRxBytes;

// to get WiFi/LAN data count, subtract total from mobile
long currentNetworkSent = totalTxBytes - currentMobileTxBytes;
long currentNetworkReceived = totalRxBytes - currentMobileRxBytes;

我认为上述算法是合理的,但是我不确定如何检查这些计数器的准确性。例如,当我通过WiFi上传一个2.7MB的文件到Dropbox时,我得到的currentMobileSent值约为10MB。即使在下一次检查之前没有浏览网页,我仍然会得到非零值,表明我在等待期间接收了一些数据字节。

有没有办法让我检查TrafficStats是如何得出这些数字的?我知道除了我的浏览器之外,可能还有其他在后台运行连接到互联网的应用程序,但是从2.7MB到10MB似乎是一个巨大的跳跃——甚至有一次“接收”了90MB,而我什么都没做。或者我计算发送和接收字节的方式有问题吗?


TrafficStats 包括 TCP/IP 数据包头的开销。它们 物理上 是传输的总字节数。请参见此处。 - Mateen Ulhaq
1个回答

15

来自TechRepublic

  1. 在Eclipse中创建一个新的Android项目。记住,要使用TrafficStats类,您必须针对Android 2.2(Froyo)或更高版本进行目标API。

  2. /res/layout文件夹中,我们将创建一个activity_main.xml资源。对于这个项目,我们只是在垂直堆叠的线性布局中使用一系列文本视图。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical">

   <TextView
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:paddingBottom="20dip"
       android:text="Traffic Stats Demo"
       android:textSize="16sp"
       android:textStyle="bold" />

   <TextView
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="Transmit Bytes"
       android:textColor="#00ff00"
       android:textSize="14sp" />

   <TextView
       android:id="@+id/TX"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="0"
       android:textSize="14sp" />

   <TextView
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="Receive Bytes"
       android:textColor="#ff0000"
       android:textSize="14sp" />

   <TextView
       android:id="@+id/RX"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
       android:text="0"
       android:textSize="14sp" />
</LinearLayout>

我们已经有了布局,现在可以继续操作/src文件夹。通过扩展Activity/AppCompatActivity类来创建MainActivity.java。同时,让我们声明三个私有类变量。

MainActivity.java

package com.authorwjf;

import android.app.Activity;
import android.app.AlertDialog;
import android.net.TrafficStats;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;

public class Main extends Activity {
    private Handler mHandler = new Handler();
    private long mStartRX = 0;
    private long mStartTX = 0;
}
我们将使用onCreate重写方法来初始化我们的私有变量,并在UI线程上安排一个回调。请注意检查TrafficStats.UNSUPPORTED枚举类型。虽然我在TrafficStats类方面的经验非常顺利,但官方的Google文档指出,一些设备可能不支持此类报告,当出现这种情况时,调用将返回上述值。因此,在这里展示了代码防御性编写的好处。 MainActivity.java
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mStartRX = TrafficStats.getTotalRxBytes();
    mStartTX = TrafficStats.getTotalTxBytes();

    if (mStartRX == TrafficStats.UNSUPPORTED || mStartTX == TrafficStats.UNSUPPORTED) {
        AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle("Uh Oh!");
        alert.setMessage("Your device does not support traffic stat monitoring.");
        alert.show();
    } else {
        mHandler.postDelayed(mRunnable, 1000);
    }
}

最后但并非不重要的是,我们需要更新显示并重新安排可运行项。

MainActivity.java

private final Runnable mRunnable = new Runnable() {
    public void run() {
        TextView RX = (TextView) findViewById(R.id.RX);
        TextView TX = (TextView) findViewById(R.id.TX);
        long rxBytes = TrafficStats.getTotalRxBytes() - mStartRX;
        RX.setText(Long.toString(rxBytes));
        long txBytes = TrafficStats.getTotalTxBytes() - mStartTX;
        TX.setText(Long.toString(txBytes));
        mHandler.postDelayed(mRunnable, 1000);
    }
};

@Tom,Mr.Tom已经接受了这个答案,这意味着这个答案对他有用。 - G M Ramesh
1
@Ramesh 抱歉,我本来想添加一条评论来解释我的负评。我有两个问题-首先,我将他的问题解释为关于他的应用程序统计数据,但现在我认为我错了(所以我已经删除了负评)。但是,我也看到这些API存在许多错误和问题-因此他的评论(2个赞)可能是对此的反应。实际上,有足够的问题,我认为警告人们TrafficStats是明智的。(糟糕,它不允许我删除我的负评-抱歉)。 - Tom
5
引用非自己的内容时需要注明来源。我不会立即删除你的回答,因为它是这个问题的唯一答案,但如果你故意再次删除归属链接,拒绝承认你的来源,我将删除你的回答。 - BoltClock
我成功获取了WiFi数据,但无法获取准确的移动数据,你能帮忙吗? - Sunil Chaudhary
1
@Matthew Quiros,"WiFi/LAN数据似乎总是太大了",你找到获取准确的WiFi/LAN数据的正确方法了吗? - thecr0w
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接