윈도우 이클립스에서 안드로이드 어플을 개발할 때 API 보는 것 대신 소스를 보려면, 두가지 중 하나를 하면 된다.
1. 소스를 다운받기
   tortoiseGit 프로그램을 이용해서 아래 주소에 git 파일을 가지고 clone한다. 
    https://android.googlesource.com/platform/frameworks/base.git  
2. 자바 소스 zip 파일 다운 받기
 https://github.com/android/platform_frameworks_base/tags 디렉토리에 접근해서 버전에 맞게 소스를 다운받으면 된다. 


예전에는 소스를 다운받는 구조로 했었는데.. 별로 안좋은 것 같다. 


웹 브라우져에서 https://github.com/android/platform_frameworks_base/tags  에 접속한다.




나는 안드로이드 2.3.3 어플을 개발중이므로, android_2.3.3_r1을 다운받는다.



안드로이드 소스 연동 이클립스에서 SurfaceView 클래스의 소스를 보려고 했을 때, 아래와 같은 화면이 나타난다.

 




여기서 Attach Source의  External File을 선택후, 다운받은 zip 파일을 연결한다. (사진에 그림을 잘 못 그렸음..)
 



아래와 같이 안드로드이 자바 소스를 확인할 수 있다. 





Posted by '김용환'
,
Posted by '김용환'
,


LG U+ 갤택에 아이스크림을 올릴기 위해서, 이것저것 해보고 있다. 
네이버 갤탭 사용자 카페에서 SK와 와탭은 되는데, LG U+ 제품은 잘 안된다고 한다.
안되도 공부하는 셈치고 해보려고 한다. 
xda 개발자가 쓴 내용을 바탕으로 갤택 7인치 아이스크림 버전을 다운받고 컴파일을 완료했다.
http://forum.xda-developers.com/showthread.php?t=1385153


1. 툴 설치

소스 컴파일을 위한 준비를 한다.
http://source.android.com/source/initializing.html
아래 참조 : http://knight76.tistory.com/entry/안드로이드-소스-컴파일-ubuntu-1104-android-gingerbread


2. repo 다운로드

$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > repo
$ chmod a+x repo


3. 안드로이드 소스 다운로드

$ mkdir android4
$ cd android4
$ repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1
$ repo sync

4.  xda의 cm9 소스  다운로드

mkdir cm9
cd cm9
repo init -u git://github.com/sgt7/android.git -b ics
repo sync

5. 컴파일 준비 및 컴파일

$ . build/envsetup.sh
including device/moto/stingray/vendorsetup.sh
including device/moto/wingray/vendorsetup.sh
including device/samsung/galaxytab/vendorsetup.sh
including device/samsung/maguro/vendorsetup.sh
including device/samsung/toro/vendorsetup.sh
including device/ti/panda/vendorsetup.sh
including vendor/cm/vendorsetup.sh
including sdk/bash_completion/adb.bash


$ lunch cm_galaxytab-userdebug

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.0.3
TARGET_PRODUCT=cm_galaxytab
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=MR1
============================================

 $ make bacon


 6. 컴파일 결과 확인
타겟 보드향이기 때문에 out/target 에 디렉토리가 있을 것이다.

android4/out/target/product/galaxytab $ ls -al
total 22428
drwxr-xr-x  8 kimyonghwan kimyonghwan    4096 2012-01-09 20:43 .
drwxr-xr-x  3 kimyonghwan kimyonghwan    4096 2012-01-09 20:36 ..
-rw-r--r--  1 kimyonghwan kimyonghwan 8563349 2012-01-09 20:43 boot.img
-rw-r--r--  1 kimyonghwan kimyonghwan   18719 2012-01-09 20:36 clean_steps.mk
drwxr-xr-x 15 kimyonghwan kimyonghwan    4096 2012-01-09 22:26 obj
-rw-r--r--  1 kimyonghwan kimyonghwan     571 2012-01-09 20:36 previous_build_config.mk
-rw-r--r--  1 kimyonghwan kimyonghwan  590256 2012-01-09 20:42 ramdisk.img
-rw-r--r--  1 kimyonghwan kimyonghwan 3330560 2012-01-09 20:42 ramdisk-recovery.cpio
-rw-r--r--  1 kimyonghwan kimyonghwan 1850517 2012-01-09 20:42 ramdisk-recovery.img
drwxr-xr-x  3 kimyonghwan kimyonghwan    4096 2012-01-09 20:42 recovery
-rw-r--r--  1 kimyonghwan kimyonghwan 8563349 2012-01-09 20:43 recovery.img
drwxr-xr-x  9 kimyonghwan kimyonghwan    4096 2012-01-09 20:42 root
drwxr-xr-x  5 kimyonghwan kimyonghwan    4096 2012-01-09 20:43 symbols
drwxr-xr-x  9 kimyonghwan kimyonghwan    4096 2012-01-09 23:10 system
drwxr-xr-x  2 kimyonghwan kimyonghwan    4096 2012-01-09 20:43 utilities


boot.img, root, system fs 은 나왔는데, zImage 파일이 없다. 자동으로 나올줄 알았는데..
cm9 공부좀 해야겠는데...

Posted by '김용환'
,
Posted by '김용환'
,


조금 어플 큰거 다운받으면 '기기에 공간이 부족하다는' 에러가 많이 받았다.

진저브레드이고, 갤택 최신 커널도 설치했고, 메모리도 많은데 무엇이 문제가 있가 했다.


“XXX” 다운로드 중에 오류가 발생했습니다.
기기에 공간이 부족합니다.

 
결론은..
설정-응용프로그램관리-(다운로드됨) 메뉴에서 –마켓 를 선택한 후 업데이트 제거 했더니 잘 받아진다. .

Posted by '김용환'
,

안드로이드 플러그인이 설치된 이클립스에서 새로운 프로젝트를 생성한다.
Create proejcty from existing sample을 선택하면 Sample 컴보박스가 뜬다.





여기서 예제들을 선택하고 개념을 파악하면 쉽다.



Posted by '김용환'
,


http://developer.android.com/sdk/index.html 에 밑에 보면,
ndk 를 다운받을 수 있다.


mac, window(cygwin 이용), linux 버전있는데, linux로 설치해본다.


ndk7이 4.0을 포함한 최신 버전이지만, ubuntu10. 11에서 awk 관련해서 버그가 있다. 32비트에서만 이슈가 있음. 이 부분에 대한 해결방법은 여기에 있다.

나는 ndk 6b를 사용했지만, 위의 7도 동일하게 사용할 수 있다.


리눅스(ubuntu)에 android-ndk-r6b.tar.gz을 설치한다.
tar jxvf android-ndk-r6b.tar.gz

설치는 완료했다.

이제 코드쪽을 본다.
이클립스에서 클래스 하나를 생성하고, jni 함수를 하나를 만들고, 동적 library를 읽도록 한다.

package com.google;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloJni extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
  TextView tv = new TextView(this);
  tv.setText(stringFromJNI());
  setContentView(tv);
    }
    public native String stringFromJNI();
 static {
  System.loadLibrary("hello-jni");   
 }

}




이클립스 workspace의 프로젝트에서 컴파일된 디렉토리로 들어가서
class파일을 통해서 header 파일을 만든다.

프로젝트이름\bin> javah -classpath . com.google.JniHello

그러면, header 파일이 생성된 것을 확인할 수 있다.

> dir
com_google_JniHello.h

이 파일을 이용해서 c 소스를 만든다.

#include <string.h>
#include <jni.h>


jstring
Java_com_google_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}




이 파일을 linux에 설치한 ndk에 특정 위치에 둔다.

설치디렉토리/jni-test/knight/jni 폴더에 둔다.
그리고, make 파일인 Android.mk도 같이 집어 넣는다.

Android.mk 파일은 다음과 같다.

# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)




 



설치디렉토리/jni-test/knight/ 디렉토리에서 빌드한다.

설치디렉토리/jni-test/knight # ../../ndk-build

컴파일이 잘되면 이렇게 so 파일이 나온다.
설치디렉토리/jni-test/knight/libs/armeabi/libhello-jni.so 파일이 생성된다.




so파일을 이클립스단으로 복사한다.
그 전에 먼저 jni 파일과 mk 파일을 복사한다.

eclipse 프로젝트의 jni 폴더를 생성하고, jni와 mk 파일을 복사한다.
그리고, so 파일을 libs에 복사한다.
디렉토리는 이렇게 나와야 한다.




이 파일에 대해서 에뮬에서 run하면 다음과 같은 결과가 나온다.




그리고, 보드에다가도 테스트해봤다.
ok 잘돈다.




소스는 아래와 같다.




ndk 를 보면, sample 디렉토리 밑에 많은 샘플(open gl 외 다양한 예제) 들이 있다.
참조해서 만들어보면 좋다.

san-angles 라는 샘플은 움직이는 예제를 보여준다. 터치해주면 잠깐 멈추고 그러니. 테스트하기에 재미있는 예제이다.



* 레퍼런스
 http://developer.android.com/sdk/ndk/index.html 

Posted by '김용환'
,

혼자 잡담) java-jni-cpp 가지고 300만원짜리 알바까지 했는데..
왠걸 다시 하니 기억이 안난다. 오홋 새록새록해~ 그래도 어떻게 하나 열심히 공부해야지~^^




정리하는 내용
- 리눅스에서 java->c 호출
- 리눅스에서 c->java 호출


그래픽이나 c/c++로 만들어진 라이브러리들을 바로 사용할 수 있도록 하는 프레임웤이다.

HelloJNI 테스트 해본다.

class HelloJNI {
 native void printHello();
 native void printString(String str);
 
 static {
  System.load("/work/JniTest/hellojni.so");
 }
 public static void main(String[] args) {
  HelloJNI myJNI = new HelloJNI();
  myJNI.printHello();
  myJNI.printString("Hello in C");
 }
}



삼바를 이용해서 리눅스 /work/JniTest 디렉토리를 생성하고,  HelloJNI.java 복사한다.

컴파일하고, 그  class 파일을 이용해서 header 파일을 생성하도록 한다.
javac HelloJNI.java
javah HelloJNI


HelloJNI.h 파일이 생성

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloJNI
 * Method:    printHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloJNI_printHello
  (JNIEnv *, jobject);

/*
 * Class:     HelloJNI
 * Method:    printString
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_HelloJNI_printString
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif






HelloJNI.c 소스를 생성

#include "HelloJNI.h"

JNIEXPORT void JNICALL Java_HelloJNI_printHello(JNIEnv *env, jobject obj) {
 printf("Hello World !!! jni\n");
}


JNIEXPORT void JNICALL Java_HelloJNI_printString(JNIEnv *env, jobject obj, jstring string) {
  const char *str = (*env)->GetStringUTFChars(env,string,0);
  printf("%s\n", str);
  return;
}





컴파일을 하여 so 파일이 만들어지도록 한다.

# gcc -c -I$JAVA_HOME/include -I$JAVA_HOME/include/linux HelloJNI.c
# gcc -shared -o HelloJNI.so HelloJNI.o
# ls -al HelloJNI.so
HelloJNI.so

 정상적으로 동작된다.

# java HelloJNI
Hello World !!!
Hello in C!!!

 


이런식으로 표현한 다양한 방식을 샘플로 적어본다.

java 코드

class JniFuncMain {
..
public static native JniTest createJniObject();
..
}

 

class JniTest
{
 private int intField;
 
 public JniTest(int num)
 {
  intField = num;
 }
 
 public int callByNative(int num)
 {
  return num;
 }
 
 public void callTest()
 {
  System.out.println("intField=" + intField);
 }
}



 cpp 코드


#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniFuncMain
 * Method:    createJniObject
 * Signature: ()LJniTest;
 */
JNIEXPORT jobject JNICALL Java_JniFuncMain_createJniObject
  (JNIEnv *env, jclass clazz)
{
 jclass targetClass;
 jmethodID mid;
 jobject newObject;
 jstring helloStr;
 jfieldID fid;
 jint staticIntField;
 jint result;
 
 // Get the Class staticIntField Value
 fid = env->GetStaticFieldID(clazz, "staticIntField", "I");
 staticIntField = env->GetStaticIntField(clazz, fid);
 printf("[CPP] Get JniFuncMain_Class_staticIntField!!\n");
 printf("[CPP] JniFuncMain_Class_staticIntField = %d\n", staticIntField);
 
 // Find the Class to create object
 targetClass = env->FindClass("JniTest");
 
 // Find the Constructor
 mid = env->GetMethodID(targetClass, "<init>", "(I)V");
 
 // Create a JniTest Object
 printf("[CPP] JniTest_Object Create!!\n");
 newObject = env->NewObject(targetClass, mid, 100);
 
 // Call the Method of Object
 mid = env->GetMethodID(targetClass, "callByNative", "(I)I");
 result = env->CallIntMethod(newObject, mid, 200);
 
 // Set the intField_field of JniObject
 fid = env->GetFieldID(targetClass, "intField", "I");
 env->SetIntField(newObject, fid, result);
 
 // return created Object
 return newObject;
}
#ifdef __cplusplus
}
#endif
 


# g++ -I/$JAVA_HOME/include -I/$JAVA_HOME/include/linux -c jnifunc.cpp
# g++ -shared -o jnifunc.so jnifunc.o
# java 메인클래스실행




이외에 c에서 java로도 호출이 가능하게 할 수 있다.


#include <jni.h>

int main()
{
 JNIEnv *env;
 JavaVM *vm;
 JavaVMInitArgs vm_args;
 JavaVMOption options[1];
 jint res;
 jclass cls;
 jmethodID mid;
 jstring jstr;
 jclass stringClass;
 jobjectArray args;
 
 // 1.JVM에 넘겨줄 아규먼트 셋팅 
 options[0].optionString = "-Djava.class.path=.";
 vm_args.version = 0x00010002;
 vm_args.options = options;
 vm_args.nOptions = 1;
 vm_args.ignoreUnrecognized = JNI_TRUE;
 
 //2.JVM 생성
 res = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);
 
 //3. 클래스 검색 과 로딩
 cls = (*env)->FindClass(env, "InvocationApiTest");
 
 //4. main 메소드 얻어오기 
 mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
 
 //5.메인메소드의 실제 파라미터값을 지정한다. 
 jstr = (*env)->NewStringUTF(env, "Hello Test from Native!!!");
 stringClass = (*env)->FindClass(env, "java/lang/String");
 args = (*env)->NewObjectArray(env, 1, stringClass, jstr);
 
 //6.main 메소드를 호출한다.
 (*env)->CallStaticVoidMethod(env, cls, mid, args);
 
 //7.JVM를 내린다.
 (*vm)->DestroyJavaVM(vm);
}




클래스 파일

public class InvocationApiTest
{
 public static void main(String[] args)
 {
  System.out.println(args[0]);
 }
}


# gcc -I/$JAVA_HOME/include -I$JAVA_HOME/include/linux -c invocationApi.c

그다음은 jvm 관련 항목을 링크 옵션으로 줘야 jvm이 실행할 수 있다.
# gcc -L$JAVA_HOME/jre/lib/i386/client invocationApi.o -ljvm
(libjvm.so 라는 파일이 $JAVA_HOME/jre/lib/i386/client/libjvm.so 파일이 있어야 한다.)

dependent한 library 가 있는지를 확인한다.

# ldd a.out 

# export LD_LIBRRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386/client

# a.out
결과 출력


다음예는
jni onload 함수를 오버라이딩해서,
a라고 하는 함수를 b라는 함수로 호출되도록 바꿔치기 를 할 수 있는 예제이다.


// hellojnimap.cpp

#include <jni.h>
#include <stdio.h>

void printHelloNative(JNIEnv *env, jobject obj);
void printStringNative(JNIEnv *env, jobject obj, jstring string);

// JNI_ONload는 오버라이딩한 것이다.
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
 JNIEnv *env = NULL;
 JNINativeMethod nm[2];
 jclass cls;
 jint result = -1;
 
 if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
  printf("Error");
  return JNI_ERR;
 }
 
 cls = env->FindClass("HelloJNI");
 
 nm[0].name = (char*)"printHello";
 nm[0].signature = (char*)"()V";
 nm[0].fnPtr = (void *)printHelloNative;
 
 nm[1].name = (char*)"printString";
 nm[1].signature = (char*)"(Ljava/lang/String;)V";
 nm[1].fnPtr = (void *)printStringNative;
 
 env->RegisterNatives(cls, nm, 2);
 
 return JNI_VERSION_1_4;
}

void printHelloNative(JNIEnv *env, jobject obj)
{
 printf("Hello World in C++!!\n");
 return;
}

void printStringNative(JNIEnv *env, jobject obj, jstring string)
{
 //const char *str = (*env)->GetStringUTFChars(env, string, 0);
 const char *str = env->GetStringUTFChars(string, 0);
 printf("%s\n", str);
 return;
}

#if 0
JNIEXPORT void JNICALL Java_HelloJNI_printHello
  (JNIEnv *env, jobject obj)
{
 printf("Hello World in C!!\n");
 return;
}

JNIEXPORT void JNICALL Java_HelloJNI_printString
  (JNIEnv *env, jobject obj, jstring string)
{
 const char *str = (*env)->GetStringUTFChars(env, string, 0);
 printf("%s\n", str);
 return;
}
#endif





//HelloJNI.java

class HelloJNI
{
 native void printHello();
 native void printString(String str);
 
 static {
  System.load("/work/JniTest/hellojnimap.so");
 }
 
 public static void main(String[] args) {
  HelloJNI myJNI = new HelloJNI();
  
  myJNI.printHello();
  myJNI.printString("Hello from c!!");
 }
 
}




# g++ -I/$JAVA_HOME/include -I$JAVA_HOME/include/linux -c hellojnimap.cpp
# g++ -shared -o hellojnimap.so hellojnimap.o
# java 메인클래스실행

Posted by '김용환'
,


안드로이드 버전에 탑재된 리눅스 커널 버전을 확인하기 위해서는 위키(http://en.wikipedia.org/wiki/Android_version_history)를 통해 확인할 수 있다.

만약 내가 누군가의 모바일 에서 리눅스 커널 버전을 application으로 확인하기 위한 앱 소스는 다음과 같다.


간단 소스


package com.google;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class OSFinderActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText(System.getProperty("os.version"));
        setContentView(tv);
    }
}




리눅스 커널 버전을 확인






소스에서도 리눅스 커널을 보고 싶다.

froyo는 kernel/Makefile을 보면 확인이 가능하다.

VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 32
EXTRAVERSION = .9

공식문서에 있는 2.6.32 가 맞다.
http://developer.android.com/sdk/android-2.2-highlights.html



진저브레드는 kernel 디렉토리가 없어서 grep 으로 찾아보니. 아래 파일에서 2.6.32 로 나온다.
system/core/ThirdPartyProject.prop

공식문서에서는 2.6.35인데.. ^^;;;
http://developer.android.com/sdk/android-2.3-highlights.html


아이스크림 샌드위치는 공식적으로 커널 버전에 대한 내용은 없다. (현재 2011.11월 말)
다만, 아래 엔가젯에서 "but it's running on an updated build (IRK48) and kernel (3.0.1).
" 이라는 내용이 언급되어 있을 뿐이다.

http://www.engadget.com/2011/09/28/ice-cream-sandwich-gets-a-two-minute-tour-courtesy-of-a-lucky-e/
 



Posted by '김용환'
,

다음 디렉토리 지운다.

(xp)
C:\Documents and Settings\사용자계정\.android

또는

(win7)
사용자 user 디렉토리\사용자계정\.android

Posted by '김용환'
,