336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
안드로이드 상에서 소캣통신 테스트를 위해 간단이 짜깁기해 만들어본 App.

호기심에 안드로이드로 서버를 구성해봤는데 뭐 당연한 결과겠지만 문제없이 잘 돌아감

좀더 이것저것 손볼까하다 귀차니즘에 결국은 컴팩트한 echo chat app.

Server
src

package test.server.tcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class main extends Activity {
    /** Called when the activity is first created. */
 
 private ServerSocket socket;
 private int port = 9999;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        try {
   socket = new ServerSocket(port);
  }catch (IOException e) {
   Log.d("DMSG", "Error : " + e);
   //e.printStackTrace();
  }
  
  while(true) {
   try {
    Socket sock = socket.accept();
    Thread checkUpdate = new echoThread(sock);
    checkUpdate.start();
   }
   catch(IOException ex) {
   }
  }
    }
   
    class echoThread extends Thread {
     
    private Socket sock;

    public echoThread(Socket sock)
    {
       this.sock = sock;
    }
   
    public void run() {
     try {
          InetAddress addr = sock.getInetAddress();
          System.out.println(addr.getHostAddress() + "Connected..");

          BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
          PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));

          String msg = null;
         
          while((msg = br.readLine()) != null)
          {
             Log.d("DMSG", "From " + addr.getHostAddress() + "'s Message: " + msg);
             pw.println(msg + " ..(from server)");
             pw.flush();

             if(msg.equals("exit")){
                break;
             }
          }

          pw.close();
          br.close();
          sock.close();
       }
       catch(Exception e)
       {
          Log.d("DMSG", "Error : " + e);
       }
    }
  
 };
}


Client
src

package test.client.tcp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

import test.client.tcp.R;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

 

/* Called when the activity is first created. */
public class main extends Activity {

 private String strMsg = "";
 private String read = "";
 private String html = "";
 private Handler mHandler;
 private Socket socket;
 private BufferedReader networkReader;
 private BufferedWriter networkWriter;
 private String ip ="127.0.0.1";
 private int port = 9999;
 private PrintWriter out;
 
 private ListView mConversationView;
 
 private ArrayAdapter<String> mConversationArrayAdapter;
 
 @Override
 protected void onStop() {
  //TODO Auto-generated method stub
  super.onStop();
  try {
   Log.d("DMSG", "Socket close...ok");
   socket.close();
  }catch (IOException e) {
   Log.d("DMSG", "Socket close fail!");
   //TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  mHandler = new Handler();
  
  try {
   setSocket(ip,port);
   Log.d("DMSG", "OK");
  }catch (IOException e1) {
   //TODO Auto-generated catch block
   e1.printStackTrace();
   Log.d("DMSG", "Error : " + e1);
  }
  
        mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);
       
        mConversationView = (ListView) findViewById(R.id.in);
        mConversationView.setAdapter(mConversationArrayAdapter);
       
        mConversationView.setTextFilterEnabled(true);        
  
  Button btn = (Button) findViewById(R.id.Button01);
  
  btn.setOnClickListener(new OnClickListener() {
   EditText et = (EditText) findViewById(R.id.EditText01);
   public void onClick(View v) {
    if (et.getText().toString() != null || !et.getText().toString().equals("")) {
     /*PrintWriter*/ out = new PrintWriter(networkWriter,true);
     String return_msg = et.getText().toString();
     out.println(return_msg);
     out.flush();
     strMsg = "Send Message : " + return_msg;
     mConversationArrayAdapter.add(strMsg);
     Log.d("DMSG", "Msg = " + return_msg);
     
     et.setText("");
    } 
   }
  });
  
  try {
   Thread checkUpdate = new echoThread(mConversationArrayAdapter, out);
   checkUpdate.start();
  }
  catch(Exception ex) {
   Log.d ("DMSG", "Error : " + ex);
  }
 }

 class echoThread extends Thread {
  
  ArrayAdapter<String> conversationArrayAdapter;
  PrintWriter pw;
  String tmp;
  
  public echoThread(ArrayAdapter<String> mConversationArrayAdapter, PrintWriter out)
  {
   this.conversationArrayAdapter = mConversationArrayAdapter;
   this.pw = out;
   Log.d("DMSG", "Thread init.");
  }
  
  public void run() {
   try {
    while (true) {
     Log.w("DMSG","chatting is running");
     
     try {
      Thread.sleep(100);
     } catch (Exception e) {
      Log.d("DMSG", "Error : " + e);
     }
     
     read = null;
     while((read = networkReader.readLine()) != null)
     {
               Log.d("DMSG", "receive msg");
               mHandler.post(showUpdate);
     }

    }
   }catch (Exception e) {
    Log.d("DMSG", "Client Thread Error!" + e);
   }
  }
 };
 
 private Runnable showUpdate = new Runnable() {
  public void run() {
   Toast.makeText(main.this,"Coming word: " + html, Toast.LENGTH_SHORT).show();
   mConversationArrayAdapter.add("Receive Message : " + read);
  }
 };
 
 public void setSocket(String ip,int port)throws IOException { 
  try {
   socket =new Socket(ip, port);
   networkWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
   networkReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   Log.d("DMSG", "Client Socket Init...");
  }catch (IOException e) {
   System.out.println(e);
   e.printStackTrace();
   Log.d("DMSG", "Client Socket Init fail!");
  }
 }
}


xml

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <ListView android:id="@+id/in"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:stackFromBottom="true"
        android:transcriptMode="alwaysScroll"
        android:layout_weight="1"/>
   <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    <EditText android:id="@+id/EditText01"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:layout_weight="1"/>
 
    <Button android:id="@+id/Button01"
     android:layout_width="80dip"
     android:layout_height="wrap_content"
     android:text="Send"/>
   </LinearLayout>
</LinearLayout>


xml(message.xml)
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="18sp"
    android:padding="5dp"/>

참고로 위의 App를 실행하기 위해선 AndroidManifest.xml에 아래와 같이 INTERNET permission을 추가해 주어야함

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

실행화면(client)

참고로 실행 장비가 Odroid-T라서 해상도및 비율이 일판 스마트폰들과는 조금 다르게 보임

'Programming > android' 카테고리의 다른 글

android에서 external command 사용하는 방법  (0) 2010.09.03
Supporting Multiple Screens  (0) 2010.05.20
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
과연 이런걸 사용할 일이 있기는 할까?

여하튼 필요로인해 구현해본 간단 source

 try {
     
     ProcessBuilder exec = new ProcessBuilder();
     exec.command("/system/busybox/bin/touch", "/sdcard/test/test.txt");
     java.lang.Process process = exec.start();
                         
     Log.d("DMSG", "oProcess.start() success!");
 }
 catch (IOException ex) {
     Log.d("DMSG", "oProcess.start() error!");
     ex.printStackTrace();
 }

여담이랄까? 위의 경우 touch를 사용하여 test.txt파일을 만드는데

permission문제로 /sdcard/test 경로에 파일이 생성되지 않는다

때문에 저장하려는 디렉토리의 권한을 설정해줘야 위의 코드는 제대로 작동한다

'Programming > android' 카테고리의 다른 글

Android TCP/IP Echo chat Server & Client  (1) 2010.10.08
Supporting Multiple Screens  (0) 2010.05.20
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Supporting Multiple Screens

Android는 화면크기와 해상도의 어떤 범위를 제공하는 다양한 Device에서 동작되도록 설계되었다. Application을 위해 Android Platform은 Devices 사이에서 한결같은 환경을 제공하며 Application이 보여질 화면에 Application UI를 적용시키는 아주 복잡한 것들을 처리해 준다. 그와 동시에 Platform은 지정된 화면 사이즈와 해상도에서 개발자의 Applicaiton이 보여질 때 그러한 Application을 정밀하게 제어할 수 있는 APIs을 Application 개발자들에게 제공해 준다.

이 문서는 Platform에 의해 제공되는 화면관련 지원 기능들에 대해 설명하며 개발자들이 Application에서 어떻게 그것들을 사용할 수 있는지를 설명하게 된다. 여기에 설명된 Practices을 따르게 되면, 모든 지원 가능한 Device 화면에서 올바르게 보여지고 하나의 .apk파일로 어떤 Device에도 올라갈 수 있는 Application을 쉽게 만들 수 있다.

만일 Android 1.5 버전이나 그 이전 버전으로 Application을 개발해서 배포를 했다면 반드시 이 문서를 읽어보고 Android 1.6 이상의 버전에서 동작하고 기존과 다른 화면 사이즈와 해상도를 지원하는 새로운 Device에서 개발자의 Application을 어떻게 올바르게 보여줄 수 있을지를 생각해봐야 한다. 대부분의 경우에는 단지 Minor한 조정작업들만이 필요하지만 모든 지원 가능한 화면에서 "test your application" 항목에서 확실하게 확인해야 한다.

특히, QVGA와 같은 작은 화면을 가지는 Device의 사용자들을 위해 기존의 Application이 사용가능한지를 확인해야 한다면 "Strategies for Legacy Applications" 부분을 살펴보고 그러한 작업을 어떻게 하는지에 대한 좀 더 자세한 정보를 얻기 바란다.


Overview of Screens Support

아래의 섹션은 Multiple 화면을 위해 Android Platform의 지원에 대한 Overview를 제공하고 있으며 이러한 문서와 APIs에서 사용된 용어와 개념들에 대한 소개, Platform이 지원하는 화면 설정에 대한 요약, 그리고 API와 기본적인 화면 호환성 기능들에 대한 Overview를 포함하고 있다.

Tems and Concepts
Screen Size
실제 물리적인 크기이며 화면의 대각선 크기로 측정된다. 간단히, Android는 실제 화면 크기들 모두를 세가지의 일반적인 크기로 정리하고 있는데, Large, Normal, and Small이 그것들 이다. Application은 이러한 세가지 크기에 대해 각각 사용자 정의된 Layouts을 제공할 수 있으며 Platform은 실제 화면 크기에 Layouts의 표현을 그대로 처리하게 된다.

Aspect Ratio

화면의 물리적인 가로와 세로의 비율 관계를 말하는 것으로 Application은 리소스 제한자인 "long"과 "notlong"을 이용하여 지정된 화면 비율에 대한 Layout 리소스들을 제공하고 있다.

Resolution

화면의 물리적인 픽셀의 총 합. 비록 해상도가 가끔 "width x height"로 표현되긴 하지만 해상도는 특정 가로대 세로의 비를 의미하지는 않는 것을 알고 있어야 한다. Android에서 Applications은 해상도에 맞게 바로 적용되지 않는다.

Density

화면의 해상도에 기초로해서 화면의 물리적인 가로와 세로에 걸쳐있는 픽셀들의 흩어짐정도를 말한다. 낮은 Density를 가지는 화면은 더 적은 가용한 픽셀들을 가지는 반면 높은 Density를 가지는 화면은 동일한 영역에서 더 많은 픽셀들을 가지게 된다.
화면의 Density는 중요한 개념이다. 왜냐하면, 화면의 픽셀단위로 가로, 세로 크기를 지정한 UI Elements을 가지고 있는 것들은 낮은 Density 화면에서는 더 크게 보이고 높은 Density 화면에서는 더 작게 보여지기 때문이다.
간단히, Android는 다양한 실제 화면 Density를 세개의 일반화된 Densities로 정리하였으며 "high", "medium", and "low"로 나타내고 있다.
Applications은 사용자 정의된 리소스들을 이러한 세가지의 Densities에 대해 각각 제공하고 있으며 Platform은 실제 화면의 Density를 만족하도록 리소스들의 스케일 조정을 처리하게 된다.

Density Independent Pixel(dip)
Application이 UI를 정의하는 과정에서 사용할 수 있는 가상의 픽셀 단위를 의미하며 Layout Dimensions 이나 위치등을 Density에 독립적인 방식으로 표현하게 된다.

Density-Independent-Pixel은 Platform애 의해 가정된 기본적인 Density인 160 dpi 화면에서 하나의 물리적인 픽셀과 동일하다. 실행시에 Platform은 화면의 실제 Density에 기초해서 필요로하는 dip 단위로 스케일조정을 처리하게 된다.
dip 단위를 화면 픽셀단위로 변환하는 것은 pixels = dips * (density / 160)의 과정을 따르게 된다. 예를 들어 240 dpi 화면에서 1 dip는 1.5 pixels이 된다. Application UI를 dip단위로 정의하는 것이 추천되며 개발한 UI를 다양한 화면에서 올바르게 보일 수 있는 확실한 방법이다.


Range of Screens Supported

Android 1.5 버전 및 그 이하에서는 하나의 화면 설정만을 지원하도록 설계되었다. 3.2인치의 화면 크기에 HVGA(320x480)의 해상도가 그것이다. Platform이 단지 하나의 화면을 타겟으로 삼았기 때문에 Application 개발자들은 다른 화면에서 자신들의 Application이 어떻게 보여질지에 대한 고려도 없이 정해져있는 화면에 대해 특화된 Application을 작성해야만 했다.

Android 1.6 이후부터 다양한 화면 크기 및 해상도에 대한 지원이 추가되었고 Platform이 동작하게 될 Devices에 대한 많은 새로운 타입과 크기를 반영하게 되었다. 이것은 개발자가 자신들의 Applications을 Devices과 화면의 어떤 범위에서 올바르게 보여질 수 있도록 앞으로 설계해야 한다는 것을 의미한다.

Application 개발자가 다양한 Devices에 대해 자신의 UI를 개발하는 방법을 단순화하고 더 많은 Devices들이 Application의 수정 없이 참여할 수 있도록 하기 위해 Platform은 실제 지원되는 화면의 크기 및 해상도를 다음과 같이 나누었다.
  A set of three generalized sizes: large, normal, and small, and
  A set of three generalized densities: high (hdpi), medium (mdpi), and low (ldpi)

Application은 필요하다면 사용자 정의된 리소스들(보통 Layouts)을 3가지의 일반화된 크기중 하나로 제공할 수 있고 또한 3가지의 일반화된 Densities 중 하나로 이미지와 같은 Drawables 리소스를 제공할 수 있다. Application은 실제 물리적인 크기나 Device 화면의 Density에 대한 작업을 할 필요가 없다. 실행시에 Platform은 정확한 크기나 Density 리소스들의 Loading을 처리하고, 이러한 과정은 일반화된 크기 혹은 현재 Device 화면의 Density에 기반해서 화면의 실제 Pixel Map으로 그러한 리소스들을 적용시키게 된다.

Android에 의해 지원되는 화면의 범위와 Platform 그것들을 매핑시키기 위한 일반화된 화면 설정들이 아래의 테이블에 있다.

   Low Density(120), ldpi Medium Density(160), mdpi  High Density(240), hdpi
  Small Screen   QVGA(240x320),
  2.6"-3.0" Diagonal
   
  Normal Screen   WQVGA(240x400),
  3.2"-3.5" Diagonal
  FWQVGA(240x432),
  3.5"-3.8" Diagonal
  HVGA(320x480),
  3.0"-3.5" Diagonal
  WVGA(480x800),
  3.3"-4.0" Diagonal
  FWVGA(480x854),
  3.5"-4.0" Diagonal
  Large Screen     WVGA(480x800),
  4.8"-5.5" DIagonal
  FWVGA(480x854),
  5.0"-5.8" Diagonal
 
위의 테이블에서 Baseline은 3.2"화면의 HVGA (320x480) 해상도이며, "normal" 크기와 "medium" Density로 할당된다. Android 1.5 이전의 버전에서 작성된 모든 Applications이 HVGA 화면으로 작성되었기 때문에 Baseline으로 사용되고 있다.

Platform은 현재 9개의 모든 경우, 즉 테이블상에 있는 크기와 Density 설정 목록을 지원하고 있으나 그것들 각각에 대한 사용자 정의된 리소스를 모두 생성할 필요는 없다.
Platform은 견고한 호환성 기능들을 제공하고 있으며 다음의 섹션들에서 설명이 된다.


How Android Supports Multiple Screens

Multiple 화면에 대한 Android의 지원 기반은 현재 Device 화면을 위해 적당한 방법으로 Applicaiton 리소스의 표현을 함께 관리해주는 내장된 호환성 기능들의 집합이라 할 수 있다. Platform은 사용자 Application의 표현에 대한 대부분을 처리한다. 그러나 개발자에게 개발한 Application이 어떻게 보여질지를 제어하는 두 가지의 중요한 방법을 또한 제공하고 있으며 만일 그것들의 사용이 필요하거나 원한다면 사용하면 된다.

  • Platform은 필요하다면 크기와 Density에 특화된 리소스들을 제공하도록 리소스 제한자들에 대한 집합을 지원하고 있으며, 그러한 리소스들의 크기를 규정하기 위한 제한자로는 "large", "normal", and "small"이 있고 리소스들의 Density를 규정하는 제한자로는 "hdpi", "mdpi", and "ldpi"가 있다.
  • Platform은 또한 Manifest 요소로 <supports-screen>을 제공하며 그것의 속성으로 "android:largeScreens", "android:normalScreens", and "android:smallScreens"의 값을 제공한다. 이러한 속성값은 지원가능한 일반화된 화면 사이즈를 지정하도록 해준다. 네번째 속성으로 "android:anyDensity"가 있으며 Application이 Multiple Densities를 위해 내부 지원을 포함할지의 여부를 가리키게 된다.


Application 실행시에 Platform은 Application 지원에 대한 3가지의 형태를 제공하며 현재 Device화면에서 가장 가능성 있는 Display를 보증할 수 있도록 해준다.

1. Pre-scaling of resources (such as image assets)
현재 화면의 Density를 기반으로, Platform은 자동으로 어떤 크기와 Density에 적합한 리소스들을 Application으로부터 Loading시키고 어떤 스케일 작업도 하지 않고 보여준다. 만약 사용가능한 매칭되는 리소스들이 없다면 Platform은 Default 리소스들을 Loading시키고 현재 화면의 일반화된 Density에 맞도록 스케일을 변경하게 된다. 리소스들이 Density에 특화된 리소스 디렉토리에서 Loading될 수 없다면 Platform은 Default 리소스들이 "medium"(160) Density를 기본으로 가지는 화면에서 올바르게 보여지도록 설계되어졌다고 가정하게 된다. 예를 들어, 현재 화면의 Density가 "high"이면 Platform은 제한자인 "hdpi"로 이름 붙여진 리소스들을 Loading하게 되고 스케일 변경 없이 그것들을 사용한다. 그러나 만약 사용가능한 리소스들이 없게 되면 Platform은 대신 Default 리소스들을 사용하며 Baseline Dentity("medium")로부터 "high"로 스케일 변경하여 사용하게 된다.

2. Auto-scaling of pixel dimensions and coordinates
만일 Application이 다른 Screen Densities을 지원하지 않는다고 한다면 Platform은 하나의 절대적인 Pixel 좌표, Pixel Dimension 값들, 그리고 Applicaiton에서 사용된 Pixel 수식을 자동으로 스케일 변경한다. Pixel로 정의되어 있는 화면 요소들이 Baseline Density인 "medium"(160)에서 존재해야 할 때 거의 근사하게 동일한 물리적 크기로 보여지도록 하기 위해 이렇게 하고 있다. Platform은 이러한 스케일 변경을 투명하게 Application에 적용하고 물리적인 Pixel Dimensions이 아닌 변경된 전체벙위의 Pixel Dimensions을 Application으로 보고하게 된다.
예를 들어, WVGA의 High-Density(240 dpi)화면을 사용하고 있는 Device가 주어졌고 주어진 Device는 480x800의 해상도를 가지며 기본이 되는 HVGA 화면(160 dpi)과 거의 동일한 크기를 갖지만 Multiple 화면을 지원하지 않는 상태로 Application을 실행한다고 가정해보자. 이러한 경우, 화면 Dimensions을 물어왔을 때 시스템은 Application에 "lie"를 하고 320x533(480x800 /1.5 = 320x533)으로 전달한다. 그렇게 되면 App이 Drawing 동작을 할 때 (10,10)부터 (100,100)까지의 사각 영역을 무효화하는 것과 마찬가지로 시스템은 그것들을 적당한 크기로 스케일 조정함으로써 자동으로 좌표를 변환시킬 것이고 실제로 (15,15)에서 (150,150)의 영역을 무효화시키게 된다. 이와 동일한 일이 다른 방향으로 이루어지기도 하는데 만약 Application이 더 낮은 Density 화면에서 동작한다면 좌표들은 더 작게 변경된다.

3. Compatibility-mode display on larger screen-sizes
현재의 화면 크기가 Application에서 지원하는 것보다 크다면, 즉 <support-screens> 요소에서 지정한 것보다 크다면 Platform은 Application을 Baseline 크기("normal")와 Densitiy("medium")로 보여주게 된다. Baseline보다 큰 화면일 경우, Platform은 검은색 배경화면으로부터 전체 화면의 Baseline 크기를 가지는 부분으로 Application을 보여주게 된다.
예를 들어, 주어진 Device는 WVGA Medium Density화면을 사용하고 "large" 화면으로 분류되었지만 Application은 "large" 화면을 지원하지 않는다고 가정해보자. 이경우, 시스템은 화면 Dimensions에 대한 질의시 다시 "lie" 를 하게 되고 320x480을 반환하게 된다. 그렇지만 그러한 Application의 스케일을 조정하는 것 대신 Application의 320x480 Interface는 좀 더 큰 480x800 화면에 "postage stamp"(큰 화면을 다 채우지 않고 검은색 배경이 남아있는 형태)로 놓여지게 된다.

일반적으로 이러한 호환성 기능들은 Android 1.5 이전 버전에서 작성된 것을 포함한 모든 Applictions이 대부분의 Devices, 특히 Device 화면이 Baseline("normal" size or larger)인 Devices에서 올바르게 보여질 수 있도록 하기 위한 것이다.

그러나 Baseline HVGA 화면으로 작성된 Applications은 좀 더 작은 화면인 QVGA에서 올바르게 보여지기 위해서는 약간의 변경이 필요하게 된다는 것에 유념해야 한다. 작은 화면의 즐어든 화면영역이 있다면 Application 개발자로서 고려해야만 하는 설계, Content, 그리고 기능 등의 관점에서 Trade-Offs이 나타나게 될 수 있다. 작은 화면에서 Display를 위해 기존 Application을 어떻게 준비하는지에 대한 더 많은 정보를 얻고자 한다면 "Stratagies for Lagacy Applications" 부분을 살펴보면 된다.


Density Independence

Density Independence의 목적은 Application이 서로 다른 Densities를 가지는 각각의 화면에서 보여질 때 Application내에 선언된 UI 요소들의 물리적인 크기(사용자 관점에서)를 동일하게 유지하기 위함이다. Density Independence는 Layouts과 아이콘과 같은 Drawables 모두에 적용된다. Density Independence를 유지하는 것은 아주 중요한데, 왜냐하면 다른 것들이 동일하고 화면 Pixel의 단위로 정의된 UI 요소의 Width 및 Height는 낮은 Density 화면에서는 물리적으로 더 크게 되고 높은 Density를 가지는 화면에서는 더 작아지게 된다. 그러한 방식으로 Density에 연관되어 있는 크기 변경들은 Application Layouts, 사용성, 그리고 Device에 설치되어 있는 다른 Application과 비교했을 때 일관성에 문제를 일으키게 된다.

Platform은 Density Independence를 Application에 기본적으로 제공하며 아래의 3 가지 방법이 있다.

  • Drawable Resources에 대한 Pre-Scaling을 통해(Loading 시간에 스케일 변경)
  • Layouts에 사용된 "dip"(Device Independenct Pixel)의 Auto-Scaling을 통해
  • Application에 사용된 Pixel의 절대 값에 대한 Auto-Scaling을 통해(Application에 android:anyDensity="false"이 설정되어 있을 경우에만 요구된다.)


아래의 예제 그림은 Platform에 의해 제공되는 Density Independence를 보여주고 있다. 비록 화면의 크기, 가로 대 세로비, 그리고 Density가 다르지만 Layouts과 Launch 이이콘들 모두가 똑같은 물리적인 크기로 보여지고 있다.


대부분의 경우에는 모든 Dimension 값들이 dip(or dp)혹은 sip(or sp)로 규정함으로써 간단하게 Application에서 Density Independence를 이용할 수 있다. 만약 Application에서 Pixel의 절대값을 사용하고 Manifest가 android:anyDensity="true"를 갖는다면 Pixel 값들의 스케일 변경이 필요하게 된다.


Manifest Attributes for Screens Support

Android 1.6 버전은 새로운 Manifest Element를 소개하고 있는데, <support-screens>가 그것이며 이러한 요소의 속성 값들이 아래에 나와 있다.
android:smallScreens : Application UI가 small 화면에서의 사용을 위해 설계되었는지의 여부
android:normalScreens :
android:largeScreens
android:anyDensity : 서로 다른 Density 환경에서 올바르게 Application UI를 관리하도록 설계되었는지의 여부
  "true" : 모든 화면의 Densities에 대해 Density 호환성 기능을 사용하지 못하도록 설정한다.
  "false" : Platform은 Density 호환성 기능을 사용할 수 있도록 한다.

일반적으로 화면 크기 속성(smallScreens, normalScreens, or largeScreens)을 "true"로 설정했을 때에는 크기와 관련한 호환성 동작을 수행하지 않고 Application이 자체적으로 자신의 UI를 관리하겠다는 것을 Platform으로 알리는 것이다. 그러나 만약 화면 크기 속성값들이 "false"로 정의된다면 Application이 그러한 화면 크기에 맞춰 설계되지 않는다는 것을 알려주는 것이다. 이러한 효과들은 Application이 지원하지 않는 화면 크기에 의해 결정되어진다.

만약 largeScreens="false"로 선언했다면 Application은 Large Screen을 가지는 Devices 사용자에 의해 설치될 수 있다. Large Screen을 가지는 Device에서 동작할 때 이러한 속성 값은 Platform으로 하여금 호환성 모드에서 Application을 동작시키도록 하여 Baseline HVGA 화면에서 그것들 보여주게 된다. smallScreens="false"로 설정되었다면 Application은 Small Screens에서 설치 될 수 있다. 그렇지만 이러한 속성 값은 Android Market에서 사용가능한 Application 목록에서 필터링 된다. 사실상, 이것은 Small-Screen Devices에 Application을 설치하지 못하도록 하는 것이다.

만일 android:anyDensity 속성을 "true"로 설정 했다면 Applicaiton 자체적으로 자신의 UI를 관리할 것임을 알리는 것이다. 이러한 경우, Application은 Device-Independenct Pixels을 사용하고 어떤 실제 Pixel 값들이나 수치를 "android.util.DisplayMetrics.density"로부터 확인된 사용가능한 스케일 변경 Factor를 이용하여 UI Dimensions을 선언하였는지를 반드시 확인해야 한다.

android:anyDensity 속성의 설정은 Drawables에 대한 Platform Pre-Scaling에 어떤 영향도 주지 않고 있음에 유의해야 한다.

다음의 예제는 어떤 Densities에서든 large, normal, and small 화면에 대한 지원을 정의하는 Manifest 파일을 보여준다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <supports-screens
          android:largeScreens="true"
          android:normalScreens="true"
          android:smallScreens="true"
          android:resizable="true"
          android:anyDensity="true" />
</manifest>



Default Values for Attributes

Resources Directory Qualifiers for Screen Size and Density

Android는 Application이 동ㅈ가하는 화면의 특성에 기반하여 리소스를 선택을 제어하기 위한 리소스 Directory 제한자들을 지원하고 있다. 크기와 Density에 특화된 리소스를 Application에 제공하기 위해 이러한 제한자들을 사용할 수 있으며 아래의 테이블을 참조하면 된다.

[테이블 추가]

Density와 화면 크기는 서로 독립적인 파라미터들이며 각각 시스템에 의해 해석되어진다. 예를 들어, WVGA High Density는 일반적인 화면으로 간주되는데 이유는 물리적인 크기가 대략 T-Mobile G1과 같기 때문이다. 반대로 WVGA Medium Density 화면은 Large 화면으로 간주되는데 이것은 낮은 Pixel Density를 제외하고는 동일한 해상도를 제공하지만 Baseline 화면보다 물리적으로 크고 Normal 크기의 화면보다 더 많은 정보를 보여줄 수 있다는 의미이기 때문이다.

아래에는 낮고 높은 Densitiy와 서로다른 Layouts Schemes을 가지는 Application의 리소스 Directory 구조에 대한 예가 있다.

res/layout/my_layout.xml            // layout for normal screen size
res/layout-small/my_layout.xml      // layout for small screen size
res/layout-large/my_layout.xml      // layout for large screen size
res/layout-large-land/my_layout.xml // layout for large screen size in landscape mode
res/drawable-ldpi/my_icon.png       // icon image for low density
res/drawable-mdpi/dpi/my_icon.png   // icon for medium density
res/drawable-hdpi/my_icon.png       // icon image for high density
res/drawable-nodpi/composite.xml    // density independent resource



Best Pratices for Screen Independence

Multiple 화면을 제공하는 목적은 어떤 다양한 화면 구성에서도 올바르게 보여질 수 있는 Application을 만들기 위함이다. 개발자가 만든 Application이 다른 화면들에서도 잘 보여지는지를 확인하는 다음과 같이 쉽게 할 수 있다.
  1. XML Layout 파일에 "px"보다는 "wrap_content", "fill_parent", 그리고 dip를 우선해서 사용한다.
  2. Absolute Layout을 피한다.
  3. 하드 코딩된 Pixel 값을 코드상에 사용하지 않는다.
  4. Density 및 해상도에 특화된 리소스들을 사용한다.


Strategies for Legacy Applications

개발자가 이미 Android 1.5 이전 Platform에서 Application을 만들어 배포를 했다면 다음에 제시되는 Devices로 올릴 수도 있는 그러한 Application을 어떻게 적용시킬 것인지를 고려해야만 한다.
   Android 1.5 이전 버전의 Platform을 구동시키는 기존 Devices
   Android 1.6 이후 버전을 구동시키고 다양한 화면 크기와 해상도를 제공하는 Devices

새로운 Devices과 그러한 Devices이 사용하는 서로 다른 화면들을 지원하기 위해 Application에 약간의 수정을 해야할 수도 있다. 그러나 그와 동시에 Application이 매우 안정적이어서 그 변경을 최소화 하고 싶어할 수도 있겠다. Multiple 화면을 가지는 새로운 Devices과 이전의 Platform 버전을 구동시키는 기존 Devices을 지원하도록 기존 Application을 확장할 수 있는 방법이 다양하게 존재한다. 어떤 Device나 모든 Devices로 하나의 .apk 파일을 배포할 수 있도록 이러한 변경을 Application에 적용할 수 있어야 한다.

추천되는 전략은 가장 최근 버전의 Platform에서 개발하는 것이고 동작시키고자 하는 최소 Platform에서 테스트하는 것이다. 아래에 그것을 하는 방법이 있다.
1. Application의 android:minSdkVersion 속성을 사용된 그대로 남겨둠으로써 기존 Devices과의 호환성을 유지시킨다. 새로운 Devices과 Multiple 화면들을 지원하기위해 속성 값을 증가시킬 필요는 없다.
2. 새로운 속성(android:targetSdkVersion)을 추가함으로써 Android 1.6 이후 Devices에 대한 호환성을 확장한다. 값을 "4"로 설정하여, 비록 기술적으로는 이전 버전의 API를 사용하더라도 Application이 Platform의 Multiple 화면의 지원을 "inherit" 받을 수 있도록 해준다.
3. <manifest>의 자식으로서 <support-screens> 요소없이 추가한다. 만약 크기나 Density 속성을 나중에 활성화시킬 필요가 있다면 이곳이 바로 그것들을 넣어줄 곳이 된다.
4. Application의 빌드 Properties을 변경하여 1.5보다는 1.6 library에서 컴파일하도록 한다.
5. Android 1.6 이후의 버전에서 테스트할 수 있도록 AVDs를 셋업하고 지원할 화면의 크기 및 Densities을 사용하는 AVDs을 만든다.
6. Android 1.5 이하의 Platform에서 Application을 테스트 할 수 있도록 AVDs을 셋업하고 생성한다.
7. Android 1.6 Library로 컴파일을 하고 생성해 둔 AVDs에서 동작시킨다.
8. Display나 기능적인 이슈들을 디버그한다.
9. 리소스와 연관된 이슈들에 대해서는 다음과 같은 방법으로 해결할 수 있다.
anyDensity="false" 속성을 <support-screens>에 추가하여 Density-Compability Scaling을 할 수 있도록 한다. 어떤 크기나 Density에 특화된 리소스들을 생성하고 그것들을 정확한 제한자들(Correct Qualifiers)이 붙어있는 디렉토리에 위치시킨다. 만약  크기 혹은 Density에 특화된 리소스 Directories에 이 문서내의 리소스 제한자들 목록에 있는 것 중 하나를 태그로 붙인다면 v<api-level> 제한자를 리소스 디렉토리들에 추가로 붙여야만 하는 것에 유념해야 한다.(예를 들어, -v4) 이것은 Application이 Android 1.5 이하의 Platform에서 동작할 때 v<api-level> 제한자가 붙어 있는 리소스들이 무시된다는 것을 보장하게 된다.
10. 만일 Application이 "large"화면을 위한 지원을 제공하지 않고 개발자는 "larger" 화면에서 Screen-Compatibility 모드를 통해 Platform이 Application을 Display하기를 바란다면 largeScreens="false" 속성을 <support-screens> 요소에 추가해야 한다.
11. Application이 "small" 화면을 지원하지 않고 Android Market 그러한 Application을 "small" 화면을 가지는 Devices의 사용자에게 제공하지 않기를 바란다면 smallScreens="false" 속성을 <supports-screens> 요소에 반드시 추가해야 한다.
12. 만들어진 Application이 지원하는 모든 Platform과 화면 크기들에서 기대한 만큼 성능을 얻을 때까지 테스트와 디버깅을 계속한다.
13. 이전 버전에서 배포시 사용했었던 개인 Key를 이용하여 Application에 Export, zipalign, and sign을 하고 그 다음 Application을 사용자에게 업데이트로 배포하면 된다.

특별히, "small" 화면을 가지고 있는 Device를 흉내낼 수 있는 AVD에서 Application을 테스트해야 하는 것을 기억해야 한다. 낮은 "density"에서 QVGA해상도를 화면에 제공하는 Devices이 현재 사용가능한 상태이다. 그러한 Devices의 사용자들은 개발자가 만들어 둔 Application을 받으려 할 것이므로 개발자인 여러분은 만들어진 Application이 그 small-screen Device에서 어떻게 보여지고 동작하는지를 이해하고 있어야 한다. 많은 경우에서, 줄어든 화면의 영역과 Density라는 것은 개발자로 하여금 그렇게 줄어든 Devices에서 디자인, Content, 그리고 기능면에서 균형관계를 맞춰줄 필요가 있다는 것을 의미하고 있다.


How to Test Your Application on Multiple Screens

Multiple 화면을 지원하는 Application을 배포하기전에 타겟이 되는 화면 사이즈와 Densities의 모두에 대해 테스트를 완전하게 해야지만 한다. Platform의 호환성 기능들을 가지고 있을 때, 혹은 화면에 특화된 UI 리소스들을 Application에 포함하고 있을 때 그것들이 어떻게 보여지는지를 테스트 할 수 있다. Android SDK는 개발자가 Application을 어떤 지원가능한 화면에서 테스트하기 위해 필요한 모든 Tools을 포함하고 있다.

"Android SDK and AVD Manager"를 통해 Android Virtual Devices을 새로 만들거나 삭제 등의 관리 작업을 할 수 있다.

Android SDK는 기본적으로 테스트를 위해 사용될 수 있는 에뮬레이터 스킨들을 제공하고 있다. 그러한 스킨들은 Android Platform의 각각 버전의 일부로 포함되며 SDK에서 설치될 수 있다. Android 1.6 Platform은 다음과 같은 기본 스킨들을 제공하고 있다.
   QVGA (240x320, low density, small screen)
   HVGA (320x480, medium density, normal screen)
   WVGA800 (480x800, high density, normal screen)
   WVGA854 (480x854 high density, normal screen)

Android 2.0은 1.6 기본 스킨외에 다음과 같이 추가되었다.
   WQVGA400 (240x400, low density, normal screen)
   WQVGA432 (240x432, low density, normal screen)

AVDs을 만들 때 "command line" Android tool을 사용하려면 아래의 스킨을 지정하는 방법에 대한 예제를 살펴보면 된다.

  android create avd ... --skin WVGA800


Application을 에뮬레이터에서 테스트하는 것을 추천하며 이러한 에뮬레이터는 실제 Device와 거의 동일한 물리적인 크기로 동작할 수 있게 설정된다. 이러한 테스트 방법은 다양한 해상도와 Densities에서 얻어진 결과를 아주 쉽게 비교할 수 있도록 만들어 준다. 그리고 그렇게 테스트하기 위해서는 개발자가 자신의 모니터에 대한 dpi 단위의 Density 값을 어느정도 알고 있을 필요가 있다.(30" Dell 모니터는 96dpi의 Density) 에뮬레이터를 실행시킬 때 개발자 모니터의 dpi를 "-scale" 옵션 값으로 사용할 수 있다.

   emulator -avd <name> -scale 96dpi


만약 ADT가 설치된 Eclipse에서 작업을 하는 경우, 실행 및 디버그 구성(Configuration)의 "Target" 탭에서 ditional Emulator Command Line Options" 필드에 -scale 96dpi를 지정할 수 있다.

-scale 옵션을 가지고 에뮬레이터가 실행되면 전체 에뮬레터의 Display를 스킨과 개발자 모니터 모두를 기반으로하여 스케일이 변경된다. Default Density, 즉 Android 1.6 SDK에 포함되어 있는 에뮬레이터 스킨들의 사용은 다음의 화면 사이즈를 Emulate 한다.
   QVGA, low density: 3.3"
   WQVGA, low density: 3.9"
   WQVGA432, low density: 4.1"
   HVGA, medium density: 3.6"
   WVGA800, high density: 3.9"
   WVGA854, high density: 4.1"

또한 단일 크기 Density 구성안에서 서로 다른 실제 화면 사이즈로 Application을 테스트해야 한다. 예를 들어, Table1에 따르면 QVGA의 최소 지원 Diagnal은 2.8"이다. 30" 모니터에 2.8" QVGA가 Display되기 위해서는 -scale 값의 변경이 필요하다. 모니터가 96dpi, QVGA의 사이즈는 2.8", 에뮬레이터의 기본 크기는 3.3"이므로 설정될 값은 96*2.8/3.3 = 81 dpi 가 되며 그 값을 넘겨주면 된다. -scale 값으로 소수점이 있는 값을 사용할 수 있는데 Scaling Factor 값을 지정하여 사용할 수 있다.

  emulator -avd <name> -scale 0.6


그리고 기본적으로 내장된 스킨에 의해 제공되지 않는 해상도나 Density를 사용하는 화면에서 Application을 테스트하고 싶다면 기존 스킨을 수정하거나 사용자가 정의한 해상도나 Density를 사용하는 AVD를 만들 수 있다.

AVD Manager에서 사용자 정의된 스킨 해상도나 Density를 "Create New AVD" Dialog에서 지정할 수 있다.
[그림 추가. Fig 4]

Android Tool에서 사용자 정의된 해상도나 Density를 가지는 AVD를 만드는 단계는 다음과 같다.
1. 새로운 AVD를 만들기 위해서는 "create avd" 명령뒤에 "--skin" 옵션을 추가하여 이름을 지정해 준다. 여기서는 WVGA800 이므로 240x432가 된다.

    android create avd -n <name> -t <targetID> --skin WVGA800

2. 사용자 정의된 Density를 사용하기 위해서는 새로운 AVD를 위한 사용자 정의된 Hardware Profile을 생성 할 지의 여부를 물어볼 때 "yes"라고 답한다.
3. "Abstracted LCD density" 값을 설정한다. Medium Density 이면 160을 입력한다.
4. 기타 다른 Hardware 옵션들을 설저앟고 AVD 생성을 완료한다.

에뮬레이터의 스킨 구성을 변경하는 것을 대신해서 에뮬레이터 스킨의 기본 Density를 사용할 수 있고 "-dpi-device" 옵션을 에뮬레이터 Command line에 추가할 수 있다.

    emulator -avd WVGA800 -scale 96dpi -dpi-device 160



Screen-Compatibility Examples

이번 섹션은 Android Platrom이 Baseline 화면 구성으로 작성된 Application을 어떻게 Display하는지에 대한 예제를 제공하고 있다. Baseline 화면 구성은 3.2"화면에 HVGA(320x400) 해상도를 가지는 것을 의미하며 Paltform이 제공하는 크기 및 Density의 호환성 기능들 모두가 활성화되어 있는 상태에서 Display하는 것을 보여주게 된다. 즉, Application이 그려지게 될 화면을 위해 내장된 지원을 제공하지 않는 그러한 Application을 Platform이 Display 할 수 있는 방법을 보여주는 예제이다. 그러나 대신 그러한 기능들은 완전히 Platfomr에 의존하게 된다.

Platform의 화면 호환성 관련 기능들은 사용자를 위해 Baseline 화면의 크기 및 Density와 거의 동일한 물리적인 Display를 보증하는 동시에 이제 경험하게 될 가상의 Baseline 화면 환경을 그러한 Application에 제공하도록 설계되었다.

Multiple 화면을 지원하도록 수정되지 않은 Legacy Applications이 그러한 Applications의 전형적인 예제가 될 것이다. 대부분의 경우, "Strategies for Legacy"에서 언급했듯이 이전에 만들었던 Application에 Multiple 화면의 지원을 추가하고 업데이트된 버전을 배포하고 싶어한다. 그러나 그렇게 하고 싶지 않다면 Platform은 아래에 설명된 것처럼 최선을 다하여 보여주는 동작을 하게 된다.

내부적으로, 이러한 동작들은 Platform에 의해 제공되는 호환성 기능들이며 현재의 Device 화면에 기반하여 제공된다.

  • 만약 Device의 화면 Density가 "medium"이 아니면 Application의 Layout과 그 내용물들의 그리기는 마치 화면이 "medium" Density인 것과 같다. 그러나 Framework는 Layout과 이미지들을 타겟의 Density에 맞추기 위해 스케일 변경을 하게 된다. 만일 타겟 Density가 High Density(160->240 Virtual dpi)이면 1.5배 스케일 작업을 하고 타겟 Density가 낮으면(160->120 Virtual dpi) 0.75배 만큼 스케일 변경을 하게 된다.
  • 만약 Device의 화면이 "small"이면 그러한 화면에서 Android 1.5 Applicationsd이 잘 동작하도록 만드는 몇가지 옵션이 있는데 Android Market은 Device로부터 이러한 화면을 지원하도록 알려지지 않은 Applications을 필터링 시켜버린다.
  • 만약 Device의 화면 크기가 "large"이면 Application의 화면은 "normal" 사이즈로 제한되고 Application의 주위는 검은색 배경으로 그려주게 된다. 예를 들어, Applicaiton이 High Density를 지원하지만 "large" 화면을 지원하지 않는다면 화면의 480x720 영역만을 사용하고 나머지는 검은색 배경화면으로 채우게 된다.

'Programming > android' 카테고리의 다른 글

Android TCP/IP Echo chat Server & Client  (1) 2010.10.08
android에서 external command 사용하는 방법  (0) 2010.09.03

+ Recent posts