前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android 悬浮窗功能的实现

Android 悬浮窗功能的实现

作者头像
黄林晴
发布于 2019-08-31 11:31:59
发布于 2019-08-31 11:31:59
6.6K00
代码可运行
举报
文章被收录于专栏:代码男人代码男人
运行总次数:0
代码可运行

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

代码语言:txt
AI代码解释
复制
                 本文链接:[https://blog.csdn.net/huangliniqng/article/details/95372212](https://blog.csdn.net/huangliniqng/article/details/95372212) 

前言

我们大多数在两种情况下可以看到悬浮窗,一个是视频通话时的悬浮窗,另一个是360卫士的悬浮球,实现此功能的方式比较多,这里以视频通话悬浮窗中的需求为例。编码实现使用KotlinJava版本留言邮箱即可。

业务场景

以微信视频通话为例,在视频通话时,我们打开其他应用或点击Home键退出时或点击缩放图标,悬浮窗会显示在其他应用之上,给人的假象是通话页面变小了,点击悬浮窗回到通过页面,悬浮窗消失。退出通话页面悬浮窗消失。

业务场景技术分析

在编码之前,我们必须将流程整理好,这样更有利于编码的实现。实现一个功能如果需要10分钟,思考的时间是7分钟,编码占用的时间只是三分钟。

1.悬浮窗可以显示在其他应用或launchers之上,这个肯定需要悬浮窗权限,而悬浮窗权限属于特殊权限,所以只能通过引导用户去打开无法像危险权限那样直接申请。可以做到后台显示则说明悬浮窗是一个Service。

2.通话页面隐藏时悬浮窗显示,通话页面显示时悬浮窗隐藏,可以看出悬浮窗和Activity的生命周期相关联,所以悬浮窗的Service和通话页面的Activity是通过bind去绑定的。

3.既然Service和Activity是通过bind去绑定的,说明当悬浮窗显示的时候,通话Activity虽然不可见但仍在运行。

结合上述技术问题分析,我们倒叙一一通过编码实现

悬浮窗实现方案

  • 实现效果
  • 准备工作
代码语言:txt
AI代码解释
复制
   首先我们新建一个项目,项目中有两个Activity,我们在第二个Activity编写通话模拟页面。在第二个页面的原因我们后面会讲到。
  • 如何将acitivity置于后台

其实很简单,我们调用一个方法即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
moveTaskToBack(true);

这个方法的含义就是将当前的任务战置于后台,so,为什么我要在第二个Activity中实现的原因之一,因为默认的Activity的启动模式是标准模式,而上面方法会将任务栈置于后台而不是一个单独的Activity,所以我们为了显示悬浮窗时不影响操作软件的其他功能,我们要将通话页面的Activity设置为singleInstance,这样当调用上面方法的时候只是将通话页面所在的Activity栈置于后台,如果你还不了解启动模式可以移步至上一篇文章:Activity的启动模式

我们现在在右上方的点击事件中添加上述代码,可以看到通话页面的Activity的已经在后台运行了。

  • 判断是否有悬浮窗权限

点击左上角图标时,我们要先判断当前app是否有悬浮窗权限,首先我们在配置文件中添加,悬浮窗的权限。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

(很多文章标题都是悬浮窗如何绕过权限,什么设置类型为TOAST或者PHONE,我想说不可能的事,TOAST类型的虽然部分机型可以显示但是就是一个普通的TOSAT会自动消失)

那么我们如何判断是否有悬浮窗权限呢,这一块不同厂商处理方案可能不一样,这里我们用一种通用的处理方案,测试表明除了(vivo部分)无效,其他多数机型都ok。并且vivo部分机型微信通话也不会弹出提示(这我就放心了~)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fun zoom(v: View) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT)
            GlobalDialogSingle(this, "", "当前未获取悬浮窗权限", "去开启", DialogInterface.OnClickListener { dialog, which ->
                dialog.dismiss()
                startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + packageName)), 0)
            }).show()

        } else {
            moveTaskToBack(true)
            val intent = Intent(this@Main2Activity, FloatWinfowServices::class.java)
            hasBind = bindService(intent, mVideoServiceConnection, Context.BIND_AUTO_CREATE)
        }
    }
}

我们通过Settings.canDrawOverlays(this)来判断当前应用是否有悬浮窗权限,如果没有,我们弹窗提示,通过

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + packageName)), 0)

跳转到开启悬浮窗权限页面。如果悬浮窗权限已开启,直接将当前任务栈置于后台,开启服务即可。

其实回调方法,并没有直接告诉我们是否授权成功,所以我们需要在回调中再次判断

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == 0) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show()
            } else {
                Handler().postDelayed({
                    val intent = Intent(this@Main2Activity, FloatWinfowServices::class.java)
                    intent.putExtra("rangeTime", rangeTime)
                    hasBind = bindService(intent, mVideoServiceConnection, Context.BIND_AUTO_CREATE)
                    moveTaskToBack(true)
                }, 1000)

            }
        }
    }
}

这里我们可以看到回调中延迟了1秒,因为测试发现某些机型反应“过快”,收到回调的时候还以为没有授权成功,其实已经成功了。

绑定Service我们需要一个ServiceConnection对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal var mVideoServiceConnection: ServiceConnection = object : ServiceConnection {

    override fun onServiceConnected(name: ComponentName, service: IBinder) {
        // 获取服务的操作对象
        val binder = service as FloatWinfowServices.MyBinder
        binder.service
    }

    override fun onServiceDisconnected(name: ComponentName) {}
}

Main2Activity的完整代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author Huanglinqing
 */
class Main2Activity : AppCompatActivity() {

    private val chronometer: Chronometer? = null
    private var hasBind = false
    private val rangeTime: Long = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
    }


    fun zoom(v: View) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT)
                GlobalDialogSingle(this, "", "当前未获取悬浮窗权限", "去开启", DialogInterface.OnClickListener { dialog, which ->
                    dialog.dismiss()
                    startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + packageName)), 0)
                }).show()

            } else {
                moveTaskToBack(true)
                val intent = Intent(this@Main2Activity, FloatWinfowServices::class.java)
                hasBind = bindService(intent, mVideoServiceConnection, Context.BIND_AUTO_CREATE)
            }
        }
    }

    internal var mVideoServiceConnection: ServiceConnection = object : ServiceConnection {

        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            // 获取服务的操作对象
            val binder = service as FloatWinfowServices.MyBinder
            binder.service
        }

        override fun onServiceDisconnected(name: ComponentName) {}
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
        if (requestCode == 0) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (!Settings.canDrawOverlays(this)) {
                    Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show()
                } else {
                    Handler().postDelayed({
                        val intent = Intent(this@Main2Activity, FloatWinfowServices::class.java)
                        intent.putExtra("rangeTime", rangeTime)
                        hasBind = bindService(intent, mVideoServiceConnection, Context.BIND_AUTO_CREATE)
                        moveTaskToBack(true)
                    }, 1000)

                }
            }
        }
    }


    override fun onRestart() {
        super.onRestart()
        Log.d("RemoteView", "重新显示了")
        //不显示悬浮框
        if (hasBind) {
            unbindService(mVideoServiceConnection)
            hasBind = false
        }

    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
    }
}
  • 新建悬浮窗Service

新建悬浮窗Service FloatWinfowServices,因为我们使用的BindService,我们在onBind方法中初始化service中的布局

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
override fun onBind(intent: Intent): IBinder? {
    initWindow()
    //悬浮框点击事件的处理
    initFloating()
    return MyBinder()
}

service中我们通过WindowManager来添加一个布局显示。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 初始化窗口
 */
private fun initWindow() {
    winManager = application.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    //设置好悬浮窗的参数
    wmParams = params
    // 悬浮窗默认显示以左上角为起始坐标
    wmParams!!.gravity = Gravity.LEFT or Gravity.TOP
    //悬浮窗的开始位置,因为设置的是从左上角开始,所以屏幕左上角是x=0;y=0
    wmParams!!.x = winManager!!.defaultDisplay.width
    wmParams!!.y = 210
    //得到容器,通过这个inflater来获得悬浮窗控件
    inflater = LayoutInflater.from(applicationContext)
    // 获取浮动窗口视图所在布局
    mFloatingLayout = inflater!!.inflate(R.layout.remoteview, null)
    // 添加悬浮窗的视图
    winManager!!.addView(mFloatingLayout, wmParams)
}

悬浮窗的参数主要设置悬浮窗的类型为

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY

8.0 以下可设置为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
wmParams!!.type = WindowManager.LayoutParams.TYPE_PHONE

代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private //设置window type 下面变量2002是在屏幕区域显示,2003则可以显示在状态栏之上
        //设置可以显示在状态栏上
        //设置悬浮窗口长宽数据
val params: WindowManager.LayoutParams
    get() {
        wmParams = WindowManager.LayoutParams()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            wmParams!!.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        } else {
            wmParams!!.type = WindowManager.LayoutParams.TYPE_PHONE
        }
        wmParams!!.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
        wmParams!!.width = WindowManager.LayoutParams.WRAP_CONTENT
        wmParams!!.height = WindowManager.LayoutParams.WRAP_CONTENT
        return wmParams
    }

当点击悬浮窗的时候回到Activity2页面,并且悬浮窗消失,所以我们只需要给悬浮窗添加点击事件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
linearLayout!!.setOnClickListener { startActivity(Intent(this@FloatWinfowServices, Main2Activity::class.java)) }

当Service走到onDestory的时候将view移除,对于Activity2页面来说 当onResume的时候 解绑Service,当onstop的时候 绑定Service。

从效果图中我们可以看到悬浮窗可以拖拽的,所以还要设置触摸事件,当移动距离超过某个值的时候让onTouch消费事件,这样就不会触发点击事件了。这个算是view比较基础的知识,相信大家都明白了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标)
private var mTouchStartX: Int = 0
private var mTouchStartY: Int = 0
private var mTouchCurrentX: Int = 0
private var mTouchCurrentY: Int = 0
//开始时的坐标和结束时的坐标(相对于自身控件的坐标)
private var mStartX: Int = 0
private var mStartY: Int = 0
private var mStopX: Int = 0
private var mStopY: Int = 0
//判断悬浮窗口是否移动,这里做个标记,防止移动后松手触发了点击事件
private var isMove: Boolean = false

private inner class FloatingListener : View.OnTouchListener {

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        val action = event.action
        when (action) {
            MotionEvent.ACTION_DOWN -> {
                isMove = false
                mTouchStartX = event.rawX.toInt()
                mTouchStartY = event.rawY.toInt()
                mStartX = event.x.toInt()
                mStartY = event.y.toInt()
            }
            MotionEvent.ACTION_MOVE -> {
                mTouchCurrentX = event.rawX.toInt()
                mTouchCurrentY = event.rawY.toInt()
                wmParams!!.x += mTouchCurrentX - mTouchStartX
                wmParams!!.y += mTouchCurrentY - mTouchStartY
                winManager!!.updateViewLayout(mFloatingLayout, wmParams)
                mTouchStartX = mTouchCurrentX
                mTouchStartY = mTouchCurrentY
            }
            MotionEvent.ACTION_UP -> {
                mStopX = event.x.toInt()
                mStopY = event.y.toInt()
                if (Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1) {
                    isMove = true
                }
            }
            else -> {
            }
        }

        //如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
        return isMove
    }
}

FloatWinfowServices所有代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class FloatWinfowServices : Service() {


    private var winManager: WindowManager? = null
    private var wmParams: WindowManager.LayoutParams? = null
    private var inflater: LayoutInflater? = null
    //浮动布局
    private var mFloatingLayout: View? = null
    private var linearLayout: LinearLayout? = null
    private var chronometer: Chronometer? = null


    override fun onBind(intent: Intent): IBinder? {
        initWindow()
        //悬浮框点击事件的处理
        initFloating()
        return MyBinder()
    }

    inner class MyBinder : Binder() {
        val service: FloatWinfowServices
            get() = this@FloatWinfowServices
    }

    override fun onCreate() {
        super.onCreate()
    }

    /**
     * 悬浮窗点击事件
     */
    private fun initFloating() {
        linearLayout = mFloatingLayout!!.findViewById<LinearLayout>(R.id.line1)
        linearLayout!!.setOnClickListener { startActivity(Intent(this@FloatWinfowServices, Main2Activity::class.java)) }
        //悬浮框触摸事件,设置悬浮框可拖动
        linearLayout!!.setOnTouchListener(FloatingListener())
    }


    //开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标)
    private var mTouchStartX: Int = 0
    private var mTouchStartY: Int = 0
    private var mTouchCurrentX: Int = 0
    private var mTouchCurrentY: Int = 0
    //开始时的坐标和结束时的坐标(相对于自身控件的坐标)
    private var mStartX: Int = 0
    private var mStartY: Int = 0
    private var mStopX: Int = 0
    private var mStopY: Int = 0
    //判断悬浮窗口是否移动,这里做个标记,防止移动后松手触发了点击事件
    private var isMove: Boolean = false

    private inner class FloatingListener : View.OnTouchListener {

        override fun onTouch(v: View, event: MotionEvent): Boolean {
            val action = event.action
            when (action) {
                MotionEvent.ACTION_DOWN -> {
                    isMove = false
                    mTouchStartX = event.rawX.toInt()
                    mTouchStartY = event.rawY.toInt()
                    mStartX = event.x.toInt()
                    mStartY = event.y.toInt()
                }
                MotionEvent.ACTION_MOVE -> {
                    mTouchCurrentX = event.rawX.toInt()
                    mTouchCurrentY = event.rawY.toInt()
                    wmParams!!.x += mTouchCurrentX - mTouchStartX
                    wmParams!!.y += mTouchCurrentY - mTouchStartY
                    winManager!!.updateViewLayout(mFloatingLayout, wmParams)
                    mTouchStartX = mTouchCurrentX
                    mTouchStartY = mTouchCurrentY
                }
                MotionEvent.ACTION_UP -> {
                    mStopX = event.x.toInt()
                    mStopY = event.y.toInt()
                    if (Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1) {
                        isMove = true
                    }
                }
                else -> {
                }
            }

            //如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
            return isMove
        }
    }

    /**
     * 初始化窗口
     */
    private fun initWindow() {
        winManager = application.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        //设置好悬浮窗的参数
        wmParams = params
        // 悬浮窗默认显示以左上角为起始坐标
        wmParams!!.gravity = Gravity.LEFT or Gravity.TOP
        //悬浮窗的开始位置,因为设置的是从左上角开始,所以屏幕左上角是x=0;y=0
        wmParams!!.x = winManager!!.defaultDisplay.width
        wmParams!!.y = 210
        //得到容器,通过这个inflater来获得悬浮窗控件
        inflater = LayoutInflater.from(applicationContext)
        // 获取浮动窗口视图所在布局
        mFloatingLayout = inflater!!.inflate(R.layout.remoteview, null)
        chronometer = mFloatingLayout!!.findViewById<Chronometer>(R.id.chronometer)
        chronometer!!.start()
        // 添加悬浮窗的视图
        winManager!!.addView(mFloatingLayout, wmParams)
    }

    private //设置window type 下面变量2002是在屏幕区域显示,2003则可以显示在状态栏之上
            //设置可以显示在状态栏上
            //设置悬浮窗口长宽数据
    val params: WindowManager.LayoutParams
        get() {
            wmParams = WindowManager.LayoutParams()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                wmParams!!.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
            } else {
                wmParams!!.type = WindowManager.LayoutParams.TYPE_PHONE
            }
            wmParams!!.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or
                    WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
            wmParams!!.width = WindowManager.LayoutParams.WRAP_CONTENT
            wmParams!!.height = WindowManager.LayoutParams.WRAP_CONTENT
            return wmParams
        }


    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        winManager!!.removeView(mFloatingLayout)
    }
}
  • 实际应用中需要考虑的一些其他问题

在使用使用的过程中,我们肯定会遇到其他问题:

1.用户使用过程中,可能会直接按Home键,这个时候如何提示呢?

产生问题原因:因为用户按Home键之后,开发者无法重写Home键逻辑,此时应用不在前台运行,无法弹窗提醒,此时用户点击APP图标进入的是第一个栈,这个时候用户就没有进入通话页面的入口了。

解决方案:

第一种解决方案 我们可以仿照微信那样去做,就是在整个通话过程中开启一个前台通知,用户点击通知时进入通话页面。

第二种解决方案 就是检测应用是否在前台,当通话页面在运行的时候,并且应用重新回到前台,我们广播到其他页面,提示权限引导即可。

2.用户在通话页面(singleInstance模式),点击Home键

应用在后台运行的时候,通话结束,Activity被finish,此时从任务程序中切回应用你会发现打开的竟然是通话页面!

这个问题简单的说就是,如果你在通话页面呼叫某人,通话过程中按Home键,然后电话挂断,此时你从任务程序中切回应用,会再次呼叫这个人,也就是这种状态下重新回到了onCreate方法。

问题产生原因:

1.因为通话页面是singleInstance模式,此时有两个任务栈,按Home键后再从任务程序中切回,此时应用只保留了第二个任务栈,已经失去了和第一个任务栈的关系,finish之后无法在回到第一个任务栈。

解决方案:

1.(不推荐)通话页面不使用singleInstance模式,这种情况下,在通话过程中无法操作软件的其他功能,一般都不采取。

2.(我目前的解决方案)设置一个标记位,标记当前是否在通话,在onCreate中如果通话已经结束了,跳转到一个过渡页面(标准模式),过渡页面中finish,就可以了,添加过渡页面的原因是我们不知道上一个页面是哪里,因为我们收到来电可能是任意页面,我们我们在过渡页面finsh之后,就再次回到了第一个任务栈。

如果有其他好的解决方案 欢迎留言。

如果需要Java版本的小伙伴 ,留言邮箱就可以了,我看到会发到邮箱哦!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年08月29日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python学习笔记之使用 python -m SimpleHTTPServer 快速搭建http服务
搭建FTP,或者是搭建网络文件系统,这些方法都能够实现Linux的目录共享。但是FTP和网络文件系统的功能都过于强大,因此它们都有一些不够方便的地方。比如你想快速共享Linux系统的某个目录给整个项目团队,还想在一分钟内做到,怎么办?
Jetpropelledsnake21
2019/08/29
9430
谈谈 Python 那些不为人知的冷知识(四)
这是 Python 中好玩但比较冷门的知识点第四篇,一篇只分享五个,不想错过的,千万记得关注一下。
张俊红
2019/10/10
5010
谈谈 Python 那些不为人知的冷知识(四)
通过Infortress 端口穿透功能访问内网电脑部署的HTTP服务
在远程办公或跨地域协作场景下,远程访问内网电脑上搭建的HTTP服务成为许多人特别是技术工程人员的一个痛点。借助Infortress的端口穿透功能,无需复杂网络配置,即可轻松访问内网HTTP服务。
用户11644095
2025/05/15
1240
python实用小工具介绍
一、秒级启动一个HTTP下载服务器 在实际工作中,时不时会有这样的一个需求:将文件传给其他同事。将文件传给同事本身并不是一个很繁琐的工作,现在的聊天工具一般都支持文件传输。但是,如果需要传送的文件较多,那么,操作起来就会比较麻烦。此外,如果文件在远程的服务器上,你要将文件传给同事,则需要先将远程服务器的文件下载到本地,然后再通过聊天工具传给同事。再或者,你并不是特别清楚要传哪几个文件给同事,所以,你们需要进行来回的交流。交流的时间成本是比较高的,会降低办事效率。此时,你们需要更加高效的方法。这个时候,如果你知道Python内置了一个下载服务器就能够显著提升效率了。例如,你的同事要让你传的文件位于某一个目录下,那么,你可以进入这个目录,然后执行下面的命令启动一个下载服务器: 本地有个一文件夹,想共享给局域网同事下载一些里面的文件,可以使用python的如下命令。 • python2的用法如下: python -m SimpleHTTPServer • python3的用法如下: python3 -m http.server --cgi 以上两种方法默认端口8000,可以制定端口,例如指定端口45678: python -m SimpleHTTPServer 45678 python3 -m http.server --cgi 45678
py3study
2020/01/07
6870
搭建HTTP服务器,Python只需要一条命令就搞定了
小伙伴们如果在工作或者学习的时候需要搭建一个简单的HTTP服务器,而不是必须安装那些复杂的HTTP服务程序,比如:Apache,Nginx等。那就可以使用Python自带的包完成一个简单的内建 HTTP 服务器。这样,你就可以把你的目录和文件都以HTTP的方式展示出来。
菜鸟小白的学习分享
2021/06/17
5.5K0
搭建HTTP服务器,Python只需要一条命令就搞定了
反转字符串/列表、改变递归次数限制、else用法...Python 冷知识(四)
本文转载自Python编程时光(ID:Python-Time) 交互式“_”操作符
AI科技大本营
2019/05/14
1.1K0
反转字符串/列表、改变递归次数限制、else用法...Python 冷知识(四)
python+浏览器设备间快速共享文件/文件夹
以前我总是使用的是U盘,拷文件这个麻烦呀,传一个文件还好,可安装东西经常是需要这需要那的,用U盘传就很麻烦。 现在我学聪明了,设备间互传文件直接用网页呀! 只要你的设备在一个局域网上,发送端上有python, 接收端有浏览器,这个问题就能解决!
用户7886150
2021/01/20
1.3K0
☀️苏州程序大白用万字解析Python网络编程与Web编程☀️《❤️记得收藏❤️》
在现实生产环境中,一个服务端不可能只就服务于一个客户端;通常一个服务端是要能服务多个客户端,以下是多任务的实现思路:
苏州程序大白
2021/10/25
9340
☀️苏州程序大白用万字解析Python网络编程与Web编程☀️《❤️记得收藏❤️》
有哪些你不知道的python小工具
python作为越来越流行的一种编程语言,不仅仅是因为它语言简单,有许多现成的包可以直接调用。
py3study
2020/01/17
7280
Python一行代码就能实现的骚操作
在使用 Python 的过程中,每当遇到一行代码就解决很实际问题的场景,我都惊呼 Python 生态牛逼,今天就来分享下这种一行代码就可以搞定的 6 个骚操作,解决实际应用中的问题。
somenzz
2021/01/22
7360
Python快速搭建HTTP服务
通常是由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80或其他端口)的TCP连接,用以为用户提供相应的服务。这里的客户端可以是浏览器,postman插件或是请求的命令(curl,wget等)等。
Wu_Candy
2022/07/04
1.5K0
Python快速搭建HTTP服务
Python效率工具 | SimpleHTTPServer
在实际工作中,我们时不时会有这样的一个需求:将文件传给其他同事。将文件传给同事本身并不是一个很繁琐的工作,现在的聊天工具一般都支持文件传输。但是,如果需要传送的文件较多,那么,操作起来就会比较麻烦。此外,如果文件在远程的服务器上,你要将文件传给同事,则需要先将远程服务器的文件下载到本地,然后再通过聊天工具传给同事。再或者,你并不是特别清楚要传哪几个文件给同事,所以,你们需要进行来回的交流。交流的时间成本是比较高的,会降低办事效率。此时,你们需要更加高效的方法。
昱良
2019/07/04
1.1K0
几个提高工作效率的Python内置小工具
專 欄 ❈本文作者:赖明星 博客地址: https://www.zhihu.com/people/mingxinglai❈ 在这篇文章里,我们将会介绍4个Python解释器自身提供的小工具。这些小工具在笔者的日常工作中经常用到,减少了各种时间的浪费,然而,却很容易被大家忽略。每当有新来的同事看到我这么使用时,都忍不住感叹,原来Python还隐藏了这么好用的功能。下面就来看一下Python自带的几个小工具 一、1秒钟启动一个下载服务器 在实际工作中,时不时会有这样的一个需求:将文件传给其他同事。将文件传给同事
Python中文社区
2018/01/31
1.2K0
几个提高工作效率的Python内置小工具
HTTP协议和静态Web服务器
超文本是超级文本的缩写,是指超越文本限制或者超链接,比如:图片、音乐、视频、超链接等等都属于超文本。
用户9399690
2022/01/20
7450
HTTP协议和静态Web服务器
使用Python实现简单的Web服务器
随着互联网的快速发展,Web服务器已经成为了我们生活中不可或缺的一部分。在本文中,我们将使用Python编写一个简单的Web服务器,它能够接收HTTP请求并返回响应,同时也支持静态文件的访问。
zayyo
2023/11/30
8630
Python-SimpleHTTPServer的应用
Linux传输文件时,除了ssh和scp、ftp常见的方法外,Python下SimpleHTTPServer小工具也可以快捷的进行传输。
小筱
2021/08/03
1K0
Python-SimpleHTTPServer的应用
世界上最可怕的搜索引擎--shodan自动化利用
Shodan,是一个暗黑系的谷歌,作为一个针对网络设备的搜索引擎,它可以在极短的时间内在全球设备中搜索到你想找的设备信息。对于渗透工作者来说,就是一个辅助我们寻找靶机的好助手。 安全工作者的日常工作少不了跟进最新漏洞和使用实战靶机进行漏洞测试,漏洞信息我们大多可以通过[Exploit-DB]和[HackNews]来获取,
Ms08067安全实验室
2019/09/24
3.5K0
世界上最可怕的搜索引擎--shodan自动化利用
Python搭建http共享文件
使用场景:在办公室或者在家使用电脑个数>=2时,想从一台电脑上把文件拷贝过去,但是由于有U盘或者懒得去弄这些的时候,就可以考虑Python在本地搭建服务器,局域网之内的电脑可以进行直接访问下载即可。
summerking
2022/10/27
1.3K0
Python搭建http共享文件
轻量化HTTP服务器环境快速搭建部署
描述:在做运维或者安全相关的测试项目的时候,需要快速搭建HTML服务器环境来下载文件或者POC,主要针对于HTML代码与文件浏览下载;
全栈工程师修炼指南
2022/09/28
1.2K0
轻量化HTTP服务器环境快速搭建部署
Python网络编程——静态Web服务器实现
可以为发出请求的浏览器提供静态文档的程序。即所访问的页面是静态的,页面中的内容不会发生变化。HTTP协议(HyperText Transfer Protocol,超文本传输协议)使用于从www服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本,还确定传输文档中的哪一部分,以及那部分内容首先显示(如文本先于图形)等
落雨
2022/02/26
30.1K0
推荐阅读
相关推荐
Python学习笔记之使用 python -m SimpleHTTPServer 快速搭建http服务
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验