文章介绍了如何在Service中显示悬浮框,在Service中弹出Dialog,在Service中做耗时的轮询操作;

背景需求:

公司的项目现在的逻辑是这样的:发送一个指令,然后3秒一次轮询去查询这个指令是否成功,在这期间界面有遮盖不可操作;

然后需求改了,因为遮盖界面不让用户操作体验不好;现在的需求是:这个轮询查询指令是否成功的操作在后端进行,界面有一个悬浮框用来提示用户正在查询指令;

嗯,在后端查询指令让用户无感,可以用Service实现,然后悬浮框使用WindowMessage实现,轮询查询指令使用Handler或IntentService实现;大致就是这样,先来个Demo;

1、开启服务:需要的信息由Activity使用Intent传递给Service;

Intent intent = new Intent(this,MyService.class);
intent.putExtra("args","晨");
startService(intent);
public class MyService extends Service {

    public MyService() {
} @Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
} @Override
public void onCreate() {
super.onCreate(); } @Override
public int onStartCommand(Intent intent, int flags, int startId) {
args = intent.getStringExtra("args");
return super.onStartCommand(intent, flags, startId);
} @Override
public void onDestroy() {
super.onDestroy();
}
}

Service中的onCreate只在创建的时候执行一次,onStartCommand()每次开启这个Service都会执行;同时别忘记这个注册:

  <service
android:name=".MyService"
android:enabled="true"
android:exported="true" />

2、在Service开启成功后创建悬浮框,

    @Override
public int onStartCommand(Intent intent, int flags, int startId) {
args = intent.getStringExtra("args");
initView();
return super.onStartCommand(intent, flags, startId);
} /**
* 使用系统级别的WindowManager展示悬浮框,需要6.0以上的权限;
*/
private void initView() {
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);
intent.setData(Uri.parse( "package:"+"com.example.administrator.xuanfudemo")); //应用的包名,可直接跳转到这个应用的悬浮窗设置;
startActivity(intent);
} else {
openWindow();
}
}else {
openWindow();
}
} private void openWindow(){
windowManager = (WindowManager) this.getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
// layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
// layoutParams.token = this.getWindow().getDecorView().getWindowToken(); //这样设置,在activity中打开悬浮框可绕过权限;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
layoutParams.format = PixelFormat.TRANSLUCENT; //透明
layoutParams.gravity = Gravity.TOP | Gravity.RIGHT; //右上角显示
view = LayoutInflater.from(this).inflate(R.layout.view_win,null);
windowManager.addView(view,layoutParams);
}
 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

悬浮框需要权限,而且这个权限属于危险级权限,API23以上需要用户手动开启;

3、使用Handler轮询查询指令;

查询到指令后弹框提示,弹出Dialog。在Service中弹出Dialog需要设置下方代码,同时还要有弹窗权限;

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
 private Handler mHandler = new Handler(){
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
switch (msg.what){
case HANDLERSIGN:
Log.i(TAG, "dispatchMessage: "+args+(++num));
mHandler.sendEmptyMessageDelayed(HANDLERSIGN,HANDLERTIME);
if (num == 5){
AlertDialog.Builder builder = new AlertDialog.Builder(MyService.this);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
MyService.this.stopSelf();
}
});
AlertDialog dialog = builder.create();
dialog.setMessage("我的计数"+num);
dialog.setTitle("提示");
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}
break;
}
}
}; private final String TAG = "ccb";
private String args;
private int num;
private final int HANDLERSIGN = 10;
private final int HANDLERTIME = 2010; @Override
public int onStartCommand(Intent intent, int flags, int startId) {
args = intent.getStringExtra("args");
initView();
initData();
return super.onStartCommand(intent, flags, startId);
} private void initData() {
mHandler.sendEmptyMessageDelayed(HANDLERSIGN,HANDLERTIME);
}

4、在服务销毁时,清空Handler信息关闭悬浮框;

    @Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: 啊,ByKill");
mHandler.removeCallbacksAndMessages(null);
if (windowManager != null) windowManager.removeView(view);
}

全部的Service代码:

悬浮框我使用了两种,另外一种是FloatWindow,有兴趣的可以去GitHub查一下;

package com.example.administrator.xuanfudemo;

import android.Manifest;
import android.app.AlertDialog;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager; import com.yhao.floatwindow.FloatWindow;
import com.yhao.floatwindow.PermissionListener;
import com.yhao.floatwindow.Screen;
import com.yhao.floatwindow.ViewStateListener; public class MyService extends Service { private WindowManager windowManager;
private View view; public MyService() {
} @Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
} @Override
public void onCreate() {
super.onCreate(); } private Handler mHandler = new Handler(){
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
switch (msg.what){
case HANDLERSIGN:
Log.i(TAG, "dispatchMessage: "+args+(++num));
mHandler.sendEmptyMessageDelayed(HANDLERSIGN,HANDLERTIME);
if (num == 5){
AlertDialog.Builder builder = new AlertDialog.Builder(MyService.this);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
MyService.this.stopSelf();
// if (windowManager != null) windowManager.removeView(view);
}
});
AlertDialog dialog = builder.create();
dialog.setMessage("我的计数"+num);
dialog.setTitle("提示");
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}
break;
}
}
}; private final String TAG = "ccb";
private String args;
private int num;
private final int HANDLERSIGN = 10;
private final int HANDLERTIME = 2010;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
args = intent.getStringExtra("args");
// initView();
initView2();
initData();
return super.onStartCommand(intent, flags, startId);
} /**
* FloatWindow使用FloatWindow库展示悬浮框;可拖拽
*/
private void initView2() {
FloatWindow
.with(getApplicationContext())
.setView(LayoutInflater.from(this).inflate(R.layout.view_win,null))
.setWidth(100) //设置控件宽高
.setHeight(Screen.height,0.2f)
.setX(100) //设置控件初始位置
.setY(Screen.height,0.3f)
.setDesktopShow(true) //桌面显示
.setViewStateListener(new ViewStateListener() {
@Override
public void onPositionUpdate(int i, int i1) { } @Override
public void onShow() { } @Override
public void onHide() { } @Override
public void onDismiss() { } @Override
public void onMoveAnimStart() { } @Override
public void onMoveAnimEnd() { } @Override
public void onBackToDesktop() { }
})
.setPermissionListener(new PermissionListener() {
@Override
public void onSuccess() { } @Override
public void onFail() { }
})
.build();
FloatWindow.get().show();
} /**
* 使用系统级别的WindowManager展示悬浮框,需要6.0以上的权限;
*/
private void initView() {
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);
intent.setData(Uri.parse( "package:"+"com.example.administrator.xuanfudemo"));
startActivity(intent);
} else {
openWindow();
}
}else {
openWindow();
}
} private void openWindow(){
windowManager = (WindowManager) this.getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
// layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
// layoutParams.token = this.getWindow().getDecorView().getWindowToken();
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
layoutParams.format = PixelFormat.TRANSLUCENT;
layoutParams.gravity = Gravity.TOP | Gravity.RIGHT;
view = LayoutInflater.from(this).inflate(R.layout.view_win,null);
windowManager.addView(view,layoutParams);
} private void initData() {
mHandler.sendEmptyMessageDelayed(HANDLERSIGN,HANDLERTIME);
} @Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: 啊,ByKill");
mHandler.removeCallbacksAndMessages(null);
if (windowManager != null) windowManager.removeView(view);
FloatWindow.destroy();
}
}

Android悬浮框,在Service中打开悬浮窗;在Service中打开Dialog;的更多相关文章

  1. Android音视频通话过程中最小化成悬浮框的实现(类似Android8.0画中画效果)

    关于音视频通话过程中最小化成悬浮框这个功能的实现,网络上类似的文章很多,但是好像还没看到解释的较为清晰的,这里因为项目需要实现了这样的一个功能,今天我把它记录下来,一方面为了以后用到便于自己查阅,一方 ...

  2. Android仿360手机卫士悬浮窗效果

    请看下图:                         首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速.好,我们现在就来模拟实现一下 ...

  3. android桌面悬浮窗实现

                            首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速.好,我们现在就来模拟实现一下类似的效果. ...

  4. Android 桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速.好,我们现在就来模拟实现一下类似的效果. 先谈一下基本的实现原理,这种桌面悬浮窗的效果很 ...

  5. Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...

  6. Android 悬浮窗、悬浮球开发

    原文:Android 悬浮窗.悬浮球开发 1.权限管理 直接看我另外一篇博客吧,传送门: https://my.oschina.net/u/1462828/blog/1933162 2.Base类Ba ...

  7. 微信小程序中悬浮窗功能的实现(主要探讨和解决在原生组件上的拖动)

    问题场景 所谓悬浮窗就是图中微信图标的按钮,采用fixed定位,可拖动和点击. 这算是一个比较常见的实现场景了. 为什么要用cover-view做悬浮窗?原生组件出来背锅了~ 最初我做悬浮窗用的不是c ...

  8. 自制悬浮框,愉快地查看栈顶 Activity

    接手陌生模块时,如何快速了解每个页面对应的类,以及它们之间的跳转逻辑.总不能在代码里一个一个地找startActivity()吧? 有时候,又想查看别人的 app 的页面组织(像淘宝.微信啊),总不能 ...

  9. Android -- 使用WindowManager实现悬浮框效果

    1,原文在这里http://blog.csdn.net/qq_17250009/article/details/52908791,我只是把里面的关键步骤给注释了一下,首先来看一下我们的效果,如图(电脑 ...

随机推荐

  1. GoJS 在 vue 项目中的使用

    GoJS 在 html vue 项目中的使用,github地址:https://github.com/cag2050/gojs_demo GoJS 在 vue-cli 2.x 项目中的使用,githu ...

  2. ElementUI(vue UI库)、iView(vue UI库)、ant design(react UI库)中组件的区别

    ElementUI(vue UI库).iView(vue UI库).ant design(react UI库)中组件的区别: 事项 ElementUI iView ant design 全局加载进度条 ...

  3. NET设计模式 第二部分 创建型模式(5):原型模式(Prototype Pattern)

    原型模式(Prototype Pattern) ——.NET设计模式系列之六 Terrylee,2006年1月 概述 在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构.这 ...

  4. golang 如何查看channel通道中未读数据的长度

    可以通过内建函数len查看channel中元素的个数. 内建函数len的定义如下: func len(v Type) int The len built-in function returns the ...

  5. 解决“chrome adobe flash player不是最新版本”的方法

    chrome地址栏输入chrome://components 更新flash后,重启chrome即可,可能需要搭梯子才能更新.

  6. 多线程练习,深刻体会了一次变量的BUG.

    package ltb20180106; public class TestBankThread { private int deposit=0;//注意全局变量的摆放. public TestBan ...

  7. 获取一个div下的li或者img元素

    上层div的xpath=//*[@id="launchpadOptionsList"] 其下的所有li的最后一个是//*[@id="launchpadOptionsLis ...

  8. 构建自己的代码库在Code-Google上

    多年工作的代码,有不少可以抽象出来作为工具类的.如果每次都把项目的工具类存放到U盘.必然会造成维护的问题.主要是你不可能天天的带u盘 去代码里复制粘贴.去code.google.com建立自己的代码库 ...

  9. Ubuntu 14.10 下Spark on yarn安装

    1 服务器分布 服务器 说明 192.168.1.100 NameNode 192.168.1.101 DataNode 192.168.1.102 DataNode 2 软件环境 2.1 安装JDK ...

  10. linux下recv 、send阻塞、非阻塞区别和用法

    非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:       基本概念: 阻塞IO:: socket 的阻塞模式 ...