Friday, October 25, 2013

Programming things to remember 2013 Oct

Application context and resources

Regardless of how you get access to the resource it will be what relates to the current configuration. So even if you get resource via application context it will provide the correct one after eg. rotation. This is true even if you retain the Resources object (provided by either Context). The configuration resolution happens when you getXXX(). (If you retain the string itself, of course it won't change)
private Resources mResources;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        if (getLastNonConfigurationInstance() == null) {
            mResource = getResources();
        } else {
            mResource = (Resources) getLastNonConfigurationInstance(); // deprecated
        }
        final TextView viewById = (TextView)this.findViewById(R.id.text1);
        viewById.setText(mResource.getString(R.string.foo));
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        return mResource;
    }

Posting delayed message to Handler of main Thread won't leak the Activity it was created from

The Handler doesn't have a strong reference to the Activity. So if a Handler is created from the main thread (from an Activity eg.), and you don't set explicitly a reference, the Activity can freely be GC-d. Of course the Handler itself will be referenced, so that may be leaked.

public class MyActivity extends Activity {
    private Handler mHandler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("|MQ", "onCreate()");

        mHandler = new MyHandler(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d("|MQ", "onResume()" + " MyActivity.this: " + MyActivity.this);
        mHandler.sendEmptyMessageDelayed(1, 30 * 1000);
    }
}


private class MyHandler extends Handler {
    private final WeakReference mMyActivity;

    public MyHandler(MyActivity myActivity) {
        mMyActivity = new WeakReference(myActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        if (mMyActivity.get() != null) {
            Log.d("|MQ", "handleMessage()" + " MyActivity.this: " + mMyActivity.get());
        } else {
            Log.d("|MQ", "handleMessage()" + " no MyActivity");
        }
    }
}

13:40:43.217 D/|MQ: onCreate()
13:40:43.267 D/|MQ: onResume() MyActivity.this: com.example.broadcastReceive.MyActivity@428d7f38
rotate device
13:40:44.638 D/|MQ: onCreate()
13:40:44.669 D/|MQ: onResume() MyActivity.this: com.example.broadcastReceive.MyActivity@429150c8
rotate device
13:40:46.190 D/|MQ: onCreate()
13:40:46.240 D/|MQ: onResume() MyActivity.this: com.example.broadcastReceive.MyActivity@428b5f08
rotate device
13:40:47.782 D/|MQ: onCreate()
13:40:47.832 D/|MQ: onResume() MyActivity.this: com.example.broadcastReceive.MyActivity@42900c30
13:41:13.279 D/|MQ: handleMessage() no MyActivity
13:41:14.671 D/|MQ: handleMessage() no MyActivity
13:41:16.252 D/|MQ: handleMessage() MyActivity.this: com.example.broadcastReceive.MyActivity@428b5f08
13:41:17.844 D/|MQ: handleMessage() MyActivity.this: com.example.broadcastReceive.MyActivity@42900c30

Anonymous handler leaks the activity

Anonymous class has an implicit reference to the class it was created from. So if such a Handler is created in the onCreate() it will have a reference to the Activity. Then if you postDelayed a Message to the Handler (or a Runnable, which will be wrapped by a Message at the end of the day), the Message will have a reference to the Handler. The main Looper has a reference to the Message, and the Application has a reference to the Looper. And so the Activity is leaked (it won't get GC-d, after onDestroy() till the Message is handled).
Example.
Additional info on the different kind of classes.

Posting delayed Messages to Handler doesn't count the time spent in deep sleep

It uses the SystemClock.uptimeMillis() as a base. More info.


No comments: