mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-13 18:18:56 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a8a92b3fb | |||
| bb97af154f | |||
| 8a21219695 | |||
| c10a2fb67a | |||
| ab2606a73d | |||
| a84a7b54cd | |||
| a9e91f4308 |
@@ -1,11 +1,18 @@
|
|||||||
|
<!-- markdownlint-disable MD022 MD024 MD032 -->
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.4.3] - 2022-07-30
|
||||||
|
### Fixed
|
||||||
|
- Fixed a deadlock in the `native-activity` backend while waiting for the native thread after getting an `onDestroy` callback from Java ([#94](https://github.com/rust-mobile/android-activity/pull/94))
|
||||||
|
|
||||||
## [0.4.2] - 2022-02-16
|
- Fixed numerous deadlocks in the `game-activity` backend with how it would wait for the native thread in various Java callbacks, after the app has returned from `android_main` ([#98](https://github.com/rust-mobile/android-activity/pull/98))
|
||||||
|
|
||||||
|
## [0.4.2] - 2022-06-17
|
||||||
### Changed
|
### Changed
|
||||||
- The `Activity.finish()` method is now called when `android_main` returns so the `Activity` will be destroyed ([#67](https://github.com/rust-mobile/android-activity/issues/67))
|
- The `Activity.finish()` method is now called when `android_main` returns so the `Activity` will be destroyed ([#67](https://github.com/rust-mobile/android-activity/issues/67))
|
||||||
- The `native-activity` backend now propagates `NativeWindow` redraw/resize and `ContentRectChanged` callbacks to main loop ([#70](https://github.com/rust-mobile/android-activity/pull/70))
|
- The `native-activity` backend now propagates `NativeWindow` redraw/resize and `ContentRectChanged` callbacks to main loop ([#70](https://github.com/rust-mobile/android-activity/pull/70))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "android-activity"
|
name = "android-activity"
|
||||||
version = "0.4.2"
|
version = "0.4.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
keywords = ["android", "ndk"]
|
keywords = ["android", "ndk"]
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
fn build_glue_for_game_activity() {
|
fn build_glue_for_game_activity() {
|
||||||
|
for f in ["GameActivity.h", "GameActivity.cpp"] {
|
||||||
|
println!("cargo:rerun-if-changed=game-activity-csrc/game-activity/{f}");
|
||||||
|
}
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.cpp(true)
|
.cpp(true)
|
||||||
.include("game-activity-csrc")
|
.include("game-activity-csrc")
|
||||||
@@ -8,12 +11,20 @@ fn build_glue_for_game_activity() {
|
|||||||
.extra_warnings(false)
|
.extra_warnings(false)
|
||||||
.cpp_link_stdlib("c++_static")
|
.cpp_link_stdlib("c++_static")
|
||||||
.compile("libgame_activity.a");
|
.compile("libgame_activity.a");
|
||||||
|
|
||||||
|
for f in ["gamecommon.h", "gametextinput.h", "gametextinput.cpp"] {
|
||||||
|
println!("cargo:rerun-if-changed=game-activity-csrc/game-text-input/{f}");
|
||||||
|
}
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.cpp(true)
|
.cpp(true)
|
||||||
.include("game-activity-csrc")
|
.include("game-activity-csrc")
|
||||||
.file("game-activity-csrc/game-text-input/gametextinput.cpp")
|
.file("game-activity-csrc/game-text-input/gametextinput.cpp")
|
||||||
.cpp_link_stdlib("c++_static")
|
.cpp_link_stdlib("c++_static")
|
||||||
.compile("libgame_text_input.a");
|
.compile("libgame_text_input.a");
|
||||||
|
|
||||||
|
for f in ["android_native_app_glue.h", "android_native_app_glue.c"] {
|
||||||
|
println!("cargo:rerun-if-changed=game-activity-csrc/native_app_glue/{f}");
|
||||||
|
}
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.include("game-activity-csrc")
|
.include("game-activity-csrc")
|
||||||
.include("game-activity-csrc/game-activity/native_app_glue")
|
.include("game-activity-csrc/game-activity/native_app_glue")
|
||||||
|
|||||||
+58
-5
@@ -308,6 +308,15 @@ static void android_app_set_window(struct android_app* android_app,
|
|||||||
ANativeWindow* window) {
|
ANativeWindow* window) {
|
||||||
LOGV("android_app_set_window called");
|
LOGV("android_app_set_window called");
|
||||||
pthread_mutex_lock(&android_app->mutex);
|
pthread_mutex_lock(&android_app->mutex);
|
||||||
|
|
||||||
|
// NB: we have to consider that the native thread could have already
|
||||||
|
// (gracefully) exit (setting android_app->destroyed) and so we need
|
||||||
|
// to be careful to avoid a deadlock waiting for a thread that's
|
||||||
|
// already exit.
|
||||||
|
if (android_app->destroyed) {
|
||||||
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (android_app->pendingWindow != NULL) {
|
if (android_app->pendingWindow != NULL) {
|
||||||
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
|
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
|
||||||
}
|
}
|
||||||
@@ -324,15 +333,30 @@ static void android_app_set_window(struct android_app* android_app,
|
|||||||
static void android_app_set_activity_state(struct android_app* android_app,
|
static void android_app_set_activity_state(struct android_app* android_app,
|
||||||
int8_t cmd) {
|
int8_t cmd) {
|
||||||
pthread_mutex_lock(&android_app->mutex);
|
pthread_mutex_lock(&android_app->mutex);
|
||||||
android_app_write_cmd(android_app, cmd);
|
|
||||||
while (android_app->activityState != cmd) {
|
// NB: we have to consider that the native thread could have already
|
||||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
// (gracefully) exit (setting android_app->destroyed) and so we need
|
||||||
|
// to be careful to avoid a deadlock waiting for a thread that's
|
||||||
|
// already exit.
|
||||||
|
if (!android_app->destroyed) {
|
||||||
|
android_app_write_cmd(android_app, cmd);
|
||||||
|
while (android_app->activityState != cmd) {
|
||||||
|
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&android_app->mutex);
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void android_app_free(struct android_app* android_app) {
|
static void android_app_free(struct android_app* android_app) {
|
||||||
pthread_mutex_lock(&android_app->mutex);
|
pthread_mutex_lock(&android_app->mutex);
|
||||||
|
|
||||||
|
// It's possible that onDestroy is called after we have already 'destroyed'
|
||||||
|
// the app (via `android_app_destroy` due to `android_main` returning.
|
||||||
|
//
|
||||||
|
// In this case `->destroyed` will already be set (so we won't deadlock in
|
||||||
|
// the loop below) but we still need to close the messaging fds and finish
|
||||||
|
// freeing the android_app
|
||||||
|
|
||||||
android_app_write_cmd(android_app, APP_CMD_DESTROY);
|
android_app_write_cmd(android_app, APP_CMD_DESTROY);
|
||||||
while (!android_app->destroyed) {
|
while (!android_app->destroyed) {
|
||||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||||
@@ -372,6 +396,16 @@ static void onSaveInstanceState(GameActivity* activity,
|
|||||||
|
|
||||||
struct android_app* android_app = ToApp(activity);
|
struct android_app* android_app = ToApp(activity);
|
||||||
pthread_mutex_lock(&android_app->mutex);
|
pthread_mutex_lock(&android_app->mutex);
|
||||||
|
|
||||||
|
// NB: we have to consider that the native thread could have already
|
||||||
|
// (gracefully) exit (setting android_app->destroyed) and so we need
|
||||||
|
// to be careful to avoid a deadlock waiting for a thread that's
|
||||||
|
// already exit.
|
||||||
|
if (android_app->destroyed) {
|
||||||
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
android_app->stateSaved = 0;
|
android_app->stateSaved = 0;
|
||||||
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
|
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
|
||||||
while (!android_app->stateSaved) {
|
while (!android_app->stateSaved) {
|
||||||
@@ -481,6 +515,15 @@ static bool onTouchEvent(GameActivity* activity,
|
|||||||
struct android_app* android_app = ToApp(activity);
|
struct android_app* android_app = ToApp(activity);
|
||||||
pthread_mutex_lock(&android_app->mutex);
|
pthread_mutex_lock(&android_app->mutex);
|
||||||
|
|
||||||
|
// NB: we have to consider that the native thread could have already
|
||||||
|
// (gracefully) exit (setting android_app->destroyed) and so we need
|
||||||
|
// to be careful to avoid a deadlock waiting for a thread that's
|
||||||
|
// already exit.
|
||||||
|
if (android_app->destroyed) {
|
||||||
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (android_app->motionEventFilter != NULL &&
|
if (android_app->motionEventFilter != NULL &&
|
||||||
!android_app->motionEventFilter(event)) {
|
!android_app->motionEventFilter(event)) {
|
||||||
pthread_mutex_unlock(&android_app->mutex);
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
@@ -563,6 +606,15 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
|
|||||||
struct android_app* android_app = ToApp(activity);
|
struct android_app* android_app = ToApp(activity);
|
||||||
pthread_mutex_lock(&android_app->mutex);
|
pthread_mutex_lock(&android_app->mutex);
|
||||||
|
|
||||||
|
// NB: we have to consider that the native thread could have already
|
||||||
|
// (gracefully) exit (setting android_app->destroyed) and so we need
|
||||||
|
// to be careful to avoid a deadlock waiting for a thread that's
|
||||||
|
// already exit.
|
||||||
|
if (android_app->destroyed) {
|
||||||
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (android_app->keyEventFilter != NULL &&
|
if (android_app->keyEventFilter != NULL &&
|
||||||
!android_app->keyEventFilter(event)) {
|
!android_app->keyEventFilter(event)) {
|
||||||
pthread_mutex_unlock(&android_app->mutex);
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
@@ -599,8 +651,9 @@ static void onTextInputEvent(GameActivity* activity,
|
|||||||
const GameTextInputState* state) {
|
const GameTextInputState* state) {
|
||||||
struct android_app* android_app = ToApp(activity);
|
struct android_app* android_app = ToApp(activity);
|
||||||
pthread_mutex_lock(&android_app->mutex);
|
pthread_mutex_lock(&android_app->mutex);
|
||||||
|
if (!android_app->destroyed) {
|
||||||
android_app->textInputState = 1;
|
android_app->textInputState = 1;
|
||||||
|
}
|
||||||
pthread_mutex_unlock(&android_app->mutex);
|
pthread_mutex_unlock(&android_app->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -199,6 +199,18 @@ impl NativeActivityGlue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The status of the native thread that's created to run
|
||||||
|
/// `android_main`
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum NativeThreadState {
|
||||||
|
/// The `android_main` thread hasn't been created yet
|
||||||
|
Init,
|
||||||
|
/// The `android_main` thread has been spawned and started running
|
||||||
|
Running,
|
||||||
|
/// The `android_main` thread has finished
|
||||||
|
Stopped,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NativeActivityState {
|
pub struct NativeActivityState {
|
||||||
pub msg_read: libc::c_int,
|
pub msg_read: libc::c_int,
|
||||||
@@ -210,8 +222,11 @@ pub struct NativeActivityState {
|
|||||||
pub content_rect: ndk_sys::ARect,
|
pub content_rect: ndk_sys::ARect,
|
||||||
pub activity_state: State,
|
pub activity_state: State,
|
||||||
pub destroy_requested: bool,
|
pub destroy_requested: bool,
|
||||||
pub running: bool,
|
pub thread_state: NativeThreadState,
|
||||||
pub app_has_saved_state: bool,
|
pub app_has_saved_state: bool,
|
||||||
|
|
||||||
|
/// Set as soon as the Java main thread notifies us of an
|
||||||
|
/// `onDestroyed` callback.
|
||||||
pub destroyed: bool,
|
pub destroyed: bool,
|
||||||
pub redraw_needed: bool,
|
pub redraw_needed: bool,
|
||||||
pub pending_input_queue: *mut ndk_sys::AInputQueue,
|
pub pending_input_queue: *mut ndk_sys::AInputQueue,
|
||||||
@@ -303,8 +318,6 @@ impl Drop for WaitableNativeActivityState {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let mut guard = self.mutex.lock().unwrap();
|
let mut guard = self.mutex.lock().unwrap();
|
||||||
guard.detach_input_queue_from_looper();
|
guard.detach_input_queue_from_looper();
|
||||||
guard.destroyed = true;
|
|
||||||
self.cond.notify_one();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,7 +369,7 @@ impl WaitableNativeActivityState {
|
|||||||
content_rect: Rect::empty().into(),
|
content_rect: Rect::empty().into(),
|
||||||
activity_state: State::Init,
|
activity_state: State::Init,
|
||||||
destroy_requested: false,
|
destroy_requested: false,
|
||||||
running: false,
|
thread_state: NativeThreadState::Init,
|
||||||
app_has_saved_state: false,
|
app_has_saved_state: false,
|
||||||
destroyed: false,
|
destroyed: false,
|
||||||
redraw_needed: false,
|
redraw_needed: false,
|
||||||
@@ -369,10 +382,11 @@ impl WaitableNativeActivityState {
|
|||||||
|
|
||||||
pub fn notify_destroyed(&self) {
|
pub fn notify_destroyed(&self) {
|
||||||
let mut guard = self.mutex.lock().unwrap();
|
let mut guard = self.mutex.lock().unwrap();
|
||||||
|
guard.destroyed = true;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
guard.write_cmd(AppCmd::Destroy);
|
guard.write_cmd(AppCmd::Destroy);
|
||||||
while !guard.destroyed {
|
while guard.thread_state != NativeThreadState::Stopped {
|
||||||
guard = self.cond.wait(guard).unwrap();
|
guard = self.cond.wait(guard).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,7 +555,13 @@ impl WaitableNativeActivityState {
|
|||||||
|
|
||||||
pub fn notify_main_thread_running(&self) {
|
pub fn notify_main_thread_running(&self) {
|
||||||
let mut guard = self.mutex.lock().unwrap();
|
let mut guard = self.mutex.lock().unwrap();
|
||||||
guard.running = true;
|
guard.thread_state = NativeThreadState::Running;
|
||||||
|
self.cond.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify_main_thread_stopped_running(&self) {
|
||||||
|
let mut guard = self.mutex.lock().unwrap();
|
||||||
|
guard.thread_state = NativeThreadState::Stopped;
|
||||||
self.cond.notify_one();
|
self.cond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,11 +927,16 @@ extern "C" fn ANativeActivity_onCreate(
|
|||||||
|
|
||||||
ndk_context::release_android_context();
|
ndk_context::release_android_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rust_glue.notify_main_thread_stopped_running();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for thread to start.
|
// Wait for thread to start.
|
||||||
let mut guard = jvm_glue.mutex.lock().unwrap();
|
let mut guard = jvm_glue.mutex.lock().unwrap();
|
||||||
while !guard.running {
|
|
||||||
|
// Don't specifically wait for `Running` just in case `android_main` returns
|
||||||
|
// immediately and the state is set to `Stopped`
|
||||||
|
while guard.thread_state == NativeThreadState::Init {
|
||||||
guard = jvm_glue.cond.wait(guard).unwrap();
|
guard = jvm_glue.cond.wait(guard).unwrap();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user