tags:

views:

125

answers:

4

I had a bunch of code in an activity that displays a running graph of some external data. As the activity code was getting kind of cluttered, I decided to extract this code and create a GraphView class:

public class GraphView extends LinearLayout {
    public GraphView(Context context, AttributeSet attrs) {
        super(context, attrs);

        LayoutInflater inflater = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        inflater.inflate(R.layout.graph_view, this, true);
    }

    public void start() {
        // Perform initialization (bindings, timers, etc) here
    }

    public void stop() {
        // Unbind, destroy timers, yadda yadda
    }
        .
        .
        .
}

Moving stuff into this new LinearLayout-derived class was simple. But there was some lifecycle management code associated with creating and destroying timers and event listeners used by this graph (I didn't want this thing polling in the background if the activity was paused, for example).

Coming from a MS Windows background, I kind of expected to find overridable onCreate() and onDestroy() methods or something similar, but I haven't found anything of the sort in LinearLayout (or any of its inherited members). Having to leave all of this initialization code in the Activity, and then having to pass it into the view seemed like it defeated the original purpose of encapsulating all of this code into a reusable view.

I ended up adding two additional public methods to my view: start() and stop(). I make these calls from the activity's onResume() and onPause() methods respectively.

This seems to work, but it feels like I'm using duct tape here. Does anyone know how this is typically done? I feel like I'm missing something...

A: 

What you probably need is the Disposable Pattern. Implement IDisposable in your class.

public class Class1 : IDisposable
{
    private bool m_IsDisposed;

    ~Class1()
    {
        Dispose(false);
    }


    [MethodImpl(MethodImplOptions.Synchronized)]
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool isDisposing)
    {
        if (m_IsDisposed)
            return;

        if (isDisposing)
        {
            FreeManagedRessources();
        }
        FreeUnmanagedRessources();
        m_IsDisposed = true;
    }

    protected virtual void FreeManagedRessources()
    {
        //Free managed ressources here. Typically by calling Dispose on them
    }

    protected virtual void FreeUnmanagedRessources()
    {
        //Free unmanaged ressources here.
    }
}
JeremySpouken
OK, but who is going to call Dispose? If it's me, then the question is the same - how do I know when to call Dispose when there is no notification that the View is being destroyed.
Scott Smith
-1 For C#, there is no IDisposable in Java
CaseyB
+1  A: 

Unfortunately, View object does not have any callbacks methods as the Activity when going from background and active mode.

Anyway, if you insist on such an approach, I guess the closest you get is to put the init code into the constructor and the destruct code into an override of finalize(). Though, the finalize() method is runned by the system when the object is not referred to anymore, making it ready to be garbage collected. It may not be called at all if vm exits. And I would not recommend this way.

Also, you don't want to create and destroy the GraphView object(s) over and over again when your app goes from pause to resume as short lived objects causes memoryleaks. You never know when the gc will free memory for these objects, even there is no references to them.

I think your approrach with start() and stop() methods are ok, just keep them simple and clean. All they got to do is to maintain the AsyncTasks (or Timer objects).

(Off-topic regarding the way you inflate your views: I use View.inflate() mostly as it saves me for a few lines of code)

PHP_Jedi
+1 for View.inflate()
Scott Smith
+1  A: 

You may be able to get some use out of overriding protected void onAttachedToWindow() and protected void onDetachedFromWindow() I have never tried, but they may be called approximately when you would like.

CaseyB
A: 

I've only done a brief experiment with with, but it appears that if you override onAttachedToWindow and onDetachedFromWindow as mentioned by CaseyB above along with overriding

protected void onWindowVisibilityChanged(int visibility)

It should give you the information you need.

I'm running into the same situation as you. I'm surprised there is no notification mechanism for this.

ThomasW