TrafficStats类实现Android监听网速
16lz
2021-01-23
一、网络状态
Android市场的逐渐降温及形成稳定,其实说明了Android的发展走向稳重成熟的阶段。
除却最开始的功能实现,到如今的用户体验至上,Android研发面临的挑战却从未冷却。基于大多数APP均是以APP为数据展示框架,实现客户与服务器数据交互,网络扮演了十分重要的角色。
网络状态,除了WiFi,数据,网络不可用等,还有网络条件不好等情形。存有标志网络速度的则能较好地标示当前网络情形。
以下则实现监听网速,展示当前网速条件。借由给客户更好的用户体验。
二、实现监听网速
TrafficStats类提供了以下方法,关于统计网速:
/** * static long getMobileRxBytes()//获取通过Mobile连接收到的字节总数,但不包含WiFi static long * getMobileRxPackets()//获取Mobile连接收到的数据包总数 static long * getMobileTxBytes()//Mobile发送的总字节数 static long * getMobileTxPackets()//Mobile发送的总数据包数 static long * getTotalRxBytes()//获取总的接受字节数,包含Mobile和WiFi等 static long * getTotalRxPackets()//总的接受数据包数,包含Mobile和WiFi等 static long * getTotalTxBytes()//总的发送字节数,包含Mobile和WiFi等 static long * getTotalTxPackets()//发送的总数据包数,包含Mobile和WiFi等 static long * getUidRxBytes(int uid)//获取某个网络UID的接受字节数 static long getUidTxBytes(int * uid) //获取某个网络UID的发送字节数 */
实现整体思路: 创建浮动窗口,用于展示网速状态;应用程序后台运行,浮动窗口依旧显示,则使用Service展示浮动窗口;在Activity中开启Service。
网速监听主体功能实现,TrafficBean.java:
package com.future.netobserverdemo.bean;import android.content.Context;import android.content.pm.ApplicationInfo;import android.content.pm.PackageManager;import android.net.TrafficStats;import android.os.Handler;import android.os.Message;import android.util.Log;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import java.io.Serializable;import java.math.BigDecimal;import java.util.Timer;import java.util.TimerTask;/** * 功能:流量Bean属性信息 * 作者:vision * 时间:2016/10/19 */public class TrafficBean implements Serializable { /** * static long getMobileRxBytes()//获取通过Mobile连接收到的字节总数,但不包含WiFi static long * getMobileRxPackets()//获取Mobile连接收到的数据包总数 static long * getMobileTxBytes()//Mobile发送的总字节数 static long * getMobileTxPackets()//Mobile发送的总数据包数 static long * getTotalRxBytes()//获取总的接受字节数,包含Mobile和WiFi等 static long * getTotalRxPackets()//总的接受数据包数,包含Mobile和WiFi等 static long * getTotalTxBytes()//总的发送字节数,包含Mobile和WiFi等 static long * getTotalTxPackets()//发送的总数据包数,包含Mobile和WiFi等 static long * getUidRxBytes(int uid)//获取某个网络UID的接受字节数 static long getUidTxBytes(int * uid) //获取某个网络UID的发送字节数 */ /** * 不支持状态【标识变量】 */ private static final int UNSUPPORT = -1; /** * 打印信息标志 */ private static final String TAG = "TrafficBean"; /** * 当前对象实例 */ private static TrafficBean instance; /** * 当前应用的uid */ static int UUID; /** * 上一次记录网络字节流 */ private long preRxBytes = 0; /** * */ private Timer mTimer = null; /** * 上下文对象 */ private Context context; /** * 消息处理器 */ private Handler handler; /** * 更新频率 */ private final int UPDATE_FREQUENCY = 1; private int times = 1; /** * 构造方法 * * @param context * @param handler * @param uid */ public TrafficBean(Context context, Handler handler, int uid) { this.context = context; this.handler = handler; this.UUID = uid; } public TrafficBean(Context context, Handler handler) { this.context = context; this.handler = handler; } /** * 获取实例对象 * * @param context * @param handler * @return */ public static TrafficBean getInstance(Context context, Handler handler) { if (instance == null) { instance = new TrafficBean(context, handler); } return instance; } /** * 获取总流量 * * @return */ public long getTrafficInfo() { long recTraffic = UNSUPPORT;//下载流量 long sendTraffic = UNSUPPORT;//上传流量 recTraffic = getRecTraffic(); sendTraffic = getSendTraffic(); if (recTraffic == UNSUPPORT || sendTraffic == UNSUPPORT) { return UNSUPPORT; } else { return recTraffic + sendTraffic; } } /** * 获取上传流量 * * @return */ private long getSendTraffic() { long sendTraffic = UNSUPPORT; sendTraffic = TrafficStats.getUidTxBytes(UUID); if (sendTraffic == UNSUPPORT) { return UNSUPPORT; } RandomAccessFile rafSend = null; String sndPath = "/proc/uid_stat/" + UUID + "/tcp_snd"; try { rafSend = new RandomAccessFile(sndPath, "r"); sendTraffic = Long.parseLong(rafSend.readLine()); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: " + e.getMessage()); sendTraffic = UNSUPPORT; } catch (IOException e) { Log.e(TAG, "IOException: " + e.getMessage()); e.printStackTrace(); } finally { try { if (rafSend != null) rafSend.close(); } catch (IOException e) { Log.w(TAG, "Close RandomAccessFile exception: " + e.getMessage()); } } return sendTraffic; } /** * 获取下载流量 * 某个应用的网络流量数据保存在系统的 * /proc/uid_stat/$UID/tcp_rcv | tcp_snd文件中 * * @return */ private long getRecTraffic() { long recTraffic = UNSUPPORT; recTraffic = TrafficStats.getUidRxBytes(UUID); if (recTraffic == UNSUPPORT) { return UNSUPPORT; } Log.i(TAG, recTraffic + " ---1"); //访问数据文件 RandomAccessFile rafRec = null; String rcvPath = "/proc/uid_stat/" + UUID + "/tcp_rcv"; try { rafRec = new RandomAccessFile(rcvPath, "r"); recTraffic = Long.parseLong(rafRec.readLine()); // 读取流量统计 } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: " + e.getMessage()); recTraffic = UNSUPPORT; } catch (IOException e) { Log.e(TAG, "IOException: " + e.getMessage()); e.printStackTrace(); } finally { try { if (rafRec != null) rafRec.close(); } catch (IOException e) { Log.w(TAG, "Close RandomAccessFile exception: " + e.getMessage()); } } Log.i("test", recTraffic + "--2"); return recTraffic; } /** * 获取当前下载流量总和 * * @return */ public static long getNetworkRxBytes() { return TrafficStats.getTotalRxBytes(); } /** * 获取当前上传流量总和 * * @return */ public static long getNetworkTxBytes() { return TrafficStats.getTotalTxBytes(); } /** * 获取当前网速 * * @return */ public double getNetSpeed() { long curRxBytes = getNetworkRxBytes(); if (preRxBytes == 0) preRxBytes = curRxBytes; long bytes = curRxBytes - preRxBytes; preRxBytes = curRxBytes; //int kb = (int) Math.floor(bytes / 1024 + 0.5); double kb = (double) bytes / (double) 1024; BigDecimal bd = new BigDecimal(kb); return bd.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 开启流量监控 */ public void startCalculateNetSpeed() { preRxBytes = getNetworkRxBytes(); if (mTimer != null) { mTimer.cancel(); mTimer = null; } if (mTimer == null) { mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { if (times == UPDATE_FREQUENCY) { Message msg = new Message(); msg.what = 1; //msg.arg1 = getNetSpeed(); msg.obj = getNetSpeed(); handler.sendMessage(msg); times = 1; } else { times++; } } }, 1000, 1000); } } /** * 停止网速监听计算 */ public void stopCalculateNetSpeed() { if (mTimer != null) { mTimer.cancel(); mTimer = null; } } /** * 获取当前应用uid * * @return */ public int getUid() { try { PackageManager pm = context.getPackageManager(); //修改 ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); return ai.uid; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return -1; }}
Service中创建浮动窗口,展示网速状态,ManagerService.java:
public class ManagerService extends Service { private static final String TAG = "ManagerService"; /** * 浮动窗口布局 */ private LinearLayout floatLL; public WindowManager.LayoutParams params; /** * 浮动串口布局参数 */ public WindowManager windowManager; public TextView floatTV; private ServiceBinder binder = new ServiceBinder(); @Override public void onCreate() { super.onCreate(); createFloatView(); } /** * 创建浮动窗口布局 */ private void createFloatView() { params = new WindowManager.LayoutParams(); windowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE); params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 设置window // type为TYPE_SYSTEM_ALERT params.format = PixelFormat.RGBA_8888;// 设置图片格式,效果为背景透明 params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作) params.gravity = Gravity.LEFT | Gravity.TOP;// 默认位置:左上角 params.width = WidgetUtils.dpToPx(getApplicationContext(), 65); params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.x = (WidgetUtils.getScreenWidth(getApplicationContext()) - params.width) / 2;// 设置x、y初始值,相对于gravity params.y = 10; // 获取浮动窗口视图所在布局 LayoutInflater inflater = LayoutInflater.from(getApplication()); floatLL = (LinearLayout) inflater.inflate(R.layout.float_layout, null); windowManager.addView(floatLL, params);// 添加mFloatLayout floatTV = (TextView) floatLL.findViewById(R.id.move_desc); floatTV.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); // 设置监听浮动窗口的触摸移动 floatTV.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标 params.x = (int) event.getRawX() - floatTV.getMeasuredWidth() / 2; Log.i(TAG, "RawX" + event.getRawX()); Log.i(TAG, "X" + event.getX()); params.y = (int) event.getRawY() - floatTV.getMeasuredHeight() / 2 - 25;// 减25为状态栏的高度 Log.i(TAG, "RawY" + event.getRawY()); Log.i(TAG, "Y" + event.getY()); windowManager.updateViewLayout(floatLL, params);// 刷新 return false; // 此处必须返回false,否则OnClickListener获取不到监听 } }); floatTV.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { Toast.makeText(getApplicationContext(), "浮动窗口被点击了!", Toast.LENGTH_SHORT).show(); } }); } public void setSpeed(String str) { floatTV.setText(str); } @Override public void onDestroy() { super.onDestroy(); if (floatLL != null && windowManager != null) { windowManager.removeView(floatLL);// 移除悬浮窗口 } startService(new Intent(this, ManagerService.class)); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY_COMPATIBILITY; } @Nullable @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); } public class ServiceBinder extends Binder { public ManagerService getService() { return ManagerService.this; } }}
Activity中绑定service,实现功能:
public class MainActivity extends AppCompatActivity { /** * Activity管理器 */ private ActivityManager activityManager; /** * 浮动图标 */ private TextView moveDesc; /** * 消息处理器 */ private Handler handler; /** * 流量信息对象 */ private TrafficBean trafficBean; /** * 服务 */ private ManagerService service; /** * 标记信息 */ private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); moveDesc = (TextView) findViewById(R.id.tv); try { handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { moveDesc.setText(msg.obj + "kb/s"); if (service != null) { service.setSpeed(msg.obj + "kb/s"); } } super.handleMessage(msg); } }; trafficBean = new TrafficBean(this, handler, 12580); trafficBean.startCalculateNetSpeed(); } catch (Exception e) { e.printStackTrace(); } Log.d(TAG, "总流量 = " + trafficBean.getTrafficInfo()); Intent intent = new Intent(MainActivity.this, ManagerService.class); bindService(intent, conn, Context.BIND_AUTO_CREATE); } private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder iBinder) { service = ((ManagerService.ServiceBinder) iBinder).getService(); } @Override public void onServiceDisconnected(ComponentName name) { service = null; } }; @Override protected void onDestroy() { super.onDestroy(); trafficBean.stopCalculateNetSpeed(); unbindService(conn); }}
展示效果:
三、细节注意
1,使用与创建对应,onCreate()中绑定,onDestory()中解绑;
2,清单文件配置权限及服务和广播接收者。
源码
上班族如何安排时间才能每天都早起,读书,跑步,冥想?
1,早起毁一天 -- 任何依靠自制力的事情,最后都会失败; 2,心态陷阱 -- 贪多,为了读书而读书,为了浏览而浏览,促进心理焦虑; ---- 通过某种行为,达到某种目的----- 目的 3,目标--手段--执行意图 --- 同样的效果,更节省意志力 ---- 爱好他,比强压自己学习更好,好太多 4,为了让自己幸福 -- 帮助自己去完成更美好的事情,同时赞美自己 ---- 自我增强存在价值更多相关文章
- android 下拉窗口菜单的实现(popupwindow)
- Android——弹出窗口中实现时间选择,文本输入,遇到Popupwindow不穿
- Android应用程序窗口View的measure过程
- android 自定义dialog,窗口动画,
- Android之浮动小窗口
- 简单几段代码实现窗口抖动
- Android 实现下载限速跟下载网速统计
- Android开发窗口实现半透明效果
- Layout___一行多列填充父窗口