夜雨聆风学习资料网

ARTICLE · 992054

利用Python开发App实战

利用Python开发App实战

按钮能点,列表能刷,电脑上跑得也挺顺。结果打成 APK 装到手机上,接口直接请求失败。

这种问题我第一次用 Python 做 App 时就碰到过。后来折腾完 Kivy、接口请求、本地存储和 Android 打包,我对 Python 开发 App 的看法变了:能不能做不是问题,问题是别拿写脚本的思路去写 App。

这次直接做个实际点的东西:服务器巡检 App。

平时有几台测试服务,偶尔需要看一下接口是不是活着。总不能每次掏电脑 curl,所以我想做个很轻的小工具,手机点一下,直接显示服务状态和响应耗时。

界面不复杂:

服务器巡检

用户中心       正常   82ms
订单服务       正常   117ms
文件服务       异常   timeout

[ 立即巡检 ]

Python 这边我用 Kivy。原因也简单,它不是拿网页套个壳,Python 代码本身就能驱动界面,而且后面能继续打 Android 包。

先把页面架起来。

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label


classInspectPage(BoxLayout):

def__init__(self, **kwargs):
        super().__init__(orientation="vertical", spacing=12, padding=18, **kwargs)

        self.title = Label(
            text="服务器巡检",
            size_hint_y=None,
            height=70,
            font_size=26
        )

        self.result = Label(
            text="还没巡检",
            halign="left",
            valign="top"
        )

        self.check_btn = Button(
            text="立即巡检",
            size_hint_y=None,
            height=56
        )
        self.check_btn.bind(on_press=self.start_check)

        self.add_widget(self.title)
        self.add_widget(self.result)
        self.add_widget(self.check_btn)

defstart_check(self, _):
        self.result.text = "正在检查..."


classOpsApp(App):

defbuild(self):
return InspectPage()


OpsApp().run()

这段东西跑起来,一个最基本的 App 就有了。

但这里只是“能显示”。

真正开始写巡检逻辑时,有个地方我第一眼就不太想这么干:

defstart_check(self, _):
    requests.get(url)

把 HTTP 请求直接塞进按钮事件里,接口稍微慢一点,整个界面就像死了一样。

桌面脚本这么写可能没感觉,手机 App 不行。

UI 线程就是 UI 线程,网络请求老老实实扔出去。

我后来改成线程执行巡检,完成后再回到主线程更新界面:

import time
import requests
from threading import Thread
from kivy.clock import Clock


SERVICES = {
"用户中心": "https://api.example.com/user/health",
"订单服务": "https://api.example.com/order/health",
"文件服务": "https://api.example.com/file/health",
}


defprobe(name, url):
    started = time.perf_counter()

try:
        resp = requests.get(url, timeout=3)
        cost = int((time.perf_counter() - started) * 1000)

if200 <= resp.status_code < 300:
return name, "正常", cost

return name, f"HTTP {resp.status_code}", cost

except requests.RequestException as exc:
return name, exc.__class__.__name__, None

按钮事件也改掉:

defstart_check(self, _):
    self.check_btn.disabled = True
    self.result.text = "正在检查..."
    Thread(target=self.run_checks, daemon=True).start()


defrun_checks(self):
    lines = []

for name, url in SERVICES.items():
        service, state, cost = probe(name, url)

if cost isNone:
            lines.append(f"{service}{state}")
else:
            lines.append(f"{service}{state}{cost}ms")

    text = "\n".join(lines)

    Clock.schedule_once(
lambda _: self.show_result(text),
0
    )


defshow_result(self, text):
    self.result.text = text
    self.check_btn.disabled = False

到这里才比较像一个真能用的小工具。

这里还有个细节我一般不会省:timeout 必须写。

移动网络比办公室环境复杂得多。Wi-Fi 切 5G、信号抖一下、DNS 卡住,这些都可能把请求拖住。没有超时控制,一个服务没响应,巡检按钮能在那里转半天。

接下来是保存配置。

写死三个地址只是演示,实际用的时候,我肯定不想每改一次 IP 都重新发 APK。

这种小数据没必要先上 SQLite,一个 JSON 文件够用了。

import json
from pathlib import Path


classServerConfig:

def__init__(self, root):
        self.file = Path(root) / "servers.json"

defload(self):
ifnot self.file.exists():
return []

try:
return json.loads(
                self.file.read_text(encoding="utf-8")
            )
except (OSError, json.JSONDecodeError):
return []

defsave(self, servers):
        payload = json.dumps(
            servers,
            ensure_ascii=False,
            indent=2
        )
        self.file.write_text(payload, encoding="utf-8")

这里我故意没把所有东西都数据库化。

一个巡检工具,可能就存十来条地址。这个时候为了“架构完整”塞一层 ORM,我反而嫌麻烦。什么时候需要查询、排序、历史记录越来越多了,再换 SQLite。

Python 做这种内部 App,我觉得最大的优势就在这:代码可以收得很短。

最后就是最容易把人折腾烦的打包。

桌面执行:

python main.py

跟 Android 上跑 APK 完全不是一回事。

通常会通过 Buildozer 管理 Android 打包,在配置里至少要把 Python 依赖声明进去,例如:

requirements = python3,kivy,requests
android.permissions = INTERNET

我第一次打包时就漏过网络权限。

电脑上接口测得好好的,APK 一装,请求全部失败。代码翻了半天没发现问题,最后才发现压根不是 Python 请求逻辑的问题。

这种坑挺典型:开发 App 之后,排查范围已经不能只盯着 Python 代码了。

权限、Android 生命周期、文件目录、网络策略、线程,这些都会冒出来。

如果只是做公司内部巡检、仓库扫描、设备管理、数据录入、简单看板这一类工具,我现在反而挺愿意用 Python。

后台接口本来就是 Python,再配一个 Kivy 客户端,一套语言把数据处理、网络请求和业务逻辑都接住,开发速度确实快。

但如果要做重动画、复杂交互、深度调用手机硬件的商业 App,我一般不会硬上 Python。

工具就是这么回事。

能拿二十行代码解决的东西,别为了“技术栈高级”写两百行;但平台已经明显不合适了,也别因为自己熟 Python 就硬扛。

这个巡检 App 后面其实还能继续加:失败接口标红、保存历史耗时、连续异常触发通知、扫描二维码录入服务器地址。

不过先把“点一下,知道哪台服务挂了”做稳,比先堆十个功能靠谱得多。

相关学习资料