from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from jnius import autoclass
from kivy.uix.label import Label
class MyApp(App):
def build(self):
fl = FloatLayout()
try:
service = autoclass('org.test.myapp.ServiceMyservice')
mActivity = autoclass('org.kivy.android.PythonActivity').mActivity
service.start(mActivity, "")
except Exception as error:
fl.add_widget(Label(text=str(error), font_size=(40)))
return fl
if __name__ == '__main__':
MyApp().run()
import pickle, socket, jnius
for x in range(5):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = 'example-78945.portmap.host'
port = 78945
s.connect((host,port))
s.send(('hello world').encode('utf-8'))
package org.test.myapp.ServiceMyservice;
import android.content.Intent;
import android.content.Context;
import org.kivy.android.PythonService;
import android.app.Notification;
import android.app.Service;
public class ServiceMyservice extends PythonService {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
protected int getServiceId() {
return 1;
}
static public void start(Context ctx, String pythonServiceArgument) {
Intent intent = new Intent(ctx, ServiceMyservice.class);
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
intent.putExtra("androidArgument", argument);
intent.putExtra("serviceTitle", "My Application");
intent.putExtra("serviceDescription", "Myservice");
intent.putExtra("serviceEntrypoint", "./service/main.py");
intent.putExtra("pythonName", "myservice");
intent.putExtra("serviceStartAsForeground", true);
intent.putExtra("pythonHome", argument);
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
ctx.startService(intent);
}
static public void stop(Context ctx) {
Intent intent = new Intent(ctx, ServiceMyservice.class);
ctx.stopService(intent);
}
}
服务启动和工作,但关闭应用程序后,服务也关闭。怎么修????
这个变通方法基本上是让服务自动重启。这意味着你的服务将从头开始。是的,这是硬编码。
向服务模板文件中的start()方法添加字符串参数
我的是重新开始的争论。对于传递给由ctx.startService()方法触发的onStartCommand()方法的活动意图来说,这将是额外的。然后将'auto restartservice'与restart参数值一起放入。
My.buildozer/Android/platform/build-
package {{ args.package }};
import android.content.Intent;
import android.content.Context;
import org.kivy.android.PythonService;
public class Service{{ name|capitalize }} extends PythonService {
{% if sticky %}
@Override
public int startType() {
return START_STICKY;
}
{% endif %}
@Override
protected int getServiceId() {
return {{ service_id }};
}
/*add 'restart' String argument to the start() method*/
static public void start(Context ctx, String pythonServiceArgument, String restart) {
Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class);
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
intent.putExtra("androidArgument", argument);
intent.putExtra("serviceTitle", "{{ args.name }}");
intent.putExtra("serviceDescription", "{{ name|capitalize }}");
intent.putExtra("serviceEntrypoint", "{{ entrypoint }}");
intent.putExtra("pythonName", "{{ name }}");
intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}");
intent.putExtra("pythonHome", argument);
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
intent.putExtra("autoRestartService", restart); /*<-- add this line*/
ctx.startService(intent);
}
static public void stop(Context ctx) {
Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class);
ctx.stopService(intent);
}
}
在PythonService的onStartCommand()中设置autoRestartService值
下面看一下PythonService的onDestroy()方法。如果服务被终止(由关闭应用程序或从最近的应用程序中滑动引起),将触发onDestroy()方法。是否重新启动服务取决于autoRestartService值。因此,通过从intent extras获取它,在onStartCommand()方法中设置它。
My.buildozer/android/platform/build-
package org.kivy.android;
import android.os.Build;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import android.app.Service;
import android.os.IBinder;
import android.os.Bundle;
import android.content.Intent;
import android.content.Context;
import android.util.Log;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Process;
import java.io.File;
//imports for channel definition
import android.app.NotificationManager;
import android.app.NotificationChannel;
import android.graphics.Color;
public class PythonService extends Service implements Runnable {
// Thread for Python code
private Thread pythonThread = null;
// Python environment variables
private String androidPrivate;
private String androidArgument;
private String pythonName;
private String pythonHome;
private String pythonPath;
private String serviceEntrypoint;
// Argument to pass to Python code,
private String pythonServiceArgument;
public static PythonService mService = null;
private Intent startIntent = null;
private boolean autoRestartService = false;
public void setAutoRestartService(boolean restart) {
autoRestartService = restart;
}
public int startType() {
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (pythonThread != null) {
Log.v("python service", "service exists, do not start again");
return START_NOT_STICKY;
}
startIntent = intent;
Bundle extras = intent.getExtras();
androidPrivate = extras.getString("androidPrivate");
androidArgument = extras.getString("androidArgument");
serviceEntrypoint = extras.getString("serviceEntrypoint");
pythonName = extras.getString("pythonName");
pythonHome = extras.getString("pythonHome");
pythonPath = extras.getString("pythonPath");
boolean serviceStartAsForeground = (
extras.getString("serviceStartAsForeground").equals("true")
);
pythonServiceArgument = extras.getString("pythonServiceArgument");
autoRestartService = (
extras.getString("autoRestartService").equals("true") //this will return boolean for autoRestartservice
);
pythonThread = new Thread(this);
pythonThread.start();
if (serviceStartAsForeground) {
doStartForeground(extras);
}
return startType();
}
protected int getServiceId() {
return 1;
}
protected void doStartForeground(Bundle extras) {
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = extras.getString("serviceDescription");
Notification notification;
Context context = getApplicationContext();
Intent contextIntent = new Intent(context, PythonActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
notification = new Notification(
context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis());
try {
// prevent using NotificationCompat, this saves 100kb on apk
Method func = notification.getClass().getMethod(
"setLatestEventInfo", Context.class, CharSequence.class,
CharSequence.class, PendingIntent.class);
func.invoke(notification, context, serviceTitle, serviceDescription, pIntent);
} catch (NoSuchMethodException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
}
} else {
// for android 8+ we need to create our own channel
// https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable
String channelName = "Background Service"; //TODO: make this configurable
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(chan);
Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
builder.setContentTitle(serviceTitle);
builder.setContentText(serviceDescription);
builder.setContentIntent(pIntent);
builder.setSmallIcon(context.getApplicationInfo().icon);
notification = builder.build();
}
startForeground(getServiceId(), notification);
}
@Override
public void onDestroy() {
super.onDestroy();
pythonThread = null;
if (autoRestartService && startIntent != null) {
Log.v("python service", "service restart requested");
startService(startIntent);
}
Process.killProcess(Process.myPid());
}
/**
* Stops the task gracefully when killed.
* Calling stopSelf() will trigger a onDestroy() call from the system.
*/
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopSelf();
}
@Override
public void run(){
String app_root = getFilesDir().getAbsolutePath() + "/app";
File app_root_file = new File(app_root);
PythonUtil.loadLibraries(app_root_file,
new File(getApplicationInfo().nativeLibraryDir));
this.mService = this;
nativeStart(
androidPrivate, androidArgument,
serviceEntrypoint, pythonName,
pythonHome, pythonPath,
pythonServiceArgument);
stopSelf();
}
// Native part
public static native void nativeStart(
String androidPrivate, String androidArgument,
String serviceEntrypoint, String pythonName,
String pythonHome, String pythonPath,
String pythonServiceArgument);
}
那里有setAutoRestartService()方法,但是我们不能调用它,因为它是非静态方法。
最后一件事,buildozer.spec
将FOREGROUND_SERVICE权限和服务添加到buildozer.spec。
android.permissions = FOREGROUND_SERVICE
...
services = myservice:./path/to/your-service.py:foreground
现在通过给出'true'字符串作为第三个位置参数来启动服务。
activity = autoclass('org.kivy.android.PythonActivity').mActivity
service = autoclass('com.omdo.example.ServiceMyservice')
service.start(activity, '', 'true')
注意:我不太懂Java,也许有人能让它更简单。
参考资料:
好吧,我是Android Studio的新手,我在玩一个愚蠢的屁噪音应用程序。我的第一次尝试是一个按钮发出噪音。现在我有三个按钮,但应用程序无法在模拟器中打开。它只说应用程序一直在关闭。我试着用谷歌搜索log cat中的每一个错误,但到目前为止没有任何效果。我尝试了两个不同的模拟器,但最初它工作得很好,所以不确定要改变什么。声音文件是。我不确定这是否重要。 这是我的原木猫 04-10 11:41:
假设我计划在整个应用程序中使用一个executorservice,向其发送新的runnable或callable以执行/提交,然后我命令立即关闭。我只想把我的“任务”交给executorservice,让他处理它们,并在提供资源的情况下执行它们(他有多少线程可用,如果需要,他可以创建多少线程,然后相应地将这些任务排队)。 根据您在Android应用程序中使用ExecutorService的经验,并
我有一个程序正在显示结果的条形图。我想等到用户关闭条形图,继续下一行代码,询问他们是否要为该图输入新信息。 正在发生的是,柱状图的场景将打开,但什么也不会显示,并且JOptionPane立即弹出问题。如果我点击“否”,则显示图表,但程序结束。如果我点击“是”,程序将返回到前面的对话框窗口,但在其他对话框结束之前不会显示图表。 我想等待提示用户,直到他们关闭条形图。在这一点上,我是如此的沮丧,以至于
当你第一次启动应用程序时,如何做图片这样的事情?多谢帮忙。
我正在Java做一个应用程序,我需要它在后台工作,当用户关闭应用程序时,他仍然保持隐藏状态