Unity3D特效百例 | 案例項目實戰源碼 | Android-Unity實戰問題匯總 |
---|---|---|
游戲腳本-輔助自動化 | Android控件全解手冊 | 再戰Android系列 |
Scratch編程案例 | 軟考全系列 | Unity3D學習專欄 |
藍橋系列 | ChatGPT和AIGC |
👉關于作者
專注于Android/Unity和各種游戲開發技巧,以及各種資源分享(網站、工具、素材、源碼、游戲等)
有什么需要歡迎底部卡片私我,交流讓學習不再孤單。
👉實踐過程
😜前言
出于業余興趣,我幾年前一直想研究關于Python移植到Android上的實現方案,就像JNI開發一樣真正的在Andorid中進行混合Python編程。但出于上班忙時間少,加上人生大事精力有限,人的惰性等等原因,一直沒有深入探索,直到2024年有項目需要集成Python,就準備查詢下資料,發現已經有人做了我一直想做的事,而且已經做得比較完善了,更可喜的是思路大體一致。
真是應了那句話:你能想到的東西,世界上一定已經存在了,只不過你還沒見到而已。
支持AGP版本和Python運行版本
😜配置環境
首先你需要確保已經安裝好了Python環境,并且環境變量也配置好了,Python 的安裝包可以一鍵幫我們都處理好,只需要你仔細看清安裝彈框中的說明做好勾選即可。
gradle的版本不同,build.gradle中的寫法不同,讀者根據自己的環境做好對應。
找到工程目錄的 build.gradle
buildscript { //寫法一repositories {google()maven { url 'https://jitpack.io' }maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }maven { url 'https://maven.aliyun.com/repository/google' }maven { url 'https://maven.aliyun.com/repository/public' }maven { url 'https://maven.aliyun.com/repository/jcenter' }maven { url "https://chaquo.com/maven" } //python插件的地址 mavenCentral()}dependencies {classpath("com.android.tools.build:gradle:7.0.2")classpath "com.chaquo.python:gradle:15.0.1" //python插件的版本,盡量用新的,支持的內容多 也修復了很多問題
// classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21"}
}
task clean(type: Delete) {delete rootProject.buildDir
}
plugins { //寫法二id 'com.android.application' version '8.0.2' apply falseid 'org.jetbrains.kotlin.android' version '1.6.21' apply falseid 'com.chaquo.python' version '15.0.1' apply false //這個是重點
}task clean(type: Delete) {delete rootProject.buildDir
}
具體項目的 build.gradle
plugins {id 'com.android.application'id 'com.chaquo.python' //集成python必備
}android {compileSdk 32defaultConfig {applicationId "com.example.myapplication"minSdk 26targetSdk 32versionCode 1versionName "1.0"ndk {abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"}python {buildPython "D:\\Program Files\\Python\\python.exe" //不同的人電腦環境不同pip {install "matplotlib" //如果想要安裝三方包
// // 需求說明符,帶或不帶版本號:
// install "scipy"
// install "requests==2.24.0"
// // 相對于項目目錄的sdist或wheel文件名:
// install "MyPackage-1.2.3-py2.py3-none-any.whl"
// // 包含setup.py的目錄,相對于項目
// // 目錄(必須包含至少一個斜杠):
// install "./MyPackage"
// // "-r" '后面跟著一個需求文件名,相對于
// // 項目目錄:
// install "-r", "requirements.txt"}}testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}
}dependencies {implementation 'androidx.appcompat:appcompat:1.3.0'implementation 'com.google.android.material:material:1.4.0'implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
}
工程下的 setting.gradle
//開發andorid的都知道 gradle 配置的寫法 變動的很大 下方的設置看需要和你的當下項目是否類似再寫。
pluginManagement {repositories {google()maven { url 'https://jitpack.io' }maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }maven { url "https://chaquo.com/maven" } //確保這個地址mavenCentral()}plugins {id 'com.android.application' version '7.1.0-alpha11'id 'com.android.library' version '7.1.0-alpha11'id 'org.jetbrains.kotlin.android' version '1.6.21'}
}
dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()maven { url 'https://jitpack.io' }maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }maven { url "https://chaquo.com/maven" } //確保這個地址mavenCentral()}
}
rootProject.name = "My Application"
include ':app'
😜代碼實戰
代碼要寫在具體項目的 main 目錄的 python 文件夾。
Python中代碼
from java import jclass
def sayHello():print("這是一個測試")def greet(name):print("--- hello,%s ---" % name)def add(a,b):return a + bdef sub(count,a=0,b=0,c=0):return count - a - b -cdef get_list(a,b,c,d):return [a,b,c,d]def print_list(data):print(type(data))# 遍歷Java的ArrayList對象for i in range(data.size()):print(data.get(i))# python調用Java類
def get_java_bean():JavaBean = jclass("org.hello.JavaBean") # 實體類的類名jb = JavaBean("python")jb.setData("json")jb.setData("xml")jb.setData("xhtml")return jb
Java中代碼
核心思想便是對象轉換,PyObject類是橋梁,fromJava函數將一個Java對象轉換為相應的Python對象,toJava函數正好相反,將Python中的對象轉換成Java中的對象
public class TestPython extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_python);if (!Python.isStarted()) {Python.start(new AndroidPlatform(this)); //可以放到app里進行初始化}Python python = Python.getInstance();PyObject pyObject = python.getModule("hello"); //調用的文件名pyObject.callAttr("sayHello"); //調用的方法名Python py = Python.getInstance();// 調用hello.py模塊中的greet函數,并傳一個參數// 等價用法:py.getModule("hello").get("greet").call("Android");py.getModule("hello").callAttr("greet", "Android");// 調用python內建函數help(),輸出了幫助信息py.getBuiltins().get("help").call();PyObject obj1 = py.getModule("hello").callAttr("add", 2,3);// 將Python返回值換為Java中的Integer類型Integer sum = obj1.toJava(Integer.class);Log.d("TAG","add = "+sum.toString());// 調用python函數,命名式傳參,等同 sub(10,b=1,c=3)PyObject obj2 = py.getModule("hello").callAttr("sub", 10,new Kwarg("b", 1), new Kwarg("c", 3));Integer result = obj2.toJava(Integer.class);Log.d("TAG","sub = "+result.toString());// 調用Python函數,將返回的Python中的list轉為Java的list 也可以使用mapPyObject obj3 = py.getModule("hello").callAttr("get_list", 10,"xx",5.6,'c');List<PyObject> pyList = obj3.asList();Log.d("TAG","get_list = "+pyList.toString());// 將Java的ArrayList對象傳入Python中使用List<PyObject> params = new ArrayList<PyObject>();params.add(PyObject.fromJava("alex"));params.add(PyObject.fromJava("bruce"));py.getModule("hello").callAttr("print_list", params);// Python中調用Java類PyObject obj4 = py.getModule("hello").callAttr("get_java_bean");JavaBean data = obj4.toJava(JavaBean.class);data.print();Python pyTe = Python.getInstance(); //matplotlib庫的方法PyObject module = pyTe.getModule("plot"); //文件名byte[] dddz = module.callAttr("plot", "1 2 3 4", "1 2 3 4").toJava(byte[].class);ImageView img = findViewById(R.id.testImg);img.setImageBitmap(BitmapFactory.decodeByteArray(dddz, 0, dddz.length));}
}
- Python.getInstance():獲取Python運行環境,映射為Java中的Python;
- py.getModule:執行指定的Python文件,映射為Java中的PyObject;
- module.callAttr:執行Python文件中指定的方法,并傳遞參數,返回PyObject對象;
kotlin代碼自行轉換即可。
😜原理
簡單說就是以android的JNI技術為橋梁,JNI技術解決了Java與C/C++混合編程的問題,而Python官方解釋器則是純C語言實現的,名為CPython解釋器,在Android上,Python解釋器就是一個so動態庫。JNI接口使得C語言能反射Java的類與方法,而Python運行在C語言之上,那么Python也就具備了調用Java的能力。整個過程就是Java調用C語言代碼,C再調用CPython解釋器從而執行Python代碼;Python調用CPython解釋器,CPython調用C語言代碼,C語言代碼再反射Java代碼,完成一次反調。這之間,粘合Java與CPython解釋器的一段C語言代碼,也就是Chaquopy框架干的事。
😜可能得問題
- 當你剛配置好環境第一次運行了 python 的 hello word 后,就開始蠢蠢欲動準備搞一搞集成三方庫。可以當你在 build.gradle 中配置好 pip 一運行,發現
有可能
出現下面的問題:
Process ‘command ‘C:\Users\Administrator\AppData\Local\Programs\Python\Python312\python.exe’’ finished with non-zero exit value 1
靜下心來好好想想,區別就在 pip 安裝三方庫上,Android 上既然是 Chaquopy 遠程安裝那么就得往該插件上考慮。在整個過程中我們的配置只有兩個地方是可變動的,
一個是 python 的安裝路徑,一個是 Chaquopy 的版本號。本地安裝路徑如果和配置不一致,不是這個錯誤。那么只有一個問題,就是 Chaquopy 的版本。
我們切換最新的試試,發現解決了。 - Chaquopy 不是支持所有的三方庫,這是最難搞的事情。具體可從這查看。點擊跳轉
- 還可以實現 Python 自動生成 Java的類,但是該功能用處不大,想要了解的可自行搜索靜態代理。
Chaquopy是線程安全的。但是,因為它基于CPython(Python參考實現),所以它受到CPython的全局解釋器鎖(GIL)的限制。這意味著盡管Python代碼可以在任意數量的線程上運行,但在任何給定時刻只會執行其中一個線程。
如果Python對象引用直接或間接引用原始Python對象的Java對象,則可以創建跨語言引用循環。任何一種語言的垃圾收集器都無法檢測到這樣的循環。所以避免內存泄漏很重要。Python代碼就需要寫的很合理。
👉其他
📢作者:小空和小芝中的小空
📢轉載說明-務必注明來源:https://zhima.blog.csdn.net/
📢這位道友請留步??,我觀你氣度不凡,談吐間隱隱有王者霸氣💚,日后定有一番大作為📝!!!旁邊有點贊👍收藏🌟今日傳你,點了吧,未來你成功??,我分文不取,若不成功??,也好回來找我。
溫馨提示:點擊下方卡片獲取更多意想不到的資源。