猫哥不懂技术

莫催稿,催稿也不交

NDK 线程的一些坑与解决

自从换了某个版本的 NDK 后,就一直坑不断,踩了几天之后,发现在线程部分坑已然变多,必须好好解决一下了

首先讲一下线程的问题,目前建议将所有可能会耗时的函数都放进线程,原因是现在的 NDK 似乎可能好像也许会在意料不到的地方 ANR,比如以下代码:

clsContext := env^^.FindClass(env, 'android/content/Context');
getServiceContet := env^^.GetMethodID(env, clsContext, 'getSystemService', '(Ljava/lang/String;)Ljava/lang/Object;');
objActivityMgr := env^^.CallObjectMethodA(env, CONTEXT, getServiceContet, argsToJValues(env, ['activity']));

你能想象这会产生一个 ANR 吗?可能我们都没有注意过这样的问题,认为这种情况没有 ANR 的可能,然而通过实际采集的 bug 告诉我们,这样的代码在 NDK 里,ANR 的机率大概在 0.2% 左右,所以这跟本不是什么能侥幸的事情。

所以将这样的代码放进线程,是很有必要的。那么下面就开始遇到坑,比如说:

clsContext := env^^.FindClass(env, 'android/content/Context');

很可惜的,在线程里若是这么干,FindClass 得到的结果永远是 nil,原因是子线程里的 JNIEnv 与主线程里的不同,因此找不到类。好吧,那么就在主线程里先完成 FindClass 吧。

var clsContext: jclass;
...
procedure ...
    clsContext := env^^.FindClass(env, 'android/content/Context');
...

看起来解决了?其实不然,实际用一下就知道了,比如说用以下代码:

thread { NativeAPI.run() }

这个时候依然会从 JNI 层抛出 accessed stale local reference 异常,引起闪退。这个闪退的原因是,局部的引用在函数结束时,已被清理。若是用老版本的 NDK 进行编译,并不会产生此问题。那么在新版本下,就需要额外多做一件事了:

var clsContext: jclass;
...
procedure ...
    clsContext := env^^.NewGlobalRef(env, env^^.FindClass(env, 'android/content/Context'));
...

至此,搞定了在线程里面 FindClass 的问题,当然了,你也可以选择用 AttachCurrentThread 的方案,不再赘述。


  • 评论列表:
  •  你猜
     发布于 2017-07-07 16:05:07  回复该评论
  • 哈哈哈哈,所以不要用 NDK才是万能解决方案。

发表评论:

Powered By Z-BlogPHP 1.5.1 Zero

Copyright Rarnu 2017. All Rights Reserved.