You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-05 12:10:55 +00:00
Provide a delegate implementation for the killProcess logic on Android
The implementation forwards the kill request to the Godot host for handling. If the Godot host is unable to handle the request, it falls back to the `OS_Unix::kill(...)` implementation.
This commit is contained in:
@@ -31,12 +31,11 @@
|
||||
package org.godotengine.editor
|
||||
|
||||
import android.Manifest
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Debug
|
||||
import android.os.Environment
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.window.layout.WindowMetricsCalculator
|
||||
@@ -64,11 +63,18 @@ open class GodotEditor : FullScreenGodotApp() {
|
||||
|
||||
private const val COMMAND_LINE_PARAMS = "command_line_params"
|
||||
|
||||
private const val EDITOR_ID = 777
|
||||
private const val EDITOR_ARG = "--editor"
|
||||
private const val EDITOR_ARG_SHORT = "-e"
|
||||
private const val EDITOR_PROCESS_NAME_SUFFIX = ":GodotEditor"
|
||||
|
||||
private const val GAME_ID = 667
|
||||
private const val GAME_PROCESS_NAME_SUFFIX = ":GodotGame"
|
||||
|
||||
private const val PROJECT_MANAGER_ID = 555
|
||||
private const val PROJECT_MANAGER_ARG = "--project-manager"
|
||||
private const val PROJECT_MANAGER_ARG_SHORT = "-p"
|
||||
private const val PROJECT_MANAGER_PROCESS_NAME_SUFFIX = ":GodotProjectManager"
|
||||
}
|
||||
|
||||
private val commandLineParams = ArrayList<String>()
|
||||
@@ -102,9 +108,10 @@ open class GodotEditor : FullScreenGodotApp() {
|
||||
|
||||
override fun getCommandLine() = commandLineParams
|
||||
|
||||
override fun onNewGodotInstanceRequested(args: Array<String>) {
|
||||
override fun onNewGodotInstanceRequested(args: Array<String>): Int {
|
||||
// Parse the arguments to figure out which activity to start.
|
||||
var targetClass: Class<*> = GodotGame::class.java
|
||||
var instanceId = GAME_ID
|
||||
|
||||
// Whether we should launch the new godot instance in an adjacent window
|
||||
// https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT
|
||||
@@ -115,12 +122,14 @@ open class GodotEditor : FullScreenGodotApp() {
|
||||
if (EDITOR_ARG == arg || EDITOR_ARG_SHORT == arg) {
|
||||
targetClass = GodotEditor::class.java
|
||||
launchAdjacent = false
|
||||
instanceId = EDITOR_ID
|
||||
break
|
||||
}
|
||||
|
||||
if (PROJECT_MANAGER_ARG == arg || PROJECT_MANAGER_ARG_SHORT == arg) {
|
||||
targetClass = GodotProjectManager::class.java
|
||||
launchAdjacent = false
|
||||
instanceId = PROJECT_MANAGER_ID
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -139,6 +148,37 @@ open class GodotEditor : FullScreenGodotApp() {
|
||||
Log.d(TAG, "Starting $targetClass")
|
||||
startActivity(newInstance)
|
||||
}
|
||||
return instanceId
|
||||
}
|
||||
|
||||
override fun onGodotForceQuit(godotInstanceId: Int): Boolean {
|
||||
val processNameSuffix = when (godotInstanceId) {
|
||||
GAME_ID -> {
|
||||
GAME_PROCESS_NAME_SUFFIX
|
||||
}
|
||||
EDITOR_ID -> {
|
||||
EDITOR_PROCESS_NAME_SUFFIX
|
||||
}
|
||||
PROJECT_MANAGER_ID -> {
|
||||
PROJECT_MANAGER_PROCESS_NAME_SUFFIX
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
if (processNameSuffix.isBlank()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
val runningProcesses = activityManager.runningAppProcesses
|
||||
for (runningProcess in runningProcesses) {
|
||||
if (runningProcess.processName.endsWith(processNameSuffix)) {
|
||||
Log.v(TAG, "Killing Godot process ${runningProcess.processName}")
|
||||
Process.killProcess(runningProcess.pid)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Get the screen's density scale
|
||||
|
||||
@@ -74,28 +74,36 @@ public abstract class FullScreenGodotApp extends FragmentActivity implements God
|
||||
public void onDestroy() {
|
||||
Log.v(TAG, "Destroying Godot app...");
|
||||
super.onDestroy();
|
||||
onGodotForceQuit(godotFragment);
|
||||
terminateGodotInstance(godotFragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onGodotForceQuit(Godot instance) {
|
||||
runOnUiThread(() -> {
|
||||
terminateGodotInstance(instance);
|
||||
});
|
||||
}
|
||||
|
||||
private void terminateGodotInstance(Godot instance) {
|
||||
if (instance == godotFragment) {
|
||||
Log.v(TAG, "Force quitting Godot instance");
|
||||
ProcessPhoenix.forceQuit(this);
|
||||
ProcessPhoenix.forceQuit(FullScreenGodotApp.this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onGodotRestartRequested(Godot instance) {
|
||||
if (instance == godotFragment) {
|
||||
// It's very hard to properly de-initialize Godot on Android to restart the game
|
||||
// from scratch. Therefore, we need to kill the whole app process and relaunch it.
|
||||
//
|
||||
// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
|
||||
// releasing and reloading native libs or resetting their state somehow and clearing statics).
|
||||
Log.v(TAG, "Restarting Godot instance...");
|
||||
ProcessPhoenix.triggerRebirth(this);
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
if (instance == godotFragment) {
|
||||
// It's very hard to properly de-initialize Godot on Android to restart the game
|
||||
// from scratch. Therefore, we need to kill the whole app process and relaunch it.
|
||||
//
|
||||
// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
|
||||
// releasing and reloading native libs or resetting their state somehow and clearing statics).
|
||||
Log.v(TAG, "Restarting Godot instance...");
|
||||
ProcessPhoenix.triggerRebirth(FullScreenGodotApp.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -462,11 +462,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
runOnUiThread(() -> {
|
||||
if (godotHost != null) {
|
||||
godotHost.onGodotRestartRequested(this);
|
||||
}
|
||||
});
|
||||
if (godotHost != null) {
|
||||
godotHost.onGodotRestartRequested(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void alert(final String message, final String title) {
|
||||
@@ -1022,11 +1020,20 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
|
||||
private void forceQuit() {
|
||||
// TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each
|
||||
// native Godot components that is started in Godot#onVideoInit.
|
||||
runOnUiThread(() -> {
|
||||
if (godotHost != null) {
|
||||
godotHost.onGodotForceQuit(this);
|
||||
}
|
||||
});
|
||||
forceQuit(0);
|
||||
}
|
||||
|
||||
@Keep
|
||||
private boolean forceQuit(int instanceId) {
|
||||
if (godotHost == null) {
|
||||
return false;
|
||||
}
|
||||
if (instanceId == 0) {
|
||||
godotHost.onGodotForceQuit(this);
|
||||
return true;
|
||||
} else {
|
||||
return godotHost.onGodotForceQuit(instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean obbIsCorrupted(String f, String main_pack_md5) {
|
||||
@@ -1180,11 +1187,10 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
|
||||
}
|
||||
|
||||
@Keep
|
||||
private void createNewGodotInstance(String[] args) {
|
||||
runOnUiThread(() -> {
|
||||
if (godotHost != null) {
|
||||
godotHost.onNewGodotInstanceRequested(args);
|
||||
}
|
||||
});
|
||||
private int createNewGodotInstance(String[] args) {
|
||||
if (godotHost != null) {
|
||||
return godotHost.onNewGodotInstanceRequested(args);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,21 +55,35 @@ public interface GodotHost {
|
||||
default void onGodotMainLoopStarted() {}
|
||||
|
||||
/**
|
||||
* Invoked on the UI thread as the last step of the Godot instance clean up phase.
|
||||
* Invoked on the render thread to terminate the given Godot instance.
|
||||
*/
|
||||
default void onGodotForceQuit(Godot instance) {}
|
||||
|
||||
/**
|
||||
* Invoked on the UI thread when the Godot instance wants to be restarted. It's up to the host
|
||||
* Invoked on the render thread to terminate the Godot instance with the given id.
|
||||
* @param godotInstanceId id of the Godot instance to terminate. See {@code onNewGodotInstanceRequested}
|
||||
*
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
default boolean onGodotForceQuit(int godotInstanceId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked on the render thread when the Godot instance wants to be restarted. It's up to the host
|
||||
* to perform the appropriate action(s).
|
||||
*/
|
||||
default void onGodotRestartRequested(Godot instance) {}
|
||||
|
||||
/**
|
||||
* Invoked on the UI thread when a new Godot instance is requested. It's up to the host to
|
||||
* Invoked on the render thread when a new Godot instance is requested. It's up to the host to
|
||||
* perform the appropriate action(s).
|
||||
*
|
||||
* @param args Arguments used to initialize the new instance.
|
||||
*
|
||||
* @return the id of the new instance. See {@code onGodotForceQuit}
|
||||
*/
|
||||
default void onNewGodotInstanceRequested(String[] args) {}
|
||||
default int onNewGodotInstanceRequested(String[] args) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user