阿齐兹的Python学习笔记——移动应用开发

移动应用开发

在Android上运行Python

至少有一个Python版本可以在Android上运行
[Scripting Layer for Android(SL4A)][1]允许在任何设备上运行Python
SL4A支持Python 2,而不是Python 3

如何解决Python版本问题?
如果把用户交互移到智能手机上,这样模型和部分控制器代码会保留在服务器上(仍然运行Python 3),而视图代码和其余控制器代码将移到智能手机上,就需要重写这些代码从而在Python 2上运行

建立开发环境

Google提供了一个跨平台的[Android模拟器][2],允许跟据需要完成手机应用开发

配置SDK和模拟器:

  • 增加一个Android平台:打开Android SDK and AVD Manager(Android SDL和AVD管理器)工具,选择Available Packages(可用包),然后安装对应版本的Android平台
  • 创建一个新的Android虚拟设备(AVD):为AVD指定一个名,并选择一个目标,选择虚拟SDcard的大小(512),点击”Create AVD”

AVD是一个模拟的Android手机

安装和配置Android脚本环境:

  • 导航到以下Web地址:http://code.google.com/p/android-scripting
  • 下载sl4_r2.apk文件并安装

为SL4A安装增加Python:

  • 下载并安装python_for_android_r1.apk
  • 运行后点击Install来下载、解压并安装面向Android的Python支持文件

在Android上测试Python

测试安装环境的脚本mydroidtest.py:

import android
app = android.Android()
msg = "Hello from Head First Python on Android"
app.makeToast(msg)

将脚本复制到模拟器的SD卡(在终端窗口执行):

tools/adb push mydroidtest.py /sdcard/sl4a/scripts

JSON数据交换格式

JSON中的JS代表JavaScript,ON代表对象记法,是Web上使用最广泛的数据交换格式之一
通过使用你喜欢的编程语言所提供的JSON库,就能创建可以交换的数据。如果可以读取一个JSON数据流,还可以在必要时重新创建数据

为什么使用JSON?

  • 这是一种基于文本的格式,与Web工作方式更一致
  • 这是一个标准,在Python2和Python3上的工作完全相同,没有兼容性问题
  • 由于JSON与语言无关,所以完全可以使用其他编程语言编写的Web工具与服务器交互
>>> import json #导入JSON库
>>> names = ['John', ['Johnny', 'Jack'], 'Michael', ['Mike', 'Mikey', 'Mick']]
>>> names
['John', ['Johnny', 'Jack'], 'Michael', ['Mike', 'Mikey', 'Mick']]
>>> to_transfer = json.dumps(names) #将Python多重列表转换为一个JSON多重列表
>>> to_transfer
'["John", ["Johnny", "Jack"], "Michael", ["Mike", "Mikey", "Mick"]]'
>>> from_transfer = json.loads(to_transfer) #将JSON多重列表转换回Python格式
>>> from_transfer
['John', ['Johnny', 'Jack'], 'Michael', ['Mike', 'Mikey', 'Mick']]
>>> names
['John', ['Johnny', 'Jack'], 'Michael', ['Mike', 'Mikey', 'Mick']]

增加一个新函数,返回选手名列表:

def get_names_from_store():
    athletes = get_from_store
    response = [athletes[each_ath].name for each_ath in athletes]
    return(response)

将数据作为一个JSON数据流返回给Web请求者;

import json
import athletemodel
import yate

names = athletemodel.get_names_from_store()

print(yate.start_response('application/json')) #Content-type行使用application/json
print(json.dumps(sorted(names)))

测试JSON生成的脚本时看到的行为可能存在差异,这取决于你使用的浏览器

SL4A Android API

SL4A技术为底层Android API提供了一个高层API

通过6个Android API调用,可以在对话框中创建一个选项列表,并创建相应的正负按钮,用来指示用户所做的选择

import android

app = android.Android() #创建一个Android应用对象

app.dialogCreateAlert("Select an athlete:") 
app.dialogSetSingleChoiceItems(['Mikey', 'Sarah', 'James', 'Julie'])
app.dialogSetPositiveButtonText("Select")
app.dialogSetNegativeButtonText("Quit")
app.dialogShow() #在应用上显示对话框
resp = app.dialogGetResponse().result #等待用户的响应

向Web服务器查询名字列表,作为一个JSON数组返回,并在智能手机上显示这个列表:

import android
import json
import time
from urllib import urlencode #这些库可以提供Web客户端功能
from urllib2 import urlopen

hello_msg = 'Welcome to Coach Kelly's Timing App'
list_title = 'Here is your list of athletes:'
quit_msg = 'Quitting Coach Kelly's App.'
web_server = 'http://192.168.1.33.8080' #Web服务器所在的Web地址
get_names_cgi = '/cgi-bin/generate_names.py'

def send_to_server(url, post_data=None):
    """这个函数取一个Web地址和一些可选数据,向Web服务器发送一个Web请求,Web响应返回给调用者"""
    if post_data:
        page = urlopen(url, urlencode(post_data))
    else:
        page = urlopen(url)
    return(page.read().decode("utf8"))

app = android.Android() #创建一个Android应用对象

def status_update(msg, how_long=2):
    """这个函数用来在手机上显示简短的消息"""
    app.makeToast(msg)
    time.sleep(how_long)

status_update(hello_msg) #显示欢迎消息
athlete_names = sorted(json.loads(send_to_server(web_server + get_names_cgi))) #把Web请求发送到服务器,然后将JSON响应转换为一个有序列表
app.dialogCreateAlert(title_list) 
app.dialogSetSingleChoiceItems(athlete_names)
app.dialogSetPositiveButtonText("Select")
app.dialogSetNegativeButtonText("Quit")
app.dialogShow()
resp = app.dialogGetResponse().result #等待用户点击一个按钮,然后赋值给resp
status_update(quit_msg) #显示退出消息

在Android上选择列表

如果点击的是第一个按钮,dialogGetResponse()调用的结果会设置为positive,如果点击的是第二个按钮,则会设置为negative

dialogGetSelectedItems()调用会返回所选列表项的索引值

get_data_cgi = '/cgi-bin/generate_data.py' #提供运行时的CGI的名

if resp['which'] in ('positive'):
    selected_athlete = app.dialogGetSelectedItems().result[0] #索引值对应对话框返回列表的第一个元素
    which_athlete = athlete_names[selected_athlete] #使用索引值查找选手名
    athlete = json.loads(send_to_server(web_server + get_data_cgi, {'which_athlete':which_athlete})) #向服务器发送一个新的Web请求,获取这个选手的数据
    athlete_title = which_athlete + 'top 3 times:'
    app.dialogCreateAlert(athlete_title) 
    app.dialogSetItems(athlete['Top3'])
    app.dialogSetPositiveButtonText('OK'))
    app.dialogShow()
    resp = app.dialogGetResponse().result

cgi-bin/generate_data.py CGI脚本:

import cgi
import json
import athletemodel
import yate

athletes = athletemodel.get_from_store() #从模型得到所有的数据
form_data = cgi.FieldStorage() #处理随请求发送的数据
athlete_name = form_data['which_athlete'].value
print(yate.start_response('application/json')) #启动一个Web请求,数据类型为JSON
print(json.dumps(athletes[athlete_name])) #在Web响应中包含制定选手的数据,采用JSON格式

如何调试

CGI机制默认地会捕获脚本发送到标准输出(STDOUT)的所有输出
将调试消息发送到Web服务器的控制台,显示到标准错误输出(STDERR):

import sys #从标准库中导入sys

print(json.dumps(athletes[athlete_name]), file=sys.stderr) #将输出重定向到stderr

现在可以在Web服务器的控制台看到Web响应发送的数据

JSON无法处理你的定制数据类型

标准库的JSON库只能处理Python的内置类型,无法处理AthleteList对象

为AthleteList增加一个方法,将数据转换为一个字典:

@property
def to_dict(self):
    return({'Name': self.name, 'DOB': self.dob, 'Top3':self.top3})

使用@property修饰这个方法,对类用户来说这个方法就像一个新的属性

为什么使用@property?
to_dict()方法并没有以任何方法改变对象数据的状态,只是把对象的属性数据作为一个字典返回。尽管to_dict()是一个方法,但它表现得更像一个属性,使用@property修饰符可以指出这一点。类的用户并不需要知道它们访问to_dict属性时实际上在运行一个方法,他们看到的只是一个统一的接口:属性访问数据,而方法用来管理数据

在真正的手机上运行你的应用

将代码复制到一个真正的设备的多种选择:

  1. 使用蓝牙文件传输
  2. 利用USB连接完成文件传输
  3. 利用USB使用Android SDK的adb工具
  4. 通过WiFi使用文件传输工具

通过WiFi使用文件传输工具:

  1. 准备你的计算机:运行一个SSH服务器
  2. 在Android手机上安装AndFTP:AndFTP工具允许在计算机和Android手机之间通过FTP、SFTP、FTPS传输文件(AndFTP默认使用FTP协议)
  3. 配置AndFTP:将它配置为使用SFTP作为传输协议与你的计算机连接

小结

  1. Python2、Python3的兼容性问题
  2. SL4A、AVD
  3. JSON库模块
  4. urllib和urllib2库模块
  5. sys模块的三种输入流

你可能感兴趣的:(阿齐兹的Python学习笔记)