我们都知道Android 的Service一般情况下是没有界面的,也就是运行在后台看不见的地方,实际上Service也可以是可见的,这篇文章简单记录一下如何让Service可见。
在安卓8以后显示悬浮窗需要动态申请“在其他应用上显示悬浮窗”权限,所以我们首先需要申请这一个权限,在manifest文件中添加一下权限:
接下来是在APP启动时申请显示悬浮窗的权限,当检查到没有权限的时候会自动跳转到系统设置界面:
if (Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, 1);
} else {
Log.e("TAG", "H");
//TODO do something you need
}
}
接下来就是Service的创建,在onCreate()方法中创建对应的弹窗布局,并显示:
private NetSpeedTask netSpeedTask;
private Timer timer;
private NetSpeedUtil netSpeedUtil;
private LinearLayout linearLayout;
private TextView textView;
private WindowManager windowManager;
private MyHandler myHandler;
@Override
public void onCreate() {
super.onCreate();
netSpeedTask = new NetSpeedTask();
timer = new Timer();
netSpeedUtil = new NetSpeedUtil();
myHandler = new MyHandler(Looper.getMainLooper(), this);
linearLayout = (LinearLayout) LayoutInflater.from(getApplicationContext()).inflate(R.layout.layout_net_speed, null);
textView = linearLayout.findViewById(R.id.net_speed);
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(linearLayout, getParams(0));
}
private WindowManager.LayoutParams getParams(int y) {
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
mParams.width = android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
mParams.height = android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;//悬浮在应用之上
mParams.format = PixelFormat.TRANSPARENT;
mParams.gravity = Gravity.TOP | Gravity.LEFT;
mParams.x = 0;
mParams.y = y;
return mParams;
}
通过startService()开启服务后就会在手机上显示一个悬浮窗,到这里“让Service可见的工作就完成了”,接下来是网速监听部分实现:
首先我们需要一个定时器,按照一定的频率获取当前实时网速,由于在子线程是不能直接更新UI的,这里我们使用Handler实现在子线程中更新UI,进而实时显示网速,但滥用Handler容易导发生内存泄露,防止发生内存泄露的方法之一就是使用弱引用对象,创建自定义的MyHadler继承Handler:
private static class MyHandler extends Handler {
private final WeakReference weakReference;
public MyHandler(@NonNull Looper looper, NetSpeedService netSpeedService) {
super(looper);
this.weakReference = new WeakReference<>(netSpeedService);
}
@SuppressLint("SetTextI18n")
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
NetSpeedService netSpeedService = weakReference.get();
if (msg.what == 1001) {
String speed = msg.obj.toString();
netSpeedService.textView.setText("当前网速 ---->" + speed);
}
}
}
计时器:
private class NetSpeedTask extends TimerTask {
@Override
public void run() {
Message message = new Message();
message.what = 1001;
message.obj = netSpeedUtil.getNetSpeed(getApplicationInfo().uid);
myHandler.sendMessage(message);
}
}
NetSpeedUtil工具类:
/**
* 网速获取工具
*/
public class NetSpeedUtil {
private long lastTotalRxBytes = 0;
private long lastTimeStamp = 0;
public String getNetSpeed(int uid) {
long nowTotalRxBytes = getTotalRxBytes(uid);
long nowTimeStamp = System.currentTimeMillis();
long speed = ((nowTotalRxBytes - lastTotalRxBytes) * 100 / (nowTimeStamp - lastTimeStamp));//毫秒转换
lastTimeStamp = nowTimeStamp;
lastTotalRxBytes = nowTotalRxBytes;
return speed + " Kb/s";
}
public long getTotalRxBytes(int uid) {
return TrafficStats.getUidRxBytes(uid) == TrafficStats.UNSUPPORTED ? 0 : (TrafficStats.getTotalRxBytes() / 1024);//转为KB
}
}
最后在服务的onStartCommand()方法中调用:
private void startTimer() {
netSpeedTask = null;
netSpeedTask = new NetSpeedTask();
timer.schedule(netSpeedTask, 500, 500);
}