Skip to content

Commit

Permalink
更新了android.view相关的方法,并改进了Java动态代理存在的重锁死锁(使用可重入互斥锁)。
Browse files Browse the repository at this point in the history
  • Loading branch information
SmileSky committed Aug 10, 2024
1 parent cd405d2 commit c8173e4
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 82 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ authors = ["mzdk100 <[email protected]>"]
description = "用于Rust的Android API的高级封装"
keywords = ["android", "api", "sdk", "jni", "java"]
license = "Apache-2.0"
version = "0.2.7"
version = "0.2.8"
edition = "2021"
readme = "README.md"
repository = "https://gitcode.net/mzdk100/droid-wrap.git"
Expand Down Expand Up @@ -65,11 +65,11 @@ test_java_nio = ["java_nio"]
[dependencies]

[dependencies.droid-wrap-utils]
version = "0.2.7"
version = "0.2.8"
path = "utils"

[dependencies.droid-wrap-derive]
version = "0.2.7"
version = "0.2.8"
path="derive"

[workspace]
Expand Down
9 changes: 8 additions & 1 deletion derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ pub fn java_class(attrs: TokenStream, input: TokenStream) -> TokenStream {
&self.#added_super
}
}

impl #generics std::ops::DerefMut for #name #generics {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.#added_super
}
}
}
} else {
quote! {
Expand Down Expand Up @@ -329,6 +335,7 @@ pub fn java_method(attrs: TokenStream, input: TokenStream) -> TokenStream {
quote! {
#(#attrs)*
#vis #sig {
#(#stmts)*
droid_wrap_utils::vm_attach!(mut env);
let ret = env.call_method(
#self_.java_ref(),
Expand Down Expand Up @@ -598,7 +605,7 @@ pub fn java_implement(attrs: TokenStream, input: TokenStream) -> TokenStream {

impl Drop for #name {
fn drop(&mut self) {
droid_wrap_utils::unbind_proxy_handler(&self.java_ref());
self.release();
}
}
};
Expand Down
15 changes: 12 additions & 3 deletions example/src/activity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ use droid_wrap::{
},
},
java::lang::{CharSequenceExt, CharSequenceImpl, RunnableImpl},
JProxy,
};
use mobile_entry_point::mobile_entry_point;
use std::sync::Arc;

#[mobile_entry_point]
fn main() {
let act = Activity::fetch();
Expand All @@ -42,8 +42,11 @@ fn main() {
"你好,这是一个用Rust构建的安卓示例。".to_char_sequence::<CharSequenceImpl>(),
));
let edit = EditText::new(&context);

let editor_listener = TextView_OnEditorActionListenerImpl::from_fn(|_, _, _| true);
edit.set_on_editor_action_listener(editor_listener.as_ref());
// 请在合适的时机手动释放,因为rust无法感知java什么时候不再需要Listener。
// editor_listener.release();

let act2 = act.clone();
act.run_on_ui_thread(
Expand All @@ -63,9 +66,15 @@ fn main() {

let wm: WindowManagerImpl = act2.get_window_manager();
let params = WindowManager_LayoutParams::new();
wm.add_view(&text_view, &params);
let _ = wm.add_view(&text_view, &params);
wm.remove_view(&text_view);
wm.add_view(&edit, &params);
let _ = wm.add_view(&edit, &params);
let runnable = RunnableImpl::from_fn(|| {
println!("post delayed");
});
edit.post_delayed(runnable.as_ref(), 100);
// 请在合适的时机手动释放,因为rust无法感知java什么时候不再需要Runnable。
// runnable.release();
})
.as_ref(),
);
Expand Down
22 changes: 18 additions & 4 deletions src/android/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,14 +708,18 @@ impl View_OnClickListener for View_OnClickListenerImpl {
接口允许您向 Activity 添加和删除子视图。要获取此类的实例,请调用 Context.getSystemService()。
*/
#[java_interface(name = "android/view/ViewManager")]
pub trait ViewManager {
pub trait ViewManager: JType {
/**
将传递的 LayoutParams 分配给传递的 View,并将该视图添加到窗口。对于某些编程错误,例如在未移除第一个视图的情况下向窗口添加第二个视图,将抛出 WindowManager.BadTokenException。
如果窗口位于辅助显示器上并且找不到指定的显示器,则抛出 WindowManager.InvalidDisplayException(请参阅 android.app.Presentation)。
`view` 要添加到此窗口的视图。
`params` 要分配给视图的 LayoutParams。
*/
fn add_view(&self, view: &View, params: &ViewGroup_LayoutParams);
fn add_view(
&self,
view: &View,
params: &ViewGroup_LayoutParams,
) -> Result<(), <Self as JType>::Error>;

#[doc(hidden)]
fn update_view_layout(&self, view: &View, params: &ViewGroup_LayoutParams);
Expand Down Expand Up @@ -755,7 +759,12 @@ pub struct WindowManagerImpl;

impl ViewManager for WindowManagerImpl {
#[java_method]
fn add_view(&self, view: &View, params: &ViewGroup_LayoutParams) {}
fn add_view(
&self,
view: &View,
params: &ViewGroup_LayoutParams,
) -> Result<(), <Self as JType>::Error> {
}

#[java_method]
fn update_view_layout(&self, view: &View, params: &ViewGroup_LayoutParams) {}
Expand Down Expand Up @@ -809,7 +818,12 @@ impl ViewGroup {

impl ViewManager for ViewGroup {
#[java_method]
fn add_view(&self, view: &View, params: &ViewGroup_LayoutParams) {}
fn add_view(
&self,
view: &View,
params: &ViewGroup_LayoutParams,
) -> Result<(), <Self as JType>::Error> {
}

#[java_method]
fn update_view_layout(&self, view: &View, params: &ViewGroup_LayoutParams) {}
Expand Down
1 change: 1 addition & 0 deletions utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]
jni = "0.21.1"
ndk-context = "0.1.1"
log = "0.4.22"
parking_lot = "0.12.3"

[build-dependencies]
android-build = "0.1.0"
Expand Down
121 changes: 50 additions & 71 deletions utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,34 @@ pub use jni::{
AttachGuard, JNIEnv, JavaVM, NativeMethod,
};
use log::{debug, error, warn};
use parking_lot::ReentrantMutex;
use std::{
cell::RefCell,
collections::HashMap,
fmt::Debug,
hash::{DefaultHasher, Hash, Hasher},
str::FromStr,
sync::{
mpsc::{channel, Receiver, Sender},
LazyLock, OnceLock, RwLock,
},
sync::{Arc, LazyLock, OnceLock},
};

// Rust 代理对象的哈希值映射到 Rust 函数
static HOOK_OBJECTS: LazyLock<
RwLock<
HashMap<
i32,
Box<
dyn Fn(&mut JNIEnv<'_>, &JObject<'_>, &JObjectArray<'_>) -> GlobalRef + Send + Sync,
ReentrantMutex<
RefCell<
HashMap<
i32,
Arc<
dyn Fn(&mut JNIEnv<'_>, &JObject<'_>, &JObjectArray<'_>) -> GlobalRef
+ Send
+ Sync,
>,
>,
>,
>,
> = LazyLock::new(|| RwLock::new(HashMap::new()));
> = LazyLock::new(|| ReentrantMutex::new(RefCell::new(HashMap::new())));
// Rust 代理对象的哈希值映射到 Java 被代理的对象的哈希值
static HOOK_OBJECTS_OTHER: LazyLock<RwLock<HashMap<u64, i32>>> =
LazyLock::new(|| RwLock::new(HashMap::new()));
// 当某个代理对象在rust层被Drop时,延迟释放HOOK_OBJECTS表中的对象,这样不至于本地闭包函数无法被调用,同时还解决了RwLock可能死锁的问题
static HOOK_DROP_CHANNEL: OnceLock<(Sender<u64>, Recv)> = OnceLock::new();

struct Recv(Receiver<u64>);
unsafe impl Sync for Recv {}
unsafe impl Send for Recv {}
impl std::ops::Deref for Recv {
type Target = Receiver<u64>;

fn deref(&self) -> &Self::Target {
&self.0
}
}
static HOOK_OBJECTS_OTHER: LazyLock<ReentrantMutex<RefCell<HashMap<u64, i32>>>> =
LazyLock::new(|| ReentrantMutex::new(RefCell::new(HashMap::new())));

/**
定义必要的trait,以便于在本地为任何数据类型实现JAVA对象所需的功能。
Expand All @@ -67,7 +57,7 @@ macro_rules! import {
rc::Rc,
sync::{Arc, Mutex},
};
use $crate::{null_value, vm_attach, GlobalRef, JObject};
use $crate::{null_value, unbind_proxy_handler, vm_attach, GlobalRef, JObject};

/**
JObjectRef trait提供从任何数据类型获取java对象的全局引用。
Expand Down Expand Up @@ -121,14 +111,21 @@ macro_rules! import {
}

/**
用于Java动态代理的创建
用于Java动态代理的创建和删除
*/
pub trait JProxy: JObjNew {
pub trait JProxy: JObjNew + JObjRef {
/**
创建一个代理对象。
`fields` 传递给struct的自定义字段。
*/
fn new(fields: Self::Fields) -> std::sync::Arc<Self>;

/**
释放代理对象,解除绑定的handler。
*/
fn release(&self) {
unbind_proxy_handler(&self.java_ref());
}
}

impl<T: JObjRef> JObjRef for &T {
Expand Down Expand Up @@ -329,10 +326,10 @@ pub fn new_proxy(interfaces: &[&str]) -> GlobalRef {
.l()
.unwrap();
let res = env.new_global_ref(&res).unwrap();
let mut lock = HOOK_OBJECTS_OTHER.write().unwrap();
let lock = HOOK_OBJECTS_OTHER.lock();
let mut hasher = DefaultHasher::new();
res.hash(&mut hasher);
lock.insert(hasher.finish(), hash_code);
lock.borrow_mut().insert(hasher.finish(), hash_code);
drop(lock);
res
}
Expand Down Expand Up @@ -369,35 +366,14 @@ pub fn bind_proxy_handler(
+ 'static,
) {
let hash_code = get_proxy_hash_code(proxy);
let mut lock = match HOOK_OBJECTS.try_write() {
Ok(lock) => lock,
Err(e) => {
error!("Can't bind proxy handler. ({})", e);
let lock = match HOOK_OBJECTS.try_lock() {
Some(lock) => lock,
None => {
error!("Can't bind proxy handler.");
return;
}
};
lock.insert(hash_code, Box::new(handler));
let (_, rx) = HOOK_DROP_CHANNEL.get_or_init(|| {
let (tx, rx) = channel();
(tx, Recv(rx))
});
loop {
match rx.try_recv() {
Ok(proxy_hash_code) => {
let lock2 = HOOK_OBJECTS_OTHER.read().unwrap();
let Some(code) = lock2.get(&proxy_hash_code) else {
continue;
};
let code = *code;
drop(lock2);
lock.remove_entry(&code);
debug!("Proxy `{}` is dropped.", proxy_hash_code);
}
Err(..) => {
break;
}
}
}
lock.borrow_mut().insert(hash_code, Arc::new(handler));
}

/// 获取java代理对象的哈希值。
Expand All @@ -419,11 +395,11 @@ pub fn get_proxy_hash_code(proxy: &GlobalRef) -> i32 {
let mut hasher = DefaultHasher::new();
proxy.hash(&mut hasher);
let proxy_hash_code = hasher.finish();
let lock = HOOK_OBJECTS_OTHER.read().unwrap();
let Some(code) = lock.get(&proxy_hash_code) else {
return 0;
let lock = HOOK_OBJECTS_OTHER.lock();
if let Some(code) = lock.borrow().get(&proxy_hash_code) {
return *code;
};
*code
0
}

//noinspection SpellCheckingInspection
Expand All @@ -446,9 +422,12 @@ pub fn unbind_proxy_handler(proxy: &GlobalRef) {
let mut hasher = DefaultHasher::new();
proxy.hash(&mut hasher);
let proxy_hash_code = hasher.finish();
if let Some((tx, _)) = HOOK_DROP_CHANNEL.get() {
let _ = tx.send(proxy_hash_code);
}
let lock = HOOK_OBJECTS_OTHER.lock();
if let Some(code) = lock.borrow().get(&proxy_hash_code) {
let lock = HOOK_OBJECTS.lock();
lock.borrow_mut().remove_entry(code);
debug!("Proxy `{}` is dropped.", proxy_hash_code);
};
}

/**
Expand All @@ -475,6 +454,7 @@ impl<T: FromStr> ParseJObjectType<T> for JObject<'_> {
s.to_str().unwrap().parse().unwrap()
}
}

//noinspection SpellCheckingInspection
fn load_rust_call_method_hook_class<'a>() -> &'a GlobalRef {
#[cfg(target_os = "android")]
Expand Down Expand Up @@ -583,19 +563,18 @@ unsafe extern "C" fn rust_callback<'a>(
}
}

let lock = HOOK_OBJECTS.read().unwrap();
if let Some(f) = lock.get(&hash_code) {
let ret = f(&mut env, &method, &args);
drop(lock);

return env.new_local_ref(ret.as_obj()).unwrap();
let lock = HOOK_OBJECTS.lock();
let func = if let Some(f) = lock.borrow().get(&hash_code) {
f.to_owned()
} else {
warn!(
"The method call has reached, but it appears that the proxy object has been dropped."
);
}
return JObject::null();
};
drop(lock);
JObject::null()
let ret = func(&mut env, &method, &args);
env.new_local_ref(ret.as_obj()).unwrap()
}

/// 把java对象数组转换成Vec
Expand Down

0 comments on commit c8173e4

Please sign in to comment.