2012-04-25 5 views
0

Мне нужно использовать родную библиотеку (это не мое решение). В библиотеке уже есть обертка JNI, и пример кода Android NDK работает. Но процедуры инициализации библиотеки возвращают собственные дескрипторы, и разработчику необходимо правильно их закрыть.Когда я должен освобождать собственные (Android NDK) ручки?

Теперь, есть интересный вопрос: где вызвать процедуры close_handle?

По крайней мере теоретически каждое неправильное завершение может привести к временным файлам, оставленным где-то на диске, или к утечке какого-либо другого типа.

Инициализация библиотеки занимает от 0,5 до 1 секунды и потребляет много памяти.

Activity является контроллером (в смысле MVC), Android может убить его по своим собственным причинам, включая поворот устройства, и единственная функция, которая будет называться, - onPause(). Таким образом, /onResume является плохим местом для долгой ресурсоемкой операции.

(я знаю о android:configChanges="keyboardHidden|orientation", но я предпочел бы решение, которое не нуждается.)

Application бы идеальным кандидатом (я считаю библиотеку, чтобы быть частью модели), но есть событие «приложение не завершено».

Service звучит многообещающе, что родная библиотека это сервис, но я просто не понимаю, как я могу иметь желаемое поведение: ручки должны быть закрыты, когда приложение завершается.

Тайм-ауты: звучит как компромисс, но на самом деле он гарантирует, что память не будет доступна в тот момент, когда она понадобится, но станет доступна через несколько секунд.

+0

Ровно на каких ручках вы ссылаетесь? – NuSkooler

+0

Детали конфиденциальны («немного здоровой паранойи никому не навредит»), но библиотека использует инициализацию в N шагах, каждый шаг возвращает дескриптор. В C они непрозрачные ручки; для Java они передаются в целые числа. N шагов, потому что, по крайней мере теоретически, вы можете повторно инициализировать несколько бит. – 18446744073709551615

+0

Вопрос был проголосован. Это странно, если это не означает, что поддержка получила аналогичный запрос :) – 18446744073709551615

ответ

0

На данный момент, похоже, мне действительно нужна услуга, но не точно IntentService: IntentService звонки stopSelf(), в то время как мое обслуживание должно повесить.

Любопытно, что вызывается Service.onDestroy(), когда пользователь выбирает «стоп» из «Запуск служб», но не тогда, когда пользователь выбирает «Сила остановки» из «Приложения». «Stop Debugging» также не вызывает вызов Service.onDestroy().

EDIT:

Мое текущее решение заключается в использовании класса, производного от обычая Service подкласса; код был заимствован из источника IntentService, найденного где-то в сети. В производном подклассе I переопределяют 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 
} 

Продолжение ...

0

На практике выяснилось, что некоторая родная библиотека утечки ресурсов; Я должен был сделать перезапуск процесса (это не убивало деятельность, но воссозданной процесс):

how to programmatically "restart" android app?

Можно только надеяться, что нет утечки библиотечной временных файлов на диск ...