アプリ中断への対処

# アプリ中断への対処

もし作っているアプリをmobile端末で動かすつもりでいるならアプリの中断に対応させるべきだと思います。中断は例えばAndroidだとHomeボタンを押したり他のアプリを立ち上げる事で生じます。そしてそのまま何事もなく再開できる事もあれば、メモリが足りなくなってOSに破棄される事もあるはずです。この記事では実際にメモ帳を作りながら中断への対応方法を調べていきます。

### desktop環境でも中断が起こるようにする

というのも既定では起こらないからです。

```python
from kivy.config import Config
Config.set('kivy', 'pause_on_minimize', 1)
```

これでdesktop環境でもwindowを最小化する事で中断のtestができるようになりました。

### メモ帳アプリのコード

今回はKivyLauncherを使ってAndroid端末で動作確認できるようにしたいので、Python2.7.2+Kivy1.9.2と互換性のあるコードを書いていきます。

```python
from __future__ import print_function

from kivy.config import Config
Config.set('kivy', 'pause_on_minimize', 1)

from kivy.app import App
from kivy.uix.textinput import TextInput


class TestApp(App):

    def build(self):
        return TextInput()


if __name__ == '__main__':
    TestApp().run()
```

メモ帳と言っても`TextInput`をそのまま使っただけの手抜きですが。

### `on_pause()`で真を返す

`on_pause()`はアプリが中断される時に呼ばれるmethodです。最初に必要なのはこれが`真`を返すように実装することです。`偽`だとKivyに対して「中断などせずに即座に終了してください」と伝える事になるので。

```python
import sys


def log(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)


class TestApp(App):

    def on_pause(self):
        log('on_pause()')
        return True
```

この`on_pause()`ですがKivy1.10.0からは既定で`真`を返すようになりましたが、それより前は偽を返すようになっているので、今回のようにKivyLauncher(Kivy1.9.2)に持っていく場合は上書きしないといけません。

### `on_pause()`の後の展開

`on_pause()`が呼ばれた後の展開は2つ確認できており、それは

- `on_resume()`が呼ばれる(アプリが破棄されずに再開できた事を意味する)
- 何も呼ばれない(アプリが破棄された事を意味する)

です。破棄される場合はその後なんの機会も与えられないので、何かを保存したいなら`on_pause()`内でしないといけないという事になります。

### `on_pause()`で保存する

```python
from os.path import join as ospath_join
import io


class TestApp(App):

    def on_pause(self):
        log('on_pause()')
        self.save_session()
        return True

    def on_resume(self):
        log('on_resume()')

    @property
    def savefilepath(self):
        return ospath_join(self.user_data_dir, 'savedata.txt')

    def save_session(self):
        with io.open(self.savefilepath, 'wt', encoding='utf-8') as writer:
            writer.write(self.root.text)
```

中で使っている`user_data_dir`ですが、これは`App`のpropertyで、データの保存先として適したdirectoryへのpathが得られます。

### アプリ開始時に復元する

そしてアプリ開始時には保存データがあるか確かめ、あれば復元します。

```python
from os.path import join as ospath_join, isfile as ospath_isfile
import io


class TestApp(App):

    def on_start(self):
        log('on_start()')
        self.restore_session()

    def restore_session(self):
        path = self.savefilepath
        if not ospath_isfile(path):
            return
        with io.open(path, 'rt', encoding='utf-8') as reader:
            self.root.text = reader.read()
```

### 正常終了時にも保存する

その方が便利でしょう。

```python
class TestApp(App):

    def on_stop(self):
        log('on_stop()')
        self.save_session()
```

### 完成

```python
from __future__ import print_function

from kivy.config import Config
Config.set('kivy', 'pause_on_minimize', 1)

from kivy.app import App
from kivy.uix.textinput import TextInput
from os.path import join as ospath_join, isfile as ospath_isfile
import io
import sys


def log(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)


class TestApp(App):

    def build(self):
        return TextInput()

    def on_pause(self):
        log('on_pause()')
        self.save_session()
        return True

    def on_resume(self):
        log('on_resume()')

    def on_start(self):
        log('on_start()')
        self.restore_session()

    def on_stop(self):
        log('on_stop()')
        self.save_session()

    @property
    def savefilepath(self):
        return ospath_join(self.user_data_dir, 'savedata.txt')

    def save_session(self):
        with io.open(self.savefilepath, 'wt', encoding='utf-8') as writer:
            writer.write(self.root.text)

    def restore_session(self):
        path = self.savefilepath
        if not ospath_isfile(path):
            return
        with io.open(path, 'rt', encoding='utf-8') as reader:
            self.root.text = reader.read()


if __name__ == '__main__':
    TestApp().run()
```

ソースコードはandroid.txtも含めて[gist](https://gist.github.com/gottadiveintopython/e4a952113cdfc3abd966a8d49b42ceae)に上げてあります。