Как стать автором
Поиск
Написать публикацию
Обновить

Android. Starting Kivy App and Service on bootup. API 35

Уровень сложностиСредний
Время на прочтение7 мин
Количество просмотров570

Эта заметка является дополнением статьи Android. Starting Kivy App and Service on bootup, в которой запускал сервисом kivy приложение на API 22 Android 5. Теперь будем запускать на последних версиях Android. C API 26 Android 8 и по текущий API 35 Android 15 который есть у меня, постигли изменения, которые необходимо внести для автостарта сервиса. Проверял работу на API 22...35, телефоны: Highscreen power five, Nokia 8, Xiaomi Redmi Note 14.

C Android 8 претерпело изменение в запуске сервиса:

    public void service_start(Context context, Intent intent) {
        String package_root = context.getFilesDir().getAbsolutePath();
        String app_root =  package_root + "/app";
        Intent ix = new Intent(context, ServiceTest.class);
        ix.putExtra("androidPrivate", package_root);
        ix.putExtra("androidArgument", app_root);
        ix.putExtra("serviceEntrypoint", "service.py");
        ix.putExtra("pythonName", "test");
        ix.putExtra("pythonHome", app_root);
        ix.putExtra("pythonPath", package_root);
        ix.putExtra("serviceStartAsForeground", "true");
        ix.putExtra("serviceTitle", "ServiceTest");
        ix.putExtra("serviceDescription", "ServiceTest");
        ix.putExtra("pythonServiceArgument", app_root + ":" + app_root + "/lib");
        ix.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(ix);
        } else {
            context.startService(ix);
        }
    }

Теперь он запускается методом startForegroundService.

Начиная с Android 14 действует ограничение на запуск служб из приёмника BOOT_COMPLETED. Поэтому придется править AndroidManifest.xml, в kivy они задаются файлами шаблонами AndroidManifest.tmpl.xml. Нас будет интересовать два таких файла. Изменением оба, т.к. они идентичны и один из них берется как основа другого.

./kivy_service_test/.buildozer/android/platform/python-for-android/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.tmpl.xml
./kivy_service_test/.buildozer/android/platform/build-arm64-v8a/dists/kivy_service_test/templates/AndroidManifest.tmpl.xml

Здесь мы идем по неправильному пути исходной статьи, так как он самый простой.

Во всех секциях service дописываем android:foregroundServiceType, т.к. теперь требуется указать тип сервиса. Не особо углублялся в эти типы, выбрал первый подходящий который решал мою задачу: location. Он разрешает запуск при сигнале BOOT_COMPLETED. Заодно прописываю android:exported, он вроде как запрещает запускать сервис из другого приложения.

        {% if service or args.launcher %}
        <service android:name="{{ args.service_class_name }}"
                 android:process=":pythonservice"
                 android:foregroundServiceType="location" 
                 android:exported="false"/>
        {% endif %}
        {% for name in service_names %}
        <service android:name="{{ args.package }}.Service{{ name|capitalize }}"
                 android:process=":service_{{ name }}" 
                 android:foregroundServiceType="location" 
                 android:exported="false" />
        {% endfor %}
        {% for name in native_services %}
        <service android:name="{{ name }}" 
                 android:foregroundServiceType="location"
                 android:exported="false" />
        {% endfor %}

        {% if args.billing_pubkey %}
        <service android:name="org.kivy.android.billing.BillingReceiver"
                 android:process=":pythonbilling" 
                 android:foregroundServiceType="location"
                 android:exported="false"/>

После открытия тега application дописываем внутри него:

        <receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
                <action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
                <action android:name="service_start" />
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.DELETE" />
            </intent-filter>
        </receiver> 

service_start мой сигнал для запуска сервиса при старте приложения, если сервис уже запущен то ни чего не произойдет.

В каждом новом проекте, эти действия придется производить руками. И возможно после команды очистки, придется заново прописывать тип сервиса.

Так же переделал запуск сервиса на старте приложения через свой сигнал:

    def service_send_start(*args):
        """
        Отправка сообщения MyBroadcastReceiver. В recivers.xml нужно прописать разрешение.
        """

        intent = Intent('service_start')
        intent.setPackage(Context.getPackageName())
        Context.sendBroadcast(intent)

В buildozer.spec добавляем разрешение FOREGROUND_SERVICE_LOCATION

android.permissions = FOREGROUND_SERVICE,
                      RECEIVE_BOOT_COMPLETED,
                      QUICKBOOT_POWERON,
                      ACCESS_COARSE_LOCATION,
                      ACCESS_FINE_LOCATION,
                      READ_EXTERNAL_STORAGE,
                      WRITE_EXTERNAL_STORAGE,
                      FOREGROUND_SERVICE_LOCATION

Список всех разрешений который мне в итоге потребовался.

Подключаем телефон к ПК. Собираем проект и устанавливаем на телефон:

buildozer android debug && adb install -r -d `ls ./bin/kivy_service_*.apk | tail -1`

Запускам:

Отладка

Запускаем adb:

adb logcat | egrep "python |Test "

Ждем печати:

python service running..... com.heattheatr.kivy_service_test

Результат работы:

08-25 14:54:14.652 21092 21112 I Test    : Android kivy bootstrap done. __name__ is __main__
08-25 14:54:14.652 21092 21112 I python  : AND: Ran string
08-25 14:54:14.652 21092 21112 I python  : Run user program, change dir and execute entrypoint
08-25 14:54:14.791 21092 21112 I Test    : [INFO   ] [Logger      ] Record log in /data/user/0/com.heattheatr.kivy_service_test/files/app/.kivy/logs/kivy_25-08-25_1.txt
08-25 14:54:14.791 21092 21112 I Test    : [INFO   ] [Kivy        ] v2.3.1
08-25 14:54:14.791 21092 21112 I Test    : [INFO   ] [Kivy        ] Installed at "/data/user/0/com.heattheatr.kivy_service_test/files/app/_python_bundle/site-packages/kivy/__init__.pyc"
08-25 14:54:14.792 21092 21112 I Test    : [INFO   ] [Python      ] v3.11.5 (main, Aug 18 2025, 22:24:06) [Clang 14.0.6 (https://android.googlesource.com/toolchain/llvm-project 4c603efb0
08-25 14:54:14.792 21092 21112 I Test    : [INFO   ] [Python      ] Interpreter at ""
08-25 14:54:14.792 21092 21112 I Test    : [INFO   ] [Logger      ] Purge log fired. Processing...
08-25 14:54:14.792 21092 21112 I Test    : [INFO   ] [Logger      ] Purge finished!
08-25 14:54:15.473 21092 21112 I Test    : python service running..... com.heattheatr.kivy_service_test 21092
08-25 14:54:25.474 21092 21112 I Test    : python service running..... com.heattheatr.kivy_service_test 21092
08-25 14:54:35.474 21092 21112 I Test    : python service running..... com.heattheatr.kivy_service_test 21092
08-25 14:54:45.475 21092 21112 I Test    : python service running..... com.heattheatr.kivy_service_test 21092

Перезагружаем телефон, смотрим adb:

08-25 15:17:33.023 12012 12012 I System.out: python Context: android.app.ReceiverRestrictedContext@2829109, Intent: android.intent.action.BOOT_COMPLETED
08-25 15:17:33.023 12012 12012 I System.out: python Build.VERSION.SDK_INT 35
08-25 15:17:33.047  1737  1755 I ActivityManager: Background started FGS: Allowed [callingPackage: com.heattheatr.kivy_service_test; callingUid: 10350; uidState: RCVR; uidBFSL: n/a; intent: Intent { flg=0x10000000 cmp=com.heattheatr.kivy_service_test/.ServiceTest (has extras) }; code:BOOT_COMPLETED; tempAllowListReason:<ad20de6 android.intent.action.BOOT_COMPLETED/u0,reasonCode:BOOT_COMPLETED,duration:20000,callingUid:1000>; targetSdkVersion:31; callerTargetSdkVersion:31; startForegroundCount:0; bindFromPackage:null: isBindService:false]
08-25 15:17:33.333 12281 12281 D ActivityThread: setEmbeddedParam packageName=com.heattheatr.kivy_service_test processName=com.heattheatr.kivy_service_test:service_Test isEmbedded=false isIsolated=false
08-25 15:17:34.028 12281 12435 I python  : Initializing Python for Android
08-25 15:17:34.028 12281 12435 I python  : Setting additional env vars from p4a_env_vars.txt
08-25 15:17:34.029 12281 12435 I python  : Changing directory to the one provided by ANDROID_ARGUMENT
08-25 15:17:34.029 12281 12435 I python  : /data/user/0/com.heattheatr.kivy_service_test/files/app
08-25 15:17:34.031 12281 12435 I python  : Preparing to initialize python
08-25 15:17:34.032 12281 12435 I python  : _python_bundle dir exists
08-25 15:17:34.032 12281 12435 I python  : calculated paths to be...
08-25 15:17:34.032 12281 12435 I python  : /data/user/0/com.heattheatr.kivy_service_test/files/app/_python_bundle/stdlib.zip:/data/user/0/com.heattheatr.kivy_service_test/files/app/_python_bundle/modules
08-25 15:17:34.032 12281 12435 I python  : set wchar paths...
08-25 15:17:34.077 12281 12435 I python  : Initialized python
08-25 15:17:34.077 12281 12435 I python  : AND: Init threads
08-25 15:17:34.078 12281 12435 I test    : testing python print redirection
08-25 15:17:34.079 12281 12435 I python  : AND: Ran string
08-25 15:17:34.079 12281 12435 I python  : Run user program, change dir and execute entrypoint
08-25 15:17:35.399 12281 12435 I test    : python service running..... com.heattheatr.kivy_service_test 12281
08-25 15:17:36.659 12885 12971 I python  : Initializing Python for Android
08-25 15:17:36.659 12885 12971 I python  : Setting additional env vars from p4a_env_vars.txt
08-25 15:17:36.661 12885 12971 I python  : Changing directory to the one provided by ANDROID_ARGUMENT
08-25 15:17:36.661 12885 12971 I python  : /data/user/0/com.intercom.intercom/files/app
08-25 15:17:36.662 12885 12971 I python  : Preparing to initialize python
08-25 15:17:36.663 12885 12971 I python  : _python_bundle dir exists
08-25 15:17:36.663 12885 12971 I python  : calculated paths to be...
08-25 15:17:36.663 12885 12971 I python  : /data/user/0/com.intercom.intercom/files/app/_python_bundle/stdlib.zip:/data/user/0/com.intercom.intercom/files/app/_python_bundle/modules
08-25 15:17:36.663 12885 12971 I python  : set wchar paths...
08-25 15:17:36.826 12885 12971 I python  : Initialized python
08-25 15:17:36.826 12885 12971 I python  : AND: Init threads
08-25 15:17:36.826 12885 12971 I intercom: testing python print redirection
08-25 15:17:36.827 12885 12971 I python  : AND: Ran string
08-25 15:17:36.827 12885 12971 I python  : Run user program, change dir and execute entrypoint
08-25 15:17:45.400 12281 12435 I test    : python service running..... com.heattheatr.kivy_service_test 12281
08-25 15:17:55.408 12281 12435 I test    : python service running..... com.heattheatr.kivy_service_test 12281
08-25 15:18:05.408 12281 12435 I test    : python service running..... com.heattheatr.kivy_service_test 12281

Спасибо за внимание.

Ссылки

Теги:
Хабы:
+2
Комментарии0

Публикации

Ближайшие события