Skip to content

Commit

Permalink
fix(android): drop MainPipe on activity destroy (#1415)
Browse files Browse the repository at this point in the history
fixes a memory leak when the activity is destroyed but the app is opened in the foreground

ref tauri-apps/tauri#11609
  • Loading branch information
lucasfernog authored Nov 8, 2024
1 parent 0c192f4 commit 58cc082
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 8 deletions.
7 changes: 7 additions & 0 deletions src/android/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ macro_rules! android_binding {
($domain:ident, $package:ident, $wry:path) => {{
use $wry::{android_setup as _, prelude::*};

android_fn!($domain, $package, WryActivity, onActivityDestroy, [JObject]);

android_fn!(
$domain,
$package,
Expand Down Expand Up @@ -258,6 +260,11 @@ fn handle_request(
Ok(*JObject::null())
}

#[allow(non_snake_case)]
pub unsafe fn onActivityDestroy(_: JNIEnv, _: JClass, _: JObject) {
super::MainPipe::send(super::WebViewMessage::OnDestroy);
}

#[allow(non_snake_case)]
pub unsafe fn handleRequest(
mut env: JNIEnv,
Expand Down
2 changes: 2 additions & 0 deletions src/android/kotlin/WryActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ abstract class WryActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
destroy()
onActivityDestroy()
}

override fun onLowMemory() {
Expand Down Expand Up @@ -125,6 +126,7 @@ abstract class WryActivity : AppCompatActivity() {
private external fun stop()
private external fun save()
private external fun destroy()
private external fun onActivityDestroy()
private external fun memory()
private external fun focus(focus: Boolean)

Expand Down
13 changes: 11 additions & 2 deletions src/android/main_pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ pub static MAIN_PIPE: Lazy<[OwnedFd; 2]> = Lazy::new(|| {
unsafe { pipe.map(|fd| OwnedFd::from_raw_fd(fd)) }
});

pub enum MainPipeState {
Alive,
Destroyed,
}

pub struct MainPipe<'a> {
pub env: JNIEnv<'a>,
pub activity: GlobalRef,
Expand All @@ -42,7 +47,7 @@ impl<'a> MainPipe<'a> {
}
}

pub fn recv(&mut self) -> JniResult<()> {
pub fn recv(&mut self) -> JniResult<MainPipeState> {
let activity = self.activity.as_obj();
if let Ok(message) = CHANNEL.1.recv() {
match message {
Expand Down Expand Up @@ -341,9 +346,12 @@ impl<'a> MainPipe<'a> {
.unwrap();
}
}
WebViewMessage::OnDestroy => {
return Ok(MainPipeState::Destroyed);
}
}
}
Ok(())
Ok(MainPipeState::Alive)
}
}

Expand Down Expand Up @@ -413,6 +421,7 @@ pub(crate) enum WebViewMessage {
LoadUrl(String, Option<http::HeaderMap>),
LoadHtml(String),
ClearAllBrowsingData,
OnDestroy,
}

pub(crate) struct CreateWebViewAttributes {
Expand Down
17 changes: 11 additions & 6 deletions src/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,17 @@ use std::{
collections::HashMap,
os::fd::{AsFd as _, AsRawFd as _},
sync::{mpsc::channel, Mutex},
time::Duration,
};

pub(crate) mod binding;
mod main_pipe;
use main_pipe::{CreateWebViewAttributes, MainPipe, WebViewMessage, MAIN_PIPE};
use main_pipe::{CreateWebViewAttributes, MainPipe, MainPipeState, WebViewMessage, MAIN_PIPE};

use crate::util::Counter;

static COUNTER: Counter = Counter::new();
const MAIN_PIPE_TIMEOUT: Duration = Duration::from_secs(10);

pub struct Context<'a, 'b> {
pub env: &'a mut JNIEnv<'b>,
Expand Down Expand Up @@ -133,8 +135,11 @@ pub unsafe fn android_setup(
let size = std::mem::size_of::<bool>();
let mut wake = false;
if libc::read(fd.as_raw_fd(), &mut wake as *mut _ as *mut _, size) == size as libc::ssize_t {
main_pipe.recv().is_ok()
let res = main_pipe.recv();
// unregister itself on errors or destroy event
matches!(res, Ok(MainPipeState::Alive))
} else {
// unregister itself
false
}
})
Expand Down Expand Up @@ -300,7 +305,7 @@ impl InnerWebView {
});

(custom_protocol.1)(webview_id, request, RequestAsyncResponder { responder });
return Some(rx.recv().unwrap());
return Some(rx.recv_timeout(MAIN_PIPE_TIMEOUT).unwrap());
}
None
},
Expand Down Expand Up @@ -337,7 +342,7 @@ impl InnerWebView {
pub fn url(&self) -> crate::Result<String> {
let (tx, rx) = bounded(1);
MainPipe::send(WebViewMessage::GetUrl(tx));
rx.recv().map_err(Into::into)
rx.recv_timeout(MAIN_PIPE_TIMEOUT).map_err(Into::into)
}

pub fn eval(&self, js: &str, callback: Option<impl Fn(String) + Send + 'static>) -> Result<()> {
Expand Down Expand Up @@ -391,7 +396,7 @@ impl InnerWebView {
pub fn cookies_for_url(&self, url: &str) -> Result<Vec<cookie::Cookie<'static>>> {
let (tx, rx) = bounded(1);
MainPipe::send(WebViewMessage::GetCookies(tx, url.to_string()));
rx.recv().map_err(Into::into)
rx.recv_timeout(MAIN_PIPE_TIMEOUT).map_err(Into::into)
}

pub fn cookies(&self) -> Result<Vec<cookie::Cookie<'static>>> {
Expand Down Expand Up @@ -440,7 +445,7 @@ impl JniHandle {
pub fn platform_webview_version() -> Result<String> {
let (tx, rx) = bounded(1);
MainPipe::send(WebViewMessage::GetWebViewVersion(tx));
rx.recv().unwrap()
rx.recv_timeout(MAIN_PIPE_TIMEOUT).unwrap()
}

fn with_html_head<F: FnOnce(&NodeRef)>(document: &mut NodeRef, f: F) {
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub enum Error {
NulError(#[from] std::ffi::NulError),
#[error(transparent)]
ReceiverError(#[from] std::sync::mpsc::RecvError),
#[cfg(target_os = "android")]
#[error(transparent)]
ReceiverTimeoutError(#[from] crossbeam_channel::RecvTimeoutError),
#[error(transparent)]
SenderError(#[from] std::sync::mpsc::SendError<String>),
#[error("Failed to send the message")]
Expand Down

0 comments on commit 58cc082

Please sign in to comment.