移動應用開發
在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=):
"""這個函數用來在手機上顯示簡短的消息"""
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[] #索引值對應對話框傳回清單的第一個元素
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屬性時實際上在運作一個方法,他們看到的隻是一個統一的接口:屬性通路資料,而方法用來管理資料
在真正的手機上運作你的應用
将代碼複制到一個真正的裝置的多種選擇:
- 使用藍牙檔案傳輸
- 利用USB連接配接完成檔案傳輸
- 利用USB使用Android SDK的adb工具
- 通過WiFi使用檔案傳輸工具
通過WiFi使用檔案傳輸工具:
- 準備你的計算機:運作一個SSH伺服器
- 在Android手機上安裝AndFTP:AndFTP工具允許在計算機和Android手機之間通過FTP、SFTP、FTPS傳輸檔案(AndFTP預設使用FTP協定)
- 配置AndFTP:将它配置為使用SFTP作為傳輸協定與你的計算機連接配接
小結
- Python2、Python3的相容性問題
- SL4A、AVD
- JSON庫子產品
- urllib和urllib2庫子產品
- sys子產品的三種輸入流