아침에 출근하다가 버스에서 라디오를 듣는데...  우르과이 "호세 무히카"대통령을 언급하면서, 대통령에 대한 존경을 표하는 내용의 방송이 나왔다.

그런데 문득 대한민국은 참 이상한 나라라는 생각이 들었다.

"왜 대통령을 존경하지?"

그때부터, 아래와 같은 생각들이 계속해서 머리에 맴돌았다. (그냥 떠오르는대로 마구 적어 본다...)


내가 보기에는, 대한민국의 많은 국민들이, 나라에서 일어나는 많은 일들의 공과(功過)를 이야기할때, "정치인의 공과(功過)"라는 관점으로 접근하는 것 같다.

박정희 전 대통령의 경우를 예로 들어 보면...


많은 사람들이 '박정희' 전 대통령의 업적 중 하나로, '경제성장'을 말한다.

일단 '박정희' 전 대통령에 대한 다른 평가는 전부 접어두자.

여기서 내가 이야기 하고 싶은 것은, '경제성장'의 업적을 자연스럽게 나라의 최고 통지자에게 돌리는 '이상한' 모습이다.

박 전 대통령의 역할이 전혀 없었다는 말을 하는건 아니다. 그건 누구도 알 수 없는 부분이다. 엄청난 기여를 했을 수도 있고, 전혀 기여하지 않았을 수도 있다. 혹은 오히려 방해가 되었을지도 모를 일이다.

그런데, 이상하게도 상당수의 국민들이 '경제성장'이라는 업적을 그냥 단순하게 '대통령'의 업적으로 이야기한다.


왜 그럴까? 그냥 단순한 '국민성'인건가? 아니면, 책임 회피? 모르겠다.

그렇지만, 내 생각은 이렇다.

박 전 대통령의 '경제성장'에 대한 공과(功過)는 논란의 여지가 있으나, 한가지 100% 확실한 것은 '경제성장'의 주역은 그 시대를 살았던 '대한민국 국민'이라는 것이다. 즉, 한강의 기적은 '최고 통치자의 업적'이 아니라, '대한민국 국민의 업적'으로 받아들여져야 한다.


다시 "호세 무히카" 대통령에 대한 이야기로 돌아가보면...

우리가 존경해야할 상대는 "호세 무히카" 대통령이 아니라, 그 사람을 대통령으로 선출한 "현재의 우르과이 유권자"가 되어야 하다.

대한민국에 과연 "호세 무히카"같은 사람이 없을까?

그럴리 없다. 어딘가 분명히 더 훌륭한 분이 계실 것이다.

그렇지만, 대한민국 유권자는 그런 사람을 대통령으로 선출하지 않았다.

"등록된 후보 중에 그런 사람이 없지 않냐?"라는 이야기를 한다면, "대한민국 유권자가, 그런 사람이 '등록하면 당선될 수 있다.'라는 희망을 가질 수 있는 정도의 수준인가?"라고 되묻고 싶다.


정리하면... 우리는 훌륭한 정치인을 칭찬하기 보다는, 그런 정치인을 알아보고 선택한 것을 자축해야 하며, 나쁜 정치인을 비난하기 보다는, 그런 정치인을 선택한 것을 반성해야 한다. 시대의 '공(功)'과 '과(過)'는 정치인의 몫이 아니라, 그 시대를 사는 국민들의 몫이다. 적어도, 민주주의 국가에서는...


Sometimes FragmentManager destroys and re-instantiates Fragment without any notification by framework's behavior.

Important point here is that default Constructor is used to re-instantiate Fragment.

In this case, without saving and restoring run-time state of Fragment, it looses all of it's state.

This is exactly same with Activity.

But, in terms of Activity, user can know and control when the Activity is destroyed and recreated.

So, in case of Activity, this issue is not big problem.


Therefore, followings should be considered to design Fragment.

- use ONLY default constructor. (FragmentManager uses only default constructor.)

- should NOT have any not-parcelable- run-time data.


But, sometimes, Fragment may need to have not-parcelable-run-time data.

In this case, using Activity can be good choice.

That is, let owner Activity have the data and access it by using getActivity() method.


FragmentPagerAdapter is very useful and easy-to-use.
But, there is one point to keep it mind when using it.
See below codes from FragmentPagerAdapter.java

    @Override
    public Object instantiateItem(View container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), position);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), position));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setStartDeferred(true);
        }

        return fragment;
    }

Usually, FragmentPagerAdapter is used with ViewPager. In this case, variable container is reference of ViewPager.

Developer who are familiar with normal AdapterView, may try to change FragmentPagerAdapter by using setAdapter interface of ViewPager without doing something else to ViewPager.

But, in this case, as you can see from above code, instantiateItem function reuses previous Fragment instance because even if FragmentPagerAdapter is changed, ViewPager is same. So, fragment name getting from makeFragmentName function returns same value.

And that is NOT expected result at normal AdatperView.


[ Android ICS ]

PatternMatcher is used for pathPattern at IntentFilter

But, PatternMatcher's algorithm is quite strange to me.

Here is algorithm of Android PatternMatcher.


If there is 'next character' of '.*' pattern in the middle of string, PatternMatcher stops loop at that point.

(See PatternMatcher.java of Android framework.)


Ex.

string  : "this is a my attachment"

pattern : ".*att.*".

Android PatternMatcher enter loop to match '.*' pattern until meet the next character of pattern

(at this example, 'a')

So, '.*' matching loop stops at index 8 - 'a' between 'is' and 'my'.

Therefore result of this match returns 'false'.


Quite strange, isn't it.

To workaround this - actually reduce possibility - developer should use annoying stupid pathPattern.


Ex.

Goal : Matching uri path which includes 'message'.

<intent-filter>

...

<data android:pathPattern=".*message.*" />

<data android:pathPattern=".*m.*message.*" />

<data android:pathPattern=".*m.*m.*message.*" />

<data android:pathPattern=".*m.*m.*m.*message.*" />

<data android:pathPattern=".*m.*m.*m.*m.*message.*" />

...

</intent-filter>


This is especially issued when matching with custom file extention.


Ex.

Goal : Matching file which extention is 'myextention'.


Below filter doesn't work as expected because of issue described above.

(Ex. "sample.test.myextention" doesn't match by PatternMatcher.)

<intent-filter>

...

<data android:pathPattern=".*\\.myextention" />

...

</intent-filter>


So, like above, stupid-dirty filter should be used as follows.

<intent-filter>

...

<data android:pathPattern=".*\\.myextention" />

<data android:pathPattern=".*\\..*\\.myextention"/>

<data android:pathPattern=".*\\..*\\..*\\.myextention"/>

<data android:pathPattern=".*\\..*\\..*\\..*\\.myextention"/>

...

</intent-filter>


Done. :-)

 소프트웨어가 커지다 보면, 자연스럽게 OOP개념들을 집어 넣을 수 밖에 없고 - 그렇지 않으면, SW 유지보수가 거의 불가능 하다. - 각 컴포넌트/모듈들을 독립적으로 구현하기 위해서 고민하게 된다.

 각 모듈이나 컴포넌트가 독립적일수록 재사용성이 높아지고 모듈간 의존도가 줄어들게 된다.

 그런데, 문제는 이렇게 서로 독립적으로 만들다 보면, error value들 역시 독립적으로 선언해야 하게 되는데, 이게... 좀... 거시기 하다.

예를 들어, 모듈 A, B, C가 있을 때, A -> B -> C 이런식으로 C가 B를 reference하고, B가 A를 reference하는 경우, C와 B는 각각 내부 구현을 숨기기 위해서, B와 A의 error value를 노출 시킬 수 없게 된다. 따라서, B는 A의 error value를 B자신의 value로 mapping해야 하고, C또한, 이렇게 mapping된 B의 value를 다시 C자신의 value로 mapping해야 하게 된다.

 쩝... overhead의 chain effect라 불릴만 하다.

그래서 많이 하는 방법이, 상당히 자주 사용되는 error value는 global하게 공통적으로 정의하고 - ex. invalid parameter, out of memory 등등 - 세부적인 것들에 대해서는 모듈 specific하게 정의하는 것이다.

예를 들면...


common_error.h

enum {

COMMON_ERR_01 = 0,

COMMON_ERR_02,

...

LIMIT_COMMON_ERR,

CUSTOM_ERR_BASE_OFFSET,

};


module1.h

enum {

MODULE1_ERR_01 = CUSTOM_ERR_BASE_OFFSET,

MODULE1_ERR_02,

...

};


module2.h

enum {

MODULE2_ERR_01 = CUSTOM_ERR_BASE_OFFSET,

MODULE2_ERR_02,

...

};


 음... 꽤나 괜찮은 해결책으로 보인다. 아니, 현재까지 생각으로는 현실적으로 가장 좋은 해결책으로 보인다.

 그렇지만, 여전히 몇 가지 문제점이 존재한다. 먼저, error value의 return type을 그냥 int 를 사용해야 하는데 - 공통된 error value의 type을 정의할 수 없다 - constant value는 inline으로 코드에 삽입되기 때문에, debugging시에 썩 좋지 못하다는 문제가 있다. 또한, return type이 그냥 int 라 code readability도 썩 좋지 못하다.  또한, global common error code가 module내에서 사용되므로, 이 모듈을 전혀 다른 SW에 porting할때, common error 부분을 해당 SW에 맞게 수정을 가해야 하는 문제도 있다.

 그렇지만, overhead와 재사용성 양쪽을 균형있게 고려한, 충분히 훌륭한 방법임에는 틀림없다.


음...

 혹시 다른 좋은 아이디어가 떠 오르면... 다시 posting하기로 하고...

Height of status bar can be read by following way. (can be found easily by Googling - ex. stack-overflow)


Rect rect= new Rect();

Window window= activity.getWindow();

window.getDecorView().getWindowVisibleDisplayFrame(rect);

return rect.top;


But, even after hiding status bar by adding LayoutParams.FLAG_FULLSCREEN at onCreate() or onResume(), height of status bar read by above way, may be still invalid (Not 0).

(I didn't analyze deeply about WindowManager. So, I'm not sure about root cause - due to animation effect??? or something else???)

To workaround this issue, the best place to read height of status bar - hidden or shown - is onWindowFocusedChanged().

Reading height of status bar at the first call of onWindowFocusedChanged() gives exact(expected) value based on my experience.

And then, this value can be reused afterward.


Sometimes developer may be faced with below log message with extremely-slow-scrolling of List.


Log includes "Window is full: requested allocation 2195889 bytes, free space 2096720 bytes, window size 2097152 bytes"


Most case of this kind of issue can be seen when SQLite DB has blob-type field whose data is quite big, and Cursor that includes this field is used at Aadapter.

In this case, Cursor easily exceeds it's maximum Window size (at Android 4.0.3, this value is about 2 MB).

After reaching to maximum Window size, Cursor need to handle Window, and at worse, field size is quite big.
So, whenever move to another row of Cursor, Cursor should handle quite complex disk operation.

So, at this moment, scrolling list becomes very very slow.


To avoid this, main Cursor of Adapter would better not to have big-size field of DB Table.

Instead of it, try to read from DB whenever the field value is demanded.


Then, average time for scrolling list may be increased. But, even at worst cases, list can show reasonable scrolling performance.


When using Adapter (especially list), sometimes(actually very frequently) data of only one item(row) is changed.

In this case, usually, changing one-under-lying data is easy.

But, updating only one list item is different story.

To update that item to the screen, - changing look-and-feel regarding changed item -  easiest way is calling notifyDataSetChanged().

But, this re-bind views of all list items. This is wasteful.

To avoid this, we should get View of item associated, and then modify it directly.

At first glance, it is easy and "Item position to View" map seems to be useful. But, it's NOT.

Why?

AdapterView usually reuse Views. So, several positions become to share same view at "position to View" map.
That is, whenever try to update item at specific item, we need to check that that item has valid visible View or not.

But, this is not-easy at current(API 15) Android API.


My suggestion is using "View to position" map(henceforth VPMap).

At AdapterView, View is reused. So, Adapter doesn't have lot's of different Views.

So, key size of the VPMap is not large (usually at most 20~30).

And we can easily update recent valid visible position of View easily at getView() of Adapter.

See below sample code.

private final HashMap<View, Integer> mView2PosMap = new HashMap<View, Integer>();
...
public void
setItemActive(int pos) {
    if (pos == mActivePos)
        return;
    View v = Utils.findKey(mView2PosMap, mActivePos);
    if (null != v)
        setToInactive(v);
    v = Utils.findKey(mView2PosMap, pos);
    if (null != v)
        setToActive(v);

    mActivePos = pos;
}
...
@Override
public View
getView(int position, View convertView, ViewGroup parent) {
    View v;
    if (null != convertView)
        v = convertView;
    else
        v = UiUtils.inflateLayout(mContext, R.layout.row);

    mView2PosMap.put(v, position);
    ...
}
...
public static <k,v> K
findKey(HashMap<View, Integer> map, V value) {
    Iterator<k> iter = map.keySet().iterator();
    while(iter.hasNext()) {
        K key = iter.next();
        if (map.get(key).equals(value))
            return key;
    }
    return null;
}

Like above, with this VPMap we can easily find associated visible View and can update only one-updated-item-View without calling 'notifyDataSetChanged()'.


Sometimes developer may need to use MediaPlayer directly instead of using VideoView.

In this case, developer should keep belows in his/her mind.

<NOTE : Gingerbread and ICS are a little different (ICS is more flexible. But, the more strict is the better.>

 - setDisplay Should be called after surface of SurfaceHolder is created.

 - video should be started after it's video size is known (after onVideoSizeChanged is called.)

 - setDisplay() should be called before prepare()


Let's summarize it.


onSurfaceCreate -> setDisplay() -> prepare()/prepareAsync()

: onVideoSizechanged

: onPrepared


=> Now, MediaPlayer is fully prepared to be started.


Tested and verified at Android 4.0.3


When using SlidingDrawer over SurfaceView, SlidingDrawer is cropped by SurfaceView while opening and closing even if SlidingDrawer is over SurfaceView.


Try with below layout.

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <SurfaceView android:id="@+id/surface" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> <SlidingDrawer ... />

</RelativeLayout>

In my opinion, that's because SufaceView holding the visible region and display on it.

And, in case of SlidingDrawer, opening and closing area is not known by WindowManager.
So, it may not be considered when holding region by SurfaceView.


Based on above hypothesis, I tried with below layout and confirmed that it works as expected.

The difference is just put a dummy View that covers all SurfaceView to let WindowManager know there is window over SurfaceView.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <SurfaceView
        android:id="@+id/surface"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <View
        android:id="@+id/touch_ground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
    <SlidingDrawer
        ...
        />
</RelativeLayout>

I don't want to tell that this is a kind of bug of Android framework. It's just common-type of issue when things are in together.

I hope that this post is helpful to others.

+ Recent posts