What's new in Android M - Run Time Permission

一:小小地啰嗦几句

恭喜前公司乔迁北京!不过,也正因为如此,变成了前公司!这段时间又忙找工作,又忙找房子的。可把我给累坏了!当然,这是题外话。

安卓6.0的第一个预览版发布已经不知道多久了。10月6号安卓也推送 android 6.0的正式版。因为向公司申请了一支 Nexus 5,所以我也在第一时间升级到了6.0(话说,这刚买回来的手机,从4.4升级到6.0不知道升级了有没有一天!)。总体体验还不错。

好以,进入正题吧。既然有一个6.0的设备,那么我当然就把 AS 项目的 targetSdkVersion改到了23。可想而知,没有好好阅读官司方文档的我,可是吃了一次大亏。

二:意外的『八阿哥』

项目中有一个要拍照的需求(相信只要是有上传头像的都会用的到哈。)正常情况,我们只要通过一个 Intent,请求打开相机就可以了。代码如下:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 1001);
在 AndroidMainfest.xml,我们申明下要打开相机的权限。
<uses-permission android:name="android.permission.CAMERA"/>
运行程序,触发上述代码,然后,『八阿哥』出现了。
可以看到,抛出的异常就是Permission Denial.来看一下完整的描述:

java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity } from ProcessRecord{84e7243 12083:com.dinghu.androidpermissiontest/u0a121} (pid=12083, uid=10121) with revoked permission android.permission.CAMERA

可以看出是我们在打开相机应用的时候,出现了异常,导致程序崩溃。但是我们明明在清单文件里已经申明了相机权限。大家可以尝试下,如果将 targetSdkVersion 改成23以下,就可以正常找开相机应用了。

三.权限分类(android 6.0)

既然我们的找码没错,那就是 SDK 6.0的权限机制改变了。好吧,那我们就去看看官网上的 change log.

打开安卓的官网,点开 Banner 后,就可以看到下面的内容了:

点开中间的Requesting Permission at Run Time.跳转之后,可以看到一个关于 android 6.0权限机制的介绍。

如果大家有注意的话,可以知道在6.0之前,我们的安卓应用,在申请权限的时候,大都是在安装的时候,一次性全部授权完的。而在最新的棉花糖版本中,安卓系统把权限进行了分类。分为 Normal Permission 与 Dangerous Permission。不过虽然进行了分类,我们在要调用相关的功能时,还是都要在 AndroidManifest.xml 对相关权限进行申明的。

(原文:On all versions of Android, your app needs to declare both the normal and the dangerous permissions it needs in its app manifest, as described in Declaring Permissions. )

3.1 Normal Permissions

Normal Permissions ,大致上的定义是指,对用户的隐私与安全不会造成很大风险的权限。官方把权限分类到这一级别的权限有:

 像以上的权限,只要在清单文件里申明,那么在程序安装的时候,就会被授权。
3.2 Dangerous Permission
在 android 6.0中,所有的 dangerous permission 又属于 permission groups.这里有一个机制要注意一下。就是如果同一个组中的权限中的其中一个被授权了的话,那个同一个组中的其它权限也会被授权。比如,用户请求了一个READ_EXTERNAL_STORAGE 的权限,在授权之后,程序会同时拥有同一个组下面的 WRITE_EXTERNAL_STORAGE.

(原文:If an app requests a dangerous permission listed in its manifest, and the app already has another dangerous permission in the same permission group, the system immediately grants the permission without any interaction with the user. For example, if an app had previously requested and been granted the  READ_CONTACTS permission, and it then requests  WRITE_CONTACTS, the system immediately grants that permission.)
附上官网关于Dangerous Permission 的列表:

 

Dangerous permissions and permission groups.

四:Request Permissions at Run Time

知道了机制之后,我们就要知道申请权限了。在安卓6.0中,权限的授权其实与 IOS 的很像。就是在用到某个权限的时候,进行申请。当然,这里说的是 Dangerous级别的。在新的 SDK 中,android 提供了一个新的方法 checkSelfPermission().这个方法,是先检查该应用是否有对应的权限。

int permissionCheck =checkSelfPermission(Manifest.permission.CAMERA);

根据官方文档,如果应用已经有对应权限,返回的是PackageManage.PERMISSION_GRANTED,否则返回PERMISSION_DENIED.之后,如果没有对应的权限,再调用一下以下函数:

requestPermissions(new String[]{},requestCode)。
那么我们的程序就应该写成:
int status =checkSelfPermission(Manifest.permission.CAMERA);
switch (status) {
    case PackageManager.PERMISSION_GRANTED:
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivityForResult(intent, 1001);
        break;
    default:
        requestPermissions(new String[]{Manifest.permission
.CAMERA},requestCode);
     break;
}
如果程序已经有拍照的权限了,那么我们就直接打开相机,如果没有,就请求权限。在请求完权限之后,我们还要重写一个回调接口:
onRequestPermissionsResult
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)。第一个参数,就是我们请求权限时传入的请求码,第二个参数,是我们请求时的权限列表(因为可以一次性请求多个权限,这里我只演示请求一个权限的情况。)第三个参数,就是对应的权限的请求结果,结果是 PERMISSION_GRANTEDA或者 PERMISSION_DENIED。那么在我们项目中,就应该写成:
 
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 0:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(intent, 1001);

            } else {
            }
    }
}
现在运行程序,是不是相机可以正常拍照了。
在第一次请求权限被拒绝之后,再次请求权限,对话框会多一个 not ask agin 的选项。
如果用户勾选了选项,并且选择了拒绝,之后再进行操作时,APP 会不给任何提示。这显然对用户来说,是非常不好的体验。毕竟做为一个用户,做了操作之后,却没有任何响应。这种做法非常糟糕。
安卓提供了一个方法ActivityCompat.shouldShowRequestPermissionRationale(activity,permission)来检查用户是否拒绝了某一个权限的请求。如果允许请求,那么返回 true,否则,返回 false.所以,我们的代码应该做如下修改:
if(shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)){
    requestPermissions(new String[]{Manifest.permission
            .CAMERA}, 0);
}
else {
    //Do something to notice user
}
当返回 false 时,我们可以以一个用好的方式(对话框等),告诉用户这个权限被拒绝了,会造成相应的影响。应该让该用户在设置中打开该权限。
当然,还有更好的方式,就是第一次请求时,就告诉用户,这个权限是用来做什么的,也就是对权限进行对应的描述。这里我就不再详细介绍了。有兴趣的朋友可以自行查找相关资料。
五:关于 SDK 版本
细心的读者可能发现,以上介绍的几个方法,大都是在 SDK 23以后才有的。也就是在 SDK 23之前,没有对应方法。那也就是我们在检查权限时,最好加一个判断:
if (Build.VERSION.SDK_INT >= 23) {

    }
}
但是这样会显得很繁琐。所以,推荐 用 android.supporet.v4下的 ActivityCompat.该类支持 SDK 4之后的版本。
那么,我们的代码就换成了:
int status = ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA);
if (status == PackageManager.PERMISSION_GRANTED) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(intent, 1001);
} else {
    if(ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.CAMERA)){
        ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission
                .CAMERA}, 0);
    }
    else {
         Snackbar.make(view, "请在设置中打开相机权限,否则无法使用该功能!", Snackbar.LENGTH_LONG)
        .setAction("Action", null).show();
    }
}
这样修改以后,我们的代码就支持SDK 4以后的版本了。当 SDK 小于23时,只要你在清单文件中声明了对应权限。那么checkSelfPermission
方法返回的值就是 true.而shouldShowRequestPermissionRationale刚在 SDK 小于23时返回 false.
 
附上源码连接:下载源码

 

 

 


[app] # (str) Title of your application title = 炒股提醒 # (str) Package name package.name = stock_reminder # (str) Package domain (needed for android/ios packaging) package.domain = org.stock # (str) Source code where the main.py live source.dir = . # (list) Source files to include (let empty to include all the files) source.include_exts = py,png,jpg,kv,atlas # (list) List of inclusions using pattern matching #source.include_patterns = assets/*,images/*.png # (list) Source files to exclude (let empty to not exclude anything) #source.exclude_exts = spec # (list) List of directory to exclude (let empty to not exclude anything) source.exclude_dirs = tests, bin, venv # (list) List of exclusions using pattern matching #source.exclude_patterns = license,images/*/*.jpg # (str) Application versioning (method 1) version = 0.1 # (str) Application versioning (method 2) # version.regex = __version__ = ['"](.*)['"] # version.filename = %(source.dir)s/main.py # (list) Application requirements # comma separated e.g. requirements = sqlite3,kivy requirements = python3,kivy==2.2.1,kivymd==1.1.1,yfinance==0.1.96,plyer # 0.1.96兼容性更好 # (str) Custom source folders for requirements # Sets custom source for any requirements with recipes # requirements.source.kivy = ../../kivy # (list) Garden requirements #garden_requirements = # (str) Presplash of the application #presplash.filename = %(source.dir)s/data/presplash.png # (str) Icon of the application #icon.filename = %(source.dir)s/data/icon.png # (str) Supported orientation (one of landscape, sensorLandscape, portrait or all) orientation = portrait # (list) List of service to declare #services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY # # OSX Specific # # # author = © Copyright Info # change the major version of python used by the app #osx.python_version = 3 # Kivy version to use #osx.kivy_version = 1.9.1 # # Android specific # # (bool) Indicate if the application should be fullscreen or not fullscreen = 0 # 自动接受SDK许可(避免手动确认) android.accept_sdk_license = True # (string) Presplash background color (for new android toolchain) # Supported formats are: #RRGGBB #AARRGGBB or one of the following names: # red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray, # darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy, # olive, purple, silver, teal. #android.presplash_color = #FFFFFF # (list) Permissions android.permissions = INTERNET,VIBRATE # (int) Target Android API, should be as high as possible. android.api = 31 # (int) Minimum API your APK will support. android.minapi = 21 # (int) Android SDK version to use #android.sdk = 20 # (str) Android NDK version to use #android.ndk = 19b # (int) Android NDK API to use. This is the minimum API your app will support, it should usually match android.minapi. #android.ndk_api = 21 # (bool) Use --private data storage (True) or --dir public storage (False) #android.private_storage = True # (str) Android NDK directory (if empty, it will be automatically downloaded.) android.ndk_path = D:\Python\NDK\android-ndk-r22b android.ndk_version = 22.1.7171670 # (str) Android SDK directory (if empty, it will be automatically downloaded.) android.sdk_path = D:\Python\SDK # (str) ANT directory (if empty, it will be automatically downloaded.) #android.ant_path = # (bool) If True, then skip trying to update the Android sdk # This can be useful to avoid excess Internet downloads or save time # when an update is due and you just want to test/build your package # android.skip_update = False # (bool) If True, then automatically accept SDK license # agreements. This is intended for automation only. If set to False, # the default, you will be shown the license when first running # buildozer. # android.accept_sdk_license = False # (str) Android entry point, default is ok for Kivy-based app #android.entrypoint = org.renpy.android.PythonActivity # (str) Android app theme, default is ok for Kivy-based app # android.apptheme = "@android:style/Theme.NoTitleBar" # (list) Pattern to whitelist for the whole project #android.whitelist = # (str) Path to a custom whitelist file #android.whitelist_src = # (str) Path to a custom blacklist file #android.blacklist_src = # (list) List of Java .jar files to add to the libs so that pyjnius can access # their classes. Don't add jars that you do not need, since extra jars can slow # down the build process. Allows wildcards matching, for example: # OUYA-ODK/libs/*.jar #android.add_jars = foo.jar,bar.jar,path/to/more/*.jar # (list) List of Java files to add to the android project (can be java or a # directory containing the files) #android.add_src = # (list) Android AAR archives to add (currently works only with sdl2_gradle # bootstrap) #android.add_aars = # (list) Gradle dependencies to add (currently works only with sdl2_gradle # bootstrap) #android.gradle_dependencies = # (list) add java compile options # this can for example be necessary when importing certain java libraries using the 'android.gradle_dependencies' option # see https://developer.android.com/studio/write/java8-support for further information # android.add_compile_options = "sourceCompatibility = 1.8", "targetCompatibility = 1.8" # (list) Gradle repositories to add {can be necessary for some android.gradle_dependencies} # please enclose in double quotes # e.g. android.gradle_repositories = "maven { url 'https://kotlin.bintray.com/ktor' }" #android.add_gradle_repositories = # (list) packaging options to add # see https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.PackagingOptions.html # can be necessary to solve conflicts in gradle_dependencies # please enclose in double quotes # e.g. android.add_packaging_options = "exclude 'META-INF/common.kotlin_module'", "exclude 'META-INF/*.kotlin_module'" #android.add_gradle_repositories = # (list) Java classes to add as activities to the manifest. #android.add_activities = com.example.ExampleActivity # (str) OUYA Console category. Should be one of GAME or APP # If you leave this blank, OUYA support will not be enabled #android.ouya.category = GAME # (str) Filename of OUYA Console icon. It must be a 732x412 png image. #android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png # (str) XML file to include as an intent filters in <activity> tag #android.manifest.intent_filters = # (str) launchMode to set for the main activity #android.manifest.launch_mode = standard # (list) Android additional libraries to copy into libs/armeabi #android.add_libs_armeabi = libs/android/*.so #android.add_libs_armeabi_v7a = libs/android-v7/*.so #android.add_libs_arm64_v8a = libs/android-v8/*.so #android.add_libs_x86 = libs/android-x86/*.so #android.add_libs_mips = libs/android-mips/*.so # (bool) Indicate whether the screen should stay on # Don't forget to add the WAKE_LOCK permission if you set this to True #android.wakelock = False # (list) Android application meta-data to set (key=value format) #android.meta_data = # (list) Android library project to add (will be added in the # project.properties automatically.) #android.library_references = # (list) Android shared libraries which will be added to AndroidManifest.xml using <uses-library> tag #android.uses_library = # (str) Android logcat filters to use #android.logcat_filters = *:S python:D # (bool) Copy library instead of making a libpymodules.so #android.copy_libs = 1 # (str) The Android arch to build for, choices: armeabi-v7a, arm64-v8a, x86, x86_64 android.archs = arm64-v8a, armeabi-v7a # (int) overrides automatic versionCode computation (used in build.gradle) # this is not the same as app version and should only be edited if you know what you're doing # android.numeric_version = 1 # # Python for android (p4a) specific # # (str) python-for-android fork to use, defaults to upstream (kivy) #p4a.fork = kivy # (str) python-for-android branch to use, defaults to master #p4a.branch = master # (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) #p4a.source_dir = # (str) The directory in which python-for-android should look for your own build recipes (if any) #p4a.local_recipes = # (str) Filename to the hook for p4a #p4a.hook = # (str) Bootstrap to use for android builds # p4a.bootstrap = sdl2 # (int) port number to specify an explicit --port= p4a argument (eg for bootstrap flask) #p4a.port = # # iOS specific # # (str) Path to a custom kivy-ios folder #ios.kivy_ios_dir = ../kivy-ios # Alternately, specify the URL and branch of a git checkout: #ios.kivy_ios_url = https://github.com/kivy/kivy-ios #ios.kivy_ios_branch = master # Another platform dependency: ios-deploy # Uncomment to use a custom checkout #ios.ios_deploy_dir = ../ios_deploy # Or specify URL and branch #ios.ios_deploy_url = https://github.com/phonegap/ios-deploy #ios.ios_deploy_branch = 1.7.0 # (str) Name of the certificate to use for signing the debug version # Get a list of available identities: buildozer ios list_identities #ios.codesign.debug = "iPhone Developer: <lastname> <firstname> (<hexstring>)" # (str) Name of the certificate to use for signing the release version #ios.codesign.release = %(ios.codesign.debug)s [buildozer] # (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) log_level = 2 # (int) Display warning if buildozer is run as root (0 = False, 1 = True) warn_on_root = 1 # (str) Path to build artifact storage, absolute or relative to spec file # build_dir = ./.buildozer # (str) Path to build output (i.e. .apk, .ipa) storage # bin_dir = ./bin # ----------------------------------------------------------------------------- # List as sections # # You can define all the "list" as [section:key]. # Each line will be considered as a option to the list. # Let's take [app] / source.exclude_patterns. # Instead of doing: # #[app] #source.exclude_patterns = license,data/audio/*.wav,data/images/original/* # # This can be translated into: # #[app:source.exclude_patterns] #license #data/audio/*.wav #data/images/original/* # # ----------------------------------------------------------------------------- # Profiles # # You can extend section / key with a profile # For example, you want to deploy a demo version of your application without # HD content. You could first change the title to add "(demo)" in the name # and extend the excluded directories to remove the HD content. # #[app@demo] #title = My Application (demo) # #[app:source.exclude_patterns@demo] #images/hd/* # # Then, invoke the command line with the "demo" profile: # #buildozer --profile demo android debug
最新发布
11-13
<think> 我们正在解析一个buildozer.spec配置文件,该文件用于使用Buildozer工具打包Kivy应用(如炒股提醒app)为Android等平台。以下是对配置文件中各关键部分的详细解释和使用方法。 ### 1. 配置文件结构 buildozer.spec文件由多个节(section)组成,每个节以`[section_name]`开头,包含多个键值对配置。主要节包括: - `[app]`: 定义应用核心信息 - `[buildozer]`: Buildozer工具自身配置 - `[loggers]`: 日志配置(通常无需修改) - `[requirements]`: 应用依赖声明 - `[source]`: 源代码包含规则 - `[p4a]`: Python-for-Android配置 - `[kivy]`: Kivy框架特定配置 - `[android]`: Android打包配置 - `[app:test]`: 测试配置(可选) ### 2. 关键配置项解析 #### [app] 节 ```ini [app] title = StockAlert # 应用名称(显示在设备上) package.name = stockalert # 应用包名(唯一标识,通常采用反向域名) package.domain = org.stockalert # 包域名(用于生成完整包名) source.dir = . # 源代码目录(相对于spec文件) source.include_exts = py,png,jpg,kv,atlas # 包含的文件扩展名 version = 1.0 # 应用版本号 requirements = python3,kivy==2.2.1,kivymd==1.1.1,plyer,requests,numpy,pandas,yfinance # Python依赖库 orientation = portrait # 屏幕方向(portrait竖屏,landscape横屏) fullscreen = 0 # 是否全屏(0=否,1=是) ``` > 注意:`requirements`中版本号使用`==`固定版本可避免打包时自动升级导致兼容性问题[^1] #### [android] 节 ```ini [android] arch = armeabi-v7a # 目标CPU架构(可选arm64-v8a) sdk.minapi = 21 # 最小Android API级别(建议≥21) sdk.targetapi = 34 # 目标Android API级别 p4a.branch = master # p4a分支 android.ndk = 25b # NDK版本 android.sdk = # SDK路径(留空自动下载) android.permissions = INTERNET,ACCESS_NETWORK_STATE,VIBRATE # 所需权限 ``` > 权限说明: > - `INTERNET`: 网络访问(获取股票数据必需) > - `ACCESS_NETWORK_STATE`: 检查网络连接状态 > - `VIBRATE`: 振动权限(用于提醒功能) #### [buildozer] 节 ```ini [buildozer] log_level = 2 # 日志级别(0=错误,1=警告,2=信息,3=调试) warn_on_root = 1 # 当以root用户运行时警告 ``` > 建议开发阶段设置`log_level=3`以便调试打包问题 ### 3. 打包流程 #### 步骤1:安装Buildozer ```bash pip install buildozer ``` #### 步骤2:初始化spec文件 ```bash buildozer init # 生成默认buildozer.spec文件 ``` #### 步骤3:修改配置 按需编辑`buildozer.spec`(如上述配置示例) #### 步骤4:打包APK ```bash buildozer -v android debug # 生成调试版APK # 或 buildozer android release # 生成发布版(需配置签名) ``` ### 4. 常见问题解决 - **依赖冲突**:在`requirements`中明确指定版本号(如`kivy==2.2.1`) - **打包失败**:检查`buildozer.spec`中的日志路径(默认`~/.buildozer`) - **权限不足**:添加缺失权限到`android.permissions` - **资源缺失**:将资源文件放入`source.dir`目录,并在`source.include_exts`中添加扩展名 ### 5. 高级配置示例 #### 添加本地库 ```ini [app] ... source.include_patterns = assets/*,data/*.json # 包含特定模式文件 ``` #### 自定义启动图标 ```ini [app] ... icon.filename = %(source.dir)s/assets/icon.png # 指定图标路径 ``` ### 打包决策流程图 ```mermaid graph TD A[编辑buildozer.spec] --> B{配置检查} B -->|通过| C[执行打包命令] B -->|失败| D[查看日志修复配置] C --> E{打包成功?} E -->|是| F[获取APK文件] E -->|否| D D --> G[分析错误类型] G --> H[依赖问题?] -->|是| I[调整requirements] G --> J[资源缺失?] -->|是| K[添加资源路径] G --> L[权限不足?] -->|是| M[添加权限] I & K & M --> B ``` §§相关问题§§ 1. 如何为Kivy应用添加自定义Android权限? 2. Buildozer打包时如何排除特定文件? 3. 怎样配置Buildozer使用本地Android SDK? 4. 如何为Kivy应用减少APK体积? [^1]: Python打包最佳实践指南(PyPA官方文档) </think> ### Buildozer.spec 配置文件解析 Buildozer.spec 是 Kivy 应用打包的核心配置文件,用于将 Python 代码转换为 Android/iOS 应用。以下是关键配置项解析及使用方法: --- #### 一、核心配置项解析 ```ini [app] title = StockAlertApp # 应用名称 (显示在设备桌面) package.name = stockalert # 包名 (反向域名格式,需唯一) package.domain = org.user # 组织域名 source.dir = . # 源代码目录 source.include_exts = py,png,jpg,kv,ttf # 包含的文件类型 version = 1.0 # 应用版本 requirements = # 依赖库清单 (重要!) python3, kivy==2.2.1, kivymd==1.1.1, pandas, yfinance, numpy, requests [android] arch = armeabi-v7a # CPU架构 (可选 arm64-v8a) sdk.minapi = 21 # 最低Android版本 (5.0+) sdk.targetapi = 34 # 目标Android版本 (14) p4a.branch = master # python-for-android 分支 android.permissions = INTERNET, ACCESS_NETWORK_STATE # 权限声明 ``` > **关键说明**: > 1. `requirements` 必须包含所有依赖(含间接依赖),缺失会导致运行时崩溃 > 2. 权限声明需匹配功能(如网络权限 `INTERNET` 对应股票数据获取)[^1] --- #### 二、打包流程 ##### 1. 安装 Buildozer ```bash pip install buildozer ``` ##### 2. 初始化配置文件 ```bash buildozer init ``` ##### 3. 修改配置后打包 ```bash buildozer -v android debug # 生成调试版APK # 输出路径: bin/stockalertapp-1.0-debug.apk ``` ##### 4. 发布签名 (可选) ```ini [app] # 在[app]节添加 android.keystore = release.keystore android.keystore_password = YOUR_PASSWORD ``` --- #### 三、常见问题解决 ##### 依赖冲突处理 ```ini # 强制指定兼容版本 (示例) requirements = kivy==2.2.1, kivymd==1.1.1, numpy==2.2.6 # 避免与pandas冲突 ``` ##### 资源文件包含 ```ini # 添加非Python文件 source.include_exts = py,png,jpg,ttf,json source.include_patterns = assets/*, data/* ``` ##### 日志调试 ```bash buildozer android debug deploy run logcat # 实时查看日志 ``` --- #### 四、配置优化建议 1. **减小APK体积**: ```ini [buildozer] # 启用P4A优化 p4a.options = --optimize=speed ``` 2. **多架构支持**: ```ini [app] android.archs = armeabi-v7a, arm64-v8a ``` 3. **启动图定制**: ```ini [app] icon.filename = %(source.dir)s/assets/icon.png presplash.filename = %(source.dir)s/assets/splash.jpg ``` > **打包决策流程图**: ```mermaid graph TD A[编辑 buildozer.spec] --> B{requirements 完整?} B -->|是| C[执行打包命令] B -->|否| D[补充缺失依赖] C --> E{打包成功?} E -->|是| F[测试APK] E -->|否| G[查看日志修复] G --> H[依赖冲突?] -->|是| I[固定版本] G --> J[资源缺失?] -->|是| K[添加文件路径] ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值