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.


Monday, October 7, 2013

Programming things to remember 2013 Sept

DownloadManager

- Attach a BroadcastReceiver to listen for DownloadManager.ACTION_DOWNLOAD_COMPLETE.
- Finish the (only) Activity. At this point the app process will still run
- Disable wifi
Result: the app process will be killed, no broadcast received when the download is paused, nor when the framework continues and finishes the download.

Broadcasts

Broadcast will be received even if the app is in the background, if BroadcastReceiver was registered. It is just a good practice to unregister it if not needed (because it may consume resource in vain). So generally you should unregister onPause() if you registered it onResume(). Test code.

If you register the broadcast receiver with the activity's context, it will receive broadcasts as long as the activity is alive. However, you can register the receiver with the applicatino context, and it will still receive broadcasts even if the activity isn't alive. cited from here

Public field (member variable) Matcher

I wrote one. Code here

NineOldAndroid animation

When you use NineOldAndroid for property animation, it will still Value-animate under the hood. Meaning if a View has button and you rotate it by 90 degrees around the y axis (or animating the visibility to gone). The button will be still clickable.