안드로이드 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
,

Shape

old/UI Design 2010. 8. 10. 17:10

1.Linear Gradient


<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient android:startColor="#FFFF00" android:endColor="#FFFFFF" android:angle="270"/>
    <corners android:radius="0dp" />
</shape>


2.Radial Gradient


<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <gradient android:type="radial"
        android:startColor="#ff0000"
        android:endColor="#ffff00"
        android:gradientRadius="300"
        android:centerX="0.5"
        android:centerY="0.7"/>
</shape>



 

3.Line


<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="line">
    <stroke android:width="1dp" android:color="#FF000000"
            android:dashWidth="1dp" android:dashGap="2dp" />
    <size android:height="5dp" />
</shape>


4.Oval

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:color="#00000000"/>
    <stroke android:width="4dp" android:color="#990000FF"
            android:dashWidth="4dp" android:dashGap="2dp" />
    <padding android:left="7dp" android:top="7dp"
            android:right="7dp" android:bottom="7dp" />
    <corners android:radius="4dp" />
</shape>


5.Ring & Sweet Gradient

<shape android:shape="ring" xmlns:android="http://schemas.android.com/apk/res/android"
    android:innerRadiusRatio="3"
    android:thicknessRatio="8"
    android:useLevel="false">
    <size android:width="48dip"
        android:height="48dip" />
    <gradient android:type="sweep"
        android:useLevel="false"
        android:startColor="#4c737373"
        android:centerColor="#4c737373"
        android:centerY="0.50"
        android:endColor="#ffffd300" />
</shape>

'old > UI Design' 카테고리의 다른 글

shape layout  (0) 2010.08.04
cutom title bar  (0) 2010.08.04
adb shell에서 특정 activity실행하기  (0) 2010.07.06
PowerManager에서 Screen ON/OFF 통지  (0) 2010.07.06
Resources - Menus, Searchable  (0) 2010.07.01
Posted by jazzlife
,

안드로이드 참고자료

etc 2010. 7. 8. 17:26
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
,

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.iftitah.android.contact">
  <application android:icon="@drawable/icon">
   <activity class=".Contact" android:label="@string/app_name">
    <intent-filter>
    <action android:value="android.intent.action.MAIN" />
    <category android:value="android.intent.category.LAUNCHER" />
   </intent-filter>
  </activity>
 </application>
.
.
</manifest>

위와 같이 activity가 정의되어 있을 때... 아래와 같이 실행하면 됨

am start -a android.intent.action.MAIN -n
com.iftitah.android.contact/com.iftitah.android.contact.Contact

'old > UI Design' 카테고리의 다른 글

shape layout  (0) 2010.08.04
cutom title bar  (0) 2010.08.04
PowerManager에서 Screen ON/OFF 통지  (0) 2010.07.06
Resources - Menus, Searchable  (0) 2010.07.01
Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
Posted by jazzlife
,
PowerManager는 Screen을 On/Off할때 WindowManager에 통지를 하고 이는 KeyguardViewMediator를 통해 LockScreen에도 전달된다.

PowerManagerService.mNotificationTask
    -> WindowManagerPolicy.screenTurnedOn()
        -> KeyguardViewMediator.onScreenTurnedOn();
    -> WindowManagerPolicy.screenTurnedOff()
        -> KeyguardViewMediator.onScreenTurnedOff();

'old > UI Design' 카테고리의 다른 글

cutom title bar  (0) 2010.08.04
adb shell에서 특정 activity실행하기  (0) 2010.07.06
Resources - Menus, Searchable  (0) 2010.07.01
Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
String & StyledText  (0) 2010.06.30
Posted by jazzlife
,

Command Line Shell For SQLite

The SQLite library includes a simple command-line utility named sqlite3 (or sqlite3.exe on windows) that allows the user to manually enter and execute SQL commands against an SQLite database. This document provides a brief introduction on how to use the sqlite3 program.

Getting Started

To start the sqlite3 program, just type "sqlite3" followed by the name the file that holds the SQLite database. If the file does not exist, a new one is created automatically. The sqlite3 program will then prompt you to enter SQL. Type in SQL statements (terminated by a semicolon), press "Enter" and the SQL will be executed.

For example, to create a new SQLite database named "ex1" with a single table named "tbl1", you might do this:

$ sqlite3 ex1
SQLite version 3.6.11
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table tbl1(one varchar(10), two smallint);
sqlite> insert into tbl1 values('hello!',10);
sqlite> insert into tbl1 values('goodbye', 20);
sqlite> select * from tbl1;
hello!|10
goodbye|20
sqlite>

You can terminate the sqlite3 program by typing your systems End-Of-File character (usually a Control-D). Use the interrupt character (usually a Control-C) to stop a long-running SQL statement.

Make sure you type a semicolon at the end of each SQL command! The sqlite3 program looks for a semicolon to know when your SQL command is complete. If you omit the semicolon, sqlite3 will give you a continuation prompt and wait for you to enter more text to be added to the current SQL command. This feature allows you to enter SQL commands that span multiple lines. For example:

sqlite> CREATE TABLE tbl2 (
   ...>   f1 varchar(30) primary key,
   ...>   f2 text,
   ...>   f3 real
   ...> );
sqlite>

Aside: Querying the SQLITE_MASTER table

The database schema in an SQLite database is stored in a special table named "sqlite_master". You can execute "SELECT" statements against the special sqlite_master table just like any other table in an SQLite database. For example:

$ sqlite3 ex1
SQLite vresion 3.6.11
Enter ".help" for instructions
sqlite> select * from sqlite_master;
    type = table
    name = tbl1
tbl_name = tbl1
rootpage = 3
     sql = create table tbl1(one varchar(10), two smallint)
sqlite>

But you cannot execute DROP TABLE, UPDATE, INSERT or DELETE against the sqlite_master table. The sqlite_master table is updated automatically as you create or drop tables and indices from the database. You can not make manual changes to the sqlite_master table.

The schema for TEMPORARY tables is not stored in the "sqlite_master" table since TEMPORARY tables are not visible to applications other than the application that created the table. The schema for TEMPORARY tables is stored in another special table named "sqlite_temp_master". The "sqlite_temp_master" table is temporary itself.

Special commands to sqlite3

Most of the time, sqlite3 just reads lines of input and passes them on to the SQLite library for execution. But if an input line begins with a dot ("."), then that line is intercepted and interpreted by the sqlite3 program itself. These "dot commands" are typically used to change the output format of queries, or to execute certain prepackaged query statements.

For a listing of the available dot commands, you can enter ".help" at any time. For example:

sqlite> .help
.backup ?DB? FILE      Backup DB (default "main") to FILE
.bail ON|OFF           Stop after hitting an error.  Default OFF
.databases             List names and files of attached databases
.dump ?TABLE? ...      Dump the database in an SQL text format
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program
.explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
.genfkey ?OPTIONS?     Options are:
                         --no-drop: Do not drop old fkey triggers.
                         --ignore-errors: Ignore tables with fkey errors
                         --exec: Execute generated SQL immediately
                       See file tool/genfkey.README in the source 
                       distribution for further information.
.header(s) ON|OFF      Turn display of headers on or off
.help                  Show this message
.import FILE TABLE     Import data from FILE into TABLE
.indices TABLE         Show names of all indices on TABLE
.iotrace FILE          Enable I/O diagnostic logging to FILE
.load FILE ?ENTRY?     Load an extension library
.mode MODE ?TABLE?     Set output mode where MODE is one of:
                         csv      Comma-separated values
                         column   Left-aligned columns.  (See .width)
                         html     HTML <table> code
                         insert   SQL insert statements for TABLE
                         line     One value per line
                         list     Values delimited by .separator string
                         tabs     Tab-separated values
                         tcl      TCL list elements
.nullvalue STRING      Print STRING in place of NULL values
.output FILENAME       Send output to FILENAME
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.restore ?DB? FILE     Restore content of DB (default "main") from FILE
.schema ?TABLE?        Show the CREATE statements
.separator STRING      Change separator used by output mode and .import
.show                  Show the current values for various settings
.tables ?PATTERN?      List names of tables matching a LIKE pattern
.timeout MS            Try opening locked tables for MS milliseconds
.timer ON|OFF          Turn the CPU timer measurement on or off
.width NUM NUM ...     Set column widths for "column" mode
sqlite>

Changing Output Formats

The sqlite3 program is able to show the results of a query in eight different formats: "csv", "column", "html", "insert", "line", "list", "tabs", and "tcl". You can use the ".mode" dot command to switch between these output formats.

The default output mode is "list". In list mode, each record of a query result is written on one line of output and each column within that record is separated by a specific separator string. The default separator is a pipe symbol ("|"). List mode is especially useful when you are going to send the output of a query to another program (such as AWK) for additional processing.

sqlite> .mode list
sqlite> select * from tbl1;
hello|10
goodbye|20
sqlite>

You can use the ".separator" dot command to change the separator for list mode. For example, to change the separator to a comma and a space, you could do this:

sqlite> .separator ", "
sqlite> select * from tbl1;
hello, 10
goodbye, 20
sqlite>

In "line" mode, each column in a row of the database is shown on a line by itself. Each line consists of the column name, an equal sign and the column data. Successive records are separated by a blank line. Here is an example of line mode output:

sqlite> .mode line
sqlite> select * from tbl1;
one = hello
two = 10

one = goodbye
two = 20
sqlite>

In column mode, each record is shown on a separate line with the data aligned in columns. For example:

sqlite> .mode column
sqlite> select * from tbl1;
one         two       
----------  ----------
hello       10        
goodbye     20        
sqlite>

By default, each column is at least 10 characters wide. Data that is too wide to fit in a column is truncated. You can adjust the column widths using the ".width" command. Like this:

sqlite> .width 12 6
sqlite> select * from tbl1;
one           two   
------------  ------
hello         10    
goodbye       20    
sqlite>

The ".width" command in the example above sets the width of the first column to 12 and the width of the second column to 6. All other column widths were unaltered. You can gives as many arguments to ".width" as necessary to specify the widths of as many columns as are in your query results.

If you specify a column a width of 0, then the column width is automatically adjusted to be the maximum of three numbers: 10, the width of the header, and the width of the first row of data. This makes the column width self-adjusting. The default width setting for every column is this auto-adjusting 0 value.

The column labels that appear on the first two lines of output can be turned on and off using the ".header" dot command. In the examples above, the column labels are on. To turn them off you could do this:

sqlite> .header off
sqlite> select * from tbl1;
hello         10    
goodbye       20    
sqlite>

Another useful output mode is "insert". In insert mode, the output is formatted to look like SQL INSERT statements. You can use insert mode to generate text that can later be used to input data into a different database.

When specifying insert mode, you have to give an extra argument which is the name of the table to be inserted into. For example:

sqlite> .mode insert new_table
sqlite> select * from tbl1;
INSERT INTO 'new_table' VALUES('hello',10);
INSERT INTO 'new_table' VALUES('goodbye',20);
sqlite>

The last output mode is "html". In this mode, sqlite3 writes the results of the query as an XHTML table. The beginning <TABLE> and the ending </TABLE> are not written, but all of the intervening <TR>s, <TH>s, and <TD>s are. The html output mode is envisioned as being useful for CGI.

Writing results to a file

By default, sqlite3 sends query results to standard output. You can change this using the ".output" command. Just put the name of an output file as an argument to the .output command and all subsequent query results will be written to that file. Use ".output stdout" to begin writing to standard output again. For example:

sqlite> .mode list
sqlite> .separator |
sqlite> .output test_file_1.txt
sqlite> select * from tbl1;
sqlite> .exit
$ cat test_file_1.txt
hello|10
goodbye|20
$

Querying the database schema

The sqlite3 program provides several convenience commands that are useful for looking at the schema of the database. There is nothing that these commands do that cannot be done by some other means. These commands are provided purely as a shortcut.

For example, to see a list of the tables in the database, you can enter ".tables".

sqlite> .tables
tbl1
tbl2
sqlite>

The ".tables" command is similar to setting list mode then executing the following query:

SELECT name FROM sqlite_master 
WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
UNION ALL 
SELECT name FROM sqlite_temp_master 
WHERE type IN ('table','view') 
ORDER BY 1

In fact, if you look at the source code to the sqlite3 program (found in the source tree in the file src/shell.c) you'll find exactly the above query.

The ".indices" command works in a similar way to list all of the indices for a particular table. The ".indices" command takes a single argument which is the name of the table for which the indices are desired. Last, but not least, is the ".schema" command. With no arguments, the ".schema" command shows the original CREATE TABLE and CREATE INDEX statements that were used to build the current database. If you give the name of a table to ".schema", it shows the original CREATE statement used to make that table and all if its indices. We have:

sqlite> .schema
create table tbl1(one varchar(10), two smallint)
CREATE TABLE tbl2 (
  f1 varchar(30) primary key,
  f2 text,
  f3 real
)
sqlite> .schema tbl2
CREATE TABLE tbl2 (
  f1 varchar(30) primary key,
  f2 text,
  f3 real
)
sqlite>

The ".schema" command accomplishes the same thing as setting list mode, then entering the following query:

SELECT sql FROM 
   (SELECT * FROM sqlite_master UNION ALL
    SELECT * FROM sqlite_temp_master)
WHERE type!='meta'
ORDER BY tbl_name, type DESC, name

Or, if you give an argument to ".schema" because you only want the schema for a single table, the query looks like this:

SELECT sql FROM
   (SELECT * FROM sqlite_master UNION ALL
    SELECT * FROM sqlite_temp_master)
WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
ORDER BY substr(type,2,1), name

You can supply an argument to the .schema command. If you do, the query looks like this:

SELECT sql FROM
   (SELECT * FROM sqlite_master UNION ALL
    SELECT * FROM sqlite_temp_master)
WHERE tbl_name LIKE '%s'
  AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
ORDER BY substr(type,2,1), name

The "%s" in the query is replace by your argument. This allows you to view the schema for some subset of the database.

sqlite> .schema %abc%

Along these same lines, the ".table" command also accepts a pattern as its first argument. If you give an argument to the .table command, a "%" is both appended and prepended and a LIKE clause is added to the query. This allows you to list only those tables that match a particular pattern.

The ".databases" command shows a list of all databases open in the current connection. There will always be at least 2. The first one is "main", the original database opened. The second is "temp", the database used for temporary tables. There may be additional databases listed for databases attached using the ATTACH statement. The first output column is the name the database is attached with, and the second column is the filename of the external file.

sqlite> .databases

Converting An Entire Database To An ASCII Text File

Use the ".dump" command to convert the entire contents of a database into a single ASCII text file. This file can be converted back into a database by piping it back into sqlite3.

A good way to make an archival copy of a database is this:

$ echo '.dump' | sqlite3 ex1 | gzip -c >ex1.dump.gz

This generates a file named ex1.dump.gz that contains everything you need to reconstruct the database at a later time, or on another machine. To reconstruct the database, just type:

$ zcat ex1.dump.gz | sqlite3 ex2

The text format is pure SQL so you can also use the .dump command to export an SQLite database into other popular SQL database engines. Like this:

$ createdb ex2
$ sqlite3 ex1 .dump | psql ex2

Other Dot Commands

The ".explain" dot command can be used to set the output mode to "column" and to set the column widths to values that are reasonable for looking at the output of an EXPLAIN command. The EXPLAIN command is an SQLite-specific SQL extension that is useful for debugging. If any regular SQL is prefaced by EXPLAIN, then the SQL command is parsed and analyzed but is not executed. Instead, the sequence of virtual machine instructions that would have been used to execute the SQL command are returned like a query result. For example:

sqlite> .explain
sqlite> explain delete from tbl1 where two<20;
addr  opcode        p1     p2     p3          
----  ------------  -----  -----  -------------------------------------   
0     ListOpen      0      0                  
1     Open          0      1      tbl1        
2     Next          0      9                  
3     Field         0      1                  
4     Integer       20     0                  
5     Ge            0      2                  
6     Key           0      0                  
7     ListWrite     0      0                  
8     Goto          0      2                  
9     Noop          0      0                  
10    ListRewind    0      0                  
11    ListRead      0      14                 
12    Delete        0      0                  
13    Goto          0      11                 
14    ListClose     0      0

The ".timeout" command sets the amount of time that the sqlite3 program will wait for locks to clear on files it is trying to access before returning an error. The default value of the timeout is zero so that an error is returned immediately if any needed database table or index is locked.

And finally, we mention the ".exit" command which causes the sqlite3 program to exit.

Using sqlite3 in a shell script

One way to use sqlite3 in a shell script is to use "echo" or "cat" to generate a sequence of commands in a file, then invoke sqlite3 while redirecting input from the generated command file. This works fine and is appropriate in many circumstances. But as an added convenience, sqlite3 allows a single SQL command to be entered on the command line as a second argument after the database name. When the sqlite3 program is launched with two arguments, the second argument is passed to the SQLite library for processing, the query results are printed on standard output in list mode, and the program exits. This mechanism is designed to make sqlite3 easy to use in conjunction with programs like "awk". For example:

$ sqlite3 ex1 'select * from tbl1' |
>  awk '{printf "<tr><td>%s<td>%s\n",$1,$2 }'
<tr><td>hello<td>10
<tr><td>goodbye<td>20
$

Ending shell commands

SQLite commands are normally terminated by a semicolon. In the shell you can also use the word "GO" (case-insensitive) or a slash character "/" on a line by itself to end a command. These are used by SQL Server and Oracle, respectively. These won't work insqlite3_exec(), because the shell translates these into a semicolon before passing them to that function.

Compiling the sqlite3 program from sources

The source code to the sqlite3 command line interface is in a single file named "shell.c" which you can download from the SQLite website. Compile this file (together with the sqlite3 library source code to generate the executable. For example:

gcc -o sqlite3 shell.c sqlite3.c -ldl -lpthread

Command Line Shell For SQLite

The SQLite library includes a simple command-line utility named sqlite3 (or sqlite3.exe on windows) that allows the user to manually enter and execute SQL commands against an SQLite database. This document provides a brief introduction on how to use the sqlite3 program.

Getting Started

To start the sqlite3 program, just type "sqlite3" followed by the name the file that holds the SQLite database. If the file does not exist, a new one is created automatically. The sqlite3 program will then prompt you to enter SQL. Type in SQL statements (terminated by a semicolon), press "Enter" and the SQL will be executed.

For example, to create a new SQLite database named "ex1" with a single table named "tbl1", you might do this:

$ sqlite3 ex1
SQLite version 3.6.11
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table tbl1(one varchar(10), two smallint);
sqlite> insert into tbl1 values('hello!',10);
sqlite> insert into tbl1 values('goodbye', 20);
sqlite> select * from tbl1;
hello!|10
goodbye|20
sqlite>

You can terminate the sqlite3 program by typing your systems End-Of-File character (usually a Control-D). Use the interrupt character (usually a Control-C) to stop a long-running SQL statement.

Make sure you type a semicolon at the end of each SQL command! The sqlite3 program looks for a semicolon to know when your SQL command is complete. If you omit the semicolon, sqlite3 will give you a continuation prompt and wait for you to enter more text to be added to the current SQL command. This feature allows you to enter SQL commands that span multiple lines. For example:

sqlite> CREATE TABLE tbl2 (
   ...>   f1 varchar(30) primary key,
   ...>   f2 text,
   ...>   f3 real
   ...> );
sqlite>

Aside: Querying the SQLITE_MASTER table

The database schema in an SQLite database is stored in a special table named "sqlite_master". You can execute "SELECT" statements against the special sqlite_master table just like any other table in an SQLite database. For example:

$ sqlite3 ex1
SQLite vresion 3.6.11
Enter ".help" for instructions
sqlite> select * from sqlite_master;
    type = table
    name = tbl1
tbl_name = tbl1
rootpage = 3
     sql = create table tbl1(one varchar(10), two smallint)
sqlite>

But you cannot execute DROP TABLE, UPDATE, INSERT or DELETE against the sqlite_master table. The sqlite_master table is updated automatically as you create or drop tables and indices from the database. You can not make manual changes to the sqlite_master table.

The schema for TEMPORARY tables is not stored in the "sqlite_master" table since TEMPORARY tables are not visible to applications other than the application that created the table. The schema for TEMPORARY tables is stored in another special table named "sqlite_temp_master". The "sqlite_temp_master" table is temporary itself.

Special commands to sqlite3

Most of the time, sqlite3 just reads lines of input and passes them on to the SQLite library for execution. But if an input line begins with a dot ("."), then that line is intercepted and interpreted by the sqlite3 program itself. These "dot commands" are typically used to change the output format of queries, or to execute certain prepackaged query statements.

For a listing of the available dot commands, you can enter ".help" at any time. For example:

sqlite> .help
.backup ?DB? FILE      Backup DB (default "main") to FILE
.bail ON|OFF           Stop after hitting an error.  Default OFF
.databases             List names and files of attached databases
.dump ?TABLE? ...      Dump the database in an SQL text format
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program
.explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
.genfkey ?OPTIONS?     Options are:
                         --no-drop: Do not drop old fkey triggers.
                         --ignore-errors: Ignore tables with fkey errors
                         --exec: Execute generated SQL immediately
                       See file tool/genfkey.README in the source 
                       distribution for further information.
.header(s) ON|OFF      Turn display of headers on or off
.help                  Show this message
.import FILE TABLE     Import data from FILE into TABLE
.indices TABLE         Show names of all indices on TABLE
.iotrace FILE          Enable I/O diagnostic logging to FILE
.load FILE ?ENTRY?     Load an extension library
.mode MODE ?TABLE?     Set output mode where MODE is one of:
                         csv      Comma-separated values
                         column   Left-aligned columns.  (See .width)
                         html     HTML <table> code
                         insert   SQL insert statements for TABLE
                         line     One value per line
                         list     Values delimited by .separator string
                         tabs     Tab-separated values
                         tcl      TCL list elements
.nullvalue STRING      Print STRING in place of NULL values
.output FILENAME       Send output to FILENAME
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.restore ?DB? FILE     Restore content of DB (default "main") from FILE
.schema ?TABLE?        Show the CREATE statements
.separator STRING      Change separator used by output mode and .import
.show                  Show the current values for various settings
.tables ?PATTERN?      List names of tables matching a LIKE pattern
.timeout MS            Try opening locked tables for MS milliseconds
.timer ON|OFF          Turn the CPU timer measurement on or off
.width NUM NUM ...     Set column widths for "column" mode
sqlite>

Changing Output Formats

The sqlite3 program is able to show the results of a query in eight different formats: "csv", "column", "html", "insert", "line", "list", "tabs", and "tcl". You can use the ".mode" dot command to switch between these output formats.

The default output mode is "list". In list mode, each record of a query result is written on one line of output and each column within that record is separated by a specific separator string. The default separator is a pipe symbol ("|"). List mode is especially useful when you are going to send the output of a query to another program (such as AWK) for additional processing.

sqlite> .mode list
sqlite> select * from tbl1;
hello|10
goodbye|20
sqlite>

You can use the ".separator" dot command to change the separator for list mode. For example, to change the separator to a comma and a space, you could do this:

sqlite> .separator ", "
sqlite> select * from tbl1;
hello, 10
goodbye, 20
sqlite>

In "line" mode, each column in a row of the database is shown on a line by itself. Each line consists of the column name, an equal sign and the column data. Successive records are separated by a blank line. Here is an example of line mode output:

sqlite> .mode line
sqlite> select * from tbl1;
one = hello
two = 10

one = goodbye
two = 20
sqlite>

In column mode, each record is shown on a separate line with the data aligned in columns. For example:

sqlite> .mode column
sqlite> select * from tbl1;
one         two       
----------  ----------
hello       10        
goodbye     20        
sqlite>

By default, each column is at least 10 characters wide. Data that is too wide to fit in a column is truncated. You can adjust the column widths using the ".width" command. Like this:

sqlite> .width 12 6
sqlite> select * from tbl1;
one           two   
------------  ------
hello         10    
goodbye       20    
sqlite>

The ".width" command in the example above sets the width of the first column to 12 and the width of the second column to 6. All other column widths were unaltered. You can gives as many arguments to ".width" as necessary to specify the widths of as many columns as are in your query results.

If you specify a column a width of 0, then the column width is automatically adjusted to be the maximum of three numbers: 10, the width of the header, and the width of the first row of data. This makes the column width self-adjusting. The default width setting for every column is this auto-adjusting 0 value.

The column labels that appear on the first two lines of output can be turned on and off using the ".header" dot command. In the examples above, the column labels are on. To turn them off you could do this:

sqlite> .header off
sqlite> select * from tbl1;
hello         10    
goodbye       20    
sqlite>

Another useful output mode is "insert". In insert mode, the output is formatted to look like SQL INSERT statements. You can use insert mode to generate text that can later be used to input data into a different database.

When specifying insert mode, you have to give an extra argument which is the name of the table to be inserted into. For example:

sqlite> .mode insert new_table
sqlite> select * from tbl1;
INSERT INTO 'new_table' VALUES('hello',10);
INSERT INTO 'new_table' VALUES('goodbye',20);
sqlite>

The last output mode is "html". In this mode, sqlite3 writes the results of the query as an XHTML table. The beginning <TABLE> and the ending </TABLE> are not written, but all of the intervening <TR>s, <TH>s, and <TD>s are. The html output mode is envisioned as being useful for CGI.

Writing results to a file

By default, sqlite3 sends query results to standard output. You can change this using the ".output" command. Just put the name of an output file as an argument to the .output command and all subsequent query results will be written to that file. Use ".output stdout" to begin writing to standard output again. For example:

sqlite> .mode list
sqlite> .separator |
sqlite> .output test_file_1.txt
sqlite> select * from tbl1;
sqlite> .exit
$ cat test_file_1.txt
hello|10
goodbye|20
$

Querying the database schema

The sqlite3 program provides several convenience commands that are useful for looking at the schema of the database. There is nothing that these commands do that cannot be done by some other means. These commands are provided purely as a shortcut.

For example, to see a list of the tables in the database, you can enter ".tables".

sqlite> .tables
tbl1
tbl2
sqlite>

The ".tables" command is similar to setting list mode then executing the following query:

SELECT name FROM sqlite_master 
WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
UNION ALL 
SELECT name FROM sqlite_temp_master 
WHERE type IN ('table','view') 
ORDER BY 1

In fact, if you look at the source code to the sqlite3 program (found in the source tree in the file src/shell.c) you'll find exactly the above query.

The ".indices" command works in a similar way to list all of the indices for a particular table. The ".indices" command takes a single argument which is the name of the table for which the indices are desired. Last, but not least, is the ".schema" command. With no arguments, the ".schema" command shows the original CREATE TABLE and CREATE INDEX statements that were used to build the current database. If you give the name of a table to ".schema", it shows the original CREATE statement used to make that table and all if its indices. We have:

sqlite> .schema
create table tbl1(one varchar(10), two smallint)
CREATE TABLE tbl2 (
  f1 varchar(30) primary key,
  f2 text,
  f3 real
)
sqlite> .schema tbl2
CREATE TABLE tbl2 (
  f1 varchar(30) primary key,
  f2 text,
  f3 real
)
sqlite>

The ".schema" command accomplishes the same thing as setting list mode, then entering the following query:

SELECT sql FROM 
   (SELECT * FROM sqlite_master UNION ALL
    SELECT * FROM sqlite_temp_master)
WHERE type!='meta'
ORDER BY tbl_name, type DESC, name

Or, if you give an argument to ".schema" because you only want the schema for a single table, the query looks like this:

SELECT sql FROM
   (SELECT * FROM sqlite_master UNION ALL
    SELECT * FROM sqlite_temp_master)
WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
ORDER BY substr(type,2,1), name

You can supply an argument to the .schema command. If you do, the query looks like this:

SELECT sql FROM
   (SELECT * FROM sqlite_master UNION ALL
    SELECT * FROM sqlite_temp_master)
WHERE tbl_name LIKE '%s'
  AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
ORDER BY substr(type,2,1), name

The "%s" in the query is replace by your argument. This allows you to view the schema for some subset of the database.

sqlite> .schema %abc%

Along these same lines, the ".table" command also accepts a pattern as its first argument. If you give an argument to the .table command, a "%" is both appended and prepended and a LIKE clause is added to the query. This allows you to list only those tables that match a particular pattern.

The ".databases" command shows a list of all databases open in the current connection. There will always be at least 2. The first one is "main", the original database opened. The second is "temp", the database used for temporary tables. There may be additional databases listed for databases attached using the ATTACH statement. The first output column is the name the database is attached with, and the second column is the filename of the external file.

sqlite> .databases

Converting An Entire Database To An ASCII Text File

Use the ".dump" command to convert the entire contents of a database into a single ASCII text file. This file can be converted back into a database by piping it back into sqlite3.

A good way to make an archival copy of a database is this:

$ echo '.dump' | sqlite3 ex1 | gzip -c >ex1.dump.gz

This generates a file named ex1.dump.gz that contains everything you need to reconstruct the database at a later time, or on another machine. To reconstruct the database, just type:

$ zcat ex1.dump.gz | sqlite3 ex2

The text format is pure SQL so you can also use the .dump command to export an SQLite database into other popular SQL database engines. Like this:

$ createdb ex2
$ sqlite3 ex1 .dump | psql ex2

Other Dot Commands

The ".explain" dot command can be used to set the output mode to "column" and to set the column widths to values that are reasonable for looking at the output of an EXPLAIN command. The EXPLAIN command is an SQLite-specific SQL extension that is useful for debugging. If any regular SQL is prefaced by EXPLAIN, then the SQL command is parsed and analyzed but is not executed. Instead, the sequence of virtual machine instructions that would have been used to execute the SQL command are returned like a query result. For example:

sqlite> .explain
sqlite> explain delete from tbl1 where two<20;
addr  opcode        p1     p2     p3          
----  ------------  -----  -----  -------------------------------------   
0     ListOpen      0      0                  
1     Open          0      1      tbl1        
2     Next          0      9                  
3     Field         0      1                  
4     Integer       20     0                  
5     Ge            0      2                  
6     Key           0      0                  
7     ListWrite     0      0                  
8     Goto          0      2                  
9     Noop          0      0                  
10    ListRewind    0      0                  
11    ListRead      0      14                 
12    Delete        0      0                  
13    Goto          0      11                 
14    ListClose     0      0

The ".timeout" command sets the amount of time that the sqlite3 program will wait for locks to clear on files it is trying to access before returning an error. The default value of the timeout is zero so that an error is returned immediately if any needed database table or index is locked.

And finally, we mention the ".exit" command which causes the sqlite3 program to exit.

Using sqlite3 in a shell script

One way to use sqlite3 in a shell script is to use "echo" or "cat" to generate a sequence of commands in a file, then invoke sqlite3 while redirecting input from the generated command file. This works fine and is appropriate in many circumstances. But as an added convenience, sqlite3 allows a single SQL command to be entered on the command line as a second argument after the database name. When the sqlite3 program is launched with two arguments, the second argument is passed to the SQLite library for processing, the query results are printed on standard output in list mode, and the program exits. This mechanism is designed to make sqlite3 easy to use in conjunction with programs like "awk". For example:

$ sqlite3 ex1 'select * from tbl1' |
>  awk '{printf "<tr><td>%s<td>%s\n",$1,$2 }'
<tr><td>hello<td>10
<tr><td>goodbye<td>20
$

Ending shell commands

SQLite commands are normally terminated by a semicolon. In the shell you can also use the word "GO" (case-insensitive) or a slash character "/" on a line by itself to end a command. These are used by SQL Server and Oracle, respectively. These won't work insqlite3_exec(), because the shell translates these into a semicolon before passing them to that function.

Compiling the sqlite3 program from sources

The source code to the sqlite3 command line interface is in a single file named "shell.c" which you can download from the SQLite website. Compile this file (together with the sqlite3 library source code to generate the executable. For example:

gcc -o sqlite3 shell.c sqlite3.c -ldl -lpthread

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

외부 키  (0) 2010.03.18
열 구성 변경  (0) 2010.03.18
용어  (0) 2010.03.17
TRANSACTION  (0) 2010.03.17
INDEX  (0) 2010.03.17
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
,
 

Android SMS call analysis




< android SDK1.5 cupcake emulator >


solicited Message

emulator HOME의 Messaging 아이콘 클릭
ConversationList.java
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ConversationList.java
onCreate();
    View, Listener 초기화 작업    
onResume();
    DraftCache.getInstance().addOnDraftChangedListener(this);
    getContentResolver().delete(Threads.OBSOLETE_THREADS_URI, null, null);
    DraftCache.getInstance().refresh();
    startAsyncQuery();
    ContactInfoCache.getInstance().invalidateCache();


New message 클릭
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ConversationList.java
@Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        if (LOCAL_LOGV) {
            Log.v(TAG, "onListItemClick: position=" + position + ", id=" + id);
        }

        if (position == 0) {
            createNewMessage();
        } else if (v instanceof ConversationHeaderView) {
            ConversationHeaderView headerView = (ConversationHeaderView) v;
            ConversationHeader ch = headerView.getConversationHeader();

            // TODO: The 'from' view of the ConversationHeader was
            // repurposed to be the cached display value, rather than
            // the old raw value, which openThread() wanted.  But it
            // turns out openThread() doesn't need it:
            // ComposeMessageActivity will load it.  That's not ideal,
            // though, as it's an SQLite query.  So fix this later to
            // save some latency on starting ComposeMessageActivity.
            String somethingDelimitedAddresses = null;
            openThread(ch.getThreadId(), somethingDelimitedAddresses);
        }
    }



        
createNewMesage()
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ConversationList.java
private void createNewMessage() {
        Intent intent = new Intent(this, ComposeMessageActivity.class);
        startActivity(intent);
    }


ComposeMessageActivity.java
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
onCreate() {
    initResourceRefs(); // Initialize members for UI elements.
    initActivityState(savedInstanceState, getIntent()); // Read parameters or previously saved              
                                                                             state of this activity.
    mRecipientList = RecipientList.from(mExternalAddress, this); // Parse the recipient list.
    initMessageList(); // Set up the message history ListAdapter
    markAsRead(mThreadId); // Mark the current thread as read.
    updateSendButtonState();
    // 이 밖에도 MMS 모드 체크, 전송 실패한 메시지 정보를 pop-up 등등의 초기화 작업을 함.
}

onStart() {
    updateWindowTitle();
    initFocus();
    registerReceiver(mHttpProgressReceiver, mHttpProgressFilter); // Register a  
                                                  // BroadcastReceiver to listen on HTTP I/O process.  
    startMsgListQuery();                                                      
    startQueryForContactInfo();
    updateSendFailedNotification();
}

onResume() {
    startPresencePollingRequest(); // enqueueMessage(Message msg, long when) 에서
                                              // MessageQueue를 확인한다. 주기적 반복
}

    
    
Send 버튼 클릭
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
/*
onCreate() {
...  
    initResourceRefs() {
    ...
        mSendButton = (Button) findViewById(R.id.send_button);
        mSendButton.setOnClickListener(this);
    }
    updateSendButtonState() {
    ...  
        mSendButton.setEnabled(enable);
        mSendButton.setFocusable(enable);
    }
}
*/

public void onClick(View v) {
    if ((v == mSendButton) && isPreparedForSending()) {  
        // if문의 두번째 메소드는 hasRecipient() && (hasAttachment() || hasText()) 를 확인한다.
        // 즉, 메시지(받는사람, 내용, 첨부)를 작성했는지를 확인
        confirmSendMessageIfNeeded();
    }
}    

   
confirmSendMessageIfNeeded()
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
//받는사람이 유효한지 확인한 후, 메시지 전송

private void confirmSendMessageIfNeeded() {
    if (mRecipientList.hasInvalidRecipient()) {
        if (mRecipientList.hasValidRecipient()) {
            String title = getResourcesString(R.string.has_invalid_recipient,
                    mRecipientList.getInvalidRecipientString());
            new AlertDialog.Builder(this)
                .setIcon(android.R.drawable.ic_dialog_alert)
                .setTitle(title)
                .setMessage(R.string.invalid_recipient_message)
                .setPositiveButton(R.string.try_to_send,
                        new SendIgnoreInvalidRecipientListener())
                .setNegativeButton(R.string.no, new CancelSendingListener())
                .show();
        } else {
            new AlertDialog.Builder(this)
                .setIcon(android.R.drawable.ic_dialog_alert)
                .setTitle(R.string.cannot_send_message)
                .setMessage(R.string.cannot_send_message_reason)
                .setPositiveButton(R.string.yes, new CancelSendingListener())
                .show();
        }
    } else {
        sendMessage();
    }
}


sendMessage()
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
private void sendMessage() {
        // Need this for both SMS and MMS.
        final String[] dests = mRecipientList.getToNumbers();
        
        // removeSubjectIfEmpty will convert a message that is solely an MMS
        // message because it has an empty subject back into an SMS message.
        // It doesn't notify the user of the conversion.
        removeSubjectIfEmpty();
        if (requiresMms()) {
            // Make local copies of the bits we need for sending a message,
            // because we will be doing it off of the main thread, which will
            // immediately continue on to resetting some of this state.
            final Uri mmsUri = mMessageUri;
            final PduPersister persister = mPersister;
            final SlideshowModel slideshow = mSlideshow;
            final SendReq sendReq = new SendReq();
            fillMessageHeaders(sendReq);
            
            // Make sure the text in slide 0 is no longer holding onto a reference to the text
            // in the message text box.
            slideshow.prepareForSend();

            // Do the dirty work of sending the message off of the main UI thread.
            new Thread(new Runnable() {
                public void run() {
                    sendMmsWorker(dests, mmsUri, persister, slideshow, sendReq);
                }
            }).start();
        } else {
            // Same rules apply as above.
            final String msgText = mMsgText.toString();
            new Thread(new Runnable() {
                public void run() {
                    sendSmsWorker(dests, msgText);
                }
            }).start();
        }
        
        if (mExitOnSent) {
            // If we are supposed to exit after a message is sent,
            // clear out the text and URIs to inhibit saving of any
            // drafts and call finish().
            mMsgText = "";
            mMessageUri = null;
            finish();
        } else {
            // Otherwise, reset the UI to be ready for the next message.
            resetMessage();
        }
}


sendSmsWorker()
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
private void sendSmsWorker(String[] dests, String msgText) {
        // Make sure we are still using the correct thread ID for our
        // recipient set.
        long threadId = getOrCreateThreadId(dests);

        MessageSender sender = new SmsMessageSender(this, dests, msgText, threadId);
        try {
            sender.sendMessage(threadId);
            setThreadId(threadId);
            startMsgListQuery();
        } catch (Exception e) {
            Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);
        }
    }


--- APPLICATION FRAMEWORK ↓ -------------------------------------------------------------

sender.sendMessage(threadId)
/open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Transaction/SmsMessageSender.java
/*
위의 sender 인스턴스 생성시 SmsMessageSender(..) 생성자 실행
public SmsMessageSender(Context context, String[] dests, String msgText,
            long threadId) {
        mContext = context;
        mMessageText = msgText;
        mNumberOfDests = dests.length;
        mDests = new String[mNumberOfDests];
        System.arraycopy(dests, 0, mDests, 0, mNumberOfDests);
        mTimestamp = System.currentTimeMillis();
        mThreadId = threadId > 0 ? threadId
                                            : Threads.getOrCreateThreadId(context,
                                                   new HashSet<String>(Arrays.asList(dests)));
        mServiceCenter = getOutgoingServiceCenter(mThreadId);
}  
*/

public boolean sendMessage(long token) throws MmsException {
        if ((mMessageText == null) || (mNumberOfDests == 0)) {
            // Don't try to send an empty message.
            throw new MmsException("Null message body or dest.");
        }

        SmsManager smsManager = SmsManager.getDefault(); //Get the default instance
                                                                                 // of the SmsManager

        for (int i = 0; i < mNumberOfDests; i++) {
            ArrayList<String> messages = smsManager.divideMessage(mMessageText);                      
                                                                      // Divide a text message into several
                                                                      // messages, none bigger than
                                                                      // the maximum SMS message size.      
                                                                      // SmsManager.java
            int messageCount = messages.size();
            ArrayList<PendingIntent> deliveryIntents =
                    new ArrayList<PendingIntent>(messageCount);
            ArrayList<PendingIntent> sentIntents =
                    new ArrayList<PendingIntent>(messageCount);
            SharedPreferences prefs =                         
                    PreferenceManager.getDefaultSharedPreferences(mContext);
            boolean requestDeliveryReport = prefs.getBoolean(
                    MessagingPreferenceActivity.SMS_DELIVERY_REPORT_MODE,
                    DEFAULT_DELIVERY_REPORT_MODE);
            Uri uri = null;
            try {
                uri = Sms.Outbox.addMessage(mContext.getContentResolver(), mDests[i],
                            mMessageText, null, mTimestamp, requestDeliveryReport, mThreadId);
            } catch (SQLiteException e) {
                SqliteWrapper.checkSQLiteException(mContext, e);
            }

            for (int j = 0; j < messageCount; j++) {
                if (requestDeliveryReport) {
                    // TODO: Fix: It should not be necessary to
                    // specify the class in this intent.  Doing that
                    // unnecessarily limits customizability.
                    deliveryIntents.add(PendingIntent.getBroadcast(
                            mContext, 0,
                            new Intent(
                                    MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION,
                                    uri,
                                    mContext,
                                    MessageStatusReceiver.class),
                            0));
                }
                sentIntents.add(PendingIntent.getBroadcast(
                        mContext, 0,
                        new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,
                                uri,
                                mContext,
                                SmsReceiver.class),
                        0));
            }
            smsManager.sendMultipartTextMessage(
                    mDests[i], mServiceCenter, messages, sentIntents,
                    deliveryIntents);
        }
        return false;
}


smsManager.sendMultipartTextmessage(...)
/open_src/Frameworks/Base/Telephony/Gsm/SmsManager.java
/**
  * Send a multi-part text based SMS.  The callee should have already
  * divided the message into correctly sized parts by calling
  * <code>divideMessage</code>.
  */

public void sendMultipartTextMessage(
            String destinationAddress, String scAddress, ArrayList<String> parts,
            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
        if (TextUtils.isEmpty(destinationAddress)) {
            throw new IllegalArgumentException("Invalid destinationAddress");
        }
        if (parts == null || parts.size() < 1) {
            throw new IllegalArgumentException("Invalid message body");
        }
        
        // divided된 메시지 parts에 따라 전송 방법이 2 가지로 나뉜다.
        if (parts.size() > 1) {
            try {
                ISms simISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
                if (simISms != null) {
                    simISms.sendMultipartText(destinationAddress, scAddress, parts,
                            sentIntents, deliveryIntents);
                }
            } catch (RemoteException ex) {
                // ignore it
            }
        } else {
            PendingIntent sentIntent = null;
            PendingIntent deliveryIntent = null;
            if (sentIntents != null && sentIntents.size() > 0) {
                sentIntent = sentIntents.get(0);
            }
            if (deliveryIntents != null && deliveryIntents.size() > 0) {
                deliveryIntent = deliveryIntents.get(0);
            }
            sendTextMessage(destinationAddress, scAddress, parts.get(0),
                    sentIntent, deliveryIntent);
        }
    }


simISms.sendMultipartText(...)  
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
SimSmsInterfaceManager.java
/**
 * Send a multi-part text based SMS
 */
   
private GSMPhone mPhone;

public void sendMultipartText(String destinationAddress, String scAddress, List<String> parts,
            List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
        Context context = mPhone.getContext();

        context.enforceCallingPermission(
                "android.permission.SEND_SMS",
                "Sending SMS message");
        if (DBG) log("sendMultipartText");
        mPhone.mSMS.sendMultipartText(destinationAddress, scAddress,     
                (ArrayList<String>) parts,
                (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>)         
                deliveryIntents);
}


mphone.mSMS.sendMultipartText(...)
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
SMSDispatcher.java
/**
 * Send a multi-part text based SMS
 */

void sendMultipartText(String destinationAddress, String scAddress, ArrayList<String> parts,
            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {

        PendingIntent sentIntent = null;
        
        int ss = mPhone.getServiceState().getState(); //Get current servcie state of phone
        
        if (ss == ServiceState.STATE_IN_SERVICE) {
            // Only check SMS sending limit while in service
            if (sentIntents != null && sentIntents.size() > 0) {
                sentIntent = sentIntents.get(0);
            }
            String appName = getAppNameByIntent(sentIntent);
            if ( !mCounter.check(appName, parts.size())) {
                HashMap<String, Object> map = new HashMap<String, Object>();
                map.put("destination", destinationAddress);
                map.put("scaddress", scAddress);
                map.put("parts", parts);
                map.put("sentIntents", sentIntents);
                map.put("deliveryIntents", deliveryIntents);
                
                SmsTracker multipartParameter = new SmsTracker(map, null, null);

                sendMessage(obtainMessage(EVENT_POST_ALERT, multipartParameter));
                return;
            }
        }
        
        sendMultipartTextWithPermit(destinationAddress,
                scAddress, parts, sentIntents, deliveryIntents);
}


sendMultipartTextWithPermit(...)
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
SMSDispatcher.java
/**
 * Send a multi-part text based SMS which already passed SMS control check.
 *
 * It is the working function for sendMultipartText().
 */

private void sendMultipartTextWithPermit(String destinationAddress,
            String scAddress, ArrayList<String> parts,
            ArrayList<PendingIntent> sentIntents,
            ArrayList<PendingIntent> deliveryIntents) {
        
        PendingIntent sentIntent = null;
        PendingIntent deliveryIntent = null;
        
        // check if in service
        int ss = mPhone.getServiceState().getState();
        if (ss != ServiceState.STATE_IN_SERVICE) {
            for (int i = 0, count = parts.size(); i < count; i++) {
                if (sentIntents != null && sentIntents.size() > i) {
                    sentIntent = sentIntents.get(i);
                }
                SmsTracker tracker = new SmsTracker(null, sentIntent, null);
                handleNotInService(ss, tracker);
            }
            return;
        }

        int ref = ++sConcatenatedRef & 0xff;

        for (int i = 0, count = parts.size(); i < count; i++) {
            // build SmsHeader
            byte[] data = new byte[3];
            data[0] = (byte) ref;   // reference #, unique per message
            data[1] = (byte) count; // total part count
            data[2] = (byte) (i + 1);  // 1-based sequence
            SmsHeader header = new SmsHeader();
            header.add(new SmsHeader.Element(
                SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
 
            if (sentIntents != null && sentIntents.size() > i) {
                sentIntent = sentIntents.get(i);
            }
            if (deliveryIntents != null && deliveryIntents.size() > i) {
                deliveryIntent = deliveryIntents.get(i);
            }
            

            /**
             * Get an SMS-SUBMIT PDU for a destination address and a message
             */
                                                                      
            SmsMessage.SubmitPdu pdus = // ↓ Encoding
                        SmsMessage.getSubmitPdu(scAddress, destinationAddress,
                            parts.get(i), deliveryIntent != null, header.toByteArray());

            HashMap<String, Object> map = new HashMap<String, Object>();
            map.put("smsc", pdus.encodedScAddress);
            map.put("pdu", pdus.encodedMessage);

            SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent);
            
            sendSms(tracker);
        }        
}

    
sendSms(tracker)
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
SMSDispatcher.java
/**
 * Send the message along to the radio.
 */

private final CommandsInterface mCm;

private void sendSms(SmsTracker tracker) {
    HashMap map = tracker.mData;

    byte smsc[] = (byte[]) map.get("smsc");
    byte pdu[] = (byte[]) map.get("pdu");

    Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
    
    mCm.sendSMS(SimUtils.bytesToHexString(smsc), SimUtils.bytesToHexString(pdu),
             reply);
    // sendSMS,, interface CommandsInterface
}


--- RIL ↓ -----------------------------------------------------------------------------------

mCm.sendSMS(...)
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
public void
    sendSMS (String smscPDU, String pdu, Message result)
    {
        RILRequest rr
                = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);

        rr.mp.writeInt(2);
        rr.mp.writeString(smscPDU);
        rr.mp.writeString(pdu);

        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
        // RILJ 로그는 여기서 프린트 된다. "[...] > SEND_SMS"
        send(rr);
    }


send(rr)
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
private void
    send(RILRequest rr)
    {
        Message msg;

        msg = mSender.obtainMessage(EVENT_SEND, rr); // 메시지큐에서 메시지 가져오기

        acquireWakeLock(); // Holds a PARTIAL_WAKE_LOCK whenever
                                       (a) There is outstanding RIL request sent to RIL deamon and no
                                            replied
                                       (b) There is a request waiting to be sent out. There is a
                                            WAKE_LOCK_TIMEOUT to release the lock, though
                                            it shouldn't happen often.

        msg.sendToTarget(); // Sends this Message to the Handler specified by getTarget().
                                     // Handler adroid.os.Message.getTarget()
                                     // Retrieve the a handler implementation that will receive this
                                        message. The object must implement
                         Handler.handleMessage().
                                        Each Handler has its own name-space for message codes,
                                        so you do not need to worry about yours conflicting with other
                                        handlers.
    }






Unsolicited Message


Vendor RIL 에서 New Message가 왔음을 알린다.

processUnsolicited (Parcel P)
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
private void
    processUnsolicited (Parcel p)
    {
        int response;
        Object ret;
        
        /**
         * Read an integer value from the parcel at the current dataPosition().
         */
        response = p.readInt();

        try {switch(response) {

        /*
              cat libs/telephony/ril_unsol_commands.h \
              | egrep "^ *{RIL_" \
              | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/'
         */

    ...      
             /**
              * Read a string value from the parcel at the current dataPosition().
              */                                                                ↑ p.readString()  
            case RIL_UNSOL_RESPONSE_NEW_SMS: ret =  responseString(p); break;
            case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret =  responseString(p);
                                                                                                   break;
            case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret =  responseInts(p); break;
    ...
            default:
                throw new RuntimeException("Unrecognized unsol response: " + response);
            //break; (implied)
        }} catch (Throwable tr) {
            Log.e(LOG_TAG, "Exception processing unsol response: " + response +
                "Exception:" + tr.toString());
            return;
        }

        switch(response) {
            case RIL_UNSOL_RESPONSE_NEW_SMS: {
                if (RILJ_LOGD) unsljLog(response);
                                   //  ↑   D/RILJ    (  610): [UNSL]< UNSOL_RESPONSE_NEW_SMS
                                   // 로그 프린트

                // FIXME this should move up a layer
                String a[] = new String[2];

                a[1] = (String)ret;

                SmsMessage sms;

                sms = SmsMessage.newFromCMT(a);                
                if (mSMSRegistrant != null) {  
                    mSMSRegistrant
                        .notifyRegistrant(new AsyncResult(null, sms, null));
                }
            break;
            }
        }
    }


SmsMessage.newFromCMT(a)
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
/**
     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
     * +CMT unsolicited response (PDU mode, of course)
     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
     *
     * Only public for debugging
     *
     * {@hide}
     */
/* package */ 
public static SmsMessage newFromCMT(String[] lines) {
    try {
        SmsMessage msg = new SmsMessage();
        msg.parsePdu(SimUtils.hexStringToBytes(lines[1]));
        return msg;
    } catch (RuntimeException ex) {
        Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
        return null;
    }
}


msg.parsePdu(...)
/open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
/**
     * TS 27.005 3.1, <pdu> definition "In the case of SMS: 3GPP TS 24.011 [6]
     * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
     * ME/TA converts each octet of TP data unit into two IRA character long
     * hexad number (e.g. octet with integer value 42 is presented to TE as two
     * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
     * something else...
     */

int mti; // TP-Message-Type-Indicator
          // 9.2.3
String scAddress; /* The address of the SMSC. May be null */
   
private void parsePdu(byte[] pdu) {
        mPdu = pdu;
        // Log.d(LOG_TAG, "raw sms mesage:");
        // Log.d(LOG_TAG, s);

        PduParser p = new PduParser(pdu);

        scAddress = p.getSCAddress();  
                                  
        /* int android.telephony.gsm.SmsMessage.PduParser.getByte() 에 의해 SCAddress길이를
         *  알수 있고, 이 integer값으로 if문을 통해
         *  PhoneNumberUtils.calledPartyBCDToString(pdu, cur, len) 메소드가 실행되고,
         *  String 타입의 리턴값을 받는다.
         */
        
        if (scAddress != null) {
            if (Config.LOGD) Log.d(LOG_TAG, "SMS SC address: " + scAddress);
        }

        // TODO(mkf) support reply path, user data header indicator

        // TP-Message-Type-Indicator
        // 9.2.3
        int firstByte = p.getByte(); /* return pdu[cur++] & 0xff; */
                                                                  
        /* 위의 PduParser 인스턴스 p 생성시 생성자에 의해 PduParser 클래스의 integer 형 변수
         * cur, mUserDataSeptetPadding 는 0 으로 셋팅된다.
         */

        mti = firstByte & 0x3;
        switch (mti) {
        // TP-Message-Type-Indicator
        // 9.2.3
        case 0:
            parseSmsDeliver(p, firstByte);
            break;
        case 2:
            parseSmsStatusReport(p, firstByte);
            break;
        default:
            // TODO(mkf) the rest of these
            throw new RuntimeException("Unsupported message type");
        }
}


parseSmsDeliver(p, firstByte)
/open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java

boolean replyPathPresent = false; // TP-Reply-Path
                                              // e.g. 23.040 9.2.2.1
SmsAddress originatingAddress; /* The address of the sender */
int protocolIdentifier; /* TP-Protocol-Identifier (TP-PID) */
int dataCodingScheme; // TP-Data-Coding-Scheme
                                // see TS 23.038
    
private void parseSmsDeliver(PduParser p, int firstByte) {
        replyPathPresent = (firstByte & 0x80) == 0x80;

        originatingAddress = p.getAddress();

        if (originatingAddress != null) {
            if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
                    + originatingAddress.address);
        }

        // TP-Protocol-Identifier (TP-PID)
        // TS 23.040 9.2.3.9
        protocolIdentifier = p.getByte();

        // TP-Data-Coding-Scheme
        // see TS 23.038
        dataCodingScheme = p.getByte();

        if (Config.LOGV) {
            Log.v(LOG_TAG, "SMS TP-PID:" + protocolIdentifier
                    + " data coding scheme: " + dataCodingScheme);
        }

        scTimeMillis = p.getSCTimestampMillis();
                                          
        // Parses an SC timestamp and returns a currentTimeMillis()-style
        // timestamp

        if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
                                                                
                                            // D/GSM     (  610): SMS SC timestamp: 1265783527000
                                            // 로그 프린트
        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

        parseUserData(p, hasUserDataHeader); //Parses the User Data of an SMS.
    }


parseUserData(p, hasUserDataHeader)
/open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
private void parseUserData(PduParser p, boolean hasUserDataHeader) {
        boolean hasMessageClass = false;
        boolean userDataCompressed = false;

        int encodingType = ENCODING_UNKNOWN;

        // Look up the data encoding scheme
        if ((dataCodingScheme & 0x80) == 0) {
            // Bits 7..4 == 0xxx
            automaticDeletion = (0 != (dataCodingScheme & 0x40));
            userDataCompressed = (0 != (dataCodingScheme & 0x20));
            hasMessageClass = (0 != (dataCodingScheme & 0x10));

            if (userDataCompressed) {
                Log.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
                        + "(compression) " + (dataCodingScheme & 0xff));
            } else {
                switch ((dataCodingScheme >> 2) & 0x3) {
                case 0: // GSM 7 bit default alphabet
                    encodingType = ENCODING_7BIT;
                    break;

                case 2: // UCS 2 (16bit)
                    encodingType = ENCODING_16BIT;
                    break;

                case 1: // 8 bit data
                case 3: // reserved
                    Log.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
                            + (dataCodingScheme & 0xff));
                    encodingType = ENCODING_8BIT;
                    break;
                }
            }
        } else if ((dataCodingScheme & 0xf0) == 0xf0) {
            automaticDeletion = false;
            hasMessageClass = true;
            userDataCompressed = false;

            if (0 == (dataCodingScheme & 0x04)) {
                // GSM 7 bit default alphabet
                encodingType = ENCODING_7BIT;
            } else {
                // 8 bit data
                encodingType = ENCODING_8BIT;
            }
        } else if ((dataCodingScheme & 0xF0) == 0xC0
                || (dataCodingScheme & 0xF0) == 0xD0
                || (dataCodingScheme & 0xF0) == 0xE0) {
            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4

            // 0xC0 == 7 bit, don't store
            // 0xD0 == 7 bit, store
            // 0xE0 == UCS-2, store

            if ((dataCodingScheme & 0xF0) == 0xE0) {
                encodingType = ENCODING_16BIT;
            } else {
                encodingType = ENCODING_7BIT;
            }

            userDataCompressed = false;
            boolean active = ((dataCodingScheme & 0x08) == 0x08);

            // bit 0x04 reserved

            if ((dataCodingScheme & 0x03) == 0x00) {
                isMwi = true;
                mwiSense = active;
                mwiDontStore = ((dataCodingScheme & 0xF0) == 0xC0);
            } else {
                isMwi = false;

                Log.w(LOG_TAG, "MWI for fax, email, or other "
                        + (dataCodingScheme & 0xff));
            }
        } else {
            Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
                    + (dataCodingScheme & 0xff));
        }

        // set both the user data and the user data header.
        int count = p.constructUserData(hasUserDataHeader,
                encodingType == ENCODING_7BIT);
        this.userData = p.getUserData();
        this.userDataHeader = p.getUserDataHeader();

        switch (encodingType) {
        case ENCODING_UNKNOWN:
        case ENCODING_8BIT:
            messageBody = null;
            break;

        case ENCODING_7BIT:
            messageBody = p.getUserDataGSM7Bit(count);
            break;                              
           // Convert a GSM alphabet 7 bit packed string (SMS string) into a java.lang.String.
               See TS 23.038 6.1.2.1 for SMS Character Packing
        case ENCODING_16BIT:
            messageBody = p.getUserDataUCS2(count);
            break;
        }

        if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");

        if (messageBody != null) {
            parseMessageBody();
        }

        if (!hasMessageClass) {
            messageClass = MessageClass.UNKNOWN;
        } else {
            switch (dataCodingScheme & 0x3) {
            case 0:
                messageClass = MessageClass.CLASS_0;
                break;
            case 1:
                messageClass = MessageClass.CLASS_1;
                break;
            case 2:
                messageClass = MessageClass.CLASS_2;
                break;
            case 3:
                messageClass = MessageClass.CLASS_3;
                break;
            }
        }
}


parseMessageBody()
/open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
private void parseMessageBody() {  // return address.length() <= 4;
        if (originatingAddress.couldBeEmailGateway()) {
            extractEmailAddressFromMessageBody();
        }
}


extractEmailAddressFromMessageBody();
/open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
/**
     * Try to parse this message as an email gateway message -> Neither
     * of the standard ways are currently supported: There are two ways
     * specified in TS 23.040 Section 3.8 (not supported via this mechanism) -
     * SMS message "may have its TP-PID set for internet electronic mail - MT
     * SMS format: [<from-address><space>]<message> - "Depending on the
     * nature of the gateway, the destination/origination address is either
     * derived from the content of the SMS TP-OA or TP-DA field, or the
     * TP-OA/TP-DA field contains a generic gateway address and the to/from
     * address is added at the beginning as shown above." - multiple addreses
     * separated by commas, no spaces - subject field delimited by '()' or '##'
     * and '#' Section 9.2.3.24.11
     */
private void extractEmailAddressFromMessageBody() {

        /*
         * a little guesswork here. I haven't found doc for this.
         * the format could be either
         *
         * 1. [x@y][ ]/[subject][ ]/[body]
         * -or-
         * 2. [x@y][ ]/[body]
         */
        int slash = 0, slash2 = 0, atSymbol = 0;

        try {
            slash = messageBody.indexOf(" /");
            if (slash == -1) {
                return;
            }

            atSymbol = messageBody.indexOf('@');
            if (atSymbol == -1 || atSymbol > slash) {
                return;
            }

            emailFrom = messageBody.substring(0, slash);

            slash2 = messageBody.indexOf(" /", slash + 2);

            if (slash2 == -1) {
                pseudoSubject = null;
                emailBody = messageBody.substring(slash + 2);
            } else {
                pseudoSubject = messageBody.substring(slash + 2, slash2);
                emailBody = messageBody.substring(slash2 + 2);
            }

            isEmail = true;
        } catch (Exception ex) {
            Log.w(LOG_TAG,
                    "extractEmailAddressFromMessageBody: exception slash="
                    + slash + ", atSymbol=" + atSymbol + ", slash2="
                    + slash2, ex);
        }
}


여기까지 parse 작업이 완료되면,
SmsMessage.newFromCMT(a) {} 부분의 return msg 부분이 실행되어
processUnsolicited (Parcel P) {} 부분의 밑부분의 if문이 실행된다.
/open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
private void processUnsolicited (Parcel p)
{
...
   
    if (mSMSRegistrant != null) {  
        mSMSRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
        
    /* mSMSRegistrant 는 Registrant의 인스턴스, 정의되어있는 곳은 RIL이 상속하고 있는
     * BaseCommands 이다. 인스턴스화된 곳을 찾아 올라가면
     * BaseCommands의 메소드인 setOnNewSMS()에서 생성됨을 알수 있고, setOnNewSMS()를
     * 호출한 곳을 역으로 올라가면,
     * setOnNewSMS()
SMSDispatcher GSMPhone       *   ⇠ PhoneFactory.makeDefaultPhones() ⇠ PhoneApp.onCreate()  
      
*    PhoneApp 는 system의 server thread가 생성하였다.
     */
        
...
}


mSMSRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
/open_src/frameworks/base/Core/Java/Os/Registrant.java
/**
 * This makes a copy of @param ar
 */

public void notifyRegistrant(AsyncResult ar)
{
    internalNotifyRegistrant (ar.result, ar.exception);
}

    


internalNotifyRegistrant(ar.result, ar.exception);
/open_src/frameworks/base/Core/Java/Os/Registrant.java
/*package*/

void internalNotifyRegistrant (Object result, Throwable exception)
{
        Handler h = getHandler();

        if (h == null) {
            clear();
        } else {                         //   Return a new Message instance from the global pool.
            Message msg = Message.obtain();

            msg.what = what;
            
            msg.obj = new AsyncResult(userObj, result, exception);
            
            h.sendMessage(msg); 
       }    
}


h.sendMesage(msg)
/open_src/frameworks/base/Core/Java/Android/Os/Handler.java
/**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  /

public final boolean sendMessage(Message msg)
{
        return sendMessageDelayed(msg, 0);
}


sendMessageDelayed(msg, 0)
/open_src/frameworks/base/Core/Java/Android/Os/Handler.java
/**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  /

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}


sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
/open_src/frameworks/base/Core/Java/Android/Os/Handler.java
   /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     */ 

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
            /* 메시지 전송 delay를 설정해주고, 메시지 큐에 넣는다.
             * 메시지큐에 메시지가 들어오면 시스템에서 handleMassage()를 실행한다.
             * handleMessage()는 Handler를 상속한 SMSDiapatcher에서 오버라이딩된
             * handleMessage()가 실행된다.
             * delay설정은 시스템의 스케줄링에 도움을 주기 위해서 이다.
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
}


handelMessage(msg)
/open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
SMSDispatcher.java
/**
     * Handles events coming from the phone stack. Overridden from handler.
     *
     * @param msg the message to handle
     */

    @Override
    public void handleMessage(Message msg) {
        AsyncResult ar;

        switch (msg.what) {
        case EVENT_NEW_SMS:
            // A new SMS has been received by the device
            if (Config.LOGD) {
                Log.d(TAG, "New SMS Message Received");

                               //    D/GSM     (  610): New SMS Message Received

            }                 // 로그 프린트

            SmsMessage sms;

            ar = (AsyncResult) msg.obj;

            // FIXME unit test leaves cm == null. this should change
            if (mCm != null) {
                // FIXME only acknowledge on store
                mCm.acknowledgeLastIncomingSMS(true, null);
            }

            if (ar.exception != null) {
                Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception);
                return;
            }

            sms = (SmsMessage) ar.result;
            dispatchMessage(sms);

            break;

        case EVENT_SEND_SMS_COMPLETE:
            // An outbound SMS has been sucessfully transferred, or failed.
            handleSendComplete((AsyncResult) msg.obj);
            break;

        case EVENT_SEND_RETRY:
            sendSms((SmsTracker) msg.obj);
            break;

        case EVENT_NEW_SMS_STATUS_REPORT:
            handleStatusReport((AsyncResult)msg.obj);
            break;

        case EVENT_SIM_FULL:
            handleSimFull();
            break;

        case EVENT_POST_ALERT:
            handleReachSentLimit((SmsTracker)(msg.obj));
            break;

        case EVENT_ALERT_TIMEOUT:
            ((AlertDialog)(msg.obj)).dismiss();
            msg.obj = null;
            mSTracker = null;
            break;

        case EVENT_SEND_CONFIRMED_SMS:
            if (mSTracker!=null) {
                if (isMultipartTracker(mSTracker)) {
                    sendMultipartSms(mSTracker);
                } else {
                    sendSms(mSTracker);
                }
                mSTracker = null;
            }
            break;
        }
    }


dispatchMessage(msg)
/open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
SMSDispatcher.java
/**
     * Dispatches an incoming SMS messages.
     *
     * @param sms the incoming message from the phone
     */
   
 /* package */

void dispatchMessage(SmsMessage sms) {
        // If sms is null, means there was a parsing error.
        // TODO: Should NAK this.
        if (sms == null) {
            return;
        }

        boolean handled = false;

        // Special case the message waiting indicator messages
        if (sms.isMWISetMessage()) {
            mPhone.updateMessageWaitingIndicator(true);

            if (sms.isMwiDontStore()) {
                handled = true;
            }

            if (Config.LOGD) {
                Log.d(TAG,
                        "Received voice mail indicator set SMS shouldStore="
                         + !handled);
            }
        } else if (sms.isMWIClearMessage()) {
            mPhone.updateMessageWaitingIndicator(false);

            if (sms.isMwiDontStore()) {
                handled = true;
            }

            if (Config.LOGD) {
                Log.d(TAG,
                        "Received voice mail indicator clear SMS shouldStore="
                        + !handled);
            }
        }

        if (handled) {
            return;
        }

        // Parse the headers to see if this is partial, or port addressed
        int referenceNumber = -1;
        int count = 0;
        int sequence = 0;
        int destPort = -1;

        SmsHeader header = sms.getUserDataHeader();
        if (header != null) {
            for (SmsHeader.Element element : header.getElements()) {
                try {
                    switch (element.getID()) {
                        case SmsHeader.CONCATENATED_8_BIT_REFERENCE: {
                            byte[] data = element.getData();
                            
                            referenceNumber = data[0] & 0xff;
                            count = data[1] & 0xff;
                            sequence = data[2] & 0xff;
                            
                            // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence
                            // is zero, or sequence > count, ignore the entire element
                            if (count == 0 || sequence == 0 || sequence > count) {
                                referenceNumber = -1;
                            }
                            break;
                        }
                        
                        case SmsHeader.CONCATENATED_16_BIT_REFERENCE: {
                            byte[] data = element.getData();
                            
                            referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff);
                            count = data[2] & 0xff;
                            sequence = data[3] & 0xff;
                            
                            // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence
                            // is zero, or sequence > count, ignore the entire element
                            if (count == 0 || sequence == 0 || sequence > count) {
                                referenceNumber = -1;
                            }
                            break;
                        }
                        
                        case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: {
                            byte[] data = element.getData();
                            
                            destPort = (data[0] & 0xff) << 8;
                            destPort |= (data[1] & 0xff);
                            
                            break;
                        }
                    }
                } catch (ArrayIndexOutOfBoundsException e) {
                    Log.e(TAG, "Bad element in header", e);
                    return;  // TODO: NACK the message or something, don't just discard.
                }
            }
        }

        if (referenceNumber == -1) {
            // notify everyone of the message if it isn't partial
            byte[][] pdus = new byte[1][];
            pdus[0] = sms.getPdu();

            if (destPort != -1) {
                if (destPort == SmsHeader.PORT_WAP_PUSH) {
                    mWapPush.dispatchWapPdu(sms.getUserData());
                }
                // The message was sent to a port, so concoct a URI for it
                dispatchPortAddressedPdus(pdus, destPort);
            } else {
                // It's a normal message, dispatch it
                dispatchPdus(pdus);
            }
        } else {
            // Process the message part
            processMessagePart(sms, referenceNumber, sequence, count, destPort);
        }
    }


dispatchPdus(pdus)
/open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
SMSDispatcher.java
/**
     * Dispatches standard PDUs to interested applications
     *
     * @param pdus The raw PDUs making up the message
     */

private void dispatchPdus(byte[][] pdus) {
        Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
        intent.putExtra("pdus", pdus);
        sendBroadcast(intent, "android.permission.RECEIVE_SMS");
}


sendBroadcast(intent, "android.permission.RECEIVE_SMS");
/open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
SMSDispatcher.java
private void sendBroadcast(Intent intent, String permission) {
        // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any
        // receivers time to take their own wake locks.
        
        mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 
        /* 메소드를 타고 들어가면, delay 주고, 핸들러의 메시지큐에 메시지를 넣는다.
         *  acquire mHandler.postDelayed() ⇠ sendMessageDelayed()                
         *  ⇠ sendMessageAtTime ⇠ enqueueMessage()
         */
        // 메시지큐에 메시지가 왔음으로 handleMessage()가 실행된다.
        mContext.sendBroadcast(intent, permission);
          // BroadcastReceiver 실행된다.
}


handleMessage(Message msg)
/open_src/Packages/Apps/Src/Com/Android/Mms/Transactions/SmsReceiverService.java
/**
         * Handle incoming transaction requests.
         * The incoming requests are initiated by the MMSC Server or by the
         * MMS Client itself.
         */
        
@Override
public void handleMessage(Message msg) {
            if (Log.isLoggable(MmsApp.LOG_TAG, Log.VERBOSE)) {
                Log.v(TAG, "Handling incoming message: " + msg);
            }
            int serviceId = msg.arg1;
            Intent intent = (Intent)msg.obj;

            String action = intent.getAction();

            if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
                handleSmsSent(intent);
            } else if (SMS_RECEIVED_ACTION.equals(action)) {
                handleSmsReceived(intent);
            } else if (ACTION_BOOT_COMPLETED.equals(action)) {
                handleBootCompleted();
            } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
                handleServiceStateChanged(intent);
            }

            // NOTE: We MUST not call stopSelf() directly, since we need to
            // make sure the wake lock acquired by AlertReceiver is released.
            SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId);
}


handleSmsReceived(intent)
/open_src/Packages/Apps/Src/Com/Android/Mms/Transactions/SmsReceiverService.java
private void handleSmsReceived(Intent intent) {
        SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
        Uri messageUri = insertMessage(this, msgs);

        if (messageUri != null) {
            MessagingNotification.updateNewMessageIndicator(this, true);
        }
}


MessagingNotification.updateNewMessageIndicator(this, true)
Open_src/Package/Apps/Mms/Src/Com/Android/Mms/Transaction/MessagingNotification.java
/**
     * Checks to see if there are any unread messages or delivery
     * reports.  Shows the most recent notification if there is one.
     *
     * @param context the context to use
     * @param isNew if notify a new message comes, it should be true, otherwise, false.
     */
public static void updateNewMessageIndicator(Context context, boolean isNew) {
        SortedSet<MmsSmsNotificationInfo> accumulator =
                new TreeSet<MmsSmsNotificationInfo>(INFO_COMPARATOR);
        Set<Long> threads = new HashSet<Long>(4);
        
        int count = 0;
        count += accumulateNotificationInfo(
                accumulator, getMmsNewMessageNotificationInfo(context, threads));
        count += accumulateNotificationInfo(
                accumulator, getSmsNewMessageNotificationInfo(context, threads));

        cancelNotification(context, NOTIFICATION_ID);
        if (!accumulator.isEmpty()) {
            accumulator.first().deliver(context, isNew, count, threads.size());
        }
    }


accumulator.first().deliver(context, isNew, count, threads.size())
Open_src/Package/Apps/Mms/Src/Com/Android/Mms/Transaction/MessagingNotification.java
public void deliver(Context context, boolean isNew, int count, int uniqueThreads) {
            updateNotification(
                    context, mClickIntent, mDescription, mIconResourceId,
                    isNew, mTicker, mTimeMillis, mTitle, count, uniqueThreads);
        }


updateNotification(...)
Open_src/Package/Apps/Mms/Src/Com/Android/Mms/Transaction/MessagingNotification.java
private static void updateNotification(
            Context context,
            Intent clickIntent,
            String description,
            int iconRes,
            boolean isNew,
            CharSequence ticker,
            long timeMillis,
            String title,
            int messageCount,
            int uniqueThreadCount) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

        if (!sp.getBoolean(
                    MessagingPreferenceActivity.NOTIFICATION_ENABLED, true)) {
            return;
        }

        Notification notification = new Notification(iconRes, ticker, timeMillis);

        // If we have more than one unique thread, change the title (which would
        // normally be the contact who sent the message) to a generic one that
        // makes sense for multiple senders, and change the Intent to take the
        // user to the conversation list instead of the specific thread.
        if (uniqueThreadCount > 1) {
            title = context.getString(R.string.notification_multiple_title);
            clickIntent = getAppIntent();
            clickIntent.setAction(Intent.ACTION_MAIN);
            clickIntent.setType("vnd.android-dir/mms-sms");
        }
        
        // If there is more than one message, change the description (which
        // would normally be a snippet of the individual message text) to
        // a string indicating how many unread messages there are.
        if (messageCount > 1) {
            description = context.getString(R.string.notification_multiple,
                    Integer.toString(messageCount));
        }

        // Make a startActivity() PendingIntent for the notification.
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, clickIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        // Update the notification.
        notification.setLatestEventInfo(context, title, description, pendingIntent);

        if (isNew) {
            boolean vibrate =     
                    sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_VIBRATE, true);
            if (vibrate) {
                notification.defaults |= Notification.DEFAULT_VIBRATE;
            }

            String ringtoneStr = sp
                    .getString(MessagingPreferenceActivity.NOTIFICATION_RINGTONE, null);
            notification.sound = TextUtils.isEmpty(ringtoneStr) ? null : Uri.parse(ringtoneStr);
        }

        notification.flags |= Notification.FLAG_SHOW_LIGHTS;
        notification.ledARGB = 0xff00ff00;
        notification.ledOnMS = 500;
        notification.ledOffMS = 2000;

        NotificationManager nm = (NotificationManager)
            context.getSystemService(Context.NOTIFICATION_SERVICE);

        nm.notify(NOTIFICATION_ID, notification);
}

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
,
1. Menus (res/menu/*)

 ; android 네임스페이스를 가지고 정의되어야 한다
  (xmlns:android="http://schemas.android.com/apk/res/android")
 
 <menu>
  ; <item>과 <group> 노드들을 포함한다.
 
 <group>
  ; <item> element 포함
  id - 고유정수 integer ID
  menuCategory - 메뉴 우선순위(container,secondary,alternative)
  orderInCategory - category 내에서 item default 순서를 정의하는 정수
  title - 메뉴 타이틀의 문자열
  titleCondensed - 간결한 문자열 타이틀
  icon - drawable icon에 대한 리소스 식별자
  alphabeticShortcut - 알파벳 단축키
  numericShortcut - 숫자 단축키
  checkable - item check 가능 여부 (true, false)
  checked - item default check 가능 여부(true, false)
  visible - item default로 보여지는지 여부(true, false)
  enabled - item default로 활성화 하는지 여부(true, false)
 
(ex)
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
   <item  
     android:id="@+id/ex_item"
     android:title="Example Item"
     android:icon="@drawable/ex_item_icon" />
   <group android:id="@+id/ex_group">
     <item  
       android:id="@+id/ex_item2"
       android:title="Example Item2"
       android:icon="@drawable/ex_item2_icon" />
   </group>
   <item  
     android:id="@+id/ex_subMenu"
     android:title="Example subMenu" >
      <menu>
        <item
          android:id="@+id/ex_subMenu"_item"
         android:title="Example subMenu item" />
     </menu>
   </item>
 </menu>



2. Searchable

    ; 검색기능을 제어하게끔 한다.

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search_label"
    android:hint="@string/search_hint"
    ....
    <actionkey android:keycode="KEYCODE_CALL"...>
/>

'old > UI Design' 카테고리의 다른 글

adb shell에서 특정 activity실행하기  (0) 2010.07.06
PowerManager에서 Screen ON/OFF 통지  (0) 2010.07.06
Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
String & StyledText  (0) 2010.06.30
Style & Theme  (0) 2010.06.30
Posted by jazzlife
,

1. Dimension Values

px : 스크린 상의 실제 픽셀
in : 스크린의 물리적 크기에 기반한 인치
mm: 스크린의 물리적 크기에 기반한 밀리미터
pt: 스크린의 물리적 크기에 기반한 인치의 1/72
dp(dip): Density-independent Pixels
sp: Scale-independent Pixels, dp와 폰트크기에 의해 결정됨

(resources구문) - <dimen name="name">1px</dimen>
(xml구문) - android:textSize="@dimen/name"
(java코드) - float dimen = Resources.getDimen(R.dimen.name)




2. Color Drawables - colors.xml

(resources구문) - <drawable name="solid_red>#f00</drawable>
(java코드)
 Drawable redDrawable = Resources.getDrawable(R.drawable.solid_red);
 TextView tv = (Textview) findViewById(R.id.text);
 tv.setBackground(redDrawable);



3. Nine-Patch Stretchable Image
    ; 확대 가능한 이미지 PNG 타입.
      확장자 *.9.png

4. Animation

1) Tweened Animation
    ; rotate, fading, moving, stretching을 포함한 간단한 애니메이션

<set>
    ; 자기 자신 또는 다른 애니메이션을 재귀적으로 보유할 수 있는 컨테이너. 많은 child 앨리먼트 포함가능.
 
  shareInterpolator - 모든 인접한 child 앨리먼트 사이에  같은 interpolator를 공유하는지에 대한 여부.

<alpha>
    ; fading, AlphaAnimation을 표현

    fromAlpha - 0.0~1.0 (0.0은 투명)
    toAlpha - 0.0~1.0 (0.0은 투명)

<scale>
    ; 크기를 변경하는 애니메이션. ScaleAnimation을 표현

    fromXScale - 시작하는 X의 크기(1.0은 변화없음)
    toXScale - 끝나는 X의 크기(1.0은 변화없음)
    fromYScale - 시작하는 Y의 크기(1.0은 변화없음)
    toYScale - 끝나는 Y의 크기(1.0은 변화없음)
    pivotX - 핀이 꽂힌 중심의 X좌표
    pivotY - 핀이 꽂힌 중심의 Y좌표

<translate>
    ; 수직/수평 움직임 애니메이션. TranslateAnimation을 표현.
    '%'로 끝나는 -100~100 값은 자신에 상대적인 백분율.
    '%p'로 끝나는 -100~100 값은 parent에 상대적인 백분율.
     접미어가 없는 부동소수점은 절대 값을 가리킨다.

    fromXDelta - 시작하는 X 위치.
    toXDelta - 끝나는 X 위치.
    fromYDelta - 시작하는 Y 위치.
    toYDelta - 끝나는 Y 위치.

<rotate>
    ; 회전 애니메이션. RotateAnimation을 표현

    fromDegrees - 회전 시작 각도
    toDegrees - 회전 끝 각도
    pivotX - 회전 중심에 대한 픽셀 X 좌표. (0,0)은 왼쪽 상단 모서리
    pivotY - 회전 중심에 대한 픽셀 Y 좌표. (0,0)은 왼쪽 상단 모서리

<interpolator tag>
    ; R.styleable 에서 정의된 임의의 interpolator 서브클래스 엘리먼트를 사용할 수 있다.
      타임라인 상에서 시각적 액션이 얼마나 빠르게 일어나는가를 설명하는 속도 곡선을 정의

: 공통 엘리먼트

duration - 효과 시간(1/1000s)
startOffset - 효과에 대한 오프셋 시작 시간(1/1000s)
fillBefore - true로 설정할 때, 애니메이션 시작 전에 적용
fillAfter - true로 설정할 때, 애니메이션 시작 후에 적용
repeatCount - 애니메이션 반복 횟수
repeatMode - repeatCount가 0보다 클 때. 끝나는 순간의 동작을 정의 (옵션: 다시시작, 역진행)
zAdjustment - 애니메이션 실행 시 z-축 지시모드 (normal, top, bottom)
interpolator
   EaseInInterpolator - 점점 빠르게
   EaseOutInInterpolator - 점점 느리게
 리스트는 R.anim에서 제공되고 지정하기 위해서는 @android:anim/interpolatorName 구문을 이용

(예)

<set android:shareInterpolator=boolean>

 <alpha android:fromAlpha="0.5"
     android:toAlpha="0.5">  |
 
 <scale android:fromXScale="0.5"
     android:toXScale="0.5"
     android:fromYScale="0.5"
     android:toYScale="0.5"
     android:pivotX="(0.5,0.5)"
     android:pivotY="(0.5,0.5)"> |

 <translate android:fromXDelta="%50"
       android:toYDelta="%50"
       android:fromYDelta="%p50"
       android:toYDelta="%p50"> |

 <rotate android:fromDegrees="1.0"
     android:toDegrees="1.0"
     android:pivotX="(1.0,1.0)"
     android:pivotY="(1.0.1.0)" |
     
 <interpolator tag>

</set>

'old > UI Design' 카테고리의 다른 글

PowerManager에서 Screen ON/OFF 통지  (0) 2010.07.06
Resources - Menus, Searchable  (0) 2010.07.01
String & StyledText  (0) 2010.06.30
Style & Theme  (0) 2010.06.30
Notification  (0) 2010.06.17
Posted by jazzlife
,

String & StyledText

old/UI Design 2010. 6. 30. 20:09
1. html tag 사용

[strings.xml]
    <string name="ex1">"This'll work"</string>
    <string name="ex2">This\'ll work</string>
    <string name="ex3">This <b><i>work</i></b></string>

2. html encoding 사용

[styledStrings.java]
       String escapedTitle = TextUtils.htmlEncode("as");
        String resultsTextFormat = this.getResources().getString(R.string.ex4);
        String resultsText = String.format(resultsTextFormat,1,escapedTitle);
        CharSequence styledResults = Html.fromHtml(resultsText);
       
        LayoutInflater inflater = LayoutInflater.from(this);
       
        View layout = inflater.inflate(R.layout.main, (ViewGroup) findViewById(R.id.root));
        TextView text = (TextView) layout.findViewById(R.id.htmlStr);
        text.setText(styledResults);
       
        setContentView(layout);

[strings.xml]
    <string name="ex4">%1d results for &lt;b>%2$s&lt;/b></string>


'old > UI Design' 카테고리의 다른 글

Resources - Menus, Searchable  (0) 2010.07.01
Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
Style & Theme  (0) 2010.06.30
Notification  (0) 2010.06.17
UI 이벤트 처리  (0) 2010.06.15
Posted by jazzlife
,

우분투에서
realvncview(vnc-4_1_3-x86_linux.tar.gz)를 실행했을때
"libstdc++-libc6.2-2.so.3: cannot open shared object file: No such file or directory"
위의 에러가 뜨면

libstdc++2.10-glibc2.2/download
1. 위의 링크중에서 빠른 지역 서버를 눌러 libstdc++2.10-glibc2.2_2.95.4-24_i386.deb를 받는다.
2. sudo dpkg --force-architecture -i libstdc++2.10-glibc2.2_2.95.4-24_i386.deb
이명령어로 패키지를 설치한다.

apt에는 libstdc++2.10버전이 사라졌으며
cd /usr/lib
ln -s  libstdc++.so.지금버전  libstdc++-libc6.2-2.so.3

하면 라이브러리 호환성이 떨어질수도...

'Web > Ubuntu' 카테고리의 다른 글

파일시스템에 대한 내용  (0) 2010.03.10
리눅스 계정관리  (0) 2010.03.08
deb, rpm, bin 설치  (0) 2010.02.26
리눅스 삼바 서버 - 리눅스 삼바 클라이언트 접속 방법  (0) 2010.02.09
Samba install & Setting  (0) 2010.02.08
Posted by jazzlife
,

deb, rpm, bin 설치

Web/Ubuntu 2010. 2. 26. 12:12

1. deb

  • sudo dpkg -i **.deb

2. rpm

  • sudo alien -k --scripts 변환할파일.rpm
  • sudo dpkg -i **.deb

3. bin

  • sh 파일명

     

'Web > Ubuntu' 카테고리의 다른 글

리눅스 계정관리  (0) 2010.03.08
libstdc++-libc6.2-2.so.3 없을 때  (0) 2010.02.26
리눅스 삼바 서버 - 리눅스 삼바 클라이언트 접속 방법  (0) 2010.02.09
Samba install & Setting  (0) 2010.02.08
turn off beep sound  (0) 2010.02.08
Posted by jazzlife
,

Intent

old/definition 2010. 2. 25. 14:55

An intent is an abstract description of an operation to be performed. It can be used with startActivity to launch an Activity, broadcastIntent to send it to any interested BroadcastReceiver components, and startService(Intent) or bindService(Intent, ServiceConnection, int) to communicate with a background Service.

An Intent provides a facility for performing late runtime binding between the code in different applications. Its most significant use is in the launching of activities, where it can be thought of as the glue between activities. It is basically a passive data structure holding an abstract description of an action to be performed. The primary pieces of information in an intent are:

  • action -- The general action to be performed, such as ACTION_VIEW, ACTION_EDIT, ACTION_MAIN, etc.

  • data -- The data to operate on, such as a person record in the contacts database, expressed as a Uri.

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

Context  (0) 2010.02.25
Posted by jazzlife
,

Context

old/definition 2010. 2. 25. 14:54
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

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

Intent  (0) 2010.02.25
Posted by jazzlife
,

style

old/UI Design 2010. 2. 22. 20:40

styles.xml

<?xml version="1.0" encoding="utf-8"?> 
<resources>
 
   
<style name="CodeFont" parent="@android:style/TextAppearance.Medium">
 
       
<item name="android:layout_width">fill_parent</item>
 
       
<item name="android:layout_height">wrap_content</item>
 
       
<item name="android:textColor">#00FF00</item>
 
       
<item name="android:typeface">monospace</item>
 
   
</style>
 
</resources>


R.style에서 다양한 속성들을 상속받아서 style로 사용한다.

(ex)
<style name="CodeFont" parent="@android:style/TextAppearance.Medium"> 

<style  name="Theme.CutomDialog" parent="android:style/Theme.Dialog">

 
상속받은 item을 정의 한다.
       
<item name="android:textColor">#00FF00</item> 
<item name="android:typeface">monospace</item>

<item name="android:windowBackground">@drawable/filled_box</item>


다른 곳에서 레이아웃을 불러와서 style을 적용하기도 한다.

[/drawable/filled_box.xml]
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#f0600000"/>
    <stroke android:width="3dp" color="#ffff8080"/>
    <corners android:radius="3dp" />
    <padding android:left="10dp" android:top="10dp"
        android:right="10dp" android:bottom="10dp" />
</shape>


사용하기
<EditText 
   
style="@style/Numbers"     ... />

이런 식으로 activity 전체에 style 적용하기도 한다.

[AndroidManifest.xml]
        <activity android:name=".ActivityCustomDialog"
                  android:label="@string/app_name"
                  android:theme="@style/Theme.CutomDialog">

'old > UI Design' 카테고리의 다른 글

Notification  (0) 2010.06.17
UI 이벤트 처리  (0) 2010.06.15
기본 UI 생성 순서  (0) 2010.02.22
R.anim  (0) 2010.02.22
R.integer  (0) 2010.02.22
Posted by jazzlife
,

1. Layout source를 작성한다.
2. values/strings.xml을 작성한다.
3. values/styles.xml을 작성한다.
4. 그 외 drawable에 필요한 source를 작성한다.
4. src/source를 작성한다.
5. AndroidManifest.xml을 작성하여 연결시킨다.

'old > UI Design' 카테고리의 다른 글

UI 이벤트 처리  (0) 2010.06.15
style  (0) 2010.02.22
R.anim  (0) 2010.02.22
R.integer  (0) 2010.02.22
AlphaAnimation for Fading Animation  (0) 2010.02.22
Posted by jazzlife
,

Animation

Tweened Animation

Android can perform simple animation on a graphic, or a series of graphics. These include rotations, fading, moving, and stretching.

Source file format: XML file, one resource per file, one root tag with no <?xml> declaration

Resource file location: res/anim/some_file.xml

Compiled resource datatype: Resource pointer to an Animation.

Resource reference name:

  • Java: R.anim.some_file
  • XML: @[package:]anim/some_file

Syntax

The file must have a single root element: this will be either a single <alpha>, <scale>, <translate>, <rotate>, interpolator element, or <set> element that holds groups of these elements (which may include another <set>). By default, all elements are applied simultaneously. To have them occur sequentially, you must specify the startOffset attribute.



Elements and Attributes

<set>
A container that can recursively hold itself or other animations. Represents an AnimationSet. You can include as many child elements of the same or different types as you like. Supports the following attribute:

  • shareInterpolator - Whether to share the same Interpolator among all immediate child elements.

<alpha>
A fading animation. Represents an AlphaAnimation. Supports the following attributes:

  • fromAlpha - 0.0 to 1.0, where 0.0 is transparent.
  • toAlpha - 0.0 to 1.0, where 0.0 is transparent.

<scale>
A resizing animation. Represents a ScaleAnimation. You can specify what is the center point of the image (the pinned center), from which it grows outward (or inward), by specifying pivotX and pivotY. So, for example, if these were 0, 0 (top left corner), all growth would be down and to the right. scale supports the following attributes:

  • fromXScale - Starting X size, where 1.0 is no change.
  • toXScale - Ending X size, where 1.0 is no change.
  • fromYScale - Starting Y size, where 1.0 is no change.
  • toYScale - Ending Y size, where 1.0 is no change.
  • pivotX - The X coordinate of the pinned center.
  • pivotY - The Y coordinate of the pinned center.
 
<translate>
A vertical/horizontal motion animation. Represents a TranslateAnimation. Supports the following attributes in any of the following three formats: values from -100 to 100, ending with "%", indicating a percentage relative to itself; values from -100 to 100, ending in "%p", indicating a percentage relative to its parent; a float with no suffix, indicating an absolute value.

  • fromXDelta - Starting X location.
  • toXDelta - Ending X location.
  • fromYDelta - Starting Y location.
  • toYDelta - Ending Y location.
 
<rotate>
A rotation animation. Represents a RotateAnimation. Supports the following attributes:

  • fromDegrees - Starting rotation, in degrees.
  • toDegrees - Ending rotation, in degrees.
  • pivotX - The X coordinate of the center of rotation, in pixels, where (0,0) is the top left corner.
  • pivotY - The Y coordinate of the center of rotation, in pixels, where (0,0) is the top left corner.
 
<interpolator tag>
You can also use any of the interpolator subclass elements defined in R.styleable. Examples include <CycleInterpolator>, <EaseInInterpolator>, and <EaseOutInterpolator>. These objects define a velocity curve that describes how quickly a visual action takes place on a timeline (fast at first and slow later, slow at first and gradually faster, and so on).

In addition to the attributes defined for each element above, the elements <alpha>, <scale>, <translate>, <rotate>, and <set> all support the following attributes (inherited from the Animation class):

duration
Duration, in milliseconds, for this effect.
startOffset
Offset start time for this effect, in milliseconds.
fillBefore
When set true, the animation transformation is applied before the animation begins.
fillAfter
When set true, the animation transformation is applied after the animation ends.
repeatCount
Defines the number of times the animation should repeat.
repeatMode
Defines the animation behavior when it reaches the end and the repeat count is greater than 0. Options are to either restart or reverse the animation.
zAdjustment
Defines the z-axis ordering mode to use when running the animation (normal, top, or bottom).
interpolator
You can optionally set an interpolator for each element to determine how quickly or slowly it performs its effect over time. For example, slow at the beginning and faster at the end for EaseInInterpolator, and the reverse for EaseOutInterpolator. A list of interpolators is given in R.anim. To specify these, use the syntax @android:anim/interpolatorName.

For more discussion and animation code samples, see the discussion in the 2D Graphics document.


(Ex) fade.xml



(Ex) hold.xml



(Ex) zoom_enter.xml



(Ex) zoom_exit.xml


'old > XML Attr' 카테고리의 다른 글

XML Attributes_TextView  (0) 2010.02.22
XML Attributes_LinearLayout  (0) 2010.02.22
Posted by jazzlife
,

R.anim

old/UI Design 2010. 2. 22. 13:11

acclerate_decelerate_interpolator :

accelerate_interpolator :

anticipate_interpolator :

anticipate_overshoot_interpolator :

bounce_interpolator :

decelerate_interpolator :

fade_in :

fade_out :

linear_interpolator :

overshoot_interpolator :

slide_in_left :

slide_out_right :

'old > UI Design' 카테고리의 다른 글

style  (0) 2010.02.22
기본 UI 생성 순서  (0) 2010.02.22
R.integer  (0) 2010.02.22
AlphaAnimation for Fading Animation  (0) 2010.02.22
Dimension  (0) 2010.02.22
Posted by jazzlife
,