我什么时候应该释放本机(Android NDK)句柄?

时间:2012-04-25 10:18:52

标签: android android-ndk java-native-interface native-code resource-leak

我必须使用本机库(这不是我的决定)。该库已经有一个JNI包装器,示例Android NDK代码可以工作。但库初始化例程返回本机句柄,开发人员需要正确关闭它们

现在,有一个有趣的问题:从哪里调用close_handle例程?

至少在理论上,每次不正确的终止都可能导致临时文件留在磁盘上某处或其他类型的资源泄漏。

库初始化需要0.5到1秒,并消耗大量内存。

Activity是一个控制器(在MVC意义上),Android可以因为它自己的原因而杀死它,包括转动设备,唯一保证被调用的函数是onPause()。因此,onPause / onResume对于长期耗费资源的操作来说是一个不好的地方。

(我知道android:configChanges="keyboardHidden|orientation",但我更喜欢不需要它的解决方案。)

Application将是理想的候选者(我认为该库是模型的一部分),但没有“应用程序终止”事件。

A Service听起来很有希望,本机库服务,但我只是看不出我是如何拥有所需行为的:当应用程序终止时,必须关闭句柄。

超时:听起来像是一种权衡,但实际上它保证了内存在需要的时候不可用,但几秒后就会可用。

2 个答案:

答案 0 :(得分:1)

目前看来我确实需要一项服务,但不完全是IntentServiceIntentService来电stopSelf(),而我的服务应该随处可见。

有趣的是,当用户从“正在运行的服务”中选择“停止”时调用Service.onDestroy(),而当用户从“应用程序”中选择“强制停止”时则调用Service.onDestroy()。 “停止调试”也不会导致Service调用。

编辑:

我目前的解决方案是使用从自定义IntentService子类派生的类;代码是从'net中某处找到的onDestroy()的来源借来的。 在派生的子类中,我重写了package com.xyz.customandroid; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; public abstract class HangAroundIntentService extends Service { /** An extra with this name and the value of boolean true marks an intent as a cancel intent. See {@link #markedAsCancelIntent(Intent)}. */ private static final String CANCEL_MARK = "com.xyz~.customandroid.HangAroundIntentService.cancelQueue()"; private static final int WHAT_MAGIC = 0; // the "what" field for messages private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; private boolean mHangAround = true; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); if (!mHangAround) { stopSelf(msg.arg1); } } } /** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public HangAroundIntentService(String name) { super(); mName = name; } /** * Remove all pending messages from the handler queue. * Processing of the message already fetched from the queue * is not terminated by this function. * * Although this function is public, it is recommended * to use the cancel intents instead. * see {@link #markedAsCancelIntent(Intent)} and {@link #isCancelIntent(Intent)}. */ public void cancelQueue() { mServiceHandler.removeMessages(WHAT_MAGIC); } /** * Sets intent redelivery preferences. Usually called from the constructor * with your preferred semantics. * * <p>If enabled is true, * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_REDELIVER_INTENT}, so if this process dies before * {@link #onHandleIntent(Intent)} returns, the process will be restarted * and the intent redelivered. If multiple Intents have been sent, only * the most recent one is guaranteed to be redelivered. * * <p>If enabled is false (the default), * {@link #onStartCommand(Intent, int, int)} will return * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent * dies along with it. */ public void setIntentRedelivery(boolean enabled) { mRedelivery = enabled; } /** * If enabled is true (default), the service does not stop after processing an intent. */ public void setServiceHangAround(boolean enabled) { mHangAround = enabled; } @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } /** * If intent is a cancel intent, {@link #cancelQueue()} is invoked immediately; * no other action is done for a cancel intent, whatever information it might contain. * * Intents that are not cancel intents are queued * to be seen from {@link #onHandleIntent(Intent)}. */ @Override public void onStart(Intent intent, int startId) { if (isCancelIntent(intent)) { cancelQueue(); } else { Message msg = mServiceHandler.obtainMessage(WHAT_MAGIC); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() { mServiceLooper.quit(); } @Override public IBinder onBind(Intent intent) { return null; } /** * This method is invoked on the worker thread with a request to process. * Only one Intent is processed at a time, but the processing happens on a * worker thread that runs independently from other application logic. * So, if this code takes a long time, it will hold up other requests to * the same IntentService, but it will not hold up anything else. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. */ protected abstract void onHandleIntent(Intent intent); /** * Mark an Intent as cancel intent. The Intent will not be placed in the queue; * instead, it will cause immediate cleaning of the queue * (unless you redefine {@link #onStart(Intent, int)} in a derived class). * @param intent to be modified * @return the original intent after modification */ public static Intent markedAsCancelIntent(Intent intent) { intent.putExtra(CANCEL_MARK, true); return intent; } /** * Check if the intent has been marked as a cancel intent. * @param intent to be checked * @return true if it is a cancel intent */ public static boolean isCancelIntent(Intent intent) { return intent.getBooleanExtra(CANCEL_MARK, false); } } ,并认为这是可用的最佳应用程序终止通知。

public class MyService extends HangAroundIntentService {
    public MyService() {
        super("MyService");
    }
    public void onDestroy() {
        MyData.getMyData().shutdown();
        super.onDestroy();
    }
    // service-specific static methods not shown
}

我的服务类定义为:

{{1}}

继续......

答案 1 :(得分:0)

实际上,事实证明一些本地库泄漏了资源;我不得不重新启动进程(这没有杀死活动,但重新创建了进程):

how to programmatically "restart" android app?

人们只能希望没有库泄漏临时磁盘文件......