Kivy A to Z -- Kivy的執行機制
1. 當看到一個在Android平臺上執行的Python程式時,我的第一個好奇的地方就是它究竟是怎麼做到的。
2. 好,費話少說,我們通過原始碼來分析一下。
3. 首先從dist/default匯入工程,如圖所示:
4. 接下來我們來理順一下整個的Python程式的引導過程
5. PythonActivity的onCreate中:
mView =new SDLSurfaceView( this, mPath.getAbsolutePath()); Hardware.view =mView; setContentView(mView);
6. SDLSurfaceView實現了:SurfaceHolder.Callback,所以接下來SDLSurfaceView.surfaceChanged將被呼叫
7. 來看看surfaceChanged做了什麼:
if (!mRunning) {
mRunning =true;
new Thread(this).start();
}else {
mChanged =true;
if (mStarted) {
nativeExpose();
}
}
從new Thread(this).start()我們知道會建立一個執行緒,在這個執行緒裡,SDLSurfaceView的run方法將被呼叫
8. 接下來再看看SDLSurfaceView.run
這個函式主要是完成一些OPENGL的初始化工作,然後呼叫 waitForStart();
waitForStart()在等什麼呢?答案是,在等待Python的初始化工作完成。
9. PythonActivity的onResume中:
if (!mLaunchedThread) { mLaunchedThread =true; new Thread(this).start(); } if (mView !=null) { mView.onResume(); }
該函式的主要作用就是建立了一個執行緒
另外,當PythonActivyt被建立的時候,mView.onResume什麼事也沒做。
10. 再來看看PythonActivity這個執行緒的run函式看看做了什麼
public void run() {
unpackData("private", getFilesDir());
unpackData("public",externalStorage);
System.loadLibrary("sdl");
System.loadLibrary("sdl_image");
System.loadLibrary("sdl_ttf");
System.loadLibrary("sdl_mixer");
System.loadLibrary("python2.7");
System.loadLibrary("application");
System.loadLibrary("sdl_main");
System.load(getFilesDir() +"/lib/python2.7/lib-dynload/_io.so" );
System.load(getFilesDir() +"/lib/python2.7/lib-dynload/unicodedata.so" );
try {
System.loadLibrary("sqlite3");
System.load(getFilesDir() +"/lib/python2.7/lib-dynload/_sqlite3.so" );
}catch(UnsatisfiedLinkError e) {
}
try {
System.load(getFilesDir() +"/lib/python2.7/lib-dynload/_imaging.so" );
System.load(getFilesDir() +"/lib/python2.7/lib-dynload/_imagingft.so" );
System.load(getFilesDir() +"/lib/python2.7/lib-dynload/_imagingmath.so" );
}catch(UnsatisfiedLinkError e) {
}
if (mAudioThread ==null ) {
Log.i("python","Starting audio thread");
mAudioThread =new AudioThread(this);
}
runOnUiThread(new Runnable () {
public void run() {
mView.start();
}
});
}
首先是從private.mp3中解壓出python的執行環境
unpackData("private", getFilesDir());
解壓的路徑是程式所在的data目錄的files檔案資料夾下
再解壓出Kivy的程式碼
unpackData("public", externalStorage);
解壓的路徑是在內部SD卡中。
在這裡主要要是載入了一堆的動態庫,然後呼叫
runOnUiThread(new Runnable () { public void run() { mView.start(); } });
轉到SDLSurfaceView.start中運行了。
11. 再來看看SDLSurfaceView.start做了什麼:
this.setFocusableInTouchMode(true);
this.setFocusable(true);
this.requestFocus();
synchronized (this) {
mStarted =true;
this.notify();
}
這裡我們比較關注的是:this.notify(),這樣SDLSurfaceView.run裡的waitForStart該返回了
12. 接著往下看SDLSurface.run裡的程式碼:
nativeResize(mWidth,mHeight);
nativeInitJavaCallbacks();
nativeSetEnv("ANDROID_PRIVATE",mFilesDirectory);
nativeSetEnv("ANDROID_ARGUMENT",mArgument);
nativeSetEnv("PYTHONOPTIMIZE","2");
nativeSetEnv("PYTHONHOME",mFilesDirectory);
nativeSetEnv("PYTHONPATH",mArgument +":" +mFilesDirectory +"/lib");
//XXX Using SetOpenFile make a crash in nativeSetEnv. I don't
// understand why, maybe because the method is static or something.
// Anyway, if you remove that part of the code, ensure the Laucher
// (ProjectChooser) is still working.
final android.content.Intent intent =mActivity.getIntent();
if (intent !=null) {
final android.net.Uri data = intent.getData();
if (data !=null && data.getEncodedPath() !=null)
nativeSetEnv("PYTHON_OPENFILE", data.getEncodedPath());
}
nativeSetMultitouchUsed();
nativeInit();
這裡做了幾件事:
設定Python的路徑
設定Kivy程式碼所在的路徑
最後呼叫native函式nativeInit即開始進行Python的初始化了。
13. 最後,來看下nativeInit做了什麼動作
我們先找到src\jni\sdl_main下的sdl_main.c
extern C_LINKAGE void
JAVA_EXPORT_NAME(SDLSurfaceView_nativeInit) ( JNIEnv* env, jobject thiz )
{
int argc = 1;
char * argv[] = { "sdl" };
main( argc, argv );
};
extern C_LINKAGE void
JAVA_EXPORT_NAME(SDLSurfaceView_nativeIsSdcardUsed) ( JNIEnv* env, jobject thiz, jint flag )
{
isSdcardUsed = flag;
}
extern C_LINKAGE void
JAVA_EXPORT_NAME(SDLSurfaceView_nativeSetEnv) ( JNIEnv* env, jobject thiz, jstring j_name, jstring j_value )
{
jboolean iscopy;
const char *name = (*env)->GetStringUTFChars(env, j_name, &iscopy);
const char *value = (*env)->GetStringUTFChars(env, j_value, &iscopy);
setenv(name, value, 1);
(*env)->ReleaseStringUTFChars(env, j_name, name);
(*env)->ReleaseStringUTFChars(env, j_value, value);
}
我們重點關注一下:SDLSurfaceView_nativeInit
JAVA_EXPORT_NAME(SDLSurfaceView_nativeInit) ( JNIEnv* env, jobject thiz )
{
int argc = 1;
char * argv[] = { "sdl" };
main( argc, argv );
};
這裡的main是在哪裡實現的呢?
先來看看sdl_main.c包含的標頭檔案:sdl_main.h
#define main SDL_main
哦,原來這個main原名叫SDL_main
那麼我們接下來搜尋一下SDL_main在什麼地方定義的吧,你會發現死活搜尋不到
再來看下jni/sdl_main/Android.mk
LOCAL_SHARED_LIBRARIES := sdl application
這裡引用了application這個動態庫
application在哪裡呢?我們找到src\jni\application\src\start.c,這裡有一個main,但是我們要找的是SDL_main
我們接著看start.c引用的標頭檔案SDL.h,在SDL.h裡引用了SDL_main.h,而SDL_main.h裡定義了:#define main SDL_main
嗯,很清楚了,start.c裡的main就是SDL_main
最後,來看下main的程式碼,做了哪些工作:
int main(int argc, char **argv) {
char *env_argument = NULL;
int ret = 0;
FILE *fd;
LOG("Initialize Python for Android");
env_argument = getenv("ANDROID_ARGUMENT");
setenv("ANDROID_APP_PATH", env_argument, 1);
//setenv("PYTHONVERBOSE", "2", 1);
Py_SetProgramName(argv[0]);
Py_Initialize();
PySys_SetArgv(argc, argv);
/* ensure threads will work.
*/
PyEval_InitThreads();
/* our logging module for android
*/
initandroidembed();
/* inject our bootstrap code to redirect python stdin/stdout
* replace sys.path with our path
*/
PyRun_SimpleString(
"import sys, posix\n" \
"private = posix.environ['ANDROID_PRIVATE']\n" \
"argument = posix.environ['ANDROID_ARGUMENT']\n" \
"sys.path[:] = [ \n" \
" private + '/lib/python27.zip', \n" \
" private + '/lib/python2.7/', \n" \
" private + '/lib/python2.7/lib-dynload/', \n" \
" private + '/lib/python2.7/site-packages/', \n" \
" argument ]\n" \
"import androidembed\n" \
"class LogFile(object):\n" \
" def __init__(self):\n" \
" self.buffer = ''\n" \
" def write(self, s):\n" \
" s = self.buffer + s\n" \
" lines = s.split(\"\\n\")\n" \
" for l in lines[:-1]:\n" \
" androidembed.log(l)\n" \
" self.buffer = lines[-1]\n" \
" def flush(self):\n" \
" return\n" \
"sys.stdout = sys.stderr = LogFile()\n" \
"import site; print site.getsitepackages()\n"\
"print 'Android path', sys.path\n" \
"print 'Android kivy bootstrap done. __name__ is', __name__");
/* run it !
*/
LOG("Run user program, change dir and execute main.py");
chdir(env_argument);
/* search the initial main.py
*/
char *main_py = "main.pyo";
if ( file_exists(main_py) == 0 ) {
if ( file_exists("main.py") )
main_py = "main.py";
else
main_py = NULL;
}
if ( main_py == NULL ) {
LOG("No main.pyo / main.py found.");
return -1;
}
fd = fopen(main_py, "r");
if ( fd == NULL ) {
LOG("Open the main.py(o) failed");
return -1;
}
/* run python !
*/
ret = PyRun_SimpleFile(fd, main_py);
if (PyErr_Occurred() != NULL) {
ret = 1;
PyErr_Print(); /* This exits with the right code if SystemExit. */
if (Py_FlushLine())
PyErr_Clear();
}
/* close everything
*/
Py_Finalize();
fclose(fd);
LOG("Python for android ended.");
return ret;
}
首先,當然是初始化Python:Py_Initialize()
接下來允許Python多執行緒:PyEval_InitThreads()
PyRun_SimpleString裡執行的程式碼的作用是將Python的輸出重定向到Android的log輸出,這樣我們就可以在logcat裡看到Python的日誌輸出了。
最後,呼叫PyRun_SimpleFile執行Kivy程式碼
14. 好,整個過程講完了,不算複雜,但是確實有些繞。
(完)
相關推薦
Kivy A to Z -- Kivy的執行機制
1. 當看到一個在Android平臺上執行的Python程式時,我的第一個好奇的地方就是它究竟是怎麼做到的。 2. 好,費話少說,我們通過原始碼來分析一下。 3. 首先從dist/default匯入工程,如圖所示: 4. 接下來我們來理順一下整個的Python程式的
Kivy A to Z -- Kivy的訊息處理機制
外面一直在下雨,比較無聊,順便總結了下Kivy的訊息的處理過程。 總的來說,在Kivy裡,處理的訊息一共有四種:按鍵訊息,滑鼠訊息,觸屏訊息,還有自定義訊息。下面來看下整個訊息的處理流程。 先來看張圖: 先來解釋下這幾個類都是幹嘛的: 1、EventDis
Kivy a to z -- Kivy的編譯過程-distribute.sh指令碼分析
1 這一節重點來分析一下distribute.sh,以此來了解一下Kivy的整個編譯過程 2 在上一篇文章中,我們講到編譯的方法: ./distribute.sh -m 'openssl pyjnius pil kivy' 3 那整個過程是怎麼進行的,下面來分析一下: 4
深入理解JVM_java代碼的執行機制01
功能 存在 oot 對象實例 符號 token 類型 格式 找對象 本章學習重點: 1、Jvm: 如何將java代碼編譯為class文件。 如何裝載class文件及如何執行class文件。 jvm如何進行內存分配和回收。 jvm多線程
【JS】JavaScript引擎的內部執行機制
under scrip str tro blog rip 回調函數 ron span 近期在復習JavaScript,看到setTimeout函數時。想起曾經剛學時,在一本書上看過setTimeout()裏的回調函數執行的間隔時間
【java】之java代碼的執行機制
() alt 分享 str clas not roc 成員 輸入 要在JVM中執行java代碼必須要編譯為class文件,JDK是如何將Java代碼編譯為class文件,這種機制通常被稱為Java源碼編譯機制。 1、JVM定義了class文件的格式,但是並沒有定義如何將ja
python裝飾器執行機制
abc print 前沿 spl ini self. color ret instance 前沿: 首先是看到了單例模型,想不明白 outer中的參數 為什麽能像 global的參數 一樣屹立不倒。 #單例模型 def single_model(cls): in
徹底弄懂 JavaScript 執行機制
函數 大名 定時 意思 技術 渲染 文字 根據 java 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果讀完本文還不懂,可以揍我。 不論你是javascript新手還是老鳥,不論是面試求職,還是日常開發工作,我們經常會遇到這樣的情況:給定的幾行
js --- 執行機制
循環 gpo 可執行 pad loop 同步任務 16px 觸發 pos 1. JS為什麽是單線程的? JS最初被設計用在瀏覽器中,那麽想象一下,如果瀏覽器中的JS是多線程的。 那麽現在有2個進程,process1 process2,由於是多進程的JS,所以他們對同一個
[轉]JS 引擎的執行機制
wan queue 多線程 .html 單擊事件 語句 eve resolve title ------------------------------------------------------ JS 引擎的執行機制 關於JS引擎的執行機制,首先牢記2點:
js為什麽是單線程的?10分鐘了解js引擎的執行機制
容易 等於 bsp -m 深入理解 block 順序 dom 依次 深入理解JS引擎的執行機制 1.JS為什麽是單線程的? 為什麽需要異步? 單線程又是如何實現異步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.說說s
js執行機制
分發 sleep 代碼執行 過程 rom spa set 是否 引擎 1. 關於javascript js是一門單線程語言,一切js版的‘多線程’都是用單線程模擬起來的。 2. js事件循環 將任務分為2類:同步任務、異步任務 同步任務進入主線程,異步任務
深入理解Dalvik虛擬機- 解釋器的執行機制
util dlink stat counter before expose 加鎖 enter 機制 Dalvik的指令運行是解釋器+JIT的方式,解釋器就
a標簽自執行點擊事件
() style AC AD 點擊事件 click 原生 pac 標簽 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco; color: #a5b2b9 } p.p2 { margin: 0.0px 0.
淺析JS異步執行機制
一個隊列 http請求 調度 等待 __name__ 服務端 nco sta req 前言 JS異步執行機制具有非常重要的地位,尤其體現在回調函數和事件等方面。本文將針對JS異步執行機制進行一個簡單的分析。 從一份代碼講起 下面是兩個經典的JS定時執行函數,這兩個函數的區別
關於linux的命令一個莫名的想法,a到z有哪些命令
linux系統命令如果有人突然問你linux系統中a到z中命令有哪些,不知道你是什麽反應。做稍微整理下,以下是a到z的常用的命令。命令a-z: a : awk,alias,ab b:blkid ,bash,brctl c:chmod,chown,cp,cd,cat d:df,date e:echo,expor
javascript的執行機制—Event Loop
如果 異步任務。 兩個 用戶 徹底 再次 IT bsp 執行順序 既然今天要談的是javascript的事件循環機制,要理解事件循環,首先要知道事件循環是什麽。 我們先從一個例子來看一下javascript的執行順序。 <script> setTime
LINQ to Object——立即執行的Enumerable類方法
sele span 是否 技術分享 lin CA cee 註意 第一個 在前面說到LINQ to Object——延時執行的Enumerable類的方法,接下來說說LINQ to Object——立即執行的Enumerable類方法。 1.ToArray 序列轉換成數組
JavaScript執行機制
指定 i++ 立即執行 使用 func bubuko 異步任務。 下一個 href 原文 簡書原文:https://www.jianshu.com/p/0d2d42fbe1dc 大綱 1、場景分析 2、執行機制相關知識點 3、以實例來說明JavaScript的執
shell條件判斷if中的-a到-z的意思
res 設備 ring1 描述 bin 數字 sed per === [ -a FILE ] 如果 FILE 存在則為真。 [ -b FILE ] 如果 FILE 存在且是一個塊特殊文件則為真。 [ -c FILE ] 如果 FILE 存在且是一個字特殊文件則為真