<jupyter_start><jupyter_text>Kivy指南-3-Android录音app> 做一个录音app来演示Python-Java交互的Pyjnius模块卓越的性能- toc: true - badges: true- comments: true- categories: [jupyter,Kivy,Android,iOS]- image: kbpic/3.7appui.png 在上一章，我们简要介绍过Kivy要实现跨平台应用，可能在不同的平台需要选择不同的代码，为一些用户增强体验效果，实现具体平台的任务。有时，这些都很简单；比如，如果Kivy发现目标系统支持它，多点触控就会启动——不需要写任何代码，但是要考虑一些点击事件原来的功能可能会与多点触控产生冲突。另外一些平台相关的任务，像代码不能在其他系统上运行，是有很多原因造成的。还记得画图app的鼠标光标吗？代码要用Pygame封装的底层SDL光标功能，如果你熟悉SDL和Pygame那就很简单。因此，为了让app可以跨平台，我们要尽量避免在系统兼容性不好的代码；因为那样可能导致程序崩溃。然而，Kivy应用具有良好的平台兼容性——Mac，Windows，Linux，iOS，Android和Raspberry Pi——都没什么大问题。![](kbpic/3.1Kivysupportsplatforms.png) 教学大纲：- 通过Pyjnius实现Python与Java的交互- 在Android系统设备上测试Kivy应用- 用Python调用Android的声音API，允许我们记录和播放声频文件- 制作一个紧凑型用户界面，类似Windows Phone- 用图标字体改进app矢量图标的显示  平台相关代码 这本书绝大多数app都是平台无关代码，因为Kivy具有高度移植性。但这一次我们做一个仅支持Android平台的应用。这么做肯定会减少我们的用户，但是它能让我们接触到一些具体平台功能的处理方法。这种需求可以实现，是立足于Kivy不断努力支持多个平台，使得用户在不同平台上具有类似的体验。因此，我们可以真正简单的做到一次编写，处处运行。但是，要实现跨平台，你就要用每个系统支持的功能。不同系统功能的最大公约数集合包括屏幕可以显示图像，如果有声卡就获取声音，接受用户的输入等等。每个Kivy应用，本质上都基于Python，还支持Python的标准模块。可以利用网络编程，支持大量的协议操作，还提供很多通用性的算法和功能。还有就是在大多数平台上，纯kivy程序的IO能力会受到限制，通用计算机系统的一小部分都是这样，像智能手机和平板电脑。让我们看看现代移动设备的API接口，这里以Android为例。我们把每个API分成两部分：一部分是Python/Kivy支持的，另一部分不是。Python/Kivy支持的特性如下：- 图形硬件加速- 支持多点触控输入- 播放声音- 支持网络Python/Kivy不支持的特性如下：- 调制解调器，语言电话和短信- 内置摄像头拍照和录像- 内置麦克录音- 数据云存储- 蓝牙和其他近场通信- 位置服务和GPS- 指纹识别- 传感器类，加速器、陀螺仪- 屏幕亮度调节- 振动功能- 电池充电百分比>这些不支持的列表里面，不同的Python模块已经支持，像Audiostream可以录音，Plyer可以实现很多功能。>因此，这些特性并非完全不能支持；实际上，这些功能在不同的平台上都是十分碎片化的，即使在Android系统上也没有统一的版本；因此，你写完具体平台的代码后，还是会发现没法儿移植。从前面的比较中可以看出，Android有一堆功能，只要一部分被Python/Kivy支持。这无疑为你用Kivy开发Android应用留下了大量的自由想象空间。你会学到Python调用Android API的知识，可以让Kivy做任何事情。另一个优势就是，你可以编写全新的类去支持具体特定硬件的移动设备，包括虚拟现实app，支持的陀螺仪游戏，全景拍摄相机等等。  Pyjnius介绍 要充分利用Android功能，就要用Java写的一堆API。我们要做的录音app，类似于Android和iOS的应用，很简单的功能。不像纯Kivy程序要从头开始，Android API为我们提供一堆录音的功能。下面我们就通过做录音app来演示Python-Java交互的[Pyjnius](https://github.com/kivy/pyjnius)模块卓越的性能，同样是Kivy开发者的项目。我们要开发的内容很简单——录音，回放功能——你会发现这种交互很简单，不需要一堆错综复杂的细节去实现这点小功能。Pyjnius最有趣的属性就是它并非在Android上面添加一个层来调用API，而是运行你直接通过Python运行Java。这样你就可以完全使用原生的Android API，可以参考适合Java开发的Android文档，不过不是Python文档。但是，这比没有API文档要好。>我们这里说Pyjnius是用来做Android开发的，其实也可以开发Java桌面应用。这是很有趣的，因为还有一个Java API的Python模块叫Jython，很慢而且不完整。Pyjnius可以让你直接使用CPython，再加上Numpy就可以让程序飞起来。>总之，让你想通过Python用Java，考虑Pyjnius吧。  Android模拟器 这章做的app是要运行在Android上的，不能运行在我们的电脑上，因此我们需要用到Android设备，如果你没有设备，也可以安装Android模拟器。一个方便高效的Android模拟器可以让你事半功倍。推荐一个模拟器，就是[Genymotion](http://www.genymotion.com/)，你可以下载一个免费版来用。不同的系统安装方法不同，我们就不提供教程了，自行谷歌之，还是比较简单的。用虚拟机安装Android模拟器的时候，下面一些建议供参考：- 建议保持Android最新版本，向后兼容性比较差；旧版本的系统级别的调试问题没有完全解决。- Android社区资源丰富，如果有问题就检索，你遇到的坑别人也踩过。- Kivy Launcher app是很不错的测试工具，你可以在[官方网站](http://kivy.org/)找到apk，建议装到手机上，方便程序调试。- 不同的模拟器质量和兼容性层次不齐。如果你发现一次没搞定，建议你换个虚拟机或模拟器试试。下面这个截图就是Genymotion启动的模拟器，完全支持Kivy Launcher。![Genymotion](kbpic/3.2AndroidGenymotion.png)  Metro UI 现在让我们用Window Phone的主屏风格来建立一个用户界面。这些不同大小的矩形彩色网格，被称为**Metro UI**风格，不过后来更名为**Modern UI**。我们的app就是要仿这个。![metroui](kbpic/3.3metroui.png)当然，我们并不是要做出这样，只是用一下风格来构建我们的界面。下面是对风格的总结：- 每个元素都是一个矩形网格- IU元素呈现扁平化特征（第一章讨论过，表面纯色，没有阴影，也没有圆角）- 格子可以根据需要变大，方便点击看起来非常简单吧。其实用Kivy实现起来也很简单。  按钮 现在开始吧，首先做个按钮`Button`类，就像我们在之前的应用里做的,这里我们重用第二章画图app的按钮：```yaml:    background_normal: 'button_normal.png'    background_down: 'button_down.png'    background_color: C('95A5A6')    font_size: 40```我们之前设计调色板时，把背景色成设置重白色。这里我们的`background_color `属性是一个底色，我们分配一个浅白色作为`background_color`。这次我们不要边框有颜色。之后就是按下按钮的颜色，`background_down`属性我们设置成25%透明白色。再在上面加上黑色，就可以获得一个深色的背景：![backgroundcolor](kbpic/3.4backgroundcolor.png)  网格结构 布局有点小复杂。Kivy没有现成的Modern UI模板可用，我们要自己仿制一个`GridLayout`部件。它和`BoxLayout`部件类似，不过是二维的，所以没有`orientation: 'horizontal'`或`'vertical'`属性。如果没有其他需求，一个`GridLayout`部件就可以搞定，但是我们还想要不同尺寸的按钮。目前，`GridLayout`部件不支持通过网格的合并成更大的网格（HTML里面的`rowspan`和`colspan`属性更方便）。因此，我们换个思路，先用一个`GridLayout`部件作为根部件，用大网格，然后再在里面增加一个`GridLayout`部件，用小网格。因为Kivy可以完美支持嵌套布局，我们可以用下面的kv代码实现`recorder.kv`文件：```yaml:import C kivy.utils.get_color_from_hexGridLayout:    padding: 15        Button:    background_color: C('3498DB')    text: 'aaa'        GridLayout:        Button:        background_color: C('2ECC71')        text: 'bbb1'                Button:        background_color: C('1ABC9C')        text: 'bbb2'                Button:        background_color: C('27AE60')        text: 'bbb3'                Button:        background_color: C('16A085')        text: 'bbb4'            Button:    background_color: C('E74C3C')    text: 'ccc'            Button:    background_color: C('95A5A6')    text: 'ddd'```翻翻前面的章节，把`main.py`也做出来就可以运行了，自己试试吧。注意类的名称是`RecorderApp`，kv文件名大写，再加`App`，不清楚参考第一章命名相关内容。注意嵌套的`GridLayout`部件是怎么和同级的大按钮放在一起的。如果你记得前面那个WP系统主平面，还得继续改进代码：四个小按钮和一个大按钮一样大。嵌套的`GridLayout`部件是这些小按钮的容器。  可视化属性 在外面的网格里，`padding`属性是为了让部件离屏幕的四边有一定边距。把其他`GridLayout`部件的可视化属性都放到一个类中：```yaml    :    cols: 2    spacing: 10    row_default_height:         (0.5 * (self.width - self.spacing[0]) -        self.padding[0])    row_force_default: True```>要注意的是`padding`和`spacing`属性都是列表，不是数值。`spacing[0]`是水平间距，之后是垂直间距。但是，我们用一个数值初始化，就像前面的代码显示的；数值可以用在两个方向。嵌套网格由具有相同间距的两列组成，`row_default_height`属性是厚度：我们可以说，“让行高等于格子宽度”。但是下面我们手动计算想要的高度，0.5是因为有两列：$$行高 = 0.5 × (屏幕宽度 - 所有边距和间距)$$如果我们不这么做，网格里面的按钮就会垂直填充全部空间，这不是我们要的效果，尤其不是很多按钮的时候，每个就会显得巨大难看。另外，我们希望所有的按钮都是方块。下面就是之前代码设计的“Modern UI”界面：![uidesign](kbpic/3.5uidesign.png)  可缩放的矢量图标 要做好看的应用UI，就得有图标，不只是按钮+文字。当然，我们可以在按钮上加图片，但是更好的办法是有图标字体——后面你会发现这种方法更具柔性。  图标字体 图标字体本质上和普通字体一样，除了它们的符号与文字无关。比如，你输入“P”就可以获得Python的logo，而不是字母P；每个字都是与字母相对于的图标。使用图标字体的不足——用这些字体的代码很难读——因为文字-图标对应关系不太明显。这点可以通过常量来代替要输入符号。还有一些字体不使用英文字母，改用Unicode码与图标对应，比如[Emoji颜文字](http://en.wikipedia.org/wiki/Emoji)。使用这类图标字体的前提是目标平台支持Unicode，这方面不是每个平台都ok，尤其是移动平台。我们这里的app只用ASCII码。  合理使用图标字体 在网页上，字体图标解决了很多图片（[栅格图](http://en.wikipedia.org/wiki/Raster_graphics)）相关的问题：- 首先是图片放大后会失真——虽然有算法可以解决这类问题，但是并不完美。相反图标字体是矢量图，可以无限放大。- 栅格图文件包含的示意图（像图标和UI元素）比矢量格式字节空间大。这就显然不能应用到JPEG格式的图片上，会使字节空间更大。- 另外，图标字体通常就是一套字体放在一个文件上，就是说一个HTTP请求就可以遍历。普通的图片分别是单个文件，明显增加HTTP请求负担；有一些方法可以改善这点，像[CSS sprites](https://css-tricks.com/css-sprites/)可以把多个图片合成一张图改善性能，不过使用不太广泛，而且有些问题。- 图标字体里面，颜色可以随意变化，在CSS文件增加`color: red`即可。尺寸、角度和其他那些普通图片不容易搞定的属性都很容易设置，就好像在位图上操作。以上这些并不适用于Kivy开发，不过图标字体的使用已经是现代网页开发的范本了，尤其是自大量优质字体开始出现以来——现在有几百种图标字体可用。>最棒的两个免费字体就是[Font Squirrel](http://www.fontsquirrel.com)和[Google Fonts](https://www.google.com/fonts)。不用介意这些网站的字体是用来做网页开发的，大多数字体都可以离线使用。>最需要注意的是字体文件格式：Kivy只能支持True Type(`.ttf`)文件格式。好在大多数字体都是这个格式，即使不是也可以格式转换。  Kivy中使用图标字体 我们的app使用由<NAME>设计的[Modern Pictograms](http://www.fontsquirrel.com/fonts/modern-pictograms)免费字体。截图如下所示：![ModernPictogramsiconfont](kbpic/3.6ModernPictogramsiconfont.png)要把字体加入Kivy程序，我们可以用第一章时钟app的方法。这里，我们不这么做，因为图标字体字体风格完全不同。还是，通过字体名称连接字体，而不是用字体文件名（`modernpics.ttf`）连接。这样，你就可以重命名字体文件或者移动文件路径，而不用每次都通篇改一遍。在`main.py`写如下代码：<jupyter_code>from kivy.app import App
from kivy.core.text import LabelBase


class RecorderApp(App):
    pass


if __name__ == "__main__":
    LabelBase.register(name="Modern Pictograms", fn_regular="modernpics.ttf")
    RecorderApp().run()<jupyter_output><empty_output><jupyter_text>这样`recorder.kv`文件就可以使用图标字体了。首先，我们把`Button`改进一下，方便后面改变字体。代码如下：```yaml:    background_normal: 'button_normal.png'    background_down: 'button_down.png'    font_size: 24    halign: 'center'    markup: True````halign: 'center'`表示我们希望每行文字都在按钮的正中间。`markup: True`是必须的，因为我们后面自定义按钮需要这样。现在，我们来升级所有按钮。这是一个例子：```yamlButton:    background_color: C('3498DB')    text:        ('[font=Modern Pictograms][size=120]'        'e[/size][/font]\nNew recording')```通常不需要为Kivy文件字符串加括号，这么做是为了显示成多行，这和一行长串是一样的。注意在`[font][size]`表情里面的`'e'`，这就是图标代码。每个按钮用一个图标，改变一个图标就是替换`recorder.kv`文件里面的一个字母。具体对应关系可以在Modern Pictograms字体网站查询。>为了方便浏览图标字体，你需要使用字体浏览器。通常，每个系统都有类似程序。- Windows系统使用Character Map- Mac系统使用Font Book- Linux系统由桌面环境决定，GNOME使用gnome-font-viewer>当然也可以网页搜索。流行字体都有详细的说明。下面就是我们的界面啦：![appui](kbpic/3.7appui.png)不错吧，可以Modern UI很像吧。>你可能想知道右上角的小绿图标是干嘛的。它们是为了给不同的设备设置不同的录音质量。另外三个按钮——录音，播放，删除——尚不足以呈现Modern UI的风格，因为它需要更丰富好玩的外观。  在Android上测试 现在，我们的app虽然没有包含任何Android平台相关的代码，但是我们也可以在Android平台上测试。能这么做的前提是Android平台上装了Kivy Launcher。把app打包成Kivy Launcher支持的程序有点琐碎。我们打算增加两个文件，`android.txt`和`icon.png`，都放在`main.py`与`recorder.kv`同名文件夹里，然后拷贝到SD卡的`/Kivy`文件夹即可，如下图所示：![SDcard](kbpic/3.8SDcard.png)当然启动Kivy Launcher，它就会扫描文件目录的完整路径，如果没SD卡还是能找到。`android.txt`非常简单：```title=App Nameauthor=Your Nameorientation=portrait````title`和`author`设置后会在程序列表显示出来。`orientation`可以是`portrait`（正常显示，高度>宽度）和`landscape`（水平显示，宽度>高度），由应用布局设计决定。`icon.png`文件是可选的，如果没有就是空白图标。建议找个漂亮的，程序的第一印象就是图标。注意`icon.png`文件名不能改变，否则Kivy Launcher找不到启动位置。把之前做过的程序都放进去，你就会看到程序列表，如下所示：![applicationslist](kbpic/3.9applicationslist.png)>如果不是这样，建议检查一下文件路径。现在就可打开程序了。这是在Android上测试Kivy程序最方便的办法——就是简单的复制文件。  用Android的API 已经完成了app的用户界面，下面我们来用Android的API实现录音的功能，要用到的两个Java类是`MediaRecorder`和`MediaPlayer`。Python和Java都是面向对象语言，看着好像差不多，但是两者对OOP理论的应用大相径庭。与Python相比，很多Java的API都在一坨坨的使用设计模式。所以，你在解决很小的任务时，也需要写一堆废话，就不用觉得奇怪。>在1913年的时候，<NAME>就写下了对Java架构的评论：>*只有一种办法可以粉碎这些“类”的阻力，那就是，在我们的社会中找到能够推陈出新的力量。*>这段话没有提及之后的Python和Pyjnius，但是观点明确——即使在一百年前，过度使用类也是不受社会欢迎的。幸运的是，我们的任何相对简单。要用Android API实现录音，我们只需要下面5个Java类：- `android.os.Environment`：这个类提供了很多有用的环境变量。我们需要用它来确定文件保存的路径。临时存放的位置就是`'/sdcard/'`或者简单的内容，但是即使不同的设备路径不一样。因此，我们别这么设置路径。- `android.media.MediaRecorder`：这是我们的主力，实现了音频和视频的抓取和保存功能。- `android.media.MediaRecorder$AudioSource`， `android.media.MediaRecorder$AudioEncoder`和`android.media.MediaRecorder$OutputFormat`：这些类列出了我们需要传递到`MediaRecorder`不同方法里面的参数。>Java类命名规则：>通常类名里面的`'$'`符号表示内部类。这种方法有点无厘头，因为你可以声明一个相似的类不用后面跟任何东西——`'$'`在Java变量和类名称中是可用的，与JavaScript里面的没有太多不同。但这种奇葩的命名方法让人无语。  加载Java类 下面的代码就是通过Pyjnius加载Java类：<jupyter_code>from jnius import autoclass

Environment = autoclass("android.os.Environment")
MediaRecorder = autoclass("android.media.MediaRecorder")
AudioSource = autoclass("android.media.MediaRecorder$AudioSource")
OutputFormat = autoclass("android.media.MediaRecorder$OutputFormat")
AudioEncoder = autoclass("android.media.MediaRecorder$AudioEncoder")<jupyter_output><empty_output><jupyter_text>如果你直接运行代码，可能会出现下面的错误：- **ImportError: No module named jnius**：如果Pyjnius没装- **jnius.JavaException: Class not found 'android/os/Environment'**：如果需要Android类没加载成功（在桌面系统上Android没装好可能会这样）。如果遇到其中任何一个错误都说明没配置好。因为代码不再是跨平台的了，让我们到Android设备或者模拟器上测试一下。这个app完全依赖Android相关的Java特性。下面我们将把Java类与Python代码结合到一起。>记得这些类的定义文档都是Java的，不是Python。你可以看看Google官方的[Android开发入门](http://developer.android.com/reference/packages.html)，然后把Java反应成Python代码，看着很恐怖，多试试，其实很简单。  查找保持路径 让我们演示一下Pyjnius这种混合语言使用API的过程。我们还可以通过Java检查SD卡是否已经挂载。```javaimport android.os.Environment;String path = Environment.getExternalStorageDirectory().getAbsolutePath();```翻译成Python就是：<jupyter_code>Environment = autoclass("android.os.Environment")
path = Environment.getExternalStorageDirectory().getAbsolutePath()<jupyter_output><empty_output><jupyter_text>两者是完全一样的。然后，我们可以通过log查看运行的结果。<jupyter_code>from kivy.logger import Logger

Logger.info('App: storage path == "%s"' % path)<jupyter_output><empty_output><jupyter_text>调试信息会显示下面这行日志：**[INFO] App: storage path == "/storage/sdcard0"**  在设备中看日志 当你在开发阶段运行Kivy应用时，日志会立刻在命令行窗口显示。当你在Kivy Launcher运行代码时，虽然不是很容易，显示日志也是非常有用的特性。要看到日志，你可以在程序运行的时候到程序目录下（`/Kivy/Recorder `），里面会生成一个新的`.kivy`目录，这个目录里面有日志文件`.kivy/logs`。如果是用Android SDK运行模拟器，可以打开开发者模式的USB调试功能，然后用`adb logcat`查看所有日志，里面包括Kivy日志。如果你用Android Studio 或Eclipse等IDE，你可以用logcat对日志进行过滤处理。当调试程序出问题或应用不能启动的时候日志文件是很冗长的。Kivy还打印了各种关于运行环境的警告日志，像缺少库文件或功能，Python模块加载失败，或者其他提示。  录音 现在让我们用Android的API来逐个实现app的功能。现在的代码就是把Android的API翻译成Python。如果你对原始的Java代码感兴趣，你可以去官方网站看[文档](http://developer.android.com/guide/topics/media/audio-capture.html)。下面的代码就是`MediaRecorder`对象初始化：<jupyter_code>storage_path = (
    Environment.getExternalStorageDirectory().getAbsolutePath() + "/kivy_recording.3gp"
)

recorder = MediaRecorder()


def init_recorder():
    recorder.setAudioSource(AudioSource.MIC)
    recorder.setOutputFormat(OutputFormat.THREE_GPP)
    recorder.setAudioEncoder(AudioEncoder.AMR_NB)
    recorder.setOutputFile(storage_path)
    recorder.prepare()<jupyter_output><empty_output><jupyter_text>这就是把啰七八嗦的Java代码直译成Python的代码。你可以自定义里面的属性。比如`AMR_NB`（**自适应多速率，Adaptive Multi-Rate**编解码器，用来做语音优化的，广泛用于GSM和其他移动网络的设备中）改成`AudioEncoder.AAC`（**进阶音讯编码，Advanced AudioCoding**标准，类似于MP3的一种编码标准）。这么改不是很合适，因为手机的麦克风不只是录制音乐，还要录制你的声音。下面就是“开始/结束录音(Begin/End)”按钮部分代码。这部分代码和第一章时钟app的思路一样：<jupyter_code>class RecorderApp(App):
    is_recording = False

    def begin_end_recording(self):
        if self.is_recording:
            recorder.stop()
            recorder.reset()
            self.is_recording = False
            self.root.ids.begin_end_recording.text = (
                "[font=Modern Pictograms][size=120]" "e[/size][/font]\nBegin recording"
            )
            return

        init_recorder()
        recorder.start()
        self.is_recording = True
        self.root.ids.begin_end_recording.text = (
            "[font=Modern Pictograms][size=120]" "%[/size][/font]\nEnd recording"
        )<jupyter_output><empty_output><jupyter_text>很简单吧，就是首先储存状态`is_recording`，然后实现各个功能：1. 开始或停止`MediaRecorder`对象；2. 改变`is_recording`状态；3. 升级按钮文字以反映当前状态。程序的最后一部分就是升级`recorder.kv`，我们要调整一下“开始/结束录音(Begin/End)”按钮代码来调用`begin_end_recording()`函数：```yaml    Button:        id: begin_end_recording        background_color: C('3498DB')        text:            ('[font=Modern Pictograms][size=120]'            'e[/size][/font]\nBegin recording')        on_press: app.begin_end_recording()```这就搞定了。现在运行程序就可以向SD卡里录音了。不过在这之前请先看看下节内容。按钮的节目如下所示：![begainendbutton](kbpic/3.10begainendbutton.png)  重要警告——权限 默认的Kivy应用没有录音权限，`android.permission.RECORD_AUDIO`。如果初始化`MediaRecorder`实例就会失败。当然有很多方法解决这个问题。首先，最容易的就是重新编译Kivy Launcher源代码，让应用获取录音权限，[最新版在这里](https://github.com/mvasilkov/kivy_launcher_hack)。在安装`.apk`文件之前，请卸载原来的版本。另外如果你感受一下Android反编译，也可以用[**apktool](https://code.google.com/p/android-apktool/)直接反编译apk文件。步骤如下：1. 下载Kivy Launcher的app文件`KivyLauncher.apk`，用apktool文件命令：    ```sh    apktool d -b -s -d KivyLauncher.apk KivyLauncher    ```2. 向`AndroidManifest.xml`文件里增加权限：    ```xml        ```3. 重新打包成`.apk`文件：    ```sh    apktool b KivyLauncher KivyLauncherWithChanges.apk    ```4. 用`jarsigner`工具为`.apk`文件签名。可以看看签名Android包的[官方文档](http://developer.android.com/tools/publishing/app-signing.htmlsigning-manually)这样，安装Kivy Launcher后就可以录音了。>你可以用同样的方法在通过Pyjnius为Python代码增加不同的权限。比如，要获得GPS API接入的权限，你的app就需要`android.permission.ACCESS_FINE_LOCATION`，其它Android设备权限可以看官方[文档](http://developer.android.com/reference/android/Manifest.permission.html)。  播放声音 播放声音很简单，也不需要权限，对应的API也更简练。我们就要一个类`MediaPlayer`：<jupyter_code>MediaPlayer = autoclass("android.media.MediaPlayer")
player = MediaPlayer()<jupyter_output><empty_output><jupyter_text>当用户按下播放(Play)按钮时，下面的代码就要运行。我们还为下一节的*删除文件*增加了`reset_player()`函数。代码如下：<jupyter_code>def reset_player():
    if player.isPlaying():
        player.stop()
    player.reset()


def restart_player():
    reset_player()
    try:
        player.setDataSource(storage_path)
        player.prepare()
        player.start()
    except:
        player.reset()<jupyter_output><empty_output><jupyter_text>这些API的方法定义都可以在官方文档找到，不过意思很简单：就是复位播放器，加载声音文件，开始播放。文件的格式是自动设置的，可以让代码简单点。>实际上这类代码应该一直放在`try ... catch`里面，因为不知道什么地方就会异常。比如文件格式不对，SD卡没插好，或是读取失败，涉及到IO的问题多了。最好的办法就是*小心驶得万年船（better safe than sorry）*。  删除文件 最后一个功能是用`java.io.File`，和Android关系不大了，是Java标准库。Android官方文档也会包含Java核心类的解释，尽管Java比Android早几十年。代码很简单，就是最后一行：<jupyter_code>File = autoclass("java.io.File")


class RecorderApp(App):
    def delete_file(self):
        reset_player()
        File(storage_path).delete()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1235962392269522, 'fraction_numerical': 0.007312614259597806, 'mean_word_length': 9.979928315412186, 'pattern_counts': {'":': 1, '<': 39, '<?xml version=': 0, '>': 61, 'https://': 5, 'lorem ipsum': 0, 'www.': 4, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '655052', 'license': 'Apache-2.0', 'n_tokens_mistral': 9717, 'n_tokens_neox': 10546, 'n_words': 381, 'path': '_notebooks/2019-04-01-kivy-ch3-sound-recorder-for-android.ipynb', 'repo_name': 'CyberSciTech/air', 'size': '15330'}