안드로이드 AIDL 문법(Android AIDL Syntax)

자주 받는 질문중의 하나는 “AIDL의 정확한 문법이 어떻게 되는가?” 입니다. 안드로이드의 문서에는 예제 형태로만 설명되어 있을 뿐 형식화된 문법이 제시되지 않고 있습니다. 대충 알고 대충 써라? 천만의 말씀.

 안드로이드(Android) 1.6 AIDL의 BNF Grammar 입니다. Yacc 문법을 따릅니다.

document:
        document_items
    |   headers document_items
    ;

headers:
        package
    |   imports
    |   package imports
    ;

package:
        PACKAGE
    ;

imports:
        IMPORT
    |   IMPORT imports
    ;

document_items:

    |   document_items declaration
    ;

declaration:
        parcelable_decl
    |   interface_decl
    ;

parcelable_decl:
        PARCELABLE IDENTIFIER ';'
    ;

interface_header:
        INTERFACE
    |   ONEWAY INTERFACE
    ;

interface_decl:
        interface_header IDENTIFIER '{' interface_items '}'
    ;

interface_items:

    |   interface_items method_decl
    ;

method_decl:
        type IDENTIFIER '(' arg_list ')' ';'
    |   ONEWAY type IDENTIFIER '(' arg_list ')' ';'
    ;

arg_list:

    |   arg
    |   arg_list ',' arg
    ;

arg:
        direction type IDENTIFIER
    ;

type:
        IDENTIFIER
    |   IDENTIFIER ARRAY
    |   GENERIC
    ;

direction:

    |   IN
    |   OUT
    |   INOUT
    ;

PARCELABLE, INTERFACE, IN, OUT, INOUT, ONEWAY 는 각각 키워드(Keyword) parcelable, interface, in, out, inout, oneway를 뜻하고, IMPORT, PACKAGE 는 각각 import, package 문장 전체를 뜻합니다. ARRAY 는 하나 이상의 []를 뜻하고, GENERIC 은 Collection<Object,String>과 같은 Java Generic 표현을 뜻합니다. IDENTIFIER 는 대략 Java 의 클래스 명으로 쓸 수 있는 문자열이라고 보시면 됩니다. 주석 관련 구문 요소들은 생략했습니다.
SDK 의 AIDL 설명에는 나오지 않지만, oneway 키워드를 인터페이스 또는 메쏘드 앞에 붙일 수 있음을 알 수 있습니다. One-way call 에 대한 설명은 android.os.IBinder.FLAG_ONEWAY 에 대한 SDK 문서에 등장합니다. 비록 AIDL 에서의 사용법을 설명하고 있지는 않지만, 메쏘드 실행이 끝나기를 기다리지 않고 호출 즉시 복귀하는 방법을 제공하고 있음을 알 수 있습니다. AIDL 에서 oneway 로 지정한 메쏘드들의 Proxy 는 모두 transact() 를 호출할 때 FLAG_ONEWAY 를 포함하게 됩니다. 인터페이스에 oneway 를 지정하면 모든 메쏘드에 oneway 를 지정한 효과가 발생합니다. 다중 배열은 지원되는 반면 인터페이스 계승은 지원되지 않음을 확인할 수 있습니다.

'old > Basic' 카테고리의 다른 글

Android Flow Diagram  (0) 2010.07.06
JNI  (0) 2010.07.06
Android Bitmap Object Resizing Tip  (0) 2010.07.06
Handler  (0) 2010.07.06
aidl  (0) 2010.07.06
Posted by jazzlife
,

Android Flow Diagram

old/Basic 2010. 7. 6. 14:59
 
Android Anatomy
Linux Kernel
  • Android는 Linux Kernel을 기반으로 하고 있다.  왜 Linux System이 아닌 Kernel인가?
    • 어찌보면 당연하다. Mobile Device 상에서 full linux 필요없다.  하지만 Android를 넷북이나 UMPC 등으로 영역을 확장한다면 좀 다른 얘기가 될지도 모른다
    • Android is not Linux
      • Android는 Linux Kernel을 기반으로 하는 Mobile Platform이라고 정의하길 선호한다.
    • Native windowing system, glibc support(gnu c library), 표준 linux utilities 등을 포함하지 않고 있다.
      • 일종의 file system 및 하드웨어를 제어하기 위한 드라이버 등, 최소한의 Linux를 채용하고 있다고 볼 수 있다.
      • Android는 Linux Kernel의 안정적인 System 운영 측면을 가져온 것이다.  Linux의 지속적인 업데이트 또한 장점이 되었을 것이다.
      •       


    • Linux 2.6.24를 기반으로 하였으나 최근 발표된 Android 1.5에서는 Linux 2.6.29로 업그레이드 됨
    • Kernel enhancements를 통해 Android를 지원
      • Alarm / Ashmen(Android shared memory) / Binder / Power Management / Low Memory Killer / Kernel Debugger / Logger
    • Why Linux kernel?
      • Great memory and process management
      • Permissions-based security model
      • Proven driver model
      • Support for shared libraries
      • It's already open source
    • Application과 Service는 별개의 process에서 동작을 하지만 상호 communicate 하거나 data를 공유할 필요가 있다.  이는 IPC (Inter Process Communication)를 통해 지원 : Binder
      • High performance through shared memory
      • Per-process thread pool for processing requests
      • Reference counting, and mapping of object reference across processes
      • Synchronous calls between processes
      • AIDL(Android Interface Definition Language)
    • PM (Power Management) Solution
      • 기본적으로 Linux Power Management를 기반으로 구성 (on top of it)
      • More aggressive power management policy - 결국 좀 더 타이트한 policy를 통해 pm을 한다는 내용
      • Components make requests to keep the power on throught "wake locks"
      • Support different types of wake locks
      • Android.os.PowerManager - use wake locks carefully!

Native Libraries
  • Bionic Libc
    •  What is - 일종의 c library로 android에서는 다음과 같은 이유로 standard c lib가 아닌 bionic libc를 쓰기로 함
      • Bionic은 custom libc implementation, optimized for embedded use
      • License 문제 - standard c lib는 GPL이므로 사용자는 자신의 code를 open 해야 함으로 이로부터 자유롭게 하기 위해
        • BSD License
      • Size - android에서는 will load in each process 해야 함으로, so it needs to be small
        • Small size and fast code paths
      • Fast - mobile device와 같은 한정된 CPU에 최적화되어 빠르다
        • Very fast and small custom pthread implementation
      • 단점 or 장점?
        • Doesn't support certain POSIX features
        • Not compatible with Gnu Libc (glibc)
        • All native code must be compiled against bionic
  • Function Libraries
    • WebKit - 현재까지 알려진 Web 엔진 가운데 가장 괜찮음 : 애플사파리(아이폰포함), Nokia 등이 WebKit 기반 Web 엔진 사용
      • Based on open source WebKit browser
      • Renders pages in full (desktop) view
      • Full CSS, Javascript, DOM, AJAX support
      • Support for single-column and adative view rendering
    • Media Framework
      • Based on PacketVideo OpenCORE platform
      • Supports standard video, audio, still-frame formats
      • Support for hardware/software codec plug-ins - 기본 지원외에 format은 plug-in을 통해 또는 hardware accelerator등이 장착된 mobile device에도 plug-in을 사용하여 fully 지원할 수 있다.
    • SQLite
      • Light-weight transactional data store
      • Back end for most platform data storage
  • Native Servers
    • Surface Flinger
      • Provides system-wide surface "composer", handling all surface rendering to frame buffer device
      • Can combine 2D and 3D surfaces and surfaces from multiple applications
      • Surfaces passed as buffers via Binder IPC calls
      • Can use OpenGL ES and 2D hardware accelerator for its compositions
      • Double-buffering using page-flip
    • Audio Flinger
      • Manages all audio output devices
      • Processes multiple audio streams into PCM audio out paths
      • Handles audio routing to various outputs
  • Hardware Abstraction Libraries
    • User space C/C++ library layer
    • Defines the interface that Android requires hardware "drivers" to implement
    • Separates the Android platform logic from the hardware interface
    • Why do we need a user-space HAL? - HAL 영역이 왜 필요한가 : 당연 - Linux에서 kernel driver가 존재할 텐데 왜 굳이 Android용 HAL을 제공하는가에 대한 문제
      • Not all components have standardized kernel driver interface - 현재 제공되는 Linux system 상에서 모든 component의 driver interface에 대한 표준화가 되어있는 것은 아니다
      • Kernel drivers are GPL which exposes any proprietary IP - kernel driver는 현재 GPL로 되어 있어 그대로 사용하게 되면 연계 소스코드에 대해 오픈을 해야 한다
      • Android has specific requirements for hardware drivers

Android Runtime
  • Dalvik Virtual Machine
    • 사용자에게 Java를 이용해 app을 작성하게 하고 이러한 Java platform 기반 app을 모바일 device상에서 동작하게 하기 위한 최적의 환경을 제공하기 위해 기존의 Java VM과는 별도로 Google이 제공하는 VM이라고 할 수 있다
    • 일반 VM과는 다음과 같은 다른 특징을 가지고 있다
      • The VM was slimmed down to use less space
      • Dalvik has no Just-in-time compiler
      • The constant pool has been modified to use only 32-bit indexes to simplify the interpreter
      • It uses its own bytecode, not Java bytecode
    • Android's custom clean-room implementation virtual machine
      • Provides application portability and runtime consistency
      • Runs optimized file format (.dex) and Dalvik bytecode
      • Java .class/.jar files converted to .dex at build time
    • Designed for embedded environment
      • Supports multiple virtual machine processes per device
      • Highly CPU-optimized bytecode interpreter
      • Uses runtime memory very efficiently
  • Core Libraries
    • Core APIs for Java language provide a powerful, yet simple and familiar development platform
    • Data structures, Utilities, File access, Network Access, Graphics, …

Application Framework
  • Core Platform Services
    • Services that are essential to the Android platform
    • Behind the scenes - applications typically don’t access them directly
    • Activity Manager, Package Manager, Window Manager, Resource Manager, Content Providers, View System
  • Hardware Services
    • Provide access to lower-level hardware APIs
    • Typically accessed through local Manager object
      • LocationManager lm = (LocationManager) Context.getSystemService(Context.LOCATION_SERVICE);
    • Telephony Service, Location Service, Bluetooth Service, WiFi Service, USB Service, Sensor Service

Android Physiology

Start-up Walkthrough

  • Runtime Walkthrough
    • It all starts with init… - similar to most Linux-based systems at startup, the bootloader loads the Linux kernel and starts the init process.
    • Init starts Linux daemons, including:
      • USB Daemon (usbd) to manage USB connections
      • Android Debug Bridge (adbd) to manage ADB connections
      • Debugger Daemon (debuggerd) to manage debug processes requests (dump memory, etc.)
      • Radio Interface Layer Daemon (rild) to manage communication with the radio
    • Init process starts the zygote process:
      • A nascent process which initializes a Dalvik VM instance
      • Loads classes and listens on socket for requests to spawn VMs
      • Forks on request to create VM instances for managed processes
      • Copy-on-write to maximize re-use and minimize footprint
    • Init starts runtime process:
      • Initializes Service Manager - the context manager for Binder that handles service registration and lookup
      • Registers Service Manager as default context manager for Binder services
    • Runtime process sends request for Zygote to start System Service
      • Zygote forks a new VM instance for the System Service process and starts the service
      • System Service starts the native system servers, including:
        • Surface Flinger, Audio Flinger
    • Native system servers register with Service Manager as IPC service targets:
    • System Service starts the Android managed services:
    • Android managed Services register with Service Manager:
    • After system server loads all services, the system is ready..
    • Each subsequent application is launched in it's own process

Layer Interaction
  • There are 3 main flavors of Android layer cake:
    • App -> Runtime Service -> lib
    • App -> Runtime Service -> Native Service -> lib
    • App -> Runtime Service -> Native Daemon -> lib
  • App -> Runtime Service -> lib
  • App -> Runtime Service -> Native Service -> lib
  • App -> Runtime Service -> Native Daemon -> lib

'old > Basic' 카테고리의 다른 글

안드로이드 AIDL 문법(Android AIDL Syntax)  (0) 2010.09.07
JNI  (0) 2010.07.06
Android Bitmap Object Resizing Tip  (0) 2010.07.06
Handler  (0) 2010.07.06
aidl  (0) 2010.07.06
Posted by jazzlife
,

JNI

old/Basic 2010. 7. 6. 14:58
이글은, 구글 안드로이드 상의 Java 애플리케이션에서 native c/c++ 모듈을 Call 할 수 있는 가에
대한 것이다. 결론적으로 말하면 쉽지 않을 것 같지만 불가능 하지는 않을 듯 하다.
직접 JNI를 통해 결과를 검증해 보지는 않았지만, 대략적인 방법들과 문제점들을 정리해 보고자
한다.
 
 
1. JNI란 무엇인가? - JNI example 중심으로
 
This section presents the ReadFile example program. This example shows how you can use
the Java Native Interface (JNI) to invoke a native method that makes C function calls to map a
file into memory.

About the Example

You can call code written in any programming language from a program written in the Java
language by declaring a native Java method, loading the library that contains the native code,
and then calling the native method. The ReadFile source code below does exactly this.
However, successfully running the program requires a few additional steps beyond compiling
the Java language source file. After you compile, but before you run the example, you have to
generate a header file. The native code implements the function defintions contained in the
generated header file and implements the business logic as well. The following sections walk
through all the steps.
import java.util.*;
class ReadFile {
 //Native method declaration
   native byte[] loadFile(String name);
 //Load the library
   static {
     System.loadLibrary("nativelib");
   }
 
   public static void main(String args[]) {
      byte buf[];
 //Create class instance
      ReadFile mappedFile=new ReadFile();
 //Call native method to load ReadFile.java
      buf=mappedFile.loadFile("ReadFile.java");
 //Print contents of ReadFile.java
      for(int i=0;i<buf.length;i++) {
          System.out.print((char)buf[i]);
      }
   }
}
Native Method Declaration 

The native declaration provides the bridge to run the native function in the Java virtual machine.
In this example, the loadFile function maps onto a C function called Java_ReadFile_loadFile.
The function implementation accepts a String that represents a file name and returns the
contents of that file in the byte array.
native byte[] loadFile(String name); Load the Library
The library containing the native code implementation is loaded by a call to System.loadLibrary().
Placing this call in a static initializer ensures this library is only loaded once per class.
The library can be loaded outside of the static block if your application requires it. You might
need to configure your environment so the loadLibrary method can find your native code library.
static {
     System.loadLibrary("nativelib");
}
Compile the Program
 
To compile the program, just run the javac compiler command as you normally would:
javac ReadFile.java
Next, you need to generate a header file with the native method declaration and implement the
native method to call the C functions for loading and reading a file.

Generate the Header File 

To generate a a header file, run the javah command on the ReadFile class. In this example,
the generated header file is named ReadFile.h. It provides a method signature that you have
to use when you implement the loadfile native function.
javah -jni ReadFile
Note: When running javah on your own classes, be sure to use the fully-qualified class name.
Method Signature 

The ReadFile.h header file defines the interface to map the Java language method to the native
C function. It uses a method signature to map the arguments and return value of the Java
language mappedfile.loadFile method to the loadFile native method in the nativelib library.
Here is the loadFile native method mapping (method signature):
/* * Class: ReadFile * Method: loadFile * Signature: (Ljava/lang/String;)[B */
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile (JNIEnv *, jobject, jstring);
The method signature parameters function as follows:
JNIEnv *: A pointer to the JNI environment. This pointer is a handle to the current thread
in the Java virtual machine, and contains mapping and other hosuekeeping information.
jobject: A reference to the method that called this native code. If the calling method is
static, this parameter would be type jclass instead of jobject.
jstring: The parameter supplied to the native method. In this example, it is the name of
the file to be read.
 
Implement the Native Method
 
In this native C source file, the loadFile definition is a copy and paste of the C declaration
contained in ReadFile.h. The definition is followed by the native method implementation.
JNI provides a mapping for both C and C++ by default.
#include <jni.h>
#include <sys/types.h>
#include <sys/
ipc
.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
  (JNIEnv * env, jobject jobj, jstring name) {
    caddr_t m;
    jbyteArray jb;
    jboolean iscopy;
    struct stat finfo;
    const char *mfile = (*env)->GetStringUTFChars(
                env, name, &iscopy);
    int fd = open(mfile, O_RDONLY);
    if (fd == -1) {
      printf("Could not open %sn", mfile);
    }
    lstat(mfile, &finfo);
    m = mmap((caddr_t) 0, finfo.st_size,
                PROT_READ, MAP_PRIVATE, fd, 0);
    if (m == (caddr_t)-1) {
      printf("Could not mmap %sn", mfile);
      return(0);
    }
    jb=(*env)->NewByteArray(env, finfo.st_size);
    (*env)->SetByteArrayRegion(env, jb, 0,
 finfo.st_size, (jbyte *)m);
    close(fd);
    (*env)->ReleaseStringUTFChars(env, name, mfile);
    return (jb);
}
 
You can approach calling an existing C function instead of implementing one, in one of two ways:
Map the name generated by JNI to the existing C function name. The Language Issues
section shows how to map between Xbase database functions and Java language code
Use the shared stubs code available from the JNI page on the java.sun.com web site.

Compile the Dynamic or Shared Object Library 

The library needs to be compiled as a dynamic or shared object library so it can be loaded at
runtime. Static or archive libraries are compiled into an executable and cannot be loaded at
runtime. The shared object or dynamic library for the loadFile example is compiled as follows:
Gnu C/Linux:
gcc  -o libnativelib.so -shared -Wl,-soname,libnative.so  
     -I/export/home/jdk1.2/include
     -I/export/home/jdk1.2/include/linux nativelib.c  
     -static -lc
Gnu C++/Linux with Xbase
g++ -o libdbmaplib.so -shared -Wl,-soname,libdbmap.so  
    -I/export/home/jdk1.2/include
    -I/export/home/jdk1.2/include/linux
    dbmaplib.cc -static -lc -lxbase

Win32/WinNT/Win2000
cl -Ic:/jdk1.2/include
   -Ic:/jdk1.2/include/win32
   -LD nativelib.c -Felibnative.dll

Run the Example
 
To run the example, the Java virtual machine needs to be able to find the native library. To do this,
set the library path to the current directory as follows:

Unix or Linux: LD_LIBRARY_PATH=`pwd` export LD_LIBRARY_PATH
Windows NT/2000/95: set PATH=%path%;.

With the library path properly specified for your platform, invoke the program as you normally would
with the interpreter command:
 java ReadFile
 
 
2. 구글 안드로이드 상에서 JNI 지원 API

아래의 두가지 함수에 대한 안드로이드 상에서의 특별한 제약이 없다면 JNI 을 지원하기 위한 최소한의
조건은 만족된 듯하다
The core functional component is the use of the 'native' keyword and the bindings to the
java.lang.System.loadLibary() API, which, does seem to be documented within the android reference:
 
java.lang.System
java.lang
public final class
java.lang.System
java.lang.Object  
       java.lang.System

Class System provides a standard place for programs to find system related information.
All System API is static.
static  void  load(String pathName) : Loads the specified file as a dynamic library.
static  void  loadLibrary(String libName) : Loads and links the library specified by the argument.

3. Dynamic & Shard Object Library 만들기
 
the question here is how to build a native .so (shared library), but we can refer to the JNI example
above for instructions. we have also been informed, through this list that you can build native code
on the device, but without UI bindings:
 
이 부분 중, 가장 어렵게 생각되는 부분은 Cross Compiler를 통해 shard object library를 만들 수 있느냐 일듯.
현재 안드로이드의 /system/lib 아래에는 안드로이드 Library들이 .so 형태로 존재함.

'old > Basic' 카테고리의 다른 글

안드로이드 AIDL 문법(Android AIDL Syntax)  (0) 2010.09.07
Android Flow Diagram  (0) 2010.07.06
Android Bitmap Object Resizing Tip  (0) 2010.07.06
Handler  (0) 2010.07.06
aidl  (0) 2010.07.06
Posted by jazzlife
,

[Intro]


Android에서 사용하는 이미지는 Bitmap이라는 클래스에서 다~ 알아서 해줍니다.
그리고 이런 Bitmap Object를 쉽게 만들 수 있도록 도와주는
BitmapFactory 클래스 라는 것도 있습니다.


BitmapFactory는 여러가지 소스로 부터 Bitmap Object를 만들어 주는 일을 하는데,
전부 static이며 decodeXXX 라는 이름을 가진 메소드들로 이루어져 있습니다.

XXX에는 어떤 것으로 부터 decode를 하여
Bitmap Object를 만들어 낼지에 대한 말들이 들어 가겠죠.



[Decoding Methods]


BitmapFactory.decodeByteArray() 메소드는 Camera.PictureCallback 으로 부터 받은
Jpeg 사진 데이터를 가지고 Bitmap으로 만들어 줄 때 많이 사용 합니다.
Camera.PictureCallback에서 들어오는 데이터가 byte[] 형식이기 때문에
저 메소드를 사용 해야 하는 것이죠.


BitmapFactory.decodeFile() 메소드는 파일을 그대로 읽어 옵니다.
내부적으로는 파일 경로를 가지고 FileInputStream을 만들어서 decodeStream을 합니다.
그냥 파일 경로만 쓰면 다 해주는게 편리 한 것이죠.


BitmapFactory.decodeResource() 메소드는 Resource로 부터 Bitmap을 만들어 내며
BitmapFactory.decodeStream() 메소드는 InputStream으로 부터 Bitmap을 만들어 냅니다.
뭐 그냥 이름만 봐도 알 수 있는 것들이지요.



[OutOfMemoryError??]


보통 이미지 파일을 읽어서 Resizing을 해야 할 때가 있는데,
그럴때는 BitmapFactory로 읽어서 Bitmap.createScaledBitmap() 메소드를 사용하여 줄이면

간단하게 처리 할 수 있습니다.


그런데 BitmapFactory를 사용할 때 주의해야 할 점이 있습니다.
아래의 예를 한번 보시죠.

Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg");
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

이미지 파일로부터 Bitmap을 만든 다음에

다시 dstWidth, dstHeight 만큼 줄여서 resized 라는 Bitmap을 만들어 냈습니다.
보통이라면 저렇게 하는게 맞습니다.


읽어서, 줄인다.


그런데 만약 이미지 파일의 크기가 아주 크다면 어떻게 될까요?
지금 Dev Phone에서 카메라로 촬영하면
기본적으로 2048 x 1536 크기의 Jpeg 이미지가 촬영된 데이터로 넘어옵니다.
이것을 decode 하려면 3MB 정도의 메모리가 필요 할 텐데,

과연 어떤 모바일 디바이스에서 얼마나 처리 할 수 있을까요?


실제로 촬영된 Jpeg 이미지를 여러번 decoding 하다보면

아래와 같은 황당한 메세지를 발견 할 수 있습니다.

java.lang.OutOfMemoryError: bitmap size exceeds VM budget

네... OutOfMemory 입니다.
더 이상 무슨 말이 필요 하겠습니까...
메모리가 딸려서 처리를 제대로 못합니다.


이것이 실제로 decoding 후 메모리 해제가 제대로 되지 않아서 그런 것인지,
하더라도 어디서 Leak이 발생 하는지에 대한 정확한 원인은 알 수 없습니다.
이것은 엔지니어들이 해결해야 할 문제 겠죠...


하지만 메모리 에러를 피할 수 있는 방법이 있습니다.



[BitmapFactory.Options.inSampleSize]


BitmapFactory.decodeXXX 시리즈는 똑같은 메소드가 두 개씩 오버로딩 되어 있습니다.
같은 이름이지만 Signature가 다른 메소드의 차이점은
BitmapFactory.Options를 파라메터로 받느냐 안받느냐의 차이죠.

BitmapFactory.Options를 사용하게 되면 decode 할 때 여러가지 옵션을 줄 수 있습니다.


여러가지 많지만 저희가 지금 사용할 것은 inSampleSize 옵션 입니다.


inSampleSize 옵션은,
애초에 decode를 할 때 얼마만큼 줄여서 decoding을 할 지 정하는 옵션 입니다.


inSampleSize 옵션은 1보다 작은 값일때는 무조건 1로 세팅이 되며,
1보다 큰 값, N일때는 1/N 만큼 이미지를 줄여서 decoding 하게 됩니다.
즉 inSampleSize가 4라면 1/4 만큼 이미지를 줄여서 decoding 해서 Bitmap으로 만들게 되는 것이죠.


2의 지수만큼 비례할 때 가장 빠르다고 합니다.
2, 4, 8, 16... 정도 되겠죠?


그래서 만약 내가 줄이고자 하는 이미지가 1/4보다는 작고 1/8보다는 클 때,
inSampleSize 옵션에 4를 주어서 decoding 한 다음에,

Bitmap.createScaledBitmap() 메소드를 사용하여 한번 더 줄이면 됩니다.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg", options);
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

당연한 이야기 이겠지만,
내가 원하고자 하는 사이즈가 딱 1/4 크기라면

Bitmap.createScaledBitmap() 메소드를 쓸 필요가 없지요.


inSampleSize 옵션을 잘 활용하면 메모리 부족 현상을 대략적으로 해소 할 수 있습니다.
참고로 제가 저 옵션을 사용한 뒤로는 메모리 에러를 본적이 한~번도 없답니다.



[Appendix]


inSampleSize 옵션을 사용하면

SkScaledBitmapSampler Object (Library Level) 를 생성 하게 되는데,
Object를 만들때 정해진 SampleSize 만큼 축소하여 width와 height를 정한 뒤에 만들게 됩니다.
그러니까 애초에 축소된 사이즈로 이미지를 decoding 하는 것이죠.



[Outro]


Android의 기본 어플리케이션 소스를 분석 하다보면
상당히 테크니컬한 기법들을 많이 얻을 수 있습니다.
어떻게 이런 방법으로 만들었나 싶을 정도로 매우 정교하고 복잡하게 만들어져 있지요.
참 대단한 것 같습니다.


아 그리고 왜 dstWidth와 dstHeight 변수 선언이 없냐고 따지시는 분들 설마 없겠죠?

'old > Basic' 카테고리의 다른 글

Android Flow Diagram  (0) 2010.07.06
JNI  (0) 2010.07.06
Handler  (0) 2010.07.06
aidl  (0) 2010.07.06
zygote & Dalvik VM  (0) 2010.07.06
Posted by jazzlife
,

Handler

old/Basic 2010. 7. 6. 14:45
 

Android(안드로이드) 개발시 사용되는 Handler(핸들러)와 통신에 사용되는 Message(메시지)에 대해서 알아보자.

 

핸들러란?

 

 시스템은 사용자가 작성한 UI에서 빠른 응답을 요구하고 있다.

만약  이상의 응당이 없을 경우 최악의 경우 작성된 프로그램이 강제로 종료가  경우도 발생할 수 있다.

이런 상황을 방지하기 위해서  오래걸리는 작업이 필요한 경우 두가지의 방법으로 해결을 할 수 있다.

첫번째는 시간이 오래걸리는 작업은 서비스로 만들어서 처리하는 방법

 새로운 쓰레드로 처리를 하는 방법

 

두번째 방법으로  생성해서 데이터 처리등의 시간이 오래걸리는 작업을 지원하기 위한 클래스가 존재하는데  핸들러(Handdler)이다.

 

간략하게 요약을 해보면

  • 백그라운드  생성을 위한 가장 유연한 방법이다.
  • 인스턴스 생성시 자동으로 안드로이드가  관리한다.
  • 메시지를 전달받게 되면 호출 되는 handlerMessage()에 실제 처리내용을
  • post(), postDelayed()를 통해서 인자로 실행하고 자하는 Runnable객체를 전달할 수 있다.
  • View단에도 Runnable객처를 인자로 전달가능하고 이럴경우 코드가 심플해지는 경우도  있지만 Handler를 추천한다.

 

 

메시지(Message)란?

 

UI등에서 실제로 처리를 담당하는  데이터를 전송하거나 작업을 요청하기 위해서 전달하는 객체이다.

 

  • 핸들러로 전달되는 객체이다.
  • 전달된 메시지는 메시지 Queue를 통해서 핸들러가  사용한다.
  • 메시지를 전달하기 위해선 핸들러의 obtainMessage()호출해서 메시지 풀의 메시지를  전달해야한다.
  • 메시지를 전달하기 위해서는 sendMessage() 등을 사용한다.

 

메시지 전달 방식의 종류

  • sendMessage() - 큐의  메시지를 삽입한다.
  • sendMessageAtFrontQueue() - 큐의 맨앞에 메시지를 삽입한다.(우선처리)
  •  - 장비기동시간을 기준으로 삽입한다.(SystemClock.uptimeMillis()참고)
  • sendMessageDelayed() - 특정시간만큼 지연 삽입한다.

'old > Basic' 카테고리의 다른 글

JNI  (0) 2010.07.06
Android Bitmap Object Resizing Tip  (0) 2010.07.06
aidl  (0) 2010.07.06
zygote & Dalvik VM  (0) 2010.07.06
안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
Posted by jazzlife
,

aidl

old/Basic 2010. 7. 6. 14:25
1. 인터페이스(aidl) 생성
2. 구현클래스 생성
3. 메니페스트에 적고 
4. 엑티비티에서 해당 클래스를가져와서
5. 구현된 함수를 사용.

의 순서로 진행됩니다.

사실 엑티비티 레벨이니 서비스 레벨이니 신경안써도 돼는 작은 어플에서는 샤용할일이 없을듯.


1. aidl
package hell.o;
interface IPlusItService {
int add(int a, int b);
}


2. impl class
package hell.o;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class PlusItService extends Service {

@Override
public IBinder onBind(Intent intent) {
if (IPlusItService.class.getName().equals(intent.getAction())) {
return plusItServiceIf;
}
return null;
}

private IPlusItService.Stub plusItServiceIf = new IPlusItService.Stub() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
};
}


3. manifest

        <service android:name="PlusItService">
<intent-filter>
<action android:name="hell.o.IPlusItService"></action>
</intent-filter>
</service>


4. class load
 
    private IPlusItService plusItServiceIf
먼저 서비스 인터페이스를 담을 변수를 선언
	Intent intent = new Intent(IPlusItService.class.getName());
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
plusItServiceIf = IPlusItService.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
plusItServiceIf = null;
}
}, BIND_AUTO_CREATE);
온크리에이트에 넣을 내용. (상식이있는 인간이라면 ServiceConnection을 밖으로 빼겠지만 졸려죽겠는데 그런거없다능.)


5. use
	int sum = plusItServiceIf.add(a, b);
요거 한줄하려고 이고생을!

지금 생각해보니 평소하던데로
XxxService , XxxServiceImpl이라는 편이 차라리 읽기 편하지 싶음.
I를 앞에붙여서 인터페이스임을 표시하는게 어느동네 표기법이더라?

'old > Basic' 카테고리의 다른 글

Android Bitmap Object Resizing Tip  (0) 2010.07.06
Handler  (0) 2010.07.06
zygote & Dalvik VM  (0) 2010.07.06
안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
Posted by jazzlife
,

zygote & Dalvik VM

old/Basic 2010. 7. 6. 14:23
안드로이드의 사상입니다. 모든 플랫폼에서 가장 중요한 것이 사상인것 같습니다. 사상이 바르고 공정해야 그 플랫폼 위에서 움직이는 어플리케이션이 바르고 안정적으로 움직일 수 있습니다.

안드로이드가 뛰어난 플랫폼이라고 생각되는 부분이 바로 이 바르고 공정한 플랫폼이라는 점입니다.

안드로이드를 처음 구동하면 홈 스크린(Home Screen)이란 것을 구동합니다. 즉 바탕화면 관리자 정도 되나요? 여튼, 이 홈 스크린을 통하여 다른 어플리케이션에 접근할 수 있습니다. 바탕화면에서 아이콘을 통하여 자신이 원하는 어플리케이션에 접근하는 개념이란 동일하죠..

헌데, 이 홈 스크린도 구글이 제공해주는 것을 사용할 수도 있고, 여러분들이 만든 것을 사용할 수 있습니다.

출처: http://www.jco.or.kr/c?mc=conference&sc=pds 중 "[2009, 10회] Dolphin-4 Android Run-time (Dalvik VM & Core Lib) 분석" 에서


이 스크린 샷이 컨퍼런스에서 본 화면인데요~ 잘 보시면 홈을 선택하게 되어 있습니다. 홈 스크린은 나름대로 중요한 위치에 있는 어플리케이션입니다. 홈 스크린이 고정되어 있다면, 사실 여러분들이 쓰고 싶지 않은 어플리케이션도 홈 스크린에 존재한다는 이유로 써야할 수 있습니다.

대표적으로 원도우스(Windows)에 기본 탑재되었던 인터넷 익스플로러(Internet Explorer)입니다. 이 인터넷 익스플로어는 제 기억에 분명히 넷스케이프보다 성능이 떨어지는 웹 브라우져 어플리케이션이었습니다. 하지만 성능이 떨어져도 기본 탑재되어 있기 때문에 자연스럽게 사용하기 시작했습니다. 넷스케이프는 다시 깔아서 써야 했기 때문이죠. 그 덕분에 인터넷 익스플로러가 세계에서 가장 많이 사용되는 웹 브라우져가 된 것입니다.

홈 스크린에 어플리케이션을 자연스럽게 껴서 넣는다면 자연스럽게 어플리케이션 사용율이 높아질 것이고 해당 분야에 세계 최고의 어플리케이션이 될 수도 있는 것입니다.

하지만, 구글은 역시 구글답게 홈 스크린도 선택하도록 하였습니다. 여러분들이 좋아하는 어플리케이션을 구동하기 쉽도록 재 구성한 홈 스크린을 사용할 수 있다는 것입니다. 아... 얼마나 구글다운 오픈 마인드(Open Mind)입니까?

사실 전 이 사실 하나만으로도 안드로이드의 장래가 매우 밝다고 생각합니다. 모든 것을 열어놓고 시작하는 이 정신이 안드로이드의 가치를 빛나게 해줍니다.



두번째로 재미있는 개념이 Zygote란 개념입니다.


출처: http://www.jco.or.kr/c?mc=conference&sc=pds 중 "[2009, 10회] Dolphin-4 Android Run-time (Dalvik VM & Core Lib) 분석" 에서



상기 그림에서도 설명이 나와 있는데요, 미리 Fork된 프로세스이지만, 마치 빈 껍대기 정도의 상태로 존재하는 프로세스입니다. 그리고 어플리케이션의 Run될때 바로 이 Zygote를 통해서 안드로이드 기반의 프로세스를 바로 실행시키는 구조입니다. 아~ 멋집니다.

미리 공통적으로 필요한 라이브러리들을 탑재한 상태로 반쯤 생성된 프로세스에 실제 핵심이 되는 어플리케이션의 로직을 태워, 해당 어플리케이션을 구동시키는 방법이라고 할 수 있습니다. 당근 어플리케이션 구동 속도가 엄청 빨라지겠죠~

사실 안드로이드에 관심있는 분이라면 한번쯤 들어보셨을 것입니다. 저도 들어는 봤었지만, 정확한 개념이해가 되지 않았는데, 이번 세션을 통하여 명확하게 이해할 수 있었습니다. 아싸~ 하나 건졌다 :-)



또 하나의 재미있는 부분이 달빅(Dalvik)이란 가상머신(Virtual Machine)에 관한 개념입니다.
달빅 자체는 매우 아래와 같은 환경을 고려한 Bytecode Interpreter입니다.
  • Slow CPU (250-500 MHz)
  • RAM Usage : Low-level : 20M, High-level : 24M (system library : 10M)
  • Little RAM (64MB) : Available RAM : 20M
  • Bus speed : 100MHz
  • Data Cache : 16~32K
  • No swap space,  Battery power

역시 혹독한 임베디드 환경을 고려하고 있네요.. 제가 모바일 어플리케이션을 작성할때 환경보다는 조금 나아졌습니다. 8 Gray 폰이나 16Gray 폰에 64Kbyte짜리 어플리케이션을 짜던 환경말이죠..

이러한 느린 CPU위에 돌아간다는 가정하에 달빅이란 가상머신을 돌리기 위한 최적화의 노력이 눈물겹습니다.



상기 Java 코드를 Bytecode로 변환하였을 경우에 Java VM과 Dalvik VM의 차이입니다.
Dalvik VM의인스트럭션이 상당히 적습니다. 즉 CPU가 처리해야 할 일이 그만큼 적다는 의미입니다.

일반적인 Java VM은 스택에 인스트럭션을 쌓아서 하나 하나씩 처리하는 반면, Dalvik VM은 ARM 프로세서에 최적화하여 인스트럭션 수를 줄였습니다. 즉 이터레이션(Iteration)을 한다는 의미인데요, 이터레이션을 할때 스택에 쌓을 것들을 변수로 처리하여, CPU의 연산횟수를 줄이는대신 인자는 늘어나게 됩니다. 결과적으로는 CPU의 처리 사이클이 줄어들게되지만, 스택대신 이터레이션을 사용하므로 Dalvik VM이 각 CPU 프로세서에 최적화되어 있어야 가능합니다.

이런 일들을 멋지게 처리해 놓았습니다. 앞으로 안드로이드의 성능도 좋을 것 같습니다.
이 외에도 여러 재미있는 이야기들을 들었습니다. 깊이있는 내용을 차근 차근히 잘 설명해주신 양정수님에게 감사드립니다.

참고로 양정수님은 Korea Adroid(http://www.kandroid.org)의 운영자이시기도 합니다.



'old > Basic' 카테고리의 다른 글

Handler  (0) 2010.07.06
aidl  (0) 2010.07.06
안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
The Froyo Code Drop  (0) 2010.07.06
Posted by jazzlife
,

RIL 이란 무엇인가?

개요.
 
안드로이드 RIL은 android.telephony 서비스들과 radio h/w간의 추상화된 계층을 제공한다.
RIL은 radio agnostic(존재하지만 정확하게 표현하기는 어려운 실체)이며, GSM 기반의 radio들에 대한 지원을 포함하고
있다.
 
아래의 그림은 안드로이드 전화통화 시스템 구조를 설명한다.
 
   
 
위의 그림에서 Radio Daemon과 Vender RIL은 파트너의 특수성을 가진 사적소유영역에 속하며, 나머지 블록은
안드로이드 영역에 속한다고 할 수 있다.
 
RIL은 두가지의 기본적인 컴포넌트로 구성된다.
 
RIL 데몬(Daemon) : 
 
RIL 데몬은 Vender RIL을 초기화하며, 안드로이드 telephony 서비스들로부터의 모든 통신을 프로세스하며,
solicited 명령어를 통해 Vender RIL로 콜을 넘긴다.
 
Vender RIL :
 
ril.h의 radio 특수성을 포함하는 Vender RIL은 radio h/w와의 모든 통신을 처리하며, unsolicited 명령어들을 통해서
RIL 데몬(rild)에 콜을 넘긴다.
 
RIL 초기화
 
안드로이드는 구동시점에 아래에 언급된 순서대로, telephony 스택과 Vender RIL 을 초기화한다.
 
1. RIL 데몬은 Vender RIL에 제공해야할 초기화 argument와 그것을 사용하기 위한 Vender RIL 라이브러리를
   결정하기 위한 rild.lib 경로와 rild.libargs 시스템 속성들을 읽어들인다.
 
2. RIL 데몬은 Vender RIL 라이브러리를 load(탑재)하며, RIL을 초기화하기 위해 RIL_Init을 호출하며,
    RIL 함수에 대한 참조를 얻는다.
 
3. RIL 데몬은 Vender RIL 함수들에 대한 하나의 참조를 제공하는 안드로이드 Telephony 스택상의  
    RIL_register를 호출한다.
 
RIL 상호작용
 
RIL 제어 관련 통신에는 두가지 형식이 존재한다.
 
Solicited commands : Solicited 명령어들은 전화걸기(DIAL) 과 HANGUP(전화끊기)와 같은 RIL 라이브러리에 의해
                                만들어 진다.
Unsolicited responses : Unsolicited responses는 CALL_STATE_CHNGED(전화 상태 변화)와 NEW_SMS(새 문자
                                   메시지)와 같은 baseband로 부터 만들어 진다.
 
Solicited
 
아래의 인용은 solicited 명령어들에 대한 인터페이스를 보여주고 있다.
 
void OnRequest (int request_id, void *data, size_t datalen, RIL_Token t);
void OnRequestComplete (RIL_Token t, RIL_Error e, void *response, size_t responselen);

 
그리고 아래와 같이 계열들로 묶여질 수 있는 60개 이상의 solicited 명령어들이 존재한다.
 
  • SIM PIN, IO, and IMSI/IMEI (11)
  • Call status and handling (dial, answer, mute…) (16)
  • Network status query (4)
  • Network setting (barring, forwarding, selection…) (12)
  • SMS (3)
  • PDP connection (4)
  • Power and reset (2)
  • Supplementary Services (5)
  • Vendor defined and support (4)
아래의 다이어그램은 안드로이드에서의 solicited call을 설명하고 있다.
 
 
 
Unsolicited
 
아래의 인용은 unsolicited 명령어들에 대한 인터페이스를 설명하고 있다.
 
void OnUnsolicitedResponse (int unsolResponse, void *data, size_t datalen);

 
그리고 아래와 같이 계열들로 묶여질 수 있는 10개 이상의 unsolicited 명령어들이 존재한다.
 
  • Network status changed (4)
  • New SMS notify (3)
  • New USSD notify (2)
  • Signal strength or time changed (2)
아래의 다이어그램은 안드로이드에서의 unsolicited call을 설명하고 있다.
 
 
 
RIL 구현하기
 
특정 radio RIL을 구현하기 위해서는, 라디오 요청을 처리하기 위해 안드로이드에서 요구되는 함수의 집합을 구현하는
공유 라이브러리를 만들어야 한다. 요구되는 함수들은 RIL header (/include/telephony/ril.h)에 정의된다.
 
안드로이드 radio 인터페이스는 radio-agnostic이며, Vender RIL은 radio를 통해 통신하기 위한 어떤 프로토콜도
사용할 수 있다. 안드로이드는 하나의 참조적 구현형태로써의 Vender RIL을 제공하고 있는데, 그것은 Hayes AT
command 집합을 사용하고 있으며, 여러분들은 telephony 테스트를 위한 쉬운 시작과 상업적 Vender RIL 들을 위한
가이드로써 사용할 수 있다. 참조적으로 구현된 RIL 소스 코드는 /commands/reference-ril/ 에서 볼 수 있다.
 
libril-<companyname>-<RIL version>.so 규약을 사용해서 공유 라이브러리 형태로 여러분의 Vender RIL을 컴파일
해라. 여기에 예로써, libril-acme-124.so가 있다.
  • libril: 모든 vender RIL 구현은 'libril' 로 시작
  • <companyname>: 특정 회사의 약어
  • <RIL version>: RIL 버전 숫자
  • so: 파일 확장자
RIL_Init
 
여러분의 Vender RIL은 모든 radio 요청들을 처리할 함수들에 대한 제어를 제공하는 RIL_Init 함수를 정의해야 한다.
RIL_Init은 RIL을 초기화하기 위해, 부트시점에서 안드로이드 RIL Daemon에 의해 호출된다.
 
RIL_RadioFunctions *RIL_Init (RIL_Env* env, int argc, char **argv);

 
RIL_Init은 radio 함수들에 대한 제어를 포함하는 RIL_RadioFuncions 구조체를 반환해야 한다.
 
type structure {
       int RIL_version;
       RIL_RequestFunc onRequest;
       RIL_RadioStateRequest onStateRequest;      
       RIL_Supports supports;
       RIL_Cancel onCancel;
       RIL_GetVersion getVersion;
}
RIL_RadioFunctions;

 
 
RIL 함수들
 
ril.h는 아래의 테이블에 설명된 함수들 같은, RIL_UNSOL_STK_CALL_SETUP, RIL_SIM_READY, RIL_SIM_NOT_READY 과
같은 RIL 상태들과 변수들을 정의한다. 더 상세하게는 헤더파일(/device/incldue/telephony/ril.h)을 살펴보길.
 
RIL Solicited 명령어 요청들.
 
Vender RIL은 solicited 명령어들을 처리하기 위해 아래의 테이블에 설명된 함수들을 제공해야 한다.
RIL solicited 명령어 요청 타입들은 RIL_REQUEST_ 라는 접두사를 가지고 ril.h에 정의된다.
상세하게는 헤더 파일을 체크하길.
 
함수명 설명
void (*RIL_RequestFunc) (int request, void *data, size_t datalen, RIL_Token t); 이것은 solicited 명령어들에 대한 시작 포인트이며, RIL_REQUST_ 접두사를 가지고 ril.h에 정의된 다양한 RIL solicited 요청 타입들을 제어할 수 있어야 한다.
  • request 는 RIL_REQUEST_* 중 하나임.
  • data 는 RIL_REQUST_* 위해 정의된 data에 대한 포인터임.
  • t 는 RIL_onResponse 에 수반되는 호출에서 사용되어야 함.
  • datalen 은 caller 소유이며, callee에 의해 수정되거나 free 되어서는 안됨.
RIL_onRequestComplete()에 대한 호출을 통해 완료되어야 함.
RIL_onRequestComplete()는 임의의 쓰레드로부터 이 함수의 반환 이전 또는 이후에 호출될 수 있다. 이것은 항상 동일한 쓰레드로부터 호출될 것이며, 그러므로 여기에서 반환이라함은 radio가 다른 명령을 수행할 준비하 되어있음을 의미한다.(이전 명령이 완료여부와 상관없이)
RIL_RadioState (*RIL_RadioStateRequest)(); 이 함수는 현재의 radio 상태를 동기적으로 반환해야 한다.
int (*RIL_Supports)(int requestCode); 이 함수는 명시된 RIL_REQUEST 코드가 지원된다면 "1"을 아니라면 "0"을 반환한다.
void (*RIL_Cancel)(RIL_Token t); 이 함수는 멈춰져 있는 요청을 취소시키기 위해 사용된다.
이 함수는 다른 쓰레드, 즉 RIL_RequestFunc 을 호출한 쓰레드가 아닌 쓰레드에 의해 호출된다.
 
취소시, callee는 요청을 포기하기 위해 최선을 다해야하며, 잠시후 RIL_Errno CALCELLED 를 가지고 RIL_onRequestComplete를 호출한다.
 
다른 결과들을 가진 이 요청들에 대한 RIL_onRequestComplete에 대한 수반되는 호출들은 묵인되지만, 무시된다.(그것은 취소 요청을 무시하기 위해 유효한 것이다)
 
RIL_Cancel 호출들은 즉시 반환되어야 하며, 취소를 기다려서는 안된다.
const char * (*RIL_GetVersion) (void); 여러분의 Vender RIL에 대한 버젼 문자열을 반환한다.
 
Vender RIL은 안드로이드 RIL 데몬에 역으로 통신하기 위해 다음과 같은 callback 메쏘드를 사용한다.
 
함수명 설명
void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen);
  • t 는 이전 호출에서 RIL_Notification 루틴에 전달된 파라메터이다.
  • 만약 e != SUCCESS 라면, response 는 null 이 될것이며, 무시된다.
  • RIL_onRequestComplete 는 가능한 빨리 return 할 것이다.
void RIL_requestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime); RIL_RequestFunc 이 호출된 동일한 쓰레드에서 사용자 정의의 callback 함수를 호출한다. 만약 relativeTime 이 명시되었다면, 그것은 callback이 발생해야하는 상대적인 시각을 명시한다. 만약 relativeTime 이 NULL 이거나 0 으로 채워진 구조체에 대한 포인터라면, callback은 최대한 빨리 발생한다.

 
RIL Unsolicited 명령어들
 
아래의 테이블에 열거된 함수들은 안드로이드 플랫폼에서 unsolicited 명령어들을 발생시키기 위해 Vendor RIL에 의해
사용되는 call-back 함수들이다. 자세하게는 ril.h 를 보길.
 
함수명 설명
void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen);
  • unsolResponse 는 RIL_UNSOL_RESPONSE_* 중 하나임.
  • data 는 RIL_UNSOL_RESPONSE_* 를 위해 정의된 data에 대한 포인터임.
  • data 는 caller 소유이며, callee 에 의해 수정되거나 free 되어서는 안됨.

'old > Basic' 카테고리의 다른 글

aidl  (0) 2010.07.06
zygote & Dalvik VM  (0) 2010.07.06
안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
The Froyo Code Drop  (0) 2010.07.06
Parcelable Object 2  (0) 2010.07.06
Posted by jazzlife
,
안드로이드 초기화(init 프로세스와 기타 서비스 등록)



안드로이드는 리눅스 커널을 이용한다. 따라서 리눅스의 시작 시퀀스 이후에 안드로이드 고유 시스템이 동작하게 된다.

기본적인 동작을 잘 설명한 곳( http://samse.tistory.com/112?srchid=BR1http%3A%2F%2Fsamse.tistory.com%2F112 )이 있어서 일부 발췌해 온다.

 



리눅스는 기본적으로 init이 가장먼저 실행된다. init.rc 라는 이름의 파일에는 init이 해야할 작업들이 기록되어 있다. 파일시스템 마운팅, 폴더 권한설정, 캐시폴더 삭제, 시작프로그램 동작 등이 기록되어 있다. 우선 데몬을 올린다. 데몬은 init에 의해 리눅스와 같이 시작되었다가 리눅스가 종료될 때 없어지는 프로그램으로서 데몬을 작성하는 규격에 따라 만들어져야 한다. Zygote가 Dalvik을 초기화 한다. C 밑에 있는 기본라이브러리들은 런타임을 통해 실행되고 상위 서비스들은 Dalvik을 통해 실행된다. 이러한 과정들을 위한 설정은 해당하는 config 파일을 수정하면 된다. 어떤 동작들을 바꾸고 싶으면 기본적으로 init.rc를 바꾸면 되고 Zygote를 바꾸고 싶으면 그 설정파일을 바꾸면 된다. 그리고 시스템서버, 서페이스매니저, 오디오매니저들이 올라간다. 그 다음에는 시스템 서비스들이 활성화 된다. 이들은 서비스이므로 서비스매니저에 등록된다.


init은 /system/core/init 디렉토리에 관련 소스가 있다. init.rc 파일은 /system/core/rootdir/에 있다.

init이 하는 일과 init.rc 파일을 작성하는 방법에 대해서는 여기( http://www.aesop.or.kr/?document_srl=46566 )를 참조한다.

 

init이 하는 일을 정리하면 다음과 같다.

1. log system 초기화

2. /init.rc와 /init.%hardware$.rc 파일 파싱

3. 2번 스텝의 2개 파일에서 초기 선동작(early-init action) 수행

4. 디바이스 종속적인 초기화 작업 (예를 들어 /dev 에 모든 디바이스 노드 만듬)

5. property system 초기화 (property system은 공유 메모리로 동작하며 윈도우의 레지스트리와 같은 역활이다)

6. 2번 스텝의 2개 파일에서 초기 동작(init action) 수행

7. property service 시작

8. 2번 스텝의 2개 파일에서 선부팅(early boot), 부팅(boot) 동작 수행

9. 2번 스텝의 2개 파일에서 property 동작 수행

10.  device/property가 /child process를 셋할때까지 대기하면서 rc 파일에 정의된 명령어를 수행한다.



위의 설명처럼 /out/target/product/XXX/init.rc 를 먼저 살펴 보자.  위에서 설명한 것처럼 폴더 권한 설정 등의 초기화 작업을 하고 나면

Demon을 올리는 작업을 하도록 되어 있다. 이를 정리하면 다음과 같다.


1) console : shell 을 시작 (system/core/sh/)

## Daemon processes to be run by init.
##
service console /system/bin/sh
    console


2) abdb : abdb daemon을 시작 (system/core/adb/)

# adbd on at boot in emulator
on property:ro.kernel.qemu=1
    start adbd


on property:persist.service.adb.enable=1
    start adbd


on property:persist.service.adb.enable=0
    stop adbd


3) servicemanager : binder system을 시작 (framework/base/cmds/servicemanager/)

service servicemanager /system/bin/servicemanager
    user system
    critical
    onrestart restart zygote
    onrestart restart media


4) vold : volume daemon 시작 (system/core/vold/)

service vold /system/bin/vold
    socket vold stream 0660 root mount

#service mountd /system/bin/mountd
#    socket mountd stream 0660 root mount


5) debuggerd : debug system을 시작 (system/debuggerd/)

service debuggerd /system/bin/debuggerd


6) rild : radio interface layer daemon을 시작 (hardware/ril/rild/)

service ril-daemon /system/bin/rild
    socket rild stream 660 root radio
    socket rild-debug stream 660 radio system
    user root
    group radio cache inet misc


7) Zygote : (frameworks/base/cmds/app_process/)

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on


8) media : AudioFlinger, MediaPlayerService, CameraService를 시작 (frameworks/base/media/mediaserver/)

service media /system/bin/mediaserver
    user media
    group system audio camera graphics inet net_bt net_bt_admin


관련 코드를 보면 굉장히 간단하다. (framework/base/media/mediaserver/main_mediaserver.cpp)

int main(int argc, char** argv)
{
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    LOGI("ServiceManager: %p", sm.get());
    AudioFlinger::instantiate();
    MediaPlayerService::instantiate();
    CameraService::instantiate();

    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}


9) bootsound : boot sound (system/media/audio/ui/boot.mp3)를 재생, (그런데 이 바이너리는 실제 찾을 수 없네...?. 그리고 샘플 파일도 안 만들어 지네)

service bootsound /system/bin/playmp3
    user media
    group audio
    oneshot


10) dbus : dbus daemon 시작 (BlueZ 즉 Bluetooth 관련 서비스) (external/dbus/bus/)

service dbus /system/bin/dbus-daemon --system --nofork
    socket dbus stream 660 bluetooth bluetooth
    user bluetooth
    group bluetooth net_bt_admin


11) hcid : hcid의 stdout, stdin을 안드로이드 logging sytstem으로 redirect (external/bluez/hcid/)

service hcid /system/bin/hcid -s -n -f /etc/bluez/hcid.conf
    socket bluetooth stream 660 bluetooth bluetooth
    socket dbus_bluetooth stream 660 bluetooth bluetooth
    # init.rc does not yet support applying capabilities, so run as root and
    # let hcid drop uid to bluetooth with the right linux capabilities
    group bluetooth net_bt_admin misc
    disabled


12) hfag : Bluetooth handsfree audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

service hfag /system/bin/sdptool add --channel=10 HFAG
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

13) hsag : Bluetooth headset audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

service hsag /system/bin/sdptool add --channel=11 HSAG
    user bluetooth
    group bluetooth net_bt_admin
    disabled
    oneshot

14) install : install package daemon 시작 (frameworks/cmds/installd/)

service installd /system/bin/installd
    socket installd stream 600 system system


15) flash_recovery : /system/recovery.img를 로드 (bootable/recovery/mtdutils/)

service flash_recovery /system/bin/flash_image recovery /system/recovery.img
    oneshot


 

강조한 것처럼 바와 같이 servicemanager을 이용해서 zygote와 media를 시작하고 SD 카드와 USB의 mount 등을 처리하는 vold 데몬을 올린다. 이후 부팅 사운드를 재생하거나 다른 필요한 데몬들을 올리게 된다. 이후 다시 강조한 부분을 보면 zygote를 이용해서 system server 를 시작한다. 이후 media를 다시 시작한다(?)


안드로이드의 system server는 Native Service와 여러가지 기본 서비스(core system service 라고도 한다)들로 이루어진다.


/framework/base/cmds/system_server/library/system_init.cpp 를 보면 system_init() 함수가 있다. 이 파일은 실제 사용한다기 보다는 초기화 흐름을 알게 해주는 코드로 보인다.


extern "C" status_t system_init()
{
    LOGI("Entered system_init()");
   
    sp<ProcessState> proc(ProcessState::self());
   
    sp<IServiceManager> sm = defaultServiceManager();
    LOGI("ServiceManager: %p\n", sm.get());
   
    sp<GrimReaper> grim = new GrimReaper();
    sm->asBinder()->linkToDeath(grim, grim.get(), 0);
   
    char propBuf[PROPERTY_VALUE_MAX];
    property_get("system_init.startsurfaceflinger", propBuf, "1");
    if (strcmp(propBuf, "1") == 0) {
        // Start the SurfaceFlinger
        SurfaceFlinger::instantiate();
    }

    // On the simulator, audioflinger et al don't get started the
    // same way as on the device, and we need to start them here
    if (!proc->supportsProcesses()) {

        // Start the AudioFlinger
        AudioFlinger::instantiate();

        // Start the media playback service
        MediaPlayerService::instantiate();

        // Start the camera service
        CameraService::instantiate();
    }

    // And now start the Android runtime.  We have to do this bit
    // of nastiness because the Android runtime initialization requires
    // some of the core system services to already be started.
    // All other servers should just start the Android runtime at
    // the beginning of their processes's main(), before calling
    // the init function.
    LOGI("System server: starting Android runtime.\n");
   
    AndroidRuntime* runtime = AndroidRuntime::getRuntime();

    LOGI("System server: starting Android services.\n");
    runtime->callStatic("com/android/server/SystemServer", "init2");
       
    // If running in our own process, just go into the thread
    // pool.  Otherwise, call the initialization finished
    // func to let this process continue its initilization.
    if (proc->supportsProcesses()) {
        LOGI("System server: entering thread pool.\n");
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
        LOGI("System server: exiting thread pool.\n");
    }
    return NO_ERROR;
}



코드를 보면 알겠지만 크게 다음 순서로 초기화 과정이 이루어진다.

1. Native Service 초기화

SurfaceFlinger

AudioFlinger

MediaPlayerService

CameraService


2. Android RunTime 시작

SystemServer


안드로이드가 동작하기 위한 여러가지 기본 서비스들(또는 JAVA 서비스)을 시작하는 곳이 SystemServer이다.


파일 위치는 다음과 같다.


/frameworks/base/services/java/com/android/server/SystemServer.java


SystemServer 클래스는 init2() 함수를 호출하게되면 ServerThread를 동작시키고 여기에서 다양한 서비스들을 시작한다.

서비스 시작하는 순서대로 대충 정리하면 다음과 같다.(run 함수의 내용을 그냥 정리한 것이며 * 표시 없는 것은 주서비스이고 * 표시 있는 것은 부가 서비스이다)


Power Manager 시작

Activity Manager 생성


ActivityManager를 생성해서 context를 받으면 이후 서비스들에는 context를 전달한다.

(아직 이 구조는 잘 모르겠지만 서비스들에서 설정이나 특정 이벤트들을 브로드 캐스팅하기 위해서는 context를 이용해야 하는 것 같다. ActivityManger에서 이를 위한 초기화 과정이 이루어지고 이후 서비스들은 context를 가지고 서로간의 통신이나 Focus 이동이 생길 수 있도록 하고 있는 것 같다. 이것은 아직은 어디까지나 추측이다.)


Telephony Registry 시작

Package Manager 시작

System Process로 Activity Manager 설정

Context Manager 시작

System Context Providers 시작

Battery Service 시작

* Hardware Service 시작

Alarm Manager 시작

Sensor Service 시작


Window Manager 시작


Bluetooth Service 시작

BluetoothDeviceService

BluetoothA2dpService


*Status Bar Service 시작

ClipboardService

InputMethodManagerService


*NetStat Service 시작

*Connectivity Service 시작


*Notification Manager 시작

Mount Service 시작

*DeviceStorageMonitor Service 시작


*Location Manager 시작

*SearchManager Service 시작

 *Clipboard Service 시작

*Fallback Checkin Service 시작

*WallpaperService 시작

*Audio Service 시작

*Headset Observer 시작

(?)AppWidgetService


*AdbSettingsObserver 시작

(?)HDMISettingsObserver 시작

'old > Basic' 카테고리의 다른 글

zygote & Dalvik VM  (0) 2010.07.06
안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
The Froyo Code Drop  (0) 2010.07.06
Parcelable Object 2  (0) 2010.07.06
Parcelable Object 1  (0) 2010.07.06
Posted by jazzlife
,

The Froyo Code Drop

old/Basic 2010. 7. 6. 12:11

The Froyo Code Drop

Googleplex 안팎으로 소스 코드를 트럭으로 나르는 Jean-Baptiste Queru의 글
 
 
 
오늘은 심장이 빨라지는 그런 날 중 하나다. 우리는 방금 안드로이드 2.2 소스 코드를 공개했다.
이는 안드로이드 에코시스템 전체를 위해 큰 진보라고 할 수 있다.
이 최신 소스 코드를 다운로드 하느라 또 한번 서버가 녹아버리는 일이 없기를…
 
이 블로그는 대개 SDK와 NDK를 사용하여 안드로이드 애플리케이션을 개발하는 것에 대해 논의한다.
그러나, 플랫폼 공헌자의 기술도 근본적으로는 애플리케이션 개발자들의 것과 다르지 않다.
동일한 기술 집합을 사용하는데 단지 역할만 다를 뿐이다. 경험이 많은 전세계의 안드로이드 프로그래머들에게
안드로이드 오픈 소스 프로젝트에서의 최근 개발 사항을 소개하고자 한다.
 
안드로이드를 가지고 일하는 엔지니어들이라면 대부분 공개되는 각 버전을 그 코드 이름으로 안다.
(안드로이드 코드명은 맛있게 차린 별미들 중에서 알파벳 순서로 선택하여 붙여진다.)
필자는 이 글에서 안드로이드 2.2를 그 코드 이름인 “프로요”라고 부를 것이다.
딱딱한 버전 숫자를 생각하는 것보다는 캘리포니아의 여름에 만끽하는 차가운 디저트를 생각하는 것이
훨씬 군침 도는 일이다. 
 
새로운 프로요 소스의 멋진 특성들에 대해 살펴본 후,
안드로이드 오픈 소스 프로젝트의 몇 가지 주목할 만한 요소들로 다시 돌아가보자.
 
필자는 안드로이드의 이전 오픈 소스 릴리스에 점점 더 많이 개입하여,
처음 코드를 공개하는 것부터 이클레어에서 오픈소스 관련 git 레벨의 작업에 이르기까지 관여해왔다.
그러한 경로를 따라, 프로요는 처음부터 끝까지 안드로이드 오픈 소스 프로젝트에 가장 초점을 맞추었던
최초의 버전이다. 이 일의 많은 분량을 함께 하며 필자를 도와준 모든 안드로이드 팀에 감사를 표한다.
여기, 필자가 자랑스러워하는 프로요의 특성 몇 가지를 소개하고자 한다.
물론 덕분에 지난 몇 달 동안 계속 바빴지만 말이다.
  • 세계 각 곳에서 안드로이드 오픈 소스 프로젝트에 업로드 했던
    수백 가지의 플랫폼 변경 사항들이 채택되어 프로요에 병합되었다.
    컨트리뷰션을 수용하여 병합하는 과정은 이제는 기름칠이 잘 된 기계가 돌아가는 것처럼 순조롭고,
    앞으로 있을 많은 컨트리뷰션들도 잘 변환할 것으로 본다.

  • 오픈 소스 공개는 단일한 스텝으로 이루어졌다.
    이제 안드로이드 2.2 플랫폼 전체를 위한 모든 소스 트리를 사용할 수 있으며, 변경 이력들도 모두 공개되었다.
    이 내용들을 살펴본다면 개발자들이 이전 버전에서 프로요로 이동하는 데 가속도가 붙을 것이다.
    뿐만 아니라 프로요는 이미 오픈 소스 마스터 트리에도 전체가 병합되었다.
    그 결과, 우리는 당장에 리뷰를 하고 프로요를 기반으로 플랫폼 컨트리뷰션을 수용할 수 있게 되었다.
    따라서 오픈 소스 트리에 대한 컨트리뷰션과
    구글 내부의 마스터 트리(이는 컨트리뷰션들이 궁극적으로 반영될 목적지이다)의 변경 사항을 병합해야 하는
    위험요소가 줄어들 것이다.

  • 디바이스 제조사들과 커스텀 시스템 빌더들이 프로요를 보다 쉽게 사용할 수 있도록 하기 위해,
    우리의 소스 트리를 오픈 소스 트리에서, 별도의 클로즈드 소스 모듈로 재구성하였다.
    우리는 오픈 소스 코드 자체에 많은 수정을 가하여 원치 않게 클로즈드 소스 소프트웨어에 의존하는 부분들을 제거했다.
    또한 안드로이드 오픈 소스 프로젝트의 소스 코드를 그 자체로 빌드할 수 있도록,
    필요한 모든 컨피규레이션 파일들을 코어 플랫폼 내에 통합시켰다.
    여러분은 이제 Dream (ADP1), Sapphire (ADP2), Passion (Nexus One) 뿐만 아니라 에뮬레이터를 위해서도,
    곧바로 온전한 오픈 소스 시스템 이미지를 빌드하고 부팅시킬 수 있다.

  • 디바이스 지원에 대해 말하자면,
    우리는 이전 버전에서는 클로즈드 소스였던 하드웨어 관련 라이브러리들도 오픈 소스화 시켰다.
    이로 인해 보다 많은 컨트리뷰션들에게 문을 열게 되었다.
    예를 들면, Dream, Sapphire, Passion을 위한 복구 UI 코드, 그리고 미디어 프레임워크와 퀄컴 칩셋 간의
    인터페이스 등을 들 수 있다.
프로요 소스 코드 공개 외에도, 필자는 안드로이드 오픈 소스 프로젝트에서의 몇 가지 개선된 사실들을 언급하고자 한다:
  • 우리는 그 동안 20 군데가 넘는 회사와 많은 개인 개발자들로부터 컨트리뷰션을 받았다.
    Gerrit 코드 리뷰 서버에는 거의 4천 명이나 되는 등록된 유저들이 있고,
    각 사람이 평균 두 건의 컨트리뷰션을 주었다.
    깊게는 C 라이브러리에서부터 락 스크린의 UI에 이르기까지, 시스템의 모든 영역에 대한 개선안들이었다.
    또한 문서의 오류를 고치거나 코드를 리포맷하는 것에서부터 개발자가 볼 수 있는 API들을 더하거나
    사용자가 볼 수 있는 기능에 이르기까지 다양한 수준의 컨트리뷰션들이었다.
    필자는 모든 공헌자들이 작업을 하며 들인 수고와 인내에 감사한다.  

  • 우리는 이제 플랫폼 컨트리뷰션들에 보다 빠르게 응대한다.
    대부분의 변경들은 업로드된 지 업무일 3, 4일 이내에 검토가 되며,
    몇 주가 지나도록 정체된 채 머무르는 변경은 얼마 되지 않는다.
    우리는 보다 빨리, 보다 자주 리뷰하려고 노력 중이다.
    이 글을 타이핑 하고 있는 지금도, 플랫폼 컨트리뷰션 중에 아직 살펴보지 못한 것은 여남은 개이고,
    그 중 사흘 된 것이 가장 오래 된 것이다.
    지난 2주 동안 플랫폼 코드에 관련된 컨트리뷰션들 중 90% 이상을 열심히 검토하였다.
    프로세스가 빠르게 전개되어 코드 리뷰 동안 상호작용이 더 활발하게 일어나길 희망한다.
    그럼에도 불구하고 전세계 공헌자들과 소통할 때 시차 문제 때문에 실시간으로 소통하는 것이 사뭇 어렵다는 것을 느낀다.

  • 지난 두 달 남짓, 우리는 Gerrit 서버에 업로드된 천 여 가지 변경 사항들에 대해 최종 결정을 내렸다.
    즉, 검토를 통해 ‘채택’ 혹은 ‘거절’ 여부가 판가름 났다는 것이다.
    안드로이드 오픈 소스 프로젝트의 모든 역사에 걸쳐 얻은 컨트리뷰션들이 매우 수준이 높아서
    그 중 80% 정도를 꾸준히 메인 저장소에 병합할 수 있었다.
    그 메인 저장소에서 공식 릴리스 대상으로 이동하는 것이다.
    말하자면, 지난 2개월 동안 매 업무일마다 평균 스무 가지 변경 사항들이 안드로이드 오픈 소스 프로젝트를 통해
    공용 git 저장소로 수용되었다는 것이다.

  • 우리는 최근 안드로이드 오픈 소스 프로젝트와 관련하여 공식 구글 그룹 두 개를 만들었다.
    Android-building은 특히 빌드 이슈를 다룰 의도로 만들어졌다(글을 포스팅 하기 전에 아카이브를 면밀히 검색해보라).
    Android-contrib은 실제적인 컨트리뷰션을 논의하는 데 사용된다(진짜로 오픈 소스에 공헌을 하고 리뷰 프로세스를
    따라갈 의도가 아니라면, 또한 여러분 스스로 한 두 시간 연구해보기 전에는 포스트하지 말아주시길.)

  • 우리는 개발자 툴을 오픈 소스 프로젝트에서 직접 만들고 있다.
    이 영역에는 닫힌 문 뒤로 몰래 일어나는 일이라고는 없다.
    여기에는 이클립스 플러그인과 에뮬레이터, 그리고 다른 많은 SDK 관련 툴들이 포함된다.

  • 특정 플랫폼 버전이 일단 오픈 소스화 되고 나면,
    그 버전과 관련된 모든 호환성 테스트 수트 (Compatibility Test Suite) 개선사항이 오픈 소스 트리에 직접 만들어진다.
    사실, 2.1 CTS의 버전 2는 100% 그러한 방식으로 이루어졌다. 개발과 테스팅, 릴리스 프로세스가
    곧장 오픈 소스 트리에서 이루어졌다. 프로요 역시 그렇다.
    그리고 우리는 현재 CTS 프로젝트의 프로요 부문으로 컨트리뷰션들을 수용하고 있다.
필자는 마지막 언급한 두 가지 측면이 애플리케이션 개발자들에게 중요하다고 믿는다.
여러분이 애플리케이션 개발자이고 여러분과 동료 개발자들이 사용하는 툴들을 개선하고 싶다면,
그 분야에서의 변경을 이루어내는 과정들이 지금은 훨씬 투명해졌다.
이와 유사하게, 애플리케이션 개발 도중 기기들 간에 호환이 안 된다는 것을 발견했을 때,
그리고 그러한 비호환성이 문자적으로나 실제적으로 안드로이드의 호환성 의미 밖에 있다고 여길 때,
그 영역을 위한 CTS 테스트로 공헌함으로써 상황을 개선하도록 도움을 줄 수 있다.
 
이제 안드로이드 2.2가 오픈 소스 세상에 공개되었으므로, 그리고 리뷰 프로세스가 원활하게 작동하고 있으므로,
필자는 수준 높은 컨트리뷰션들이 보다 많이 나와서 미래의 안드로이드 버전에 사용될 수 있기를 기대한다.
필자의 가장 달콤한 꿈은 (또한 가장 끔찍한 악몽이기도 하지만) 좋은 컨트리뷰션들이 하도 많이 쏟아져서
내가 도저히 따라잡을 수 없을 정도가 되는 것이다. 부디 이 단꿈에서 나를 깨우지 말아주시길.
Posted by jazzlife
,

Parcelable Object 2

old/Basic 2010. 7. 6. 11:55

[In Case of Inner Class]


저번에는 아주 간단한 Rect 클래스로 parcelable하게 만들어 보았습니다.
이번에는 좀 더 복잡한 형태의 클래스를 가지고 parcelable하게 만들어 보도록 하죠.

이번에 parcelable하게 만들어 볼 클래스는 EyePoint 클래스 입니다.


public class EyePoint {
    public Point left;
    public Point right;
   
    public class Point {
        public int x;
        public int y;
    }
}


눈 좌표를 나타내는 클래스 인데, 왼쪽 눈과 오른쪽 눈 좌표를 가지고 있습니다.
그리고 다시 한쪽 눈 좌표는 x, y integer 값을 가지고 있죠.

뭐 물론 주우욱 풀어서 leftX, leftY, rightX, rightY 4개의 필드를 만들어서 표현 할 수도 있겠습니다만,
전 그냥 EyePoint 클래스 안에 다시 Point 클래스를 만들었습니다. 뭐 제 마음이죠.


그럼 일단 안쪽에 있는 EyePoint$Point 클래스 부터 parcelable하게 만들어 봅시다.


Step 1.


public class EyePoint implements Parcelable {
    public Point left;
    public Point right;

   
   
    // EyePoint$Point Inner Class
    public static class Point implements Parcelable {
        public int x;
        public int y;
       
        public int describeContents() {
            return 0;
        }
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(x);
            dest.writeInt(y);
        }
        public static final Parcelable.Creator<Point> CREATOR = new Creator<Point>() {
            public Point createFromParcel(Parcel source) {
                Point p = new Point();
                p.x = source.readInt();
                p.y = source.readInt();
                return p;
            }
            public Point[] newArray(int size) {
                return new Point[size];
            }
        };
    }
}


저번에 만들었던 Rect 클래스 보다 무려 필드의 수가 반 밖에 안됩니다.
이쯤이야 금방 만들 수 있지요...


한가지 주의할 점은 Point 클래스의 CREATOR가 static이니
Point 클래스도 static이 되어야 EyePoint를 통해서 접근 할 수 있는것이 좀 다른 점입니다.
어차피 Eclipse에서 다 잡아 주니까 부담없이 코딩 하시면 되겠습니다.



[Write Parcelable And Read Parcelable]


자... 그럼 이제 EyePoint 클래스를 parcelable하게 만들어 봅시다.
Point 타입의 필드가 left, right 두개가 있으니 이것을 Parcel에다가 써야 하는데...

어떤 타입으로 써야 할까요?


눈치가 빠르신 분들은 이미 writeParcelable() 메소드를 사용하면 될거라 생각 하셨을 겁니다.


이미 Point는 Parcelable Interface를 implements 했으니 이미 parcelable합니다.
그래서 writeParcelable() 메소드를 사용해서 Parcel에다 쓸 수 있는 것이죠.


그런데 writeParcelable() 메소드를 잘 살펴보면,

다른 Primitive 타입과는 다른 인자가 하나 더 붙어 있습니다.

public final void writeParcelable (Parcelable p, int parcelableFlags)

parcelableFlags의 정체는 데체 무어란 말인가...
근데 지금은 그냥 별 생각 없이 ZERO로 채워 주시면 됩니다. 아핫핫... 넘어갑시다.


Step 2.


public class EyePoint implements Parcelable {
    public Point left;
    public Point right;

   
    public int describeContents() {
        return 0;
    }
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeParcelable(left,  0);
        dest.writeParcelable(right, 0);
    }
   
   
    // EyePoint$Point Inner Class
    public static class Point implements Parcelable {
        public int x;
        public int y;
       
        public int describeContents() {
            return 0;
        }
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(x);
            dest.writeInt(y);
        }
        public static final Parcelable.Creator<Point> CREATOR = new Creator<Point>() {
            public Point createFromParcel(Parcel source) {
                Point p = new Point();
                p.x = source.readInt();
                p.y = source.readInt();
                return p;
            }
            public Point[] newArray(int size) {
                return new Point[size];
            }
        };
    }
}

 

보시는 바와 같이 그냥 0을 썼습니다. 뭐 별거 없네요...

그럼 이제 Parcel에다 쓰는 부분이 끝났으니 CREATOR를 만들어 봅시다.


아까는 writeParcelable() 메소드를 사용 했으니

반대로 readParcelable() 메소드를 사용 하면 됩니다.


참 말은 쉽군요. 그럼 한번 살펴 봅시다.

public final T readParcelable (ClassLoader loader)

잉? 다른건 그냥 읽었는데 이건 ClassLoader가 필요하군요.
데체 이 ClassLoader는 쌩뚱맞게 어디서...


우리가 읽고 싶은것은 Point 타입입니다.
그래서 Point의 ClassLoader를 지정해 주어야지 Point 값을 읽어 올 수 있습니다.


ClassLoader를 얻어내는 방법은 의외로 간단합니다.
Intent를 쓸 때와 비슷한 방법으로 얻어 오면 되는 것이죠.


ClassLoader loader = EyePoint.Point.class.getClassLoader();


Intent를 쓸 때는 Class까지만 넘겨주면 되는 거였는데,

Class 객체에서 getClassLoader()를 호출하게 되면 ClassLoader를 쉽게 얻어 올 수 있습니다.


Step 3.


public class EyePoint implements Parcelable {
    public Point left;
    public Point right;

   
    public int describeContents() {
        return 0;
    }
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeParcelable(left,  0);
        dest.writeParcelable(right, 0);
    }
    public static final Parcelable.Creator<EyePoint> CREATOR = new Creator<EyePoint>() {
        public EyePoint createFromParcel(Parcel source) {                   
            EyePoint e = new EyePoint();
                   
            ClassLoader loader = EyePoint.Point.class.getClassLoader();
            e.left  = source.readParcelable(loader);
            e.right = source.readParcelable(loader);
           
            return e;
        }
        public EyePoint[] newArray(int size) {
            return new EyePoint[size];
        }
    };
   
   
    // EyePoint$Point Inner Class
    public static class Point implements Parcelable {
        public int x;
        public int y;
       
        public int describeContents() {
            return 0;
        }
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(x);
            dest.writeInt(y);
        }
        public static final Parcelable.Creator<Point> CREATOR = new Creator<Point>() {
            public Point createFromParcel(Parcel source) {
                Point p = new Point();
                p.x = source.readInt();
                p.y = source.readInt();
                return p;
            }
            public Point[] newArray(int size) {
                return new Point[size];
            }
        };
    }
}

 

네.. 이런 것이죠.
이제 왠만한 클래스들은 parcelable하게 만들 수 있겠죠?



[Appendix]

네... 위에서도 그냥 지나 쳤던 문제의 parcelableFlags가 아직 남아 있죠...
역시 이것의 사용 용도에 대해서는 미궁입니다.

이것역시 integer값으로, bit mask 형식으로 사용하기 위한 인자 입니다.


지금 사용 가능한 플래그는

Parcelable.PARCELABLE_WRITE_RETURN_VALUE (0x00000001) 뿐입니다.

소스를 거의다 뒤져 보았는데, 이걸 사용하는 곳은 ParcelFileDescriptor 클래스 밖에 없었습니다.
대부분은 0으로 채워져 있었지요.


ParcelFileDescriptor는 또 데체 어디에 쓰이는건지 원...
보면 볼 수록 알 수 없는 Android 인 것 같습니다.

'old > Basic' 카테고리의 다른 글

안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
The Froyo Code Drop  (0) 2010.07.06
Parcelable Object 1  (0) 2010.07.06
안드로이드 기본 개념  (0) 2010.07.06
JNI (Java Native Interface)  (0) 2010.07.06
Posted by jazzlife
,

Parcelable Object 1

old/Basic 2010. 7. 6. 11:54
[Parcel Class?]

Android의 핵심 중에서 Binder Driver라는 녀석이 있습니다.
Linux Kernel의 driver로 되어 있고, IPC이긴 하지만 기존의 IPC와는 살짝 다른 녀석 입니다.


저도 어떻게 만들었는지는 잘 모릅니다만,
shared memory를 통하여 오버헤드도 적고 프로세스 사이에 비동기로 호출도 이루어 진다고 합니다.
그리고 Binder는 기존 IPC처럼 데이터만 전달 하는게 아니라,
하나의 프로세스에서 다른 프로세스로 Object를 넘길 수도 있게끔 설계 되어 있습니다.
(물론 Serialize 기술을 사용하면 Object도 주고 받을 순 있지요.)


Binder를 통해서 넘기는 메세지 (data 와 object references) 는
Parcel 클래스에 저장이 되어서 넘어가게 됩니다. Parcel은 메세지 컨테이너인 셈이죠.

Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport.

Parcel 클래스는 고성능 IPC 전송을 가능하게 끔 설계가 되어 있기 때문에,

모든 객체에 대해서 평범하게 serialization하지 않습니다.
그래서 모든 Object들을 Binder를 통해 주고 받을 수는 없습니다.



[Parcelable Interface]


Primitive Type과 Primitive Array Type은 기본적으로 parcelable 합니다.

뭐 그냥 일반 데이터인거죠.
그리고 Parcelable Interface를 implements한 클래스가 (당연히) parcelable 합니다.


오늘 우리가 할 일이 바로 이 Parcelable Interface를 사용하여
parcelable Object를 만드는 일 인 것이죠.

사실 parcelable한 type들이 많이 있습니다만, 나머지는 잘 모르겠군요... 어렵습니다.
여튼 오늘은 parcelable Object를 만드는 것만 생각 합시다.



[Parcelable Rect]


Rect 클래스야 다들 쉽게 만드실 겁니다.
left, top, right, bottom 4개의 필드만 있으면 되죠.


integer든 float이든 상관 없습니다만....
Android에는 Rect 클래스의 float 버전이 RectF 클래스 라고 따로 되어 있기 때문에,

저는 integer로 만들겁니다.


public class Rect {
    public int left;
    public int top;
    public int right;
    public int bottom;
}


뭐 아주 간단 합니다. 그냥 C에서의 구조체 수준이네요.
이것을 parcelable하게 바꾸어 봅시다.


Step 1.


public class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;
   
    public int describeContents() {
        return 0;
    }
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(left);
        dest.writeInt(top);
        dest.writeInt(right);
        dest.writeInt(bottom);
    }
}


Parcelable을 implements하게 되면 꼭 추가해야 한다는 메소드 두 개도 같이 넣었습니다.
내용도 채워 봤어요.


describeContents() 메소드는 일단 건너뛰고...
writeToParcel() 메소드는 Parcel에 데이터를 쓰는 부분 입니다.
그냥 무작정 쓰면 됩니다. 전 아무 생각 없이 순서대로 그냥 썼습니다.


그럼 이제 에러도 없으니 parcelable한 Rect 클래스가 되었느냐... 라고 한다면 아직 아닙니다.
쓰는건 있는데 읽는건 없네요...


Parcel로 부터 값을 읽어 오기 위해서는 Parcelable.Creator Interface 가 필요합니다.



[Parcelable.Creator Interface]


Parcel 에서 Parcelable 클래스의 인스턴스를 만들 때
CREATOR라는 static field를 찾아서 실행 합니다.
CREATOR는 Parcelable.Creator<T> type 으로 만들어져야 하는데
이건 선언과 동시에 반드시 initialize 되어야 합니다.


클래스 따로 만들어서 initialize 하기도 쉽지 않습니다.
그냥 익명 클래스로 만들어 버립시다.


Step 2.


public class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;
   
    public int describeContents() {
        return 0;
    }
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(left);
        dest.writeInt(top);
        dest.writeInt(right);
        dest.writeInt(bottom);
    }
   
    public static final Parcelable.Creator<Rect> CREATOR = new Creator<Rect>() {
        public Rect createFromParcel(Parcel source) {
            Rect r   = new Rect();
            r.left   = source.readInt();
            r.top    = source.readInt();
            r.right  = source.readInt();
            r.bottom = source.readInt();
            return r;
        }
        public Rect[] newArray(int size) {
            return new Rect[size];
        }
    };
}


Parcelable.Creator<T> 클래스는 createFromParcel() 과 newArray() 메소스가 필요 합니다.


newArray() 메소드도 일단 넘어가고...
createFromParcel() 메소드를 보면 리턴이 Rect입니다.
그래서 Parcel에서 값을 읽어 새로운 Rect 인스턴스를 리턴 하는 구조로 만들면 끝입니다.


readInt()를 했는데, 이것은 writeToParcel() 메소드에서 썼던 순서대로 읽어 오는 것입니다.
쓸 때는 무작정 썼지만 읽을 때는 조심스럽게 읽어야하죠.


이제 비로소 parcelable Rect 클래스를 만들었습니다.
그냥 별거 없군요...



[Appendix]


위에서 describeContents() 메소드와 newArray() 메소드는 그냥 넘어 갔었습니다.
네... 뭐 사실 잘 모르기도 합니다만,
그냥 보통 parcelable Object를 만드는데에 크게 중요한 부분은 아니지요. 아하하...


describeContents() 에서는 어떤 특별한 객체를 포함하고 있는지에 대한 설명을
리턴값으로 표현 하는 것이라고 보면 됩니다.
bit mask 형식의 integer를 리턴 하며,
값을 체크 할 때 bit mask 체크를 해서 어떤 것들이 들어 있는지 알 수 있습니다.


현재 사용하는 플래그는 Parcelable.CONTENTS_FILE_DESCRIPTOR (0x00000001)
하나 밖에 정의 되어 있지 않습니다.
소스를 다 뒤져 보지 않아서 또 어떤 값들이 쓰이는지는 확인 해보지 못했네요...


newArray() 메소드는 그냥 배열로 만들어서 리턴 하는 겁니다.
Parcel.createTypedArray() 메소드에서 newArray() 메소드를 호출 하는 걸 확인 했습니다.
나중에 createTypedArray() 메소드를 사용 할 일이 있다면 newArray()가 호출이 되는 것이지요.

'old > Basic' 카테고리의 다른 글

The Froyo Code Drop  (0) 2010.07.06
Parcelable Object 2  (0) 2010.07.06
안드로이드 기본 개념  (0) 2010.07.06
JNI (Java Native Interface)  (0) 2010.07.06
http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
Posted by jazzlife
,

안드로이드에서 사용하는 언어는 자바이지만 SUN의 자바와 API와 버추얼머신이 다르다.
Dalvik 이라는 회사의 버추얼머신이 있었는데 구글에서 회사를 통째로 사들였다고 한다. 이때문에 안드로이드는 SUN의 라이센스로부터 자유롭다.



안드로이드 플랫폼은 크게 네 부분으로 나뉜다. 커널부분, 하드웨어추상레이어, 라이브러리, 응용프로그램 프레임워크. 라이브러리들은 C/C++로 구현이 되어 있다. 기존의 여러 프로젝트 들을 통합하였기 때문에 C로 구현된 것들이 대부분이고 C++도 있다. 응용프로그램 프레임워크는 자바로 구현되어 있다. 프레임워크와 라이브러리 사이에는 자바와 C 사이의 서로다른 호출 규약등을 맵핑하는 JNI라는 마셜러가 존재한다. 하드웨어 드라이버를 C로 만들면 JNI를 구현해야 한다. 이를 위한 구글 가이드가 있고 샘플도 공개되어 있다. 그리고 SDK를 생성하여 응용프로그램 개발자에게 전달하면 된다. 하드웨어 부분은 구글에서 표준으로 정해두었기 때문에 따로 만질 필요가 없다. 추상레이어도 스펙이 다 되어있어 응용프로그램을 개발할 때는 API로 보이게 된다.
 기존 임베디드리눅스와 안드로이드의 차이점중 한가지는 장치드라이버들( 그래픽, 오디오, 카메라 등 )이 커널영역이 아닌 유저모드에서 동작한다. 유저모드로 동작 시키면 자신에게 할당되어 있는 번지에만 접근할 수 있기 때문에 조금 더 안전하지만 하드에어 제어시에는 MMU등을 참조하는 등의 추가적인 작업이 필요하기 때문에 조금 불편할 수 있다. 이렇게 드라이버들을 유저영역에 넣은 이유는 라이센스문제, 안정성, 문서화의 편리성( 커널버전이 업데이트 될 때마다 드라이버에 대한 문서를 갱신할 필요가 없다 ), 플러그인 방식으로 드라이버 배포가 가능한점이 있다. 안드로이드에서는 기존 리눅스의 라이브러리로 만든 것들은 동작하지 않는다고 보면 된다. 같은 이름의 라이브러리라 할지라도 안드로이드의 라이브러리들은 스마트폰에 탑재하는 것을 기준으로 불필요한 코드들을 제거하고 최적화를 해두었기 때문이다. POSIX Thread 관련 라이브러리를 예로 들면 create, join 등의 필수 함수를 제외한 나머지함수는 모두 제거되어있다.

Android Kernel

왜 안드로이드는 리눅스 커널 기반일까. 생각하기 나름이겠지만 우선 오픈소스이고 안전하게 입증된 드라이버 모델(well abstracted )이 적용되어 있고, 메모리 프로세스 관리 모델을 제공하고, 하드웨어와 입출력에 대한 보안모델을 제공하고, 공유라이브러리를 지원하는 등 비용과 시간이 적게 드는 이유가 한몫 한것 같다.

현재 커널 안정화버전은 2.6.27 이다. 구글에서 순수 리눅스 커널을 가져다가 안드로이드 용으로 패치한 것이다. 기존의 리눅스와는 다르게 GLIBC를 지원하지 않고 EABI( Embedded Arm Binary Interface?)와 Binder를 사용한다. 그리고 커널 기능 향상을 위해 Alarm, Ashamem(공유메모리), Low memory-killer, debugger, logger 컴포넌트가 추가되었다.

유저모드는 허가된 자원에만 접근이 가능하고 프로그램의 안정성을 높일 수 있지만 하드웨어 효율성은 떨어질 수 있다. 반대로 커널모드는 하드웨어 효율성은 높지만 안정성은 떨어질 수 있다. 안드로이드는 기본 드라이버들을 제외한 나머지 드라이버들은 유저스페이스에서 동작한다. 일반 PC는 사람의 생명을 빼았지는 않지만, 심장박동 컨트롤러, 핸드폰만 가지고 조난 당했을 경우와 같이 스마트폰을 모델로 하면 생명과 연결될 수 있기 때문에 안정성이 속도나 효율보다 더 중요하다.
 
이전에는 일반적으로 일반 gcc를 썼지만 최근의 리눅스 커널을 빌드할 경우에는 ARM사에서 표준으로 정해둔 ABI( arm binary interface ) 규격의 컴파일러를 사용한다. 현재 안드로이드는 EABI( Extended??..) 기능을 지원한다. 일반 컴파일러에서 빌드한 것을 EABI에서 실행할 수 없기 때문에 EABI 컴파일러로 다시 빌드해야 한다. 최근의 임베디드 컴파일러는 대부분 EABI컴파일러 이다. 실수 연산방식에 있어서 하드플로팅포인트와 소프트웨어 플로팅포인트를 혼합하여 빌드할 수 있다. 기존의 ABI는 빌드옵션에서 VFP(Vector Floating Point)를 선택하거나 해제할 수 있었는데 이는 환경이 달라지면 다시 빌드 해야하는 단점이 있었다. EABI는 혼용이 되기 때문에 빌드의 부담이 줄어든다. 또한 구조체 팩킹이 편해졌고 호환성이 증가하였다. 그리고 syscall 규약에 변경사항이 있다. 이전에는 전달인자 4개까지는 레지스터를 쓰고 5개부터는 스택을 사용했는데 레지스터를 r0, r1, r2, r4 처럼 연속된 순서로 했었다.  EABI는 레지스터 간격이 달라지는등 호출 규약이 달라졌다.

Kernel Enhancement

기존의 임베디드 리눅스에 없던 안드로이드만의 향상된 기능에는 알람, 로우메모리킬러, 공유메모리드라이버, 커널디버거, 바인더, 파워매니지먼트, 로거가 있다. 결국 포팅 이슈는 이것들과 관련될 것이다. 스마트폰으로 만들다 보니까 알람이 기본적으로 제공되어야 한다. 포팅 시 이 기능을 사용하고 싶지 않다면 다른기능과 맞물려 있기 때문에 확인해보고 작업을 진행해야 한다. 공유메모리드라이버는 안드로이드에서 커스터마이징을 했고 메모리 할당이 필요하면 전부 ashmem에 있는 라이브러리를 이용한다.
리눅스에서는 각 프로세스마다 우선순위를 주고 메모리가 부족하면 우선순위가 낮은 것을 없앤다. 안드로이드는 프로세스마다 우선순위를 주지 않고 그룹마다 우선순위를 준다. 그 이유는 바인더 때문이다.  메모리 부족 시 우선순위가 낮은 그룹을 제거한다. 안드로이드에는 프로그램 종료 기능이 없다. 화면은 내릴 수 있지만 알아서 프로그램이 제거되기 때문이다. 자바로 응용프로그램을 만들기 때문에 가비지컬렉터가 있다. 종료버튼이 있다 하더라도 UI가 없어질 뿐 그 자체가 바로 종료되는 것은 아니다.
이중 가장 중요한 것은 바인더라고 할 수 있다. 바인더에 의해 안드로이드와 기존 리눅스의 차이가 생긴다. 커널버전 2.6.30이상부터는 커널과 안드로이드가 통합된다는 말이 있는데 2.6.29이전의 커널은 바인더폴트( 메모리 회수에 문제가 있는 버그)가 있었다. 그럼 기존에 나왔던 안드로이드폰들도 이 버그가 있었을까? 아마도 개발인력이 많기 때문에 자체적으로 해결했을 것 같다. 바인더 서비스를 넣으면 기존의 전통적인 커널구조가 객체지향 구조로 바뀐다. 컴포넌트 중심으로 양쪽간 메시지를 주고받는 구조로 바뀐다는 말이다. 이것의 장점은 한쪽이 잘못 되어도 반대쪽까지 잘못되지는 않는다는 점. 응답을 못받거나 결과만 이상하게 나올 뿐이다.
전원관리기능은 기존의 리눅스가 가지고 있는 3단계에 2개를 더해 5단계로 이루어진다. 안드로이드 커널을 설정할때는 자신의 하드웨어에 전원관리 모듈이 없어도 반드시 포함시켜야 한다. 포팅을 하게 되면 시간이 지날수록 이것들에 집중해야 할 것이다.

Binder


바인더는 프로그램과 데이터를 공유하기 위한 것이다. 기존의 IPC는 보안이슈와 오버헤드이슈가 있다. 리눅스에서도 오픈바인더를 쓸 수 있지만 안드로이드에서 더 활성화를 시켜 두었다. 2.6.29에 기존 버그들이 완벽하게 패치되었다. 이전의 방식은 커널을 통해서 데이터를 공유하였다.(커널의 공유메모리 영역에 데이터를 올려두고 시그널등의 이벤트를 받아 메시지 큐 등을 써서 - 세마포어, 뮤텍스, 스레드 등 ). 바인더는 커널쪽의 메인호스팅이 없고 바인드라는 서비스가 있는 것이다. 바인드를 호출하면 응용프로그램과 디바이스쪽에 바인드서비스가 붙어 서로 통신하게 된다. 바인드서비스는 커널이 아니다. 서로다른 서비스간, 응용프로그램간 데이터를 주고받을 때 동작하게 된다. 여기서의 단점은, 기존의 방식은 하나의 매개체가 있고 공유해서 썼지만 바인드는 각각의 서비스마다 바인더가 있어야 하기 때문에 메모리 낭비가 되는 측면이 있다. 그럼에도 불구하고 쓰는 이유는 바인더는 각각 별개로 동작하기 때문에 주고 받는 통신을 가로챔 당할 가능성이 더 낮아지므로 보안성이 더 오르기 때문이다. 메모리 낭비를 줄이기 위해 바인더를 230kb정도의 크기로 최소화 시켰다. 바인더는 원래 PalmOS에 있던 것이기 때문에 라이센스가 있다. 약 3000라인정도 된다.
바인더는 스레드 풀을 유지한다. 드라이버, 매니저 서비스마다 바인더가 양쪽에 붙게 되는데 풀로 관리한다는 것은 요구가 있기 전에 미리 자료구조화 시켜 자원을 확보해 두고 바로 맵핑해서 쓸 수 있도록, 바인드 하는데 시간이 걸리지 않도록 되어 있다.  응용 A가 B 디바이스드라이버와 통신을 하게 되면 채널이 생성되는데 만약 채널안에 연결되는 프로그램이 A 이외에 C도 존재 한다면 B가 종료되었다고 해서 바인더가 종료되면 안된다. 이를 위해 참조계수가 있어서 참조계수가 0이 되면 바인드서비스를 해제한다.

Low Memory Killer
리소스가 부족할 때 자동으로 실행된다. 안드로이드는 그룹 당 우선순위를 주어 해당되는 그룹을 한번에 해제한다. 그룹은 참조계수랑 연관이 있다.  ( 아마도 참조계수가 0 인 것들이 먼저 해제 될듯. )
아래 그림은 Low Memory Killer 소스의 일부분이다. 프로세스가 실행되면 프로세스테이블에 등록되어 관리가 된다. 메모리 부족현상이 생기면 shirink 함수가 실행되어 링크드리스트로 되어있는 프로세스컨트롤 블록들을 끝까지 탐색한다. 제거되어야 할 프로세스가 있으면 SIGKILL을 전송한다.


Power Management
기존 리눅스드라이버의 파워매니저에 계층을 하나 더 올렸다. 보통 핸드폰은 5단계를 사용한다. 키보드-액정-CPU 순으로 전원이 차단되면서 제일 마지막은 D램에만 전원을 공급하는 상태가 된다. 전원은 CPU가 아닌 PMIC라는 전원공급칩이 제어를 한다. D램에만 전원이 공급되는 상태라도 터치스크린은 잠기지 않는다. 이 상태에서 발생하는 인터럽트도 PMIC가 관리한다.

Native Libraries

기존의 라이브러리를 그대로 사용하지 않고 EABI로 빌드된 라이브러리이다. 기존의 리눅스는 PC가 모태이기 때문에, 스마트 디바이스환경을 위해 만들어진 것이 아니기 때문에 arm또는 스마트디바이스에 최적화 되어 있지 않다. 바인더가 있고 자바의 JNI를 통해 C코드와 연결되기 때문에 필수적으로 사용되는 C라이브러리 사이즈를 줄이고 효율을 더 좋게 하였다. 안드로이드의 네이티브 라이브러리는 Bionic Lib이라고 부른다. 임베디드 리눅스쪽 라이브러리를 안드로이드로 가져오면 동작하지 않는다. 대부분이 BSD 라이센스기 때문에 코드공개의 의무가 없다. 그리고 프로세스마다 로드 되므로 작은 사이즈( libc.so 사이즈 231Kb )로 되어 있고 경량 pthread가 구현되어 있다. 모든 Native 코드는 bionic과 함께 컴파일 되어야 한다. 웹킷은 오픈소스 브라우저 기반이다. 애플의 사파리, 노키아의 심비안에 이미 적용되어 성능은 검증되어 있다. 브라우저 속도가 아주 빠른것이 특징이다. HTML 표준을 따르고 Active X는 지원하지 않는다. 미디어프레임워크는 PocketVideo OpenCORE 플랫폼 기반이다. 동영상 디코딩을 하며 표준 Video, Audio, Still-frame 포맷을 지원한다. 이를 이용해 상용제품을 양산할 경우 코덱 라이센스에 대한 비용이 발생할 수 있다. SQLite 는 기본 데이터 베이스이다. Mysql과 거의 유사하다. 위치기반 서비스등을 할 때 유용하게 쓰일 수 있다.
Surface Manager는 모든 응용프로그램의 surface rendering을 프레임버퍼로 전달한다. 프레임버퍼는 LCD와 CPU속도에 차이가 있기 때문에  DRAM 또는 SRAM에 똑같은 구조를 만들어 두고 메모리 블록 전체를 복사해서 한번에 LCD에 출력한다. 이 때 그 메모리 공간을 프레임버퍼라고 한다. 기존의 임베디드리눅스는 2D, 3D를 따로 처리했지만 이 경우에는 동시에 2D, 3D를 처리한다. 화면 합성이나 결합, 반투명 효과등을 한번에 처리할 수 있다. 2D는 단일버퍼로 충분하지만 3D는 데이터량이 많아 단일버퍼로는 병목현상이 생길 수 있기 때문에 프레임버퍼를 더블프레임을 쓴다.  기존의 버퍼 사이즈를 두배로 늘려주면 된다. 더블버퍼링을 2D에도 적용하면 전경그림과 배경그림을 별도로 관리할 수 있어 오버헤드가 줄어든다.

Audio Manager는 오디오 처리를 한다. 오디로 출력 라우팅 기능이 구현되어 있다. 이전에는 OSS를 사용했는데 안드로이드에서 제대로 동작하지 않기 때문에 ALSA를 써야한다. 기본적으로 ALSA는 디폴트 볼륨이 0 으로 설정되어 있기 때문에 테스트를 하기 위해서는 init 부분에서 볼륨설정을 먼저 해줘야 한다.

Android Runtime
 

SUN의 자바는 명령들이 전부 8비트 길이를 가지지만 안드로이드는 4바이트이다. 기존의 SUN은 명령어가 스택에 들어가기 때문에 PUSH,POP명령어를 쓰고 Dalvik은 뱅크드레지스터를 이용한 복사명령을 이용하고 레지스터에서 바로 실행시키기 때문에 속도가 더 빠르다. 4바이트가 레지스터에 전부 들어가기 때문에 낮은사양에서도 느려지지 않는 효과도 있다. 프로그램동작은 자바코드이고 드라이버들은 대부분 C/C++이지만 그 사이에 JNI가 있기때문에 동작이 가능하다. JNI는 자바코드에서 C나 C++  라이브러리를 호출할 수 있도록 만들어진 규약이다. 안드로이드에서 응용프로그램은 C/C++로도 만들 수 있다. 대신 UI를 가지기는 힘들다. 백그라운드 서비스를 제작할 경우 굳이 자바로 할 필요는 없다.

HAL ( Hardware Abstraction Layer )

예전에는 하드웨어 드라이버를 하드웨어 제작자가 만들었지만 요즘은 추상계층을 두어 상위 드라이버나 하위 네이티브 드라이버를 서로 독립적으로 개발할 수 있고 응용프로그램도 독립적으로 동작할 수 있다. 이는 일관된 함수 이름과 형식이 있기때문에 가능하다. 개발자가 구현하기 쉽게 표준화된 API들이 존재하며 모든 제조사가 자신의 컴포넌트를 안드로이드 플랫폼에 넣을 수 있도록 구성되었다. HAL은 라이센스문제를 피하고 안정성을 위해 유저스페이스에 존재한다.

Application Framework
액티비티 매니저는 응용프로그램의 생명주기를 담당한다. 패키지 매니저는 시스템에서 동작중인 응용프로그램들의 정보를 담당한다. 윈도우 매니저는 모든 응용프로그램과 관련된 화면을 담당한다. 뷰 시스템은 표준 위젯을 담당한다. 처음 바탕화면이 위젯이다. 윈도우는 dll 파일이 많지만 안드로이드는 하나의 패키지 파일로 되어있어 프로그램 배포가 쉽다.

Bootup Sequence

리눅스는 기본적으로 init이 가장먼저 실행된다. init.rc 라는 이름의 파일에는 init이 해야할 작업들이 기록되어 있다. 파일시스템 마운팅, 폴더 권한설정, 캐시폴더 삭제, 시작프로그램 동작 등이 기록되어 있다. 우선 데몬을 올린다. 데몬은 init에 의해 리눅스와 같이 시작되었다가 리눅스가 종료될 때 없어지는 프로그램으로서 데몬을 작성하는 규격에 따라 만들어져야 한다. Zygote가 Dalvik을 초기화 한다. C 밑에 있는 기본라이브러리들은 런타임을 통해 실행되고 상위 서비스들은 Dalvik을 통해 실행된다. 이러한 과정들을 위한 설정은 해당하는 config 파일을 수정하면 된다. 어떤 동작들을 바꾸고 싶으면 기본적으로 init.rc를 바꾸면 되고 Zygote를 바꾸고 싶으면 그 설정파일을 바꾸면 된다. 그리고 시스템서버, 서페이스매니저, 오디오매니저들이 올라간다. 그 다음에는 시스템 서비스들이 활성화 된다. 이들은 서비스이므로 서비스매니저에 등록된다.

Bootup Sequence - Zygote
Zygote가 실행되면 시스템 서비스가 활성화 된다. 응용프로그램에서는 android.process.* 을 가지고 접근할 수있다. Zygote와 시스템서버간에는 IPC 소켓으로( 127.0.0.x ) 통신을 한다.

< 부팅 완료 후 각 프로세스들의 상태 >
Android Internals Reference
http://code.google.com/intl/ko/android/
http://groups.google.com/group/android-internals
http://www.android-internals.org/
http://groups.google.com/groups/profile?enc_user=_EKOshMAAADzFnauhYxa0ga8JtF8CI5fWMj6vob75xS36mXc24h6ww
http://groups.google.com/groups/profile?enc_user=lYDbNxEAAAD8uJiqPP7Wd-bc9b1O3waCkdEasx1kiYTQavV7mdW13Q

'old > Basic' 카테고리의 다른 글

Parcelable Object 2  (0) 2010.07.06
Parcelable Object 1  (0) 2010.07.06
JNI (Java Native Interface)  (0) 2010.07.06
http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
how to make sqplite3 table  (0) 2010.02.09
Posted by jazzlife
,

JNI(Java Native Interface)

플랫폼 독립성을 버리고, 기능을 취한다.

자바 프로그램을 작성하다보면, 부득이하게 시스템 의존적 코딩을 필요로 하는 때가 있다. 하지만, 자바로서는 그러한 욕구를 감당할 수는 없다. 따라서, 자바의 클래스 파일이 C/C++의 함수를 호출하여, 그 결과만 되돌려받게 하는 것도 한 방법이다. 그렇지만 이 자체로서 이미 플랫폼 독립적이라는 특성은 사라졌다고 볼 수밖에 없다.

프로그램 작성

첫째 단계, 자바 프로그램 작성

우선, Native접근이 필요한 메쏘드만 native 키워드를 사용해 구현하지 않은 채 그대로 두고, 나머지는 모두 구현한 후, 컴파일한다.

    public class Hello {
        public native void getHello();

        static {
            System.loadLibrary("hello");
        }
       
        public static void main(String[] args) {
            new Hello().getHello();
        }
    }

    javac Hello.java

public native void getHello() : getHello 함수가 native method라는 것을 의미한다.

static {
        System.loadLibrary("hello");
}

이것은 공유 라이브러리를 실행시 로드하는 코드로 반드시 static 초기화에 포함되어야 한다. 이것은 플랫폼에 따라 다르게 행동하는데, 만일 Win32이면, hello.dll을, 솔라리스이면, libhello.so라는 이름의 라이브러리를 찾는다.

둘째 단계, C프로그램을 위한 헤더 파일 생성

javah와 -jni옵션을 이용하여, 헤더파일을 생성해 보자.

    javah -jni Hello

생성된 헤더 파일 Hello.h는 다음과 같다.

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

    /* Header for class Hello */

    #ifndef _Included_Hello
    #define _Included_Hello
    #ifdef __cplusplus
    extern "C" {
    #endif


    /*
     * Class:     Hello
     * Method:    getHello
     * Signature: ()V
     */


    JNIEXPORT void JNICALL Java_Hello_getHello
      (JNIEnv *, jobject);

    #ifdef __cplusplus
    }
    #endif
    #endif

여기서 생성된 함수 이름에 주목해 보자. '_'를 구분자로 크게 몇 부분으로 나뉘어져 있다.

    • Java : 항상 앞에 붙는다.
    • Hello : 클래스의 이름
    • getHello : 메쏘드의 이름

셋째 단계, 함수를 구현한다.

HelloImpl.c라는 이름으로 함수를 구현해 보자.

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

    JNIEXPORT void JNICALL Java_Hello_getHello(JNIEnv *env, jobject obj)
    {
        printf("OOPSLA\n");
        return;
    }

넷째 단계, C파일을 컴파일하여, 공유 라이브러리를 생성한다.

이때, 비주얼 C++을 이용할 경우 다음과 같은 옵션을 준다.

cl /I본인의 자바 디렉토리)\include /I(본인의 자바 디렉토리)\include\win32 -LD HelloImp.c -Fehello.dll

솔라리스에서 컴파일할 경우 다음과 같은 옵션을 준다.

cc -G -I(자바 홈 디렉토리)/include -I(자바 홈 디렉토리)/include/solaris HelloImp.c -o libhello.so

마지막 단계, 프로그램을 실행한다.

    java Hello

실행 결과:

    OOPSLA

자바의 타입과 C++/C의 타입 매핑 문제

네이티브 메쏘드를 호출하는 것까지는 좋다. 하지만, 그 결과값이 있을 경우에는 타입의 괴리(Impedence)가 존재할 수밖에 없다. 따라서, jni.h에는 자바의 타입을 흉내낼 수 있는 여러 가지 타입을 정의해 놓았다.

Primitive Types

네이티브 메쏘드에서는 보통 자바의 타입 이름 앞에 j가 붙은 이름을 가진 타입을 사용함으로써, 마치 자바 프로그램을 작성하는 듯이 프로그램할 수 있다. 예를 들어, boolean은 jni.h에 jboolean이라는 이름의 타입과 매핑된다.

  • jboolean  : 자바의 boolean(8비트) - typedef unsigned char   jboolean
  • jbyte : 자바의 byte(8) - typedef signed char jbyte(WIN32의 경우이다. - System Dependent)
  • jchar  : 자바의 char(16) - typedef unsigned short  jchar
  • jshort : 자바의 short(16) - typedef short  jshort
  • jint : 자바의 int(32) - typedef long jint(WIN32의 경우이다. - System Dependent)
  • jlong : 자바의 long(64) - typedef __int64 jlong(WIN32의 경우이다. - System Dependent)
  • jfloat : 자바의 float(32) - typedef float jfloat
  • jdouble : 자바의 double(64) - typedef double  jdouble;
  • void : 자바의 void

위의 리스트에서, 오른쪽은 실제 jni.h가 이 타입들을 어떻게 정의하고 있는지를 보여 준다. 여기서, jbyte, jint, jlong은 시스템에 따라 적용할 타입이 변경될 소지가 있으므로, WIN32의 경우, 자바디렉토리\include\win32\jni_md.h에 정의되어 있다. 솔라리스인 경우에는 참고로 다음과 같다.

    #ifdef _LP64 /* 64-bit Solaris */
    typedef int jint;
    #else
    typedef long jint;
    #endif
    typedef long long jlong;
    typedef signed char jbyte;

객체 타입

이 경우에도 비슷하다. 일반 객체는 jobject타입으로 통용하고, 나머지 특수한 클래스에 대해 매핑되는 타입을 두기로 한다.

  • jobject : 모든 자바 객체로 뒤에 나열된 모든 타입의 슈퍼 타입이다.
  • jstring : 자바의 java.lang.String클래스이다.
  • jclass : 자바의 java.lang.Class클래스이다.
  • jarray : 자바의 배열이다.
  • jthrowable ; 자바의 java.lang.Throwable 예외 사항이다.

배열

기타, 기본 타입의 배열을 위한 배열 타입도, 하나의 클래스로 정의되어 있다.

    jbooleanArray;
    jbyteArray;
    jcharArray;
    jshortArray;
    jintArray;
    jlongArray;
    jfloatArray;
    jdoubleArray;
    jobjectArray;


함수와 메쏘드간의 인자 전달

문제는 jstring을 비롯한 타입들은 실제 printf문 같은 곳에서 직접 쓰일 수 없다는 것이다. 이것들은 다시, C/C++의 타입으로 변환되어 사용되어야 한다. 이것에 관한 여러 함수들이 JNIEnv라는 구조체에 정의되어 있다.

예를 들어, String객체를 C메쏘드에 넘기도록 앞의 Hello.java를 수정해 보자. 자바 소스는 단순히 선언을 바꾸는 것으로 끝난다.

    public native void getHello(String name);

재컴파일과, javah를 이용해 만든 새로운 헤더 파일을 이용해 HelloImpl.c를 새로 작성해 보면,

    JNIEXPORT void JNICALL Java_Hello_getHello(JNIEnv *env, jobject obj,jstring name)
    {
          char *str_name = (*env)->GetStringUTFChars(env, name, 0);
          printf("Hello %s\n",str_name);
          (*env)->ReleaseStringUTFChars(env, name, str_name);
          return;
    }

이와 같이, JNIEnv의 멤버 함수로부터 도움을 받아야 한다.

이제 각 타입에 대해 인자 전달과 결과값 반환 방법을 알아 보자.

Primitive Types

기본 타입의 경우에는 다음과 같이 기존의 C/C++타입중 호환되는 타입의 재명명이므로, 그다지 인자 전달에 문제가 없다.

    typedef unsigned char   jboolean;
    typedef unsigned short  jchar;
    typedef short           jshort;
    typedef float           jfloat;
    typedef double          jdouble;

    WIN32:
    typedef long jint;
    typedef __int64 jlong;
    typedef signed char jbyte;

다만, boolean의 경우에는 true/false값이 다음과 같이 전달된다.

    #define JNI_FALSE 0
    #define JNI_TRUE 1

예를 들어, boolean타입의 인자를 주고, 반환받는 경우,

    Hello.java:
    Hello h = new Hello();
    System.out.println("h.getBoolean(true) = "+h.getBoolean(true));    // 결과 : true

    HelloImpl.c:
    JNIEXPORT jboolean JNICALL Java_Hello_getBoolean(JNIEnv * env, jobject jobj, jboolean jbool)
    {
           ......
            return JNI_TRUE;
    }

배열 타입

각 기본 타입에 따라 그 배열을 다룰 수 있는 배열 클래스가 제공된다.

    jarray
    jbooleanArray
    jbyteArray
    jcharArray
    jshortArray
    jintArray
    jlongArray
    jfloatArray
    jdoubleArray

모든 배열 클래스들은 jarray 클래스의 하위 클래스이다.

배열의 길이 파악

모든 배열의 길이를 알아낼 수 있는 함수는 다음과 같다.

    jsize GetArrayLength(JNIEnv *, jarray array)

(jsize는 jint와 동일하다)

자바의 배열을 C의 배열로 변환하는 함수

각 타입마다 자바 타입의 배열을 C타입으로 변환시켜 주는 함수가 있다.

  • jboolean * GetBooleanArrayElements(JNIEnv *env,jbooleanArray array, jboolean *isCopy)
  • jbyte * GetByteArrayElements(JNIEnv *env,jbyteArray array, jboolean *isCopy)
  • jchar * GetCharArrayElements(JNIEnv *env,jcharArray array, jboolean *isCopy)
  • jshort * GetShortArrayElements(JNIEnv *env,jshortArray array, jboolean *isCopy)
  • jint * GetIntArrayElements(JNIEnv *env,jintArray array, jboolean *isCopy)
  • jlong * GetLongArrayElements(JNIEnv *env,jlongArray array, jboolean *isCopy)
  • jfloat * GetFloatArrayElements(JNIEnv *env,jfloatArray array, jboolean *isCopy)
  • jdouble * GetDoubleArrayElements(JNIEnv *env,jdoubleArray array, jboolean *isCopy)

정수의 경우를 예로 들어 보자.

       jsize iSize = (*env)->GetArrayLength(env,jiarray);
       jint *iArray = (*env)->GetIntArrayElements(env,jiarray,0);

       for(i=0; i < iSize; i++) {
                printf("iArray[%d] = %d\n",i,iArray[i]);
                iArray[i]++;
       }
       (*env)->ReleaseIntArrayElements(env,jiarray,iArray, 0);

 자바의 배열을 JNI에서 사용할 경우, 이것은 현재의 실행 루틴을 수행하는 쓰레드가 이 배열 객체에 대해 PIN 연산을 수행하거나, 아니면 전체의 로컬 복사본을 사용하게 되는 것이다. 이로 인해, 이 연산의 반대 급부로 반드시 UNPIN연산에 해당하는 ReleaseIntArrayElements가 불려지는 것이 좋다.

  • void ReleaseBooleanArrayElements(JNIEnv *env,jbooleanArray array,  jboolean *elems,  jint mode)
  • void ReleaseByteArrayElements(JNIEnv *env,jbyteArray array, jbyte *elems, jint mode)
  • void ReleaseCharArrayElements(JNIEnv *env,jcharArray array, jchar *elems,  jint mode)
  • void ReleaseShortArrayElements(JNIEnv *env,jshortArray array, jshort *elems,  jint mode)
  • void ReleaseIntArrayElements(JNIEnv *env,jintArray array, jint *elems, jint mode)
  • void ReleaseLongArrayElements(JNIEnv *env,jlongArray array, jlong *elems, jint mode)
  • void ReleaseFloatArrayElements(JNIEnv *env,jfloatArray array, jfloat *elems, jint mode)
  • void ReleaseDoubleArrayElements(JNIEnv *env,jdoubleArray array, jdouble *elems, jint mode)

이 외에도 다음과 같은 함수들이 있다.

  • jdoubleArray NewDoubleArray(jsize len)를 비롯해 각 기본 타입마다 새로운 자바 배열을 생성할 수 있다.
  • void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean *buf)를 비롯해 각 기본 타입마다 Sub배열을 획득할 수 있는 함수가 제공된다.

자바 객체의 사용

네이티브 코드 내에서 자바 객체를 사용할 수도 있다. 즉 콜백과 비슷한 형식을 지원한다. private영역까지 접근할 수 있다.

자바 객체의 클래스 정보 얻기

일단, 인자로 넘겨지는 자바 객체에 대한 클래스 정보를 획득하는 함수가 제공된다.

    jclass cls = (*env)->GetObjectClass(env, jobj);

여기서 주의할 것은 반환값으로 돌아오는 cls의 레퍼런스값은 오직 이 네이티브 메쏘드가 수행이 끝날 동안에만 유효하다는 것이다.

자바 객체 및 클래스 타입의 래퍼런스는 네이티브 메쏘드 실행시마다 결정되므로, 한 번 실행해 얻은 레퍼런스를 다음번 메쏘드에서 다시 사용하려는 것은 잘못된 것이다.

따라서, 원칙적으로 객체 레퍼런스 변수를 전역 변수로 할당하는 것은 위험한 일이다. 하지만, 꼭 허용되지 않는 것은 아니다. 전역 레퍼런스로 지정해 줄 수 있는 함수가 있다.

     jobject NewGlobalRef(JNI_Env* env, jobject lobj)



네이티브 코드를 호출한 객체의 메쏘드에 대한 ID얻기

객체의 메쏘드를 호출하기 위해 그 메쏘드의 ID를 획득해야 한다. 이것은 메쏘드의 이름과 시그너쳐를 이용하는데, 이름은 별 무리가 없겠으나, 시그너쳐는 까다롭다.

시그너쳐는 메쏘드의 인자 리스트와 반환 타입 정보를 스트링화한 것으로, 다음과 같은 규칙이 있다.

  • 시그너쳐 : "(인자 리스트)반환값"으로 이루어진 스트링이다.
  • 인자리스트의 인자들은 아래의 타입 기호에 따라 표기되며, 서로 ';'으로 구분된다.
  • 반환값 역시 타입 기호에 따라 표시된다.

타입 기호는 다음과 같다. 이것은 자바 가상 머신 스펙에서 클래스 파일 포맷 부분에 등장하는 기호와 동일하다.

    Z :  boolean  
    B :  byte
    C :  char
    S :  short
    I  :  int
    J :  long
    F :  float
    D : double

  • 이들의 배열은 이들의 타입 기호 앞에 '['를 붙인다. 즉 int 배열은 [I가 된다.
  • 클래스 타입의 경우, L뒤에 클래스명을 패키지명까지 포함해 나열하면 된다. 즉, String클래스인 경우, Ljava.lang.String이다.

이것은 수작업으로 할 경우, 실수가 많을 수 있으나, javap를 이용해, 쉽게 기호화된 시그너쳐를 확인할 수 있다.

    javap -s -p Hello

    Compiled from Hello.java
    class Hello extends java.lang.Object {
        static {};
            /*   ()V   */
        Hello();
            /*   ()V   */
        public static void coo(java.lang.String, java.lang.String);
            /*   (Ljava/lang/String;Ljava/lang/String;)V   */
        private void foo(java.lang.String, java.lang.String);
            /*   (Ljava/lang/String;Ljava/lang/String;)V   */
        public native boolean getBoolean(boolean);
            /*   (Z)Z   */
        public native byte getByte(byte);
            /*   (B)B   */
        public native char getChar(char);
            /*   (C)C   */
        public native double getDouble(double);
            /*   (D)D   */
        public native float getFloat(float);
            /*   (F)F   */
        public native java.lang.String getHello(java.lang.String);
            /*   (Ljava/lang/String;)Ljava/lang/String;   */
        public native int getInt(int);
            /*   (I)I   */
        public native int getIntArray(int[])[];
            /*   ([I)[I   */
        public native long getLong(long);
            /*   (J)J   */
        public native short getShort(short);
            /*   (S)S   */
        public static void main(java.lang.String[]);
            /*   ([Ljava/lang/String;)V   */
    }

이것은 다음과 같이 Hello클래스에 두 메쏘드를 추가한 후의 javap결과이다.

       private void foo(String str,String str1) {
            System.out.println("Hello.foo() = "+str+str1);
        }

        public static void coo(String str,String str1) {
            System.out.println("Hello.coo() = "+str+str1);
        }

이제 foo메쏘드의 ID를 획득해 보자.

    // 클래스 정보 획득
    jclass cls = (*env)->GetObjectClass(env, obj);

    // 메쏘드 ID
    jmethodID fooM = (*env)->GetMethodID(env, cls, "foo", "(Ljava/lang/String;Ljava/lang/String;)V");

    // 그런 메쏘드가 없으면, 0반환
    if(fooM == 0) {
        printf("Method foo isn't found\n");
    }
    else {
               ......
    } 


인자 생성 및 메쏘드 호출

이제 메쏘드를 호출하는 일이 다음 순서가 되겠다. 물론 여기에 해당되는 함수들이 제공되는데, 이 함수들을 통해, 클래스 내의 메쏘드를 호출하므로, 메쏘드 ID와 더불어 메쏘드의 인자들을 리스트화해서 보내야 한다. 두 가지 방법이 있다.

  • C의 va_list구조체를 이용한다.
  • jvalue 유니온를 이용한다.

여기서는 jvalue 유니온을 이용해 보자. 그 구조는 다음과 같다.

    typedef union jvalue {
        jboolean z;
        jbyte    b;
        jchar    c;
        jshort   s;
        jint     i;
        jlong    j;
        jfloat   f;
        jdouble  d;
        jobject  l;
    } jvalue;

그러면, 스트링 두 개를 인자로 받는  foo메쏘드를 호출해 보자. String은 객체이므로, l에 스트링 객체의 레퍼런스를 할당하면 된다.

    jvalue* args;
    .......

    args = (jvalue*)malloc(sizeof(jvalue)*2);
    args[0].l = (*env)->NewStringUTF(env,"oops");
    args[1].l = (*env)->NewStringUTF(env,"la");
    (*env)->CallVoidMethodA(env, obj, fooM,args );   // JNIEnv, jobject, Method ID, jvalue

여기에서는 foo가 void 타입이므로,  CallVoidMethodA가 쓰였지만, 이 외에에도 반환값에 따라 여러 함수가 제공된다. 이들의 인자는 위와 동일하다.

  • jboolean CallBooleanMethodA
  • jbyte CallByteMethodA
  • jchar CallCharMethodA
  •  jshort CallShortMethodA
  •  jint CallIntMethodA
  •  jfloat CallFloatMethodA
  •  jdouble CallDoubleMethodA

참고로, va_list를 이용하여 인자를 전달하는 함수들은 A로 끝나지 않고, V로 끝나는데, 역시 마지막 인자만 va_list타입으로 수정해 주면 된다.


정적 메쏘드 호출

정적 메쏘드 또한 호출할 수 있는 함수가 따로 있다.

메쏘드 ID : GetStaticMethodID함수 이용

    jMethodID cooM = (*env)->GetStaticMethodID(env, cls, "coo", "(Ljava/lang/String;Ljava/lang/String;)V");
    if(cooM == 0) {
            printf("Method foo isn't found\n");
        }
    else {  .....
    }

인자 형성 방법은 동일하다.

메쏘드 호출 : CallStaticVoidMethodA 이용

    cooM = (*env)->GetStaticMethodID(env, cls, "coo", "(Ljava/lang/String;Ljava/lang/String;)V");
    if(cooM == 0) {
            printf("Method foo isn't found\n");
    }
    else {
            args = (jvalue*)malloc(sizeof(jvalue)*2);
            args[0].l = (*env)->NewStringUTF(env,"papa");
            args[1].l = (*env)->NewStringUTF(env,"ya");
            (*env)->CallStaticVoidMethodA(env, cls, cooM,args );
    }

 여기서 주의할 것은 CallStaticVoidMethodA의 두 번째 인자는 jobject타입이 아닌, jclass타입이란 것이다. 클래스 메쏘드이니 당연하기도 하다.


상위 클래스의 메쏘드 호출

한 걸음 더 나아가 상위 클래스의 메쏘드도 호출할 수 있다. 여기에 해당되는 메쏘드는 모두 NonVirtual이라는 이름을 포함하고 있는데, c++을 배운 이라면, NonVirtual의 의미를 알고 있을 것이다.

상위 클래스 타입의 jclass 객체 획득

    jclass sClass = (*env)->GetSuperclass(env,cls);

메쏘드 ID : GetMethodID함수 이용

    superFooM = (*env)->GetMethodID(env, sClass, "foo", "(Ljava/lang/String;Ljava/lang/String;)V");
    if(superFooM == 0) {
          printf("Method foo isn't found\n");
    }
    else {
           .....
    }

 메쏘드를 호출: CallNonvirtualVoidMethodA

    args = (jvalue*)malloc(sizeof(jvalue)*2);
    args[0].l = (*env)->NewStringUTF(env,"can");
    args[1].l = (*env)->NewStringUTF(env,"dy");
     (*env)->CallNonvirtualVoidMethodA(env,obj,sClass ,superFooM,args );

멤버 필드 접근

 필드를 접근하는 방법 또한 메쏘드를 접근하는 방법과 크게 다르지 않다. 필드가 Private일지라도 접근할 수 있다. 이 또한 몇 가지 함수에 의존하고 있는데, 그 함수의 형식이 메쏘드를 호출하는 함수와 거의 유사하기 때문이다. 일단 다음의 기호를 잊지 말자.

    Z :  boolean  
    B :  byte
    C :  char
    S :  short
    I  :  int
    J :  long
    F :  float
    D : double

  • 이들의 배열은 이들의 타입 기호 앞에 '['를 붙인다. 즉 int 배열은 [I가 된다.
  • 클래스 타입의 경우, L뒤에 클래스명을 패키지명까지 포함해 나열하면 된다. 즉, String클래스인 경우, Ljava.lang.String이다.

 이제 Hello.java에 몇 가지 필드를 추가해 보자.

    private int intVal;
    private static int iStaticIntVal;
    private String strVal = "Hello";

이를 재컴파일 후, javah를 실행하고, 새로이 생긴 함수의 선언부를 HelloImpl.c로 복사해 이것에 대해 추가적으로 구현해 보기로 하자.

필드를 접근하는 것은 두 단계로 이루어진다.

  • 클래스와 필드의 이름과 타입을 이용해 필드의 ID를 얻어낸다.
  • 필드의 ID와 객체를 이용해 실제 필드의 값을 얻어낸다.

인스턴스 필드 접근

먼저 필드의 ID를 얻어내기 위한 함수는 다음과 같다.

     jfieldID GetFieldID(JNI_Env* env,jclass clazz, const char *name, const char *sig)

그리고, 인스턴스 필드를 접근하는 함수들은 다음과 같다.

1) Read 함수
  • jobject GetObjectField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jboolean GetBooleanField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jbyte GetByteField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jchar GetCharField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jshort GetShortField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jint GetIntField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jlong GetLongField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jfloat GetFloatField(JNI_Env* env,jobject obj, jfieldID fieldID)
  • jdouble GetDoubleField(JNI_Env* env,jobject obj, jfieldID fieldID)
2) Write 함수
  • void SetObjectField(JNI_Env* env,jobject obj, jfieldID fieldID, jobject val)
  • void SetBooleanField(JNI_Env* env,jobject obj, jfieldID fieldID, jboolean val)
  • void SetByteField(JNI_Env* env,jobject obj, jfieldID fieldID,jbyte val)
  • void SetCharField(JNI_Env* env,jobject obj, jfieldID fieldID, jchar val)  
  • void SetShortField(JNI_Env* env,jobject obj, jfieldID fieldID,jshort val)
  • void SetIntField(JNI_Env* env,jobject obj, jfieldID fieldID,jint val)
  • void SetLongField(JNI_Env* env,jobject obj, jfieldID fieldID, jlong val)
  • void SetFloatField(JNI_Env* env,jobject obj, jfieldID fieldID, jfloat val)
  • void SetDoubleField(JNI_Env* env,jobject obj, jfieldID fieldID, jdouble val)

클래스 필드 접근

Static 필드를 접근하기 위해 먼저 ID를 얻어내야 한다.

    jfieldID GetStaticFieldID(JNI_Env* env,jclass clazz, const char *name,  const char *sig)

그리고, 필드를 접근하는 함수들은 다음과 같다.

Gettor 함수
  • jobject GetStaticObjectField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jboolean GetStaticBooleanField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jbyte GetStaticByteField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jchar GetStaticCharField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jshort GetStaticShortField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jint GetStaticIntField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jlong GetStaticLongField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jfloat GetStaticFloatField(JNI_Env* env,jclass clazz, jfieldID fieldID)
  • jdouble GetStaticDoubleField(JNI_Env* env,jclass clazz, jfieldID fieldID)
Settor 함수
  • void SetStaticObjectField(JNI_Env* env,jclass clazz, jfieldID fieldID, jobject value)
  • void SetStaticBooleanField(JNI_Env* env,jclass clazz, jfieldID fieldID, jint value)
  • void SetStaticByteField(JNI_Env* env,jclass clazz, jfieldID fieldID, jbyte value)
  • void SetStaticCharField(JNI_Env* env,jclass clazz, jfieldID fieldID, jchar value)
  • void SetStaticShortField(JNI_Env* env,jclass clazz, jfieldID fieldID, jshort value)
  • void SetStaticIntField(JNI_Env* env,jclass clazz, jfieldID fieldID, jint value)
  • void SetStaticLongField(JNI_Env* env,jclass clazz, jfieldID fieldID, jlong value)
  • void SetStaticFloatField(JNI_Env* env,jclass clazz, jfieldID fieldID, jfloat value)
  • void SetStaticDoubleField(JNI_Env* env,jclass clazz, jfieldID fieldID, jdouble value)

예제

이제 이들을 사용해 Hello 클래스의 필드들을 접근해 보기로 하자. Hello.java에 native메쏘드인 다음을 추가하고 컴파일 후, HelloImpl.c에서, 다음을 중심으로 코딩해 보자.

    public native void testFieldAccess()

정적 변수 접근

정적 필드인 iStaticIntVal 필드를 접근하여, 값을 읽어들인 후, 다시 2를 더해 값을 써 보자.

    jfieldID jFieldId;
    jint iStaticIntVal;
    jclass cls = (*env)->GetObjectClass(env, jobj);

    jFieldId = (*env)->GetStaticFieldID(env,cls,"iStaticIntVal","I");
    if(jFieldId == 0) {
                    printf("Field iStaticIntVal not Found in Hello class\n");
                    return;
    }

    //  값을 읽어들인 후,
    iStaticIntVal = (*env)->GetStaticIntField(env,cls,jFieldId);

    // 2를 더해, 다시 그 값을 쓴다.
    (*env)->SetStaticIntField(env,cls,jFieldId,(iStaticIntVal+2));  

인스턴스 변수 접근

    jint iVal;
    jfieldID jFieldId;
    jclass cls = (*env)->GetObjectClass(env, jobj);
    jFieldId = (*env)->GetFieldID(env,cls,"intVal","I");
    if(jFieldId == 0) {
                    printf("Field intVal not Found in Hello class\n");
                    return;
    }
    iVal = (*env)->GetIntField(env,jobj,jFieldId);
    printf("iVal in C = %d\n",iVal);
    (*env)->SetIntField(env,jobj,jFieldId,iVal);

그런데, 실제로 int를 가지고 실험해 본 결과, C에서 값을 잘 넘겨받는 것까지는 좋은데, 다시 자바로 값을 써 줄 때, 인자가 잘 전달되지 않는 현상을 보였다. 좀더 생각해 볼 문제이다.

스트링 접근

스트링은 객체이므로 객체를 접근하는 방식을 통해 접근해야 한다. 우선 GetFieldID 함수의 인자로 "Ljava/lang/String;"을 주목하면, 객체의 접근 방식과 동일함을 알 수 있다. 여기에서 세미콜론(;)을 빠뜨리면, 인식하지 못하므로 주의하기 바란다.

    jstring jstr;
    jfieldID jFieldId;
    jclass cls = (*env)->GetObjectClass(env, jobj);

    jFieldId = (*env)->GetFieldID(env,cls,"strVal","Ljava/lang/String;");
    if(jFieldId == 0) {
                    printf("Field strVal not Found in Hello class\n");
                    return;
    }

    jstr = (*env)->GetObjectField(env,jobj,jFieldId);
    (*env)->SetStaticIntField(env,cls,jFieldId,(iStaticIntVal+2));  
    str = (*env)->GetStringUTFChars(env, jstr, 0);
    printf("Get String : %s\n",str);

    새로운 스트링을 생성하여, 이것의 레퍼런스값을 str 필드에 할당하려면, 다음과 같이 한다.
    jstr = (*env)->NewStringUTF(env, "123");
    (*env)->SetObjectField(env, jobj, jFieldId, jstr);

기타 객체를 호출하는 방법도 스트링과 거의 동일하다. (*env)->GetObjectField(env,jobj,jFieldId);를 통해, jobject타입의 객체를 얻어낸 후, 필요에 따라 이 객체의 메쏘드를 호출하면 된다.


예외 사항

예외 사항 던지기

이제 예외 사항을 던지는 방법을 알아보자.  예외 사항들은 각각 클래스로 되어 있으므로, 이들의 클래스 정보를 얻어낸 후, 다음의 함수를 이용하면 된다.

     jint ThrowNew(JNI_Env* env, jclass clazz, const char *msg)

Hello.java에서 testFieldAccess가 java.io.IOException을 던지도록 수정해 보자.

    public native void testFieldAccess() throws java.io.IOException;

    javac Hello.java
    javah -jni Hello

 이제 새로이 HelloImpl.c에서, testFieldAccess의 끝에 다음을 추가해 보자.

       jclass eCls;

       eCls = (*env)->FindClass(env, "java/io/IOException");
        if(eCls != 0) {
                (*env)->ThrowNew(env, eCls, "IOException from C");
        }

예외 사항 감지하기

JNI의 C코드에서, 자바의 메쏘드를 호출했을 때.예외 사항이 돌아올 수 있다. 이 때, 감지하는 데 사용되는 함수는 다음과 같다.

    jthrowable ExceptionOccurred(JNIEnv* env)

이것은 jthrowable타입의 예외 사항 객체를 반환해 준다. 그 예외 사항에 정보를 출력하는 데 사용되는 함수도 제공된다.

    void ExceptionDescribe(JNI_Env* env)

그리고, 예외 사항이 처리된 후, 다시 예외 사항이 없는 것처럼 하려면, 다음과 같은 함수를 수행한다.

    void ExceptionClear(JNI_Env* env);

이제 Hello.java에 새로운 메쏘드를 추가해 보자.

    public void returnException() throws java.io.IOException {
            throw new java.io.IOException("IOException from Java");
    }

이 메쏘드를 C에서 호출하면, 예외 사항이 그쪽으로 반환될 것이다. 이것을 처리하는 코드는 다음과 같다.

           jmethodID jmId;
           jthrowable jexcp;

           jmId = (*env)->GetMethodID(env,cls,"returnException","()V");
            if(jmId == 0) {
                    printf("Method returnException not found\n");
            }
            else {
                    (*env)->CallVoidMethod(env,jobj,jmId);
                    jexcp = (*env)->ExceptionOccurred(env);  
                    if(jexcp) {
                            (*env)->ExceptionDescribe(env);   // 예외 사항 정보 출력
                            (*env)->ExceptionClear(env);
                    }
            }

가비지 콜렉션

강제 레퍼런스 해제

원래 네이티브 메쏘드 내의 로컬 변수들이나 전역 변수들이 각각 지니고 있는 자바 객체에 대한 레퍼런스는 네이티브 메쏘드가 끝난 후에야 비로소 없어진다. 따라서, 네이티브 메쏘드가 오랜 시간동안 수행될 경우, 초반에만 실질적으로 사용될 뿐, 후반부에서는 쓰이지 않는 객체의 레퍼런스조차 메쏘드 전체가 수행이 끝날 때까지 남아있게 되는 셈이다. 이때 강제로 레퍼런스를 무효화시킬 수 있는 방법이 있다. 다음의 함수를 사용하면 된다.

  • void DeleteLocalRef(JNI_Env* env,jobject obj)   // 지역 변수
  • void DeleteGlobalRef(JNI_Env* env,jobject gref)  // 전역 변수

동기화

synchronized블록과 같은 동기화 블록을 C 코드내에서 지정해야 할 때가 있다. 바로 쓰레드에 의해 네이티브 메쏘드가 호출되었을 때, 이 메쏘드가 공유 변수들을 접근할 수 있기 때문이다. 그러한 용도로 사용되는 함수는 다음과 같다.

  • (*env)->MonitorEnter(JNI_Env* env,jobject obj);
  • (*env)->MonitorExit(JNI_Env* env, jobject obj);

 이 두 메쏘드 사이에 있는 코드들은 쓰레드들간에 동기화된다.

'old > Basic' 카테고리의 다른 글

Parcelable Object 1  (0) 2010.07.06
안드로이드 기본 개념  (0) 2010.07.06
http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
how to make sqplite3 table  (0) 2010.02.09
What is Android?  (0) 2010.02.04
Posted by jazzlife
,
http://mc500.tistory.com/entry/Goole-Android-Build-해-보기-1

'old > Basic' 카테고리의 다른 글

Parcelable Object 1  (0) 2010.07.06
안드로이드 기본 개념  (0) 2010.07.06
JNI (Java Native Interface)  (0) 2010.07.06
how to make sqplite3 table  (0) 2010.02.09
What is Android?  (0) 2010.02.04
Posted by jazzlife
,

[지원 형식]
INTEGER, REAL, TEXT, BLOB

[테이블 만들기]
CREATE TABLE Students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fname TEXT NOT NULL,
lname TEXT NOT NULL );

CREATE TABLE Tests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
testname TEXT,
weight REAL DEFAULT .10 CHECK (weight<=1));

[자료삽입]
INSERT into Students
(fname, lname)
VALUES
('Harry', 'Potter');

INSERT into Tests
(testname, weight)
VALUES
('YourW', .25);

[테이블 조회]
SELECT * FROM Tests;
집계확인
SELECT SUM(weight) FROM Tests;

[조회 결과를 조합하여 출력]
SELECT fname||' '|| lname AS fullname, id FROM Students;

[외부키와 복합키가 있는 테이블 만들기]
CREATE TABLE TestResults (
studentsid INTEGER REFERENCES Students(id),
testid INTEGER REFERENCES Tests(id),
score INTEGER CHECK (score<=100 AND score>=0),
PRIMARY KEY (studentsid, testid));

출력해보기
SELECT studentid, testid, weig FROM TestResults;

[수정하기]
UPDATE TestResults
SET score=50
WHERE studentid=2 AND testid=2;

[지우기]
DELETE FROM TestResults WHERE studentid=2 AND testid=2;
*WHERE로 지정하지 않으면 테이블 전체가 삭제되니 주의

[다중 테이블 질의]
SELECT
Students.fname||' '|| Students.lname AS StudentName,
Tests.testname,
TestResults.score
FROM TestResults
JOIN Students
                   ON (TestResults.studentid=Students.id)

JOIN Tests

                   ON (TestResults.testid=Test.id)

WHERE testid=6;

[계산식 추가]
SELECT
Students.fname||' '|| Students.lname AS StudentName,
SUM((Tests.weight*TestResults.score)) AS TotalWeightedScore
FROM TestResults
JOIN Students
                   ON (TestResults.studentid=Students.id)

JOIN Tests

                   ON (TestResults.testid=Test.id)

WHERE testid=6;


[필터링과 정렬]
SELECT
Students.fname||' '|| Students.lname AS StudentName,
SUM(Tests.weight*TestResults.score) AS TotalWeightedScore
FROM TestResults
JOIN Students
                   ON (TestResults.studentid=Students.id)

JOIN Tests

                   ON (TestResults.testid=Test.id)

GROUP BY TestResults.studentid
ORDER BY TotalWeightedScore DESC;

[하위 질의 활용]
SELECT
Students.fname||' '|| Students.lname AS StudentName,
Students.id AS StudentID,
(SELECT COUNT(*)
FROM TestResults
WHERE TestResults.studentid=Students.id
AND TestResults.score>60)
AS TestsPassed
FROM Students;

[테이블 삭제]
DROP TABLE TestResults;

'old > Basic' 카테고리의 다른 글

Parcelable Object 1  (0) 2010.07.06
안드로이드 기본 개념  (0) 2010.07.06
JNI (Java Native Interface)  (0) 2010.07.06
http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
What is Android?  (0) 2010.02.04
Posted by jazzlife
,

What is Android?

old/Basic 2010. 2. 4. 16:02

What is Android?

Android is a software stack for mobile devices that includes an operating system, middleware and key applications. The Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.


Features

  • Application framework enabling reuse and replacement of components
  • Dalvik virtual machine optimized for mobile devices
  • Integrated browser based on the open source WebKit engine
  • Optimized graphics powered by a custom 2D graphics library; 3D graphics based on the OpenGL ES 1.0 specification (hardware acceleration optional)
  • SQLite for structured data storage
  • Media support for common audio, video, and still image formats (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF)
  • GSM Telephony (hardware dependent)
  • Bluetooth, EDGE, 3G, and WiFi (hardware dependent)
  • Camera, GPS, compass, and accelerometer (hardware dependent)
  • Rich development environment including a device emulator, tools for debugging, memory and performance profiling, and a plugin for the Eclipse IDE


Android Architecture

The following diagram shows the major components of the Android operating system. Each section is described in more detail below.

Android System Architecture


Applications

Android will ship with a set of core applications including an email client, SMS program, calendar, maps, browser, contacts, and others. All applications are written using the Java programming language.


Application Framework

By providing an open development platform, Android offers developers the ability to build extremely rich and innovative applications. Developers are free to take advantage of the device hardware, access location information, run background services, set alarms, add notifications to the status bar, and much, much more.
 

Developers have full access to the same framework APIs used by the core applications. The application architecture is designed to simplify the reuse of components; any application can publish its capabilities and any other application may then make use of those capabilities (subject to security constraints enforced by the framework). This same mechanism allows components to be replaced by the user.


Underlying all applications is a set of services and systems, including:


  • A rich and extensible set of Views that can be used to build an application, including lists, grids, text boxes, buttons, and even an embeddable web browser
  • Content Providers that enable applications to access data from other applications (such as Contacts), or to share their own data
  • A Resource Manager, providing access to non-code resources such as localized strings, graphics, and layout files
  • A Notification Manager that enables all applications to display custom alerts in the status bar
  • An Activity Manager that manages the lifecycle of applications and provides a common navigation backstack


For more details and a walkthrough of an application, see the Notepad Tutorial.


Libraries

Android includes a set of C/C++ libraries used by various components of the Android system. These capabilities are exposed to developers through the Android application framework. Some of the core libraries are listed below:


  • System C library - a BSD-derived implementation of the standard C system library (libc), tuned for embedded Linux-based devices
  • Media Libraries - based on PacketVideo's OpenCORE; the libraries support playback and recording of many popular audio and video formats, as well as static image files, including MPEG4, H.264, MP3, AAC, AMR, JPG, and PNG
  • Surface Manager - manages access to the display subsystem and seamlessly composites 2D and 3D graphic layers from multiple applications
  • LibWebCore - a modern web browser engine which powers both the Android browser and an embeddable web view
  • SGL - the underlying 2D graphics engine
  • 3D libraries - an implementation based on OpenGL ES 1.0 APIs; the libraries use either hardware 3D acceleration (where available) or the included, highly optimized 3D software rasterizer
  • FreeType - bitmap and vector font rendering
  • SQLite - a powerful and lightweight relational database engine available to all applications


Android Runtime

Android includes a set of core libraries that provides most of the functionality available in the core libraries of the Java programming language.


Every Android application runs in its own process, with its own instance of the Dalvik virtual machine. Dalvik has been written so that a device can run multiple VMs efficiently. The Dalvik VM executes files in the Dalvik Executable (.dex) format which is optimized for minimal memory footprint. The VM is register-based, and runs classes compiled by a Java language compiler that have been transformed into the .dex format by the included "dx" tool.


The Dalvik VM relies on the Linux kernel for underlying functionality such as threading and low-level memory management.


Linux Kernel

Android relies on Linux version 2.6 for core system services such as security, memory management, process management, network stack, and driver model. The kernel also acts as an abstraction layer between the hardware and the rest of the software stack.



EDGE 란 Enhanced Data rates for GSM Evolution 의 약자,

2.5세대 무선 접속 규격으로 GSM 과 구성이나 스펙트럼, frame 구조는 같으나 높은 data rates 를 지원.

휴대전화, PDA, 기타 무선 인터넷 장치를 이용하여, 전화통화와 웹서핑을 즐기며 비디오·오디오·멀티미디어

컨텐츠를 다운받아 재생할 수 있음

EDGE 표준은 보통 무선 서비스보다 세 배 넓은 대역폭인 473.6 kbps의 레이트로 데이터를 전달할 수 있게 고안


EDGE

Symbol Rate - 270.833 kHz
Bits per symbol - 3
Modulation format - 3pi/8-rotating 8PSK
Baseband filter - "linearized" Gaussian
Envelope - Non-constant amplitude 


'old > Basic' 카테고리의 다른 글

Parcelable Object 1  (0) 2010.07.06
안드로이드 기본 개념  (0) 2010.07.06
JNI (Java Native Interface)  (0) 2010.07.06
http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
how to make sqplite3 table  (0) 2010.02.09
Posted by jazzlife
,