[ Tested on Android 4.4_r1 with 3.4 goldfish kernel ]


android NDK의 경우 alloc하지 않은 메모리를 free하더라도 allocate된 영역이라면, 에러(segmentation fault)를 발생시키지 않는것 같다.

물론 그렇다고해서 정상적으로 free된다는 뜻은 아니다. 아래의 code와 비슷한  형태로 test code를 만들고, 실험해 보면, 메모리 leak이 발생하고 있음을 쉽게 알 수 있다.

test code는 아래와 같다.

#include <stdio.h> #include <stdlib.h> int main(int argc, const char *argv) { char *p = malloc(4096 * 5); p += 2 * 4096; free(p); // <= error가 발생하지 않음. 그렇다고 해서 free되는 것도 아님. p+=10000000; free(p); // <= "[1] + Stopped (signal)" 발생... (이게 기대했던 건데...) return 0; }

쩝....

이건, Device의 libc에서 지원해 줘야 하는데, bionic의 dlmalloc compile option에서 'DEBUG' option을 켤 경우, 이 문제가 해결된다...

뭐.. 속도를 위해서 희생한 거니... 어쩔 수 없다지만... 그래도 아쉽긴... 아쉽다...

<< DEBUG switch 켜기 >>

diff --git a/libc/upstream-dlmalloc/malloc.c b/libc/upstream-dlmalloc/malloc.c
index 3ef9b61..9efc27d 100644
--- a/libc/upstream-dlmalloc/malloc.c
+++ b/libc/upstream-dlmalloc/malloc.c
@@ -520,7 +520,7 @@ MAX_RELEASE_CHECK_RATE   default: 4095 unless not HAVE_MMAP
   disable, set to MAX_SIZE_T. This may lead to a very slight speed
   improvement at the expense of carrying around more memory.
 */
-
+#define DEBUG 1 /* YHCHO test */
 /* Version identifier to allow people to support multiple versions */
 #ifndef DLMALLOC_VERSION
 #define DLMALLOC_VERSION 20806


아래의 간단한 layout xml을 보자...


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@+id/button" <=== 여기

    tools:context=".MainActivity" >

    <Button

        android:id="@+id/button"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="say hello" />

</RelativeLayout>



자... paddingTop에는 당연히 dimension이 들어가야 한다. 그런데... 일부로 엉뚱하게도 '@+id/button'을 넣어봤다.

그런데.. compile error가 발생하지 않는다.!

resource compiler가 실제 id를 찾아서 id의 type을 읽어서 판단해 주지는 못하는 것으로 보인다.

대신 runtime에 아래와 같은 error를 발생시킨다.


11-09 02:47:49.750: E/AndroidRuntime(975): FATAL EXCEPTION: main

11-09 02:47:49.750: E/AndroidRuntime(975): Process: com.example.ttstest, PID: 975

11-09 02:47:49.750: E/AndroidRuntime(975): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.ttstest/com.example.ttstest.MainActivity}: android.view.InflateException: Binary XML file line #1: Error inflating class android.widget.RelativeLayout

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2176)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.access$700(ActivityThread.java:135)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.os.Handler.dispatchMessage(Handler.java:102)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.os.Looper.loop(Looper.java:137)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.main(ActivityThread.java:4998)

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Method.invokeNative(Native Method)

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Method.invoke(Method.java:515)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)

11-09 02:47:49.750: E/AndroidRuntime(975): at dalvik.system.NativeStart.main(Native Method)

11-09 02:47:49.750: E/AndroidRuntime(975): Caused by: android.view.InflateException: Binary XML file line #1: Error inflating class android.widget.RelativeLayout

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.createView(LayoutInflater.java:620)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.inflate(LayoutInflater.java:469)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.inflate(LayoutInflater.java:397)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.inflate(LayoutInflater.java:353)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.Activity.setContentView(Activity.java:1928)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.example.ttstest.MainActivity.onCreate(MainActivity.java:23)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.Activity.performCreate(Activity.java:5243)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)

11-09 02:47:49.750: E/AndroidRuntime(975): ... 11 more

11-09 02:47:49.750: E/AndroidRuntime(975): Caused by: java.lang.reflect.InvocationTargetException

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Constructor.constructNative(Native Method)

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Constructor.newInstance(Constructor.java:423)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.createView(LayoutInflater.java:594)

11-09 02:47:49.750: E/AndroidRuntime(975): ... 23 more

11-09 02:47:49.750: E/AndroidRuntime(975): Caused by: java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x12

11-09 02:47:49.750: E/AndroidRuntime(975): at android.content.res.TypedArray.getDimensionPixelSize(TypedArray.java:464)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.View.<init>(View.java:3560)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.View.<init>(View.java:3475)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.ViewGroup.<init>(ViewGroup.java:464)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.widget.RelativeLayout.<init>(RelativeLayout.java:236)

11-09 02:47:49.750: E/AndroidRuntime(975): ... 26 more



쩝... layout을 작성할때는.. 항상 조심하자...

compiler가 이런걸 걸러주지 못하니... 조심스럽게 작성해야 할 수 밖에...

Creating libraries - Static library, shared library

NOTE : In case jni shared library, 'jni' code should in shared library source code. That is, you can put all other source codes in static library, but 'jni' should NOT in static library source

[ Android.mk ] for libraries for NDK.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS) LOCAL_MODULE := libX LOCAL_SRC_FILES := src0.c src1.c ... LOCAL_CFLAGS += LOCAL_C_INCLUDES += include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libXS LOCAL_SRC_FILES := jni.c LOCAL_CFLAGS += LOCAL_C_INCLUDES += LOCAL_STATIC_LIBRARIES := libX include $(BUILD_SHARED_LIBRARY)

Now you can find

shared library in <project root>/libs/armeabi/

static library in <project root>/obj/local/armeabi/



Creating executable with prebuilt static library (ex. static library built above - libX.a)


[ Android.mk ] for libraries for NDK.

LOCAL_PATH := $(call my-dir) ####################################################### # ####################################################### include $(CLEAR_VARS) LOCAL_MODULE := myexe LOCAL_SRC_FILES := main.c LOCAL_CFLAGS := LOCAL_LDFLAGS := LOCAL_C_INCLUDES += LOCAL_LDLIBS := -L <example/above/libX/project/root>/obj/local/armeabi/ -lX include $(BUILD_EXECUTABLE)

Now you can get 'myexe' executable.


보통 Eclipse에서 Javadoc을 사용하기 위해서는 아래의 링크를 참조하면 된다.

http://stackoverflow.com/questions/9873152/how-to-attach-javadoc-or-sources-to-jars-in-libs-folder


그렇지만, ADT의 경우는 약간 다르다.

위의 링크를 자세히 살펴보면... 아래쪽에 ADT의 경우 libs directory를 사용하면 안된다는 내용의 minor 답변을 볼 수 있을 것이다.

그렇다!

ADT의 경우는 libs directory를 일반적은 Eclipse와는 달리 사용하는 듯 하다.

따라서, ADT에서 javadoc을 사용하기 위해서는 libs directory가 아닌 다른 외부 directory를 만들어서 Add JARs 메뉴를 통해서 jar archive를 build path에 추가해야 한다. 그 다음, 해당  jar archive가 project의 Referenced Libraries 아래에 정상적으로 추가되었는지 확인한다.

위의 과정이 정상적으로 이루어 졌다면, 위의 stackoverflow 에서의 답변(ADT관련 답변) 에서 언급한 대로


해당 jar archive 선택 -> 우클릭 -> Properties 선택 -> Javadoc Location 메뉴 선택 -> javadoc location을 입력 -> project close -> project open (reopen)


과정을 거치면, java doc이 정상적으로 동작하는 것을 확인할 수 있다.

Tested on Android ICS.


- Making Android project that has normal java entry function - 'public static void main(String[] args) { ...  }'.

  (ex. make 'public static void main(String[] args) { ... }' at 'my.test.Main' )

my/test/Main.java

-----------------


    package my.test;

public static void main(String[] args) {

System.out.println("Hello!");

}

- Making 'xxx.apk' by exporting Android project. (You don't need to care about signing.)

- Let's test with following commands

    $> adb push xxx.apk /data

And then programs can be run with following commands

    $> export CLASSPATH=/data/xxx.apk
    $> app_process /system/bin my.test.Main
or
    $> export CLASSPATH=/data/xxx.apk
    $> dalvikvm my.test.Main
or
    $> dalvikvm -classpath /data/mytest.apk my.test.Main

'main' function should be run.


IMPORTANT POINT here is knowing that 'apk' is a kind of 'jar' in JAVA.

See inside 'apk', then you can figure out structure of 'apk' is very similar with 'jar'.

And this is the hint of above way.


And, you can also find classes.dex at <android project root>/bin/ . and this is just like classes.jar of java.

So, this classes.dex can be replace xxx.apk above.


WARNING!




"Project Properties -> Java Build Path -> Order and Export tab" 에 가서 check 'Android Private Libraries' check box.


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. :-)

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.


+ Recent posts