jnius调用Intent.putExtra()需传Boolean/Long但仅接收String报错
Kivy+Jnius 调用安卓日历插入事件传参异常解决方案
问题场景
基于Kivy+Jnius开发Python安卓应用时,需通过android.content.Intent的ACTION_INSERT动作调用系统日历实现事件插入,初始实现代码如下:
from jnius import autoclass Intent = autoclass("android.content.Intent") Calendar = autoclass("java.util.Calendar") CalendarContract = autoclass("android.provider.CalendarContract") Events = autoclass("android.provider.CalendarContract$Events") JS = autoclass("java.lang.String") # JB = autoclass("java.lang.Boolean") intent = Intent() date = Calendar.getInstance() date.set(2022, 5, 26) intent.setData(Events.CONTENT_URI) intent.putExtra(Events.TITLE, JS("Some Title")) intent.putExtra( CalendarContract.EXTRA_EVENT_ALL_DAY, JS("true"), ) intent.putExtra( CalendarContract.EXTRA_EVENT_BEGIN_TIME, JS(str(date.getTimeInMillis())), ) intent.setAction(Intent.ACTION_INSERT) PythonActivity = autoclass("org.kivy.android.PythonActivity") currentActivity = cast("android.app.Activity", PythonActivity.mActivity) currentActivity.startActivity(intent)
异常表现
代码运行后可正常拉起系统日历的事件编辑页面,但仅事件标题TITLE字段传参生效,事件时间、全天事件标识均使用系统默认值,logcat抛出错误:
Key allDay expected Boolean but value was a java.lang.String
对照安卓官方API文档确认参数类型要求:
CalendarContract.EXTRA_EVENT_ALL_DAY需传入Boolean类型值CalendarContract.EXTRA_EVENT_BEGIN_TIME需传入Long类型值
先后尝试4种传参方式均失败:
- 传入
JB("true")(JB为autoclass加载的java.lang.Boolean类):Jnius抛出异常Invalid instance of 'java/lang/Boolean' passed for a 'java/lang/String',表现为putExtra()方法仅接收String类型参数 - 传入
cast("java.lang.Boolean", JS("true")):同样报Boolean实例不符合String参数要求的错误 - 传入
cast("java.lang.CharSequence", JS("true")):报allDay字段期望Boolean但收到java.lang.String的错误 - 直接传入Python原生布尔值
True:抛出java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean异常
根因分析
异常核心原因是Jnius的Java重载方法自动匹配机制:Intent.putExtra()存在多个参数类型不同的重载版本,传参时如果没有传入明确匹配的Java类型,Jnius会默认匹配到putExtra(String name, String value)的重载版本,所有非String类型参数要么被强转为String,要么直接抛出类型不匹配错误,最终导致系统日历收到的参数类型和API要求不符。
修复方案
修复时需明确传入对应Java类型的参数,让Jnius匹配到正确的putExtra重载,同时补全代码中缺失的依赖导入、修正Calendar月份计数的常见坑,修复后可运行代码如下:
from jnius import autoclass, cast # 补全cast方法的导入 Intent = autoclass("android.content.Intent") Calendar = autoclass("java.util.Calendar") CalendarContract = autoclass("android.provider.CalendarContract") Events = autoclass("android.provider.CalendarContract$Events") # 加载需要的Java包装类 JBoolean = autoclass("java.lang.Boolean") JLong = autoclass("java.lang.Long") # 构造Intent时直接指定Action更符合安卓开发规范 intent = Intent(Intent.ACTION_INSERT) date = Calendar.getInstance() # 注意:Java Calendar月份从0开始计数,0对应1月,原代码写5实际对应6月 # 建议使用Calendar自带的月份常量避免计数偏移 date.set(2022, Calendar.JUNE, 26) intent.setData(Events.CONTENT_URI) # String类型参数正常传入即可 intent.putExtra(Events.TITLE, "Some Title") # 全天标识传入明确的Boolean类型,直接使用JBoolean常量无需手动new实例 intent.putExtra( CalendarContract.EXTRA_EVENT_ALL_DAY, JBoolean.TRUE ) # 开始时间传入Long类型的时间戳 intent.putExtra( CalendarContract.EXTRA_EVENT_BEGIN_TIME, JLong(date.getTimeInMillis()) ) PythonActivity = autoclass("org.kivy.android.PythonActivity") currentActivity = cast("android.app.Activity", PythonActivity.mActivity) currentActivity.startActivity(intent)
额外说明
- 该问题属于Jnius调用Java重载方法的常见用法问题,不属于Kivy框架bug,无需向Kivy项目提交issue。
- 如果需要传结束时间,同样使用
JLong包装时间戳传入CalendarContract.EXTRA_EVENT_END_TIME参数即可。 - 若需要传事件地点、描述等String类型字段,直接传入Python字符串即可,不需要手动转换为
java.lang.String。
内容的提问来源于stack exchange,提问作者bagdenaj
相关产品推荐
相关产品推荐

