반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 받은 트랙백

글 보관함


Using the AsyncTask Class

 

이번 글에서는 쓰레드를 생성하고 관리하기 위해 AsyncTask 클래스를 사용하는 방법을 알아보겠습니다.
 기본적으로 AsyncTask 클래스는 백그라운드에서 프로세스를 실행하기 위해 비동기 task 를 생성합니다. 이 AsyncTask 클래스는 프로그레스를 보여주고 태스크를 완료하기 위해 쓰레드와 동기화 하는 여러개의 이벤트 핸들러들을 제공합니다. AsyncTask 는 여러 태스크드을 실행할 수 있습니다. 네트워크에서 데이터를 받아오고 백그라운드로 데이터를 저장하고 하는 일들을요.
 

AsyncTask에는 약간의 design flaw들이 있는데요. 이것들은 핸들러와 pool에 근거하고 한번에 몇개의 쓰레드만을 핸들링 할 수 있습니다. 왜냐하면 AsyncTask는 compatibility library에 포함되어 있기 때문이죠.


AsyncTask를 초기화하기 위해 AsyncTask 클래스를 extend 하고 파라미터를 제공합니다. 그 파라미터 들은 Input Parameters, Progress Values, Result Values 가 됩니다. 이 파라미터들을 전달하고 싶지 않으면 그냥 void 를 사용하시면 됩니다. 이 subclass는 아래의 이벤트 핸들러들을 override 해야 합니다.

- doInBackground : 이 메소드는 백그라운드 쓰레드에서 실행합니다. user와 interact 가 필요 없는 코드를 이 핸들러에 넣습니다. Input Parameter 가 이 메소드에 전달 될 겁니다. 이 메소드에서 publishProgress() 메소드가 호출되고 onProgressUpdate() 메소드가 메인 쓰레드안에서 실행 될 겁니다. 이 publishProgress() 와 onProgressUpdate() 메소드들을 사용해서 이 백그라운드 쓰레드는 메인 쓰레드와 communicate를 해 작업의 progress를 보여주기 위해 UI 엘리먼트 들을 업데이트 합니다. 이 메소드에서 작동하는 코드는 별도의 백그라운드 쓰레드에서 실행되는 것을 기억해 두세요.


- onProgressUpdate() : 이 핸들러를 override 해서 task 내에서 작업의 progress를 가리키기 위해 UI를 업데이트 합니다. 이 핸들러는 publishProgress()로 전달된 파라미터 세트를 받습니다. 이 핸들러는 실행이 되면 쓰레드와 동기화 됩니다. 그러니까 안전하게 UI 엘리먼트를 수정할 수 있습니다.


- onPostExecute : 이름에서 알 수 있듯이 이 메소드는 doInBackground() 메소드가 완료된 후에 호출 됩니다. doInBackground() 메소드에 의해 return 된 Result Value들은 이 메소드에 전달 됩니다. 이 핸들러는 비동기 task 가 완료 됐을 때 inform 될 수 있습니다.

이 AsyncTask 클래스는 두개의 콜백 메소드를 제공합니다.


- onPreExecute : 이름을 보면 알 수 있듯이, 이 메소드는 doInBackground() 메소드가 호출되기 전에 호출 됩니다. 이 메소드는 메인 쓰레드에서 작동되고 setup 같은 작업을 여기서 하게 됩니다.


- onCancelled : 쓰레드의 cancellation을 관리 합니다. 이 메소드는 쓰레드의 실행을 interrupt하고 onPostExcute() 메소드의 실행을 막습니다.


NOTE : onPostExecute, onPreExecute, onProgressUpdate, onCancelled를 오버라이드 하는 것은 선택사항 입니다. 반드시 오버라이딩을 해야만 하는 것은 아닙니다.


백그라운드 쓰레드에서 1에서 10까지의 숫자를 순서대로 표시하는 어플리케이션을 만들겠습니다.
이름은 AsyncTasksApp 입니다. 일단 화면에 숫자를 표시하려면 TextView를 만들어야 겠죠.


아래 activity_async_tasks_app.xml 파일이 있습니다.


<LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/seqnums"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="@dimen/text_size"
        android:textStyle="bold"  />
</LinearLayout>


한 개의 TextView 만 있죠?


다음은 자바 파일을 보겠습니다.

AsyncTasksAppActivity.java


public class AsyncTasksAppActivity extends Activity {
    TextView seqNums;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_tasks_app);
        seqNums=(TextView) findViewById(R.id.seqnums);
        new PrintSequenceTask().execute(1);
    }

    private class PrintSequenceTask extends
        AsyncTask<Integer, Integer, Void> {
        @Override
        protected void onPreExecute() {
            seqNums.setText("Sequence numbers begins");
        }

        @Override
        protected Void doInBackground(Integer... args) {
            for (int i = args[0]; i <= 10; i++) {
                publishProgress(i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... args) {
           seqNums.setText(args[0].toString());
        }

        @Override
        protected void onPostExecute(Void result) {
           seqNums.setText("Sequence numbers over");
        }
    }
}




딱 보면 PrintSequenceTask 클래스가 AsyncTask를 extends 한 것을 보실 수 있으실 겁니다. onCreate() 메소드를 보시면 이 클래스를 호출할 때 파라미터를 1을 전달했는데요. 그건 숫자를 1 부터 표시하기 위해 전달 한 겁니다.  이 PrintSequenceTask 클래스가 호출 된 후 doInBackground() 메소드가 실행되기 전데 onPreExecute() 메소드가 실행되서 Sequence numbers bigins 라는 텍스트를 화면에 뿌려 줄 겁니다. 그 다음에 doInBackground() 메소드가 실행되서 for 루프가 실행되겠죠. 아까 onCreate() 에서 1을 파라미터로 받았으니까 int i 는 1 부터 실행 될 겁니다. 1에서 10까지 for 루프가 돌면서 그 값들을 publishProgress()에 할당 합니다. 이 publishProgress() 메소드가 호출 되면 메인 쓰레드에서 onProgressUpdate() 메소드가 실행됩니다. 이 때 publishProgress() 에 전달 된 값이 그대로 onProgressUpdate()에도 전달이 됩니다. 그러니까 여기서는 1부터 10까지의 숫자가 전달이 되겠죠.


onProgressUpdate() 메소드에서는 TextView에 전달된 값을 표시하는 로직이 있네요. 1에서 10까지 숫자가 표시될 때 각각 1초간의 쉬는 시간이 있습니다. 최종적으로 doInBackground() 안의 작업이 끝나게 되면 즉 모든 숫자들이 다 표시되고 난 후 onPostExecute() 메소드가 실행됩니다. 여기서는 Sequence numbers over 라는 텍스트가 화면에 뿌려지도록 했습니다.

그러니까 전체적으로 보면 어플리케이션이 실행되면 Sequence numbers begins라는 글자가 화면에 표시되고 1부터 10까지의 숫자가 1초 간격으로 차례대로 표시되다가 마지막에는 Sequence numbers over 라는 텍스트가 표시 될 겁니다.


Summary

이 챕터에서는 어플리케이션의 퍼포먼스를 더 좋게 만드는 것과 관련해서 몇가지 다뤄봤습니다. dual-core chips와 quad-core 프로세서가 어플리케이션 experience를 더 좋게 만든다는 것도 다뤘구요. garbage collection이 더 이상 사용되지 않는 메모리를 새로운 어플리케이션에 할당한다는 내용도 배웠습니다. 또한 어플리케이션의 task를 쓰레드를 사용해서 백그라운드로 돌리는 절차에 대해서도 보셨습니다. 마지막으로 백그라운드에서 비동기적으로 태스크를 생성화고 관리하기 위해 AsyncTask 클래스를 어떻게 사용하는지에 대해서도 배웠습니다.

반응형

Comment


Using Multiple Threads



이번에는 두개의 쓰레드를 실행시키는 것에 대해 알아 보겠습니다. 두개의 쓰레드를 통해서 어떻게 두개의 태스크가 동기적으로 실행되는지에 대해 보게 될 겁니다. 이 두개의 쓰레드에 대해 한개의 핸들러를 생성할 겁니다. 이 쓰레드들은 1에서부터 10까지의 순차적인 숫자를 표시합니다. 첫번째 쓰레드가 1초에 숫자 하나씩 1에서부터 10까지 차례대로 표시를 하고 두번째 쓰레드는 2초에 한번씩 1부터 10까지 차례대로 표시를 합니다.


이 두 쓰레드들은 동기적으로 task를 시작할 겁니다. 하지만 첫번째 쓰레드가 먼저 끝나겠죠. 두 번째 쓰레드가 끝나는 시간의 딱 반만 소요 될 겁니다.



handleMessage() 메소드는 이 순차적인 번호를 표시하기 위해 핸들어 안에서 오버라이드 될 겁니다. 순차적인 번호들은 Message 파라미터를 통해서 이 메소드에 넘겨집니다. 이 두 쓰레드의 Message object를 구별하기 위해서 Message object내의 calling 쓰레드 이름이 Bundle 에서 명명 됩니다. 



이 앱을 만들기 위해서 layout xml 에는 4개의 TextView 가 들어가게 됩니다. 두개는 쓰레드 이름이 표시될 거고 두개는 그 값이 표시될 겁니다. 




activity_multiple_threads_app.xml


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MultipleThreadsAppActivity" >
    <TextView
        android:id="@+id/thread1" 
        android:text="Thread1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dip" 
        android:layout_marginLeft="20dip"
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />
       <TextView
        android:id="@+id/thread2" 
        android:text="Thread2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dip" 
        android:layout_marginLeft="100dip"
        android:layout_toRightOf="@id/thread1"
        android:textSize="@dimen/text_size"
        android:textStyle="bold"/>
    <TextView
        android:id="@+id/seqnums1" 
        android:text="0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dip" 
        android:layout_marginLeft="30dip"
        android:layout_below="@id/thread1"
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />
   <TextView
        android:id="@+id/seqnums2" 
        android:text="0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dip" 
        android:layout_marginLeft="150dip"
        android:layout_below="@id/thread2"
        android:layout_toRightOf="@id/seqnums1" 
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />

</RelativeLayout>



각각 id를 갖고 있고 이 id로 나중에 자바 파일에서 각 TextView를 구분해서 활용할 겁니다.
그리고 textSize는 dimention 에서 정의된 text_size를 사용할 거구요.
이 값은 res/values 폴더 안에 dimens.xml 이라는 파일 안에 있습니다.



그 다음에 핸들러를 정의하고  이 두 쓰레드들의 메세지를 보내고 받기 위해 아래와 같이 자바파일을 만듭니다.


MultipleThreadsAppActivity.java



public class MultipleThreadsAppActivity extends Activity {
static    private TextView seqNums1, seqNums2;
       
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiple_threads_app);
        seqNums1 = (TextView)findViewById(R.id.seqnums1);
        seqNums2 = (TextView)findViewById(R.id.seqnums2);
    }
   
static    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg)        {
            String threadInvoked=msg.getData().getString("threadName");
            if (threadInvoked.equals("thread1"))
        seqNums1.setText(msg.obj.toString());
            if (threadInvoked.equals("thread2"))
                seqNums2.setText(msg.obj.toString());
        }
        };

        @Override
        protected void onStart() {
            super.onStart();
     Thread     thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
             try {
           for(int i=1;i<=10;i++){
                                Thread.sleep(1000);
                                Message msg = new Message(); 
                                Bundle bundle = new Bundle();
                                bundle.putString("threadName", "thread1");
                                msg.setData(bundle);
                                msg.obj=String.valueOf(i);
                                handler.sendMessage(msg);
       }
                            } catch (InterruptedException e) {
                               e.printStackTrace();
                            }   
        }
        });

 Thread thread2 = new Thread(new Runnable()        {
    @Override
        public void run() {
         try {
           for(int i=1;i<=10;i++){
                                 Message msg = new Message();
                                Thread.sleep(2000);
                                Bundle bundle = new Bundle();
                                bundle.putString("threadName", "thread2");
                                msg.setData(bundle);
                                msg.obj=String.valueOf(i);
                                handler.sendMessage(msg);
           }
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
        }
        });
 thread1.start();
 thread2.start();
    }
}



onCreate() 메소드를 보면 숫자가 들어갈 TextView의 객체들을 만들었습니다. 다음에 onStart() 메소드를 보면 Thread class를 사용하는 걸 보실 수 있습니다. 두개의 쓰레드를 만들어서 두개의 runnable object에 사용하고 있습니다. 1에서부터 10까지의 숫자를 표시하기 위해서 run() 메소드에서는 Message object를 생성했습니다. 그리고 Bundle 객체도 하나 생성해서 그 안에 쓰레드 이름을 key, value 한 쌍으로 넣었습니다. 그것을 Message에 setData()를 사용해서 세팅했습니다. 그리고 msg.obj에 숫자를 대입합니다. 그리고 마지막으로 이 메세지를 Handler object를 통해서 TextView에 전달을 하고 그 값이 화면에 뿌려지게 됩니다.
첫번째 쓰레드는 1초 간격으로 작업을 하고 두번째 쓰레드는 2초 간격으로 작업을 합니다.


이 어플리케이션을 실행하고 나면 두 쓰레드 모두 1서부터 숫자를 표시할 겁니다. 첫번째 쓰레드는 10까지 표시하는데 두번째 쓰레드보다 반 밖에 시간이 안 걸릴 겁니다.

반응형

Comment


Understanding Threads



액티비티, 서비스, 브로드캐시트 리시버 같은 모든 안드로이드 어플리케이션 컴포넌트들은 main application thread 안에서 실행됩니다. 다른 말로 하면 main application thread 는 모든 user-interface와 관련된 task들을 수행합니다. 어플리케이션의 효율성을 증가시키기 위해서 오랫동안 수행되는 혹은 비동기로 수행되는 태스크들과 user와 interact를 하지 않는 태스크들은 백그라운드 쓰레드 수행을 하도록 합니다.



또한 운영체제는 메인 쓰레드가 message에 responsive 하기를 기대하고 있기 때문에 이 메인 쓰레드를 block 할 수 있는 operation은 별도의 쓰레드에서 실행 됩니다. 그래서 메인 쓰레드는 다른 쓰레드들을 컨트롤 하고 User Interface (UI) element들을 업데이트 합니다.


백그라운드 쓰레드로 프로세스 태스크를 옮기는 방법에는 아래의 두가지가 있습니다.


- Handler class 사용하기
- AsyncTask class 사용하기



우선 Handler class 를 사용해서 백그라운드 쓰레드를 사용해서 태스크를 실행하는 방법을 알아 보겠습니다.



백그라운드 프로세스 태스크를 위해 우리가 만든 thread를 implement 하고 그것과 동기화 하기 위해 Handler class 를 사용할 겁니다. 핸들러 클래스는 그것을 생성한 쓰레드와 연결 될 겁니다. 그래서 핸들러 클래스가 초기화 된 그 쓰레드에 코딩을 하실 수 있습니다. 핸들러 클래스와 communicate 할 수 있는 두가지 방법은 messages와 runnable object를 통해서 하는 것입니다. 그 핸들러 클래스에서 object 를 생성한 후 그 object는 현재 쓰레드의 MessageQueue와 연결된 메세지와 runnable object들 처리합니다. 이 태스크는 현재의 쓰레드에 의해 실행되어야 하며 그 실행되기 까지 MessageQueue에서 기다리게 됩니다.



메세지를 처리하기 위해 handleMessage() 메소드를 오버라이드 합니다. runnable object를 처리하기 위해서는 post() 메소드를 사용합니다. 이 쓰레드는 sendMessage(Message msg) 메소드나 sendEmptyMessage() 메소드를 통해서 메세지를 post 할 수 있습니다.



핸들러 클래스를 사용하는 개념을 이해하기 위해 쓰레드를 사용해서 1부터 10까지 숫자를 순차적으로 display 하는 어플리케이션을 만들겠습니다. 이 앱의 이름은 TreadApp 이라고 하겠습니다. 여기에는 순차적인 숫자들만 표시합니다. 그러니까 레이아웃에는 TextView 만 필요하겠죠. 아래 layout xml을 참조하세요.



activity_thread_app.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/seqnums"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="@dimen/text_size"
        android:textStyle="bold" />
</LinearLayout>


보시다시피 TextView 의 id는 seqnums 입니다. 자바 파일 안에서 이 id를 가지고 활용을 하게 될 겁니다. 폰트 사이즈는 dimension 리소스의 text_size를 사용해서 표시 됩니다. 쓰레드 핸들러를 정의해서 메세지를 보내고 받기 위해 아래와 같이 자바 코드를 만듭니다.


ThreadAppActivity.java



public class ThreadAppActivity extends Activity {
    static TextView seqNums;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_app);
        seqNums=(TextView)findViewById(R.id.seqnums);
    }

    static Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
        seqNums.setText(msg.obj.toString());       
        }
    };

    @Override
    protected void onStart() {
        super.onStart();
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=1;i<=10;i++){
                    try {
                        Thread.sleep(1000);
                        Message msg = new Message();
                        msg.obj=String.valueOf(i);
                        handler.sendMessage(msg);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
    }
}


onCreate() 메소드를 보시면 위에서 만들었던 layout xml을 contentView로 세팅을 하고 그 안에 있는 TextView에 대한 객체도 만들었습니다.
onStart() 메소드를 보시면 여기서 Thread 클래스를 사용하는걸 보실 수 있을 겁니다. 바로 runnable object와 함께 쓰레드를 사용하는 방법입니다.  run() 메소드에서 이 쓰레드를 실행시킵니다.



TextView에 1에서 10까지의 순차적인 번호를 표시하기 위해 run() 메소드에서 Message object를 생성하고 그 순차적인 번호를 add 합니다. 이 값들이 obj에 add 되고 나서 이 메세지를 Handler object를 통해서 send 하게 됩니다.



handler 라고 하는 Handler object가 생성돼 있죠? 이 안에 handleMessage()라고 하는 콜백 메소드안에서 이 obj 값이 TextView에 표시되도록 하는 코드가 있습니다.



이 앱을 실행시키면 1초 간격으로 1부터 10까지 화면에 표시할 겁니다. 




반응형

Comment


Understanding the Utility of Garbage Collection



Garbage collection은 메모리 관리와 관련 된 건데요. dynamic 하게 할당하고 자동으로 recycle 하도록 하는 겁니다. 만약 프로그램에 의해 사용되어지는 메모리가 더 이상 사용되어 지지 않게 됐다면 시스템 적에서 자동적으로 recover 되는 겁니다. Garbage collection은 garbage collector 에 의해 수행 됩니다. 메모리 할당과 recycle을 위해 잘 보고 있다가 할당하고 있던 프로그램이 더 이상 사용하지 않게 되면 다른 어플리케이션에 그것을 할당하는 거죠. Garbage collector는 메모리 할당이 특정 한계점에 도달하면 자동으로 invoke 됩니다. 그래서 이 Garbage collector 는 개발자가 메모리 블럭의 할당 해제를 직접 하지 않아도 알아서 메모리 누수나 아니면 너무 일찍 할당 해제해 생기는 문제점들을 사전에 막아 줍니다.



안드로이드에서 메모리 관리는 Dalvik의 garbage collector가 처리 합니다. mark 하고 sweep 하는 방법으로 작동 합니다. 즉 메모리가 어떤 객체에 의해 사용되어 지만 mark 를 하고 그 mark 된 객체는 garbage collect되지 않도록 하는 식이죠) mark 되지 않은 객체들은 sweep 되서 다른 어플리케이션이 사용할 수 있도록 메모리를 free 시킵니다. garbage collecton task는 대개 100에서 200 ms 가 소요 됩니다. 그리고 garbage collection을 하는 동안 어플리케이션의 퍼포먼스는 떨어지게 됩니다. 실행되고 있는 어플리케이션은 이 marking 과 sweeping 작업이 진행되는 동안 suspend 될 겁니다. 작고 아주 짧게 살아있는 object들을 많이 가지고 있는 어플리케이션들은 이 garbage collector를 더욱 자주 실행시키는 결과를 초래할 겁니다. 이렇게 garbage collector 가 너무 자주 실행되는 것을 방지하기 위해 Android SDK 는 allocation tracker 라는 DDMS(Dalvik Debug Monitor service)의 한 부분인 아주 유용할 tool을 제공합니다.



Note
기술적으로 안드로이드에는 garbage collection 이 없습니다. 왜냐하면 안드로이드는 C 언어를 기반으로 한 Operating System 이기 때문이죠. 여기서는 Dalvik runtime내에서 사용되는 Garbage Collection에 대해 얘기하고 있는 겁니다.



DDMS는 안드로이드 SDK를 다운로드 하면 그 안에 딸려서 있는 아주 강력한 디버깅 툴 입니다. DDMS는 이클립스 IDE의 오른쪽 위에 있는 DDMS 아이큰을 선택하거나 Window - Open Perspective - DDMS 옵션을 선택하시면 실행할 수 있습니다.



DDMS를 실행할 때 자동적으로 연결된 Android device나 실행중인 에뮬레이터에 연결 됩니다.



아래는 DDMS를 실행 시켰을 때의 화면 입니다. 




DDMS 의 왼쪽 위에는 Devices 라는 탭이 있습니다. 이것은 컴퓨터에 연결된 디바이스들을 보여 줍니다. 에뮬레이터도 해당 되구요. 그 중 하나를 선택하면 오른쪽에 그에 대한 정보가 나옵니다. 이 Devices 탭 안에는 아래와 같은 아이콘들이 있습니다.

Debug - 디버깅을 하기 위해 사용


Update Hean - 해당 프로세스의 heap information을 볼 수 있도록 함. 이 아이콘을 클릭하고 난 후 오른쪽 pane 의 heap tab을 사용 해 heap 정보를 볼 수 있다.


Dump HPROF file - 메모리 누수를 감지할 수 있는 HPROF 파일을 보여준다.


Cause GC - Gabage collection procedure를 시작 시킨다.


Update Threads - 해당 프로세스의 쓰레드 정보를 보여 준다. 이 아이콘을 클릭 한 후 오른쪽 pane의 Thread 텝을 클릭하면 이 쓰레드 정보를 볼 수 있다.


Start Method Profiling - 다른 메소드들이 몇번 호출 됐는지를 알 수 있다. 그리고 각각이 얼마나 시간을 소비 했는지도 알 수 있다.


Stop Process - 해당 프로세스를 stop 시킨다.


Screen Cpature - 디바이스나 에뮬레이터의 screen 을 capture 한다.


Dump View Hierarchy for UI Automator - screen level trace를 capture 하기 위해 특정 파일을 prompt 한다. CPU frequency changes, CPU idle events, CPU load, Disk I/O 등등의 이벤트들을 Trace 한다. 그리고 이런 trace 는 파일로 저장된다.



그리고 오른쪽 pane 에는 아래와 같은 tab들이 있습니다.

Thread - 해당 프로세스에 있는 쓰레드에 대한 정보를 보여 준다.
Heap - 그 프로세스의 Heap 정보를 보여 준다. Cause GC 버튼을 선택하면 Garbage collection 프로세스가 시작한다. object type과 할당된 메모리의 크기 등이 표시된다. object type을 선택하면 bar graph가 표시 몇개의 object들이 할당됐고 얼마나 메모리를 차지하고 있는지 등에 대한 정보들을 보여 줄 것이다.



Allocation Tracker - 어플리케이션에 할당당된 object들을 추적한다. Start Tracking 버튼을 클릭하면 여러분이 분석하고자 하는 코드를 실행시키기 위해 어플리케이션과 interact 하게 될 것이다. Get Allocations 버튼을 클릭하면 어플리케이션에 할당된 object들의 리스트를 보게 된다. 이 리스트는 위쪽에 표시되는데 만약 그 object중에 하나를 선택하게 되면 그 할당을 이끌고 있는 track trace가 그 아래쪽에 표시될 것이다. 할당된 object의 타입 뿐만 아니라 쓰레드, 클래스, 파일, 그리고 어떤 라인인지에 대한 정보도 볼 수 있다. Stop Tracking 버튼을 클릭하면 데이터를 모두 clear 하고 restart 할 것이다.






반응형

Comment


Cores and Threads



안드로이드 어플리케이션을 빠르고 효율적으로 실행시키기 위해 dual-core chips 와 quad-core processor 들이 개발 되었고 안드로이드 디바이스에 적용되었습니다. 이런 multicore architecture들의 장점을 활용하기 위해 thread scheduler 같은 안드로이드 모듈에 관련된 기술이 만들어 졌습니다.  이 thread scheduler가 하는 일은 서로 다른 core들 사이에서 어플리케이션이 분할 되서 할당되어 그 성능을 최대치로 활용하는 겁니다.





어플리케이션을 효과적으로 운영하기 위해서는 메모리 관리도 중요한데요. garbage collectgor 를 가동하는 것은 메모리 recycling 의 아주 핵심적인 부분 입니다. 그리고 한가지 더 언급하자면 어플리케이션의 최적화 입니다. 바로 백그라운드 쓰레드에 의해서 서로 interactive 하지 않은 태스크를 갖는 겁니다. 쓰레드는 어플리케이션의 멀티 태스킹기능을 가능하도록 해 줍니다.



요 며칠간 multicore processor architecture를 활용하는 것과 메모리 관리에 있어서의 garbage의 역할 그리고 쓰레드와 AsyncTask 클래스를 사용해서 백그라운드로 어플리케이션의 task 들을 수행해 퍼포먼스를 개선시키는 것과 관련해서 다뤄 보겠습니다. 



Multicore Processor Architectures 의 활용 이해하기


당연히 멀티태스킹이 싱글 태스킹 보다 더 나은 겁니다. 싱글 태스킹에서는 한 task 가 작업을 수행할 때 다른 task들은 작업하는 task 가 수행을 끝낼때까지 stop 해 있어야 합니다. 반면에 멀티태스킹은 한가지 task 를 동시에 더 진행할 수 있습니다. 이 multi tasking 을 수행하도록 하기 위해 multicore processor architecture 가 사용됩니다. 안드로이드 3.0 이상의 버전에서 바로 이 single processor architecture나 multicore processor architecture를 활용할 수 있는 기능이 제공 되었습니다.



Multiple core들은 어플리케이션의 작업을 서로 다른 core에서 동시에 수행하도록 나눠 줌으로서 퍼포먼스 향상을 가져 옵니다. 이 multicore processor 는 thread scheduler에 의해 효율적으로 사용할 수 있습니다. multicore mobile processor의 잇점은 모든 core들에  소프트웨어의 task들이 할당되고 schedule 될 때 진정한 의미가 있는 것입니다. 만약 scheduling 이 제대로 작동하지 않으면 오히려 그 어플리케이션의 퍼포먼스는 안 좋아 질 수 있습니다.



많은 안드로이드 기계들이 dual-core chip들이나 quad-core processor 기능들을 가지고 있습니다. 이 dual- 그리고 quas-core 전화기를 최적화시키기 위해 몇가지 변화가 있었습니다. 그리고 그 중에 thread scheduler 는 이 새로운 quad-core 와 dual-core chip들에 대해 최고의 퍼포먼스를 보여줄 수 있도록 개선 됐습니다.


반응형

Comment


오늘은 Android Tablet Developer's Cook Book에 나오는 JSON 관련 예제를 공부할 겁니다.


Chapter 15에 나오는 예제인데요. ConsumeJSONWebserviceApp 이라는 프로젝트 입니다.


일단 SampleJSON.php 라는 파일이 아래 경로에 있습니다.


http://www.gosolkit.com/SampleJSON.php


이 php가 만들어낸 json 데이터는 아래와 같습니다.


[{"state":"Alabama","capital":"Montgomery","latitude":"32.361538","longitude":"-86.279118"},{"state":"Alaska","capital":"Juneau","latitude":"58.301935","longitude":"-134.419740"},{"state":"Arizona","capital":"Phoenix","latitude":"33.448457","longitude":"112.073844"},{"state":"California","capital":"Secramento","latitude":"36.448457","longitude":"112.073844"},{"state":"Gangwon","capital":"Chunchon","latitude":"33.448457","longitude":"112.073844"},{"state":"Rhode Island","capital":"Providence","latitude":"33.448457","longitude":"112.073844"},{"state":"New Jersey","capital":"Trenton","latitude":"33.448457","longitude":"112.073844"}]

위 URL은 책에 있는 URL이랑은 다릅니다.

제 개인 URL 을 사용했구요. json 데이터도 안에 그 내용을 조금 더 추가했습니다.


원격에 있는 php로 생성한 JSON 파일을 받아와서 안드로이드 앱에서 사용하는 예제 입니다.


우선 첫번째로 layout xml 파일을 보죠.


activity_consume_jsonwebservice_app.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"> 
    <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/state_name"
            android:hint="Enter State Name" 
                android:textSize="@dimen/text_size"   />
    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/submit_btn"
            android:text="Submit"
                android:textSize="@dimen/text_size" />
    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/response"
                android:textSize="@dimen/text_size" />
</LinearLayout>


LinearLayout으로 감쌌고 orientation은 vertical 입니다.

그 안에 EditText와 Button 그리고 TextView 이렇게 세개의 view 가 있습니다.



EditText에 주 이름을 넣고 Submit 버튼을 누르면 그 아래 TextView에 원격에서 받은 JSON 데이터를 파싱해서 뿌려 줄 건가 봅니다.


그러면 이제 자바 파일을 보죠.


ConsumeJSONWebserviceAppActivity.java


public class ConsumeJSONWebserviceAppActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_consume_jsonwebservice_app);
        Button submitButton = (Button)this.findViewById(R.id.submit_btn);
        submitButton.setOnClickListener(new Button.OnClickListener(){
             public void onClick(View v)  { 
                    new ReadJSONFeed().execute("http://www.gosolkit.com/SampleJSON.php");               
             }
        });
    }
     
    private class ReadJSONFeed extends AsyncTask<String, String, String> {
        protected void onPreExecute() {}

        @Override
        protected String doInBackground(String... urls) {
       HttpClient httpclient = new DefaultHttpClient();
       StringBuilder builder = new StringBuilder();
            HttpPost httppost = new HttpPost(urls[0]);
           try {
           HttpResponse response = httpclient.execute(httppost);
        StatusLine statusLine = response.getStatusLine();
       int statusCode = statusLine.getStatusCode();
       if (statusCode == 200) {
            HttpEntity entity = response.getEntity();
            InputStream content = entity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(content));
            String line;
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }     
    return builder.toString();
        }

        protected void onPostExecute(String result) {
            String state="";
            String stateInfo="";
            EditText stateName = (EditText) findViewById(R.id.state_name);
            String searchState=stateName.getText().toString();
            try{
             JSONArray countriesArray = new JSONArray(result);       
                   for (int i =0 ; i<countriesArray.length();i++) {
                       JSONObject jObject = countriesArray.getJSONObject(i);
                      state = jObject.getString("state");
                      if(searchState.equalsIgnoreCase(state))
                      {
                          stateInfo+="Capital: "+jObject.getString("capital")+"\n";
                          stateInfo+="Latitude: "+jObject.getString("latitude")+"\n";
                          stateInfo+="Longitude: "+jObject.getString("longitude")+"\n";
                      }
                   }
            }
             catch (JSONException e) {
                 e.printStackTrace();
                 }
            TextView resp = (TextView) findViewById(R.id.response);
            if(stateInfo.trim().length() >0 )
             resp.setText(stateInfo);   
            else
                resp.setText("Sorry no match found");
        }
    }
}



그렇게 복잡한거 같지는 않죠?


먼저 onCreate()를 보죠.

activity_consume_jsonwebservice_app.xml 을 Layout 으로 사용한다고 세팅했습니다.

바로 위에서 본 xml 입니다.


그리고 버튼에 대한 객체를 만들고 이 버튼에 onClickListener를 달았습니다.

이 버튼을 클릭하면 ReadJSONFeed() 라는 클래스를 호출하네요. 그리고 excute() 라는 메소드에 아까 소개했던 원격 URL 을 pass 합니다.


그러면 ReadJSONFeed() 클래스를 보겠습니다.

이 클래스는 AsyncTask 를 상속합니다.

여기를 보시면 해당 API를 보실 수 있습니다.


UI thread를 쉽게 사용할 수 있게 해 준다고 하네요. background 로 실행되게 해서 그 결과를 UI thread에 publish 하는 군요. 따로 thread나 handler들을 작성하지 않아도 된다고 하네요.

이 AsyncTask는 짧은 기간 동안 수행할 작업에 사용됩니다. 만약 긴 기간동안 수행할 작업을 쓰레드로 사용하시려면 Executor, ThreadPoolExecutor and FutureTask 같은 java.util.concurrent pacakge 를 사용 하셔야 합니다.

이 asynchronous task(비동기 작업)은 Params, Progress and Result 이렇게 3가지가 있고 4개의 step들이 있습니다.

onPreExecute, doInBackground, onProgressUpdate and onPostExecute


그러면 계속 ReadJSONFeed 클래스를 보겠습니다.

첫번째 줄에는 위에 봤든 onPreExecute() 메소드가 있네요. 그 안에는 아무것도 없구요.

그냥 넘어가도 되겠습니다.


그 다음에는 또 위에서 봤던 doInBackground() 메소드가 나옵니다.

이 안에 어떤 로직을 넣으면 Background 로 돌아가게 됩니다.


우선 HTTPClient와 HttpPost, StringBuilder 를 초기화 했습니다.

HttpResponse를 초기화 하면서 httpclient의 execute메소드를 통해 httppost를 pass 해 줍니다.

여기 httppost에는 아까 버튼 눌렀을 때 pass 했던 url이 들어 있습니다.

그러면 response 안에 그 결과값 즉 json 데이터들이 담기겠죠.

그 다음에는 StatusLine을 초기화 하면서 이 response의 line을 get 합니다.

즉 json 내용을 get 하는거죠.

그리고 이 statusLine 의 statusCode를 statusCode라는 변수에 담습니다.

이 statusCode에는 성공했는지 실패 했는지에 대한 정보가 담기겠죠.

다음 if 문에서는 만약에 이 작업이 성공했으면 HttpEntity 를 생성해서 여기에 response의 Entity를 가져 옵니다. 그리고 그 내용을 InputStrean에 담고요.

다음에 BufferedReader를 이용해서 그 내용을 읽습니다.


Java에서 파일 내용을 읽어 들일 때 사용하는 InputStream과 BufferedReader가 여기서도 사용되네요.

그 다음에 line이라는 String을 만들어서 모든 줄을 이 line에 넣습니다.


여기까지가 doInBackground() 메소드가 하는 일입니다.

간단하네요. 원격에 있는 정보를 가져와서 이것을 사람이 읽을 수 있는 정보로 line이라는 String에 담은 겁니다.


그 다음에 실행되는 메소드는 onPostExecute() 입니다.

한번 볼까요?


state, stateInfo 라는 String을 만들었습니다. 주 이름과 그 주에 대한 정보를 담을 변수들 인 것 같습니다.

그리고 layout xml에서 만든 EditText에 대한 변수를 만들었습니다.


그리고 searchState라는 String에 이 stateName의 내용을 담을 겁니다.


다음에 나오는 내용들은 JSON을 파싱하는 로직 입니다.

아까 doInBackGround에서는 line이라는 String에 이 JSON 데이터를 그냥 담았죠.

이것을 파싱해서 사람이 알아볼 수 있도록 화면에 뿌려줄 겁니다.


onPostExecute(String result) 라고 위에 돼 있죠. 여기서는 pass 받은 string을 result 라는 변수이름으로 사용할 겁니다.


보시면 이 result 를 countriesArray라는 JSONArray 에 담습니다.


그 다음 for 문을 돌려서 아까 만들었던 변수들에 해당 내용들을 담습니다.

state에는 주 이름이 그리고 searchState에 있는 내용들은 Capital, Latitude, Longitute 별로 따로 사람이 읽기 좋게 편집해서 stateInfo에 담습니다.


그 다음에는 try catch 문 밖을 보시면 되는데요.


TextView 객체를 만들었죠.

이 text 뷰에 위에 만들었던 stateInfo를 setText() 해서 화면에 표시합니다.


그러면 아래와 같은 화면을 보실 수 있습니다.





여기서의 주요 개념은 AsyncTask 를 이용해서 쉽게 Background 작업을 하도록 한다는 것과

원격의 데이터를 받아 오는 방법 여기서는 HttpClient, StringBuilder, HttpPost, HttpResponse 등이 사용됐습니다. 그리고 이 데이터를 바이너리 형태로 돼 있을 텐데 이것을 InputStream과 BufferedReader를 이용해서 사람이 읽을 수 있는 문자로 파싱하는 방법을 배웠습니다.

그리고 그 파싱된 JSON 타입을 다시 key 값을 이용해서 value를 가져와서 필요에 따라 편집하는 방법도 배웠습니다.


아주 중요한 개념들이 많네요.


안드로이드 앱과 웹 서비스와의 네트워킹도 아주 쉽게 사용할 수 있도록 만들었군요.




반응형

Comment



그러면 이번시간에는 TweeningAnimAppActivity.java 파일을 분석해 보겠습니다.


public class TweeningAnimAppActivity extends Activity {
    ImageView imgView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tweening_anim_app);
        Button alphaButton = (Button) findViewById(R.id.alpha_button);
        Button rotateButton = (Button) findViewById(R.id.rotate_button);
        Button scaleButton = (Button) findViewById(R.id.scale_button);
        Button translateButton = (Button) findViewById(R.id.translate_button);
        imgView = (ImageView)findViewById(R.id.imgview);
        rotateButton.setOnClickListener(new View.OnClickListener() {  
             @Override
             public void onClick(View v) {
                 RotateAnimation animation = new RotateAnimation(0,180, Animation.RELATIVE_TO_SELF,0.0f, Animation.ABSOLUTE, 0.2f);
                 animation.setDuration(3000);
                 imgView.setAnimation(animation);
                 animation.start();
             }
         });

        alphaButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animation animation = new AlphaAnimation(1.0f, 0.1f);
                animation.setDuration(3000);
                imgView.setAnimation(animation);
                animation.start();
            }
        });

        scaleButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AnimationSet set = new AnimationSet(true);
                Animation animation1 = new ScaleAnimation(1.0f, 2.0f,1.0f, 2.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                animation1.setDuration(3000);
                set.addAnimation(animation1);
                Animation animation2 = new ScaleAnimation(1.0f, 0.5f, 1.0f, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                animation2.setDuration(3000);
                animation2.setStartOffset(3000);
                set.addAnimation(animation2);
                imgView.startAnimation(set);
            }
        });
        translateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AnimationSet set = new AnimationSet(true);
                Animation animation1 = new TranslateAnimation(0,-150,0,0);
                animation1.setDuration(3000);
                animation1.setFillAfter(true);
                set.addAnimation(animation1);
                Animation animation2 = new TranslateAnimation(0,0,0,200);
                animation2.setDuration(3000);
                animation2.setStartOffset(3000);
                animation2.setFillAfter(true);
                set.addAnimation(animation2);
                imgView.startAnimation(set);
            }
        });
    }
}




우선 첫 줄에는 ImageView 를 초기화 시켰습니다.

이 ImageView에 애니메이션 효과를 적용할 겁니다.

그 다음 onCreate() 메소드를 봐야겠죠.
이전 글에서 다뤘던 activity_tweening_anim_app.xml 을 ContentView 로 세팅했습니다.
버튼 4개와 1개의 이미지뷰로 구성된 RelativeLayout 이었죠.

그 다음에는 버튼 4개에 대한 객체를 만들었습니다.
xml 에 있는 이런 view를 객체로 만들때는 findViewById()를 사용하죠.
그리고 아까 초기화 했던 imgView 객체도 만들구요.

그 다음에는 4개의 버튼들에 리스너를 달아주는 부분입니다.

rotateButton을 클릭했을 때는 어제 봤던 RotateAnimation 클래스를 실행시켜 줍니다.
애니메이션 시간은 3초로 하고 이 애니메이션은 imgView에 적용합니다.
마지막 animation.start()를 호출해서 실제 애니메이션을 실행합니다.

alphaButton에는 AlphaAnimation()클래스를 호출하는 부분을 넣었습니다.
그 이후는 rotateButton과 같습니다.

scaleButton에는 위와 다른 부분이 있네요.
처음에 AnimationSet()에 대한 객체를 만들었습니다.
그리고 ScaleAnimation()를 3초간 실행한다는 것을 AnimationSet에 add를 하구요.
그 다음에 다시 ScaleAnimation() 을 3초간 실행하고 3초간 StartOffset()을 실행한다는 것을 AnimationSet 에 add를 했습니다.
마지막에는 이 AnimationSet를 start 했구요.

setStartOffset()은 해당 시간만큼 지연해서 애니메이션을 시작한다는 겁니다.
그러니까 여기서는 첫번째 ScaleAnimation()이 3초간 이뤄지니까 그 3초가 지난다음에 두번째 ScaleAnimation()이 시작된다는 얘기죠.

transateButton에도 AnimationSet 가 적용이 됐는데요. 여기는 또 다른 setFillAfter(true) 가 있습니다.
이것을 true로 하게 되면 애니메이션이 끝난 다음에 다시 원래 위치로 돌아가지 않고 종료된 위치에 정지해 있는 겁니다.
여기서는 첫번째 TranslateAnimation() 은 왼쪽으로 150 만큼 3초간 가고 그 다음은 아래로 200만큼 3초간 움직일 것 같네요.
첫번째 애니메이션이 끝난 후 그 끝난 위치에서 이 ImageView가 아래로 움직일 겁니다.
다시 원위치로 가서 아래로 움직이는게 아니라요.
ㄱ 자를 거울로 본 모양으로 ImageView가 움직이겠네요.

이 앱을 실행하면 아래와 같은 화면이 나옵니다.



처음에 Alpha 버튼을 누르면 그림이 3초간 점점 흐려졌다가 다시 원래로 돌아옵니다.
new AlphaAnimation(1.0f, 0.1f)
이게 그렇게 만든건데요.
1.0 에서 0.1 까지 투명도가 변한다는 겁니다.
이건 간단하게 이해할 수 있습니다.
숫자를 반대로 주면 흐린상태에서 점점 뚜렷한 상태로 변하게 되겠죠.
앱 시작할 때 로고를 이렇게 흐리게 했다가 다시 진하게 하는 효과를 줄 수도 있을 것 같네요.

두번째 Rotate 는 이미지를 가운데를 중심으로 바람개비 돌듯이 한바퀴 돌리는 겁니다.
예전에 제가 Spin the Bottle 앱을 만들 때 이 애니메이션으로 구현하려고 막 노력한적이 있었습니다.
막 for문 써서 계속 돌아가다가 일정 시간 지나면 천천히 돌다가 멈추도록 하는거요.

그러다가 이 애니메이션가지고는 Reality 를 살리지 못하겠다고 판단해서 다른 방법을 찾았습니다.
저는 SurfaceView를 사용해서 Spin the Bottle 에서 병 돌아가는 효과를 주었습니다.

어쨌든 이 RotateAnimation() 도 필요할 때가 있을 텐데요.
RotateAnimation animation = new RotateAnimation(0,360, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
파라미터를 보면요.
처음 두 파라미터인 0도에서 360도 돈다는 것은 쉽게 이해할 수 있습니다.
세번째 파라미터는 해당 object가 돌아가는 중심이라고 했습니다. pivotXType인데요. 여기는 정수형이 들어갑니다.
이 값이 0이면 왼쪽을 나타냅니다.
여기에 들어가는 값은 Animation.ABSOLUTE와 Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_PARENT. 등이 있습니다.
이 소스에는 Animation.RELATIVE_TO_SELF 가 적용됐는데요. 그 값은 1 입니다.
이 1이 적용됐을 때 그 객체는 자신의 가운데 포인트를 중심으로 Rotate 하게 됩니다. 바람개비처럼요.
이것을 Animation.ABSOLUTE로 바꿔주면 이미지 왼쪽을 중심으로 이미지가 한바퀴 빙 돌게 됩니다.
이 Animation.ABSOLUTE의 값이 0입니다.
다음에 나오는 Animation.RELATIVE_TO_PARENT 의 값은 2 입니다.
이 값을 여기에 넣으면 이미지가 아니라 화면의 오른쪽 끝을 중심으로 크게 원을 도네요.
여기에 Integer가 들어갈 수 있으니까 제가 한번 3도 넣어보고 4도 넣어 봤거든요.
그랬더니 0 (Animation.ABSOLUTE) 일때와 같은 결과가 나오네요.


그 다음 파라미터는 pivotXvalue 인데 여기서는 0.5f가 설정됐습니다.
이것을 0.9로 한번 바꾸면 어떻게 될까요?
그러면 바람개비 처럼 돌던것이 이미지 오른쪽을 중심으로 도네요.
아까 pivotXType을 Animation.ABSOLUTE로 했을 때 이미지 왼쪽을 중심으로 도는것과 딴 반대의 경우입니다.
1.5로 하면 이미지의 더 오른쪽을 중심으로 돌구요.
그 이후에 나오는 Parameter 인 pivotYType과 pivotYvalue 도 x,y 축만 바꿔서 비슷한 효과를 줍니다.
이 값들을 이것 저것 여러개 바꿔봤는데요.

이 애니메이션을 가지고 flipping 효과를 줄 수는 없겠네요.
이 방법은 다른데서 찾아봐야겠습니다.

그리고 에뮬레이터로 실행하니까 시간이 오래 걸려서 다양한 시도는 못 해 보겠습니다.

다음으로 ScaleAnimation()을 보죠.

ScaleAnimation(1.0f, 2.0f,1.0f, 2.0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
처음부터 4번째까지의 파라미터들은 fromX,toX,fromY,toY 입니다.
그러니까 전체적으로 이미지가 2배 커지게 되겠네요.
그 다음 4개는 위 RotateAnimation 과 마찬가지로 pivotXType,pivotXvalue,pivotYType,pivotYvalue 입니다.
이것도 Scaling의 중심부분을 잡아주는 파라미터들입니다.

그 다음에 나오는 translate 애니메이션을 보죠.
TranslateAnimation(0,-150,0,0);
fromXDelta,toXDelta,fromYDelta,toYDelta 입니다.
이미지의 출발과 종료 지점을 나타내는 겁니다.
위의 값은 toXDelta만 -150이니까 왼쪽으로 150만큼 이동한다는 의미입니다.

flipping 애니메이션 효과를 찾으려고 하다가 보게 된 Tweening Animation 예제였었는데요.
어쨌던 이 4가지 안드로이드에서 제공하는 애니메이션 클래스에 대해 자세하게 배운것 같습니다.

이건 여기서 만족하고 책이나 인터넷 써칭을 통해서 flipping 효과를 어떻게 낼지에 대해서 더 research 해 봐야 겠습니다.



반응형

Comment


지금 참여하고 있는 프로젝트에서 TDD 관련 Role 을 맡고 있는데요.

안드로이드 Tablet 용 Native 애플리케이션 개발쪽 Role 을 맡을 수도 있을 것 같아서 얼마전 책 두권을 샀습니다.

그 중 하나가 B.M. Harwani 가 지은 The Android Tablet Developer's Cookbook 입니다.


기왕 샀으니 열심히 공부해야겠죠.


오늘은 책장을 넘기다가 눈에 띄는 부분을 공부하려구요.

Chapter 8에 있는 Tweening Animation 인데요.

이 앱을 공부해 보겠습니다.


이 앱에는 4가지 타입의 애니메이션이 있습니다.


Alpha animation - view 의 투명도를 바꿈

Rotate animation - 주어진 pivot point 나 axis 를 중심으로 특정 각도로 view를 rotate 시킴

Scale animation - X나 Y 축을 중심으로 view 를 축소하거나 확대 함.

Translate animation - X나 Y 축을 따라서 view를 움직임


원래 view 를 flipping 시키는 효과를 어디서 찾아보던 중이었거든요.

Rotate animation 이 바로 제가 원하는 효과인지 한번 봐야겠습니다.


그럼 일단 layout xml인 activity_tweening_anim_app.xml을 보겠습니다.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <ImageView android:id="@+id/imgview"
        android:layout_width="@dimen/image_width"
        android:layout_height="@dimen/image_height"
        android:src="@drawable/ic_launcher"
        android:layout_centerInParent="true" />
    <Button
        android:id="@+id/alpha_button"
        android:text="Alpha"
        android:textSize="@dimen/text_size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"  />
    <Button
        android:id="@+id/rotate_button"
        android:text="Rotate"
        android:textSize="@dimen/text_size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/alpha_button" />
    <Button
        android:id="@+id/scale_button"
        android:text="Scale"
        android:textSize="@dimen/text_size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
         android:layout_toRightOf="@id/rotate_button" />
    <Button
        android:id="@+id/translate_button"
        android:text="Translate"
        android:textSize="@dimen/text_size"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/scale_button"  />
</RelativeLayout>



전체 Layout 은 RelativeLayout 으로 감쌌습니다.

그리고 그 안에 1개의 ImageView와 4개의 Button들이 있습니다.

각 버튼마다 위에 소개한 4개의 효과 중 하나를 동작하도록 할 거 같습니다.

각 view 마다 모두 id 가 있죠? 나중에 java에서 갖다가 사용할 겁니다.

각 뷰마다 size는  @dimen/ 으로 시작하는데 이것은 res-values 폴더 아래에 있는 dimens.xml 안에 설정한 값을 사용하겠다는 겁니다.


dimens.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">14sp</dimen>
        <dimen name="image_width">100dp</dimen>
    <dimen name="image_height">120dp</dimen>
</resources>


이 크기들은 일반 phone 크기에 맞춘 것입니다. 


이 소스를 보면 values-sw600dp 라는 폴더가 있는데요. 이 안에 있는 dimens.xml을 보면 아래와 같습니다.


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">24sp</dimen>
        <dimen name="image_width">140dp</dimen>
    <dimen name="image_height">160dp</dimen>
</resources>


이것은 7인치 타블렛 용입니다.

그리고 values-sw720dp 는 10 인치 이상의 tablet 을 위한 겁니다.


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">32sp</dimen>
        <dimen name="image_width">180dp</dimen>
    <dimen name="image_height">200dp</dimen>
</resources>


크기의 비율을 잘 봐 두면 나중에 실무에 도움을 받을 수 있겠네요.

이렇게 설정을 해 놓으면 각 device 마다 해당 해상도에 맞는 dimens.xml 을 갖다가 쓰게 됩니다. 다양한 크기의 해상도를 가진 안드로이드 앱을 개발할 때는 이런 구성을 미리 생각하고 프로그래밍에 들어가야 겠습니다.


아까 소개했던 4개의 애니메이션 효과들은 각각 해당 class 를 가지고 있습니다.


* AlphaAnimation


먼저 AlphaAnimation 을 보겠습니다.


이 클래스는 view의 투명도를 조절하기 위해 만든 건데요. 그 구성은 아래와 같습니다.


public AlphaAnimation(float from_alpha, float to_alpha)


여기서 첫번째 파라미터인 from_alpha 는 이 애니메이션이 시작할 때의 alpha 값입니다. 값은 0.0에서 1.0까지 설정할 수 있습니다.  1.0은 제일 선명한 거고 0.0은 완전 투명한 겁니다.

두번째 파라미터인 to_alpha 는 이 애니메이션이 끝날 때의 alpha 값입니다.


예를 들어 Animation animation = new AlphaAnimation(1.0f,0.1f) 라고 선언하면 아주 뚜렷한 이미지에서 거의 투명한 이미지로 바뀌게 되겠죠.



* TranslateAnimation


이 클래스는 view를 움직이도록 합니다. syntax는 아래와 같습니다.


public TranslateAnimation (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

fromXDelta Change in X coordinate to apply at the start of the animation
toXDelta Change in X coordinate to apply at the end of the animation
fromYDelta Change in Y coordinate to apply at the start of the animation
toYDelta Change in Y coordinate to apply at the end of the animation



* RotateAnimation


public RotateAnimation (float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

Parameters
fromDegrees Rotation offset to apply at the start of the animation.
toDegrees Rotation offset to apply at the end of the animation.
pivotXType Specifies how pivotXValue should be interpreted. One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or Animation.RELATIVE_TO_PARENT.
pivotXValue The X coordinate of the point about which the object is being rotated, specified as an absolute number where 0 is the left edge. This value can either be an absolute number if pivotXType is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
pivotYType Specifies how pivotYValue should be interpreted. One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or Animation.RELATIVE_TO_PARENT.
pivotYValue The Y coordinate of the point about which the object is being rotated, specified as an absolute number where 0 is the top edge. This value can either be an absolute number if pivotYType is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.



* ScaleAnimation


public ScaleAnimation (float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)


Parameters
fromX Horizontal scaling factor to apply at the start of the animation
toX Horizontal scaling factor to apply at the end of the animation
fromY Vertical scaling factor to apply at the start of the animation
toY Vertical scaling factor to apply at the end of the animation
pivotXType Specifies how pivotXValue should be interpreted. One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or Animation.RELATIVE_TO_PARENT.
pivotXValue The X coordinate of the point about which the object is being scaled, specified as an absolute number where 0 is the left edge. (This point remains fixed while the object changes size.) This value can either be an absolute number if pivotXType is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
pivotYType Specifies how pivotYValue should be interpreted. One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or Animation.RELATIVE_TO_PARENT.
pivotYValue The Y coordinate of the point about which the object is being scaled, specified as an absolute number where 0 is the top edge. (This point remains fixed while the object changes size.) This value can either be an absolute number if pivotYType is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.



4개의 애니메이션을 구현하기 위해 따로 코딩을 하지 않고 그냥 안드로이드 API 에서 해당 클래스를 불러다 쓰는 군요.

제가 원하는 flipping 효과를 낼 수 있을 지 어떨지 잘 모르겠네요.


다음 글에서 실제 자바 코드를 분석해 보고 어떻게 실행되는지도 살펴 보겠습니다.

그리고 실제 코드를 이것 저것 수정해서 어떤 효과들을 낼 수 있는지도 추가적으로 알아보겠습니다.


반응형

Comment


오늘도 Do it 안드로이드 프로그래밍에서 제공하는 예제로 공부를 했습니다.


예제소스는 http://android-town.org/ 로 가면 무료로 제공하고 있으니 필요하신 분들은 받으셔서 보셔도 됩니다.

동영상강좌는 https://www.youtube.com/user/easyspub 에서 제공되고 있습니다.

책을 직접 구입하셔서 보시면 훨씬 더 도움이 될 것 같네요.


이 예제 중 book3 에 있는 SamplePageSliding 을 공부했는데요.

지금 하고 있는 일과 관련해서 화면을 하나 만들어야 되서 이 예제를 바탕으로 좀 수정을 했습니다.


이 예제는 Android 의 Animation 기능을 이용해서 버튼을 누르면 오른쪽에 메뉴화면이 뜨고 또 버튼을 누르면 이 메뉴화면이 부드럽게 사라지는 효과를 주었습니다.


저는 이 메뉴를 오른쪽으로 옮기고 버튼을 없애는 대신 두 손가락으로 sliding을 할 경우 왼쪽에서 메뉴가 부드럽게 나타났다고 다시 두 손가락으로 sliding 하면 부드럽게 사라지는 기능을 만들었습니다.


우선 Layout xml 을 볼께요.


activity_main.xml


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#ffffffff">
        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="26px"
            android:text="AAA Bank Check - 9513   Available balance $2,354.32"
            android:textColor="#000000"
            android:layout_marginLeft="350dp"
            android:layout_marginTop="100dp"
            />
    </LinearLayout>
   
    <LinearLayout
        android:id="@+id/slidingPage01"
        android:layout_width="250dp"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_gravity="left"
        android:background="#ff888888"
        android:visibility="gone">
        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="20px"
            android:text="Direct banking  $642.77 "
            android:textColor="#ff000000"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="10dp"
            />
        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="20px"
            android:text="Money Market - 3670"
            android:textColor="#ff000000"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="10dp"           
            />
                                       
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="right|center_vertical"
        android:background="#00000000">
        <Button 
            android:id="@+id/openBtn01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Open"
            android:visibility="invisible"
            />
    </LinearLayout>

</FrameLayout>



전체 화면은 FrameLayout 을 사용했습니다. Animation 기능을 구현해야 하기 때문에 이 FrameLayout 을 사용했을 겁니다.

FrameLayout 의 특징이 화면에 여러 뷰나 레이아웃을 겹쳐 놓고 한순간에 하나의 레이아웃을 보이도록 할 수 있습니다. 계속 이 레이아웃을 바꾸면 애니메이션 효과가 나올 수 있겠죠.


그 다음엔 LinearLayout 이 있고 orientation은 vertical 로 돼 있습니다.

이 LinearLayout 안에 있는 view들은 위에서 아래로 정렬이 될 겁니다.

그 아래 보니까 그냥 TextView 하나만 있네요.


일단 화면에 이 TextView가 표시 될 겁니다.


다음에 보니까 다시 LinearLayout 이 나왔습니다.

이것도 orientation이 vertical 이네요. view들이 위에서 아래로 배치가 될겁니다.

그런데 그 다음에 layout_gravity 가 left로 돼 있습니다. 이것은 기준이 왼쪽이 된다는 겁니다.

원래 소스에는 이것이 right로 돼 있었습니다.

그런데 저는 메뉴가 왼쪽에서 나오도록 하기 위해 이것을 right으로 바꿨습니다.

다음에는 TextView가 두개가 나오는데요.

이 두개는 아래 위로 배치될 겁니다.

이 두번째 LinearLayout 에 animation 효과가 적용되서 좌우로 sliding 될 겁니다.

이 LinearLayout 의 id 가 slidingPage01 이라는 것을 잘 봐 두세요.


그 다음에 LinearLayout 이 또 나오네요.

orientation은 vertical 이고 gravity 가 right|center_vertical 이네요.

이 의미는 가로로는 오른쪽으로 배치를 하는데 세로로는 가운데에 정렬한다는 뜻입니다.

이 Linear Layout 에는 Button 만 하나 있네요.

그런데 저는 이 버튼을 사용하지 않을거기 때문에 그냥 android:visibility="invisible" 로 해서 화면에서 없앴습니다.


이제 Java 파일을 볼까요?


MainActivity.java


public class MainActivity extends Activity {

    boolean isPageOpen = false;

    Animation translateLeftAnim;
    Animation translateRightAnim;

    LinearLayout slidingPage01;

    Button openBtn01;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        slidingPage01 = (LinearLayout) findViewById(R.id.slidingPage01);

        translateLeftAnim = AnimationUtils.loadAnimation(this, R.anim.translate_left);
        translateRightAnim = AnimationUtils.loadAnimation(this, R.anim.translate_right);

        SlidingPageAnimationListener animListener = new SlidingPageAnimationListener();
        translateLeftAnim.setAnimationListener(animListener);
        translateRightAnim.setAnimationListener(animListener);

        openBtn01 = (Button) findViewById(R.id.openBtn01);
        openBtn01.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                if (isPageOpen) {
                    slidingPage01.startAnimation(translateRightAnim);
                } else {
                    slidingPage01.setVisibility(View.VISIBLE);
                    slidingPage01.startAnimation(translateLeftAnim);
                }
            }
        });

    }

    private class SlidingPageAnimationListener implements AnimationListener {
        public void onAnimationEnd(Animation animation) {
            if (isPageOpen) {
                slidingPage01.setVisibility(View.INVISIBLE);

                openBtn01.setText("Open");
                isPageOpen = false;
            } else {
                openBtn01.setText("Close");
                isPageOpen = true;
            }
        }

        public void onAnimationRepeat(Animation animation) {
        }

        public void onAnimationStart(Animation animation) {
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
   
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getPointerCount() > 2) {
            return false;
        }
        else if (event.getPointerCount() == 2) {
        int action = event.getAction();
        float downX = 0;
        float upX = 0;

       
        int pureaction = action & MotionEvent.ACTION_MASK;
        if (isPageOpen) {
            slidingPage01.startAnimation(translateRightAnim);
        } else {
            slidingPage01.setVisibility(View.VISIBLE);
            slidingPage01.startAnimation(translateLeftAnim);
        }
      }
      return true;
    }
}



우선 onCreate() 메소드 부터 보겠습니다.

layout xml 로 activity_main 을 세팅했죠.

그 다음에 한 것이 sliding 효과를 줄 LinearLayout 객체에 넣은 겁니다.

아까 봤던 두번째 LinearLayout 인 slidingPage01 을요.


다음엔 animation 효과를 객체에 담은 건데요.

translate_left와 translate_right는 res의 amin 이라는 폴더안에 xml 형태로 있습니다.


translate_left.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    >
    <translate
        android:fromXDelta="-20%p"
        android:toXDelta="0%p"
        android:duration="500"
        android:repeatCount="0"
        android:fillAfter="true"
        />
</set>


translate_right.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    >
    <translate
        android:fromXDelta="0%p"
        android:toXDelta="-20%p"
        android:duration="500"
        android:repeatCount="0"
        android:fillAfter="false"
        />
</set>


저는 메뉴가 좌측에서 나오도록 하기 위해서 from, toXDelta 값들을 바꿨습니다.

애니메이션의 시작점과 끝점을 가리키는 겁니다.


그 다음에는 SlidingPageAnimationListener 의 객체를 만들어서 방금 만들었던 Animation에 이 리스너를 set 해 넣습니다.


그리고 Button 을 만들어서 이 버튼을 클릭하면 좌측 화면이 사라졌다가 나타났다가 하는 동작을 하도록 코딩을 해 줍니다.


사실 이 부분은 원본 소스에서 사용되는 부분이구요.

저는 버튼클릭은 없애고 대신 두손가락으로 sliding 하는 이벤트를 Listening 하다가 그에 반응하는 기능을 넣고 싶었거든요.


그래서 onTouchEvent() 메소드를 구현한 겁니다.


이 메소드를 보죠.


public boolean onTouchEvent(MotionEvent event) {
        if(event.getPointerCount() > 2) {
            return false;
        }
        else if (event.getPointerCount() == 2) {
        int action = event.getAction();
        float downX = 0;
        float upX = 0;

       
        int pureaction = action & MotionEvent.ACTION_MASK;
        if (isPageOpen) {
            slidingPage01.startAnimation(translateRightAnim);
        } else {
            slidingPage01.setVisibility(View.VISIBLE);
            slidingPage01.startAnimation(translateLeftAnim);
        }
      }
      return true;
    }


화면에서 일어나는 이벤트를 감지해서 어떤 동작을 하고 싶으면 이 메소드를 구현하면 됩니다.


getPointerCount()는 화면에 닿은 손가락 갯수를 받는 거구요.

화면에 2개의 손가락이 닿았을 공우 isPageOpen 이 true 이면 translateRightAmin 이 실행되고 false 이면 translateLeftAnim 이 실행 되도록 만든 겁니다.


원본 샘플 파일을 받아서 실행해 보시고 위에서 제가 바꾼 것들을 따라서 바꿔가면서 결과를 단계별로 확인하시면 쉽게 이해하실 수 있을 겁니다.


그리고 이 두 소스를 기반으로 본인이 구현하고 싶은 기능을 나름대로 생각해서 구현하면 공부하는데 훨씬 도움이 될 겁니다.



반응형

Comment


그럼 구체적으로 sliding 되는 화면들은 어떤 구성을 갖게 될지 PersonPage.java를 분석하면서 공부해 보도록 하겠습니다.

일단 화면 구성을 할 layout xml 을 보도록 하죠.
슬라이딩으로 넘어갈 화면들은 여기서 정한 Layout 을 사용할 겁니다.





person_page.xml



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/nameText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30dp"
        />
    <Button
        android:id="@+id/callButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:text="전화걸기"
        android:focusable="false"
        />
    <ImageView
        android:id="@+id/iconImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>



일단 전체 구조는 LinearLayout 으로 잡아놨고 그 안에 TextView, Button, ImageView를 배치 했습니다.

그러면 이제 PersonPage.java를 보겠습니다.



public class PersonPage extends LinearLayout {
    Context mContext;

    TextView nameText;
    Button callButton;
    ImageView iconImage;

    public static final int CALL_NUMBER = 1001;

    public PersonPage(Context context) {
        super(context);
        init(context);
    }

    public PersonPage(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        mContext = context;

        // inflate XML layout
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.person_page, this, true);

        iconImage = (ImageView) findViewById(R.id.iconImage);
        nameText = (TextView) findViewById(R.id.nameText);
        callButton = (Button) findViewById(R.id.callButton);
        callButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                String callNumber = (String) callButton.getTag();
                Toast.makeText(mContext, "전화번호 : " + callNumber, Toast.LENGTH_LONG).show();
            }
        });
    }
    public void setImage(int resId) {
        iconImage.setImageResource(resId);
    }
    public void setCallNumber(String number) {
        callButton.setTag(number);
    }
    public String getNameText() {
        return nameText.getText().toString();
    }
    public void setNameText(String nameStr) {
        nameText.setText(nameStr);
    }
}



먼저 선언된 인스턴스들을 보면 Context, TextView, Button, ImageView 이런 것들이 있네요.
Context는 당연한거고 나머지는 Layout xml에서 선언된 view들인데요. 이걸 불러내서 이런 저런 처리를 하려고 하나 봅니다.
그 다음에 CALL_NUMBER라는 int 형이 final 로 1001이란 값을 받았네요.
이건 왜 이런지 소스를 보면서 알아가 보도록 하겠습니다.

일단 생성자인 PersonPage() 메소드에서는 init()메소드를 call 하고 있습니다.
그러니까 가장 먼저 init() 메소드가 실행이 될 겁니다.

init() 메소드에서는 다시 inflater 가 나오네요. 이것은 xml 문서의 리소스들을 받아 객체로 만들어 줘서 control할 수 있도록 만들어 주는 놈 입니다.
inflater 에 person_page.xml 을 세팅했습니다.

그러면 이제 person_page.xml 파일 안에 있는 리소스들에 대한 객체를 만들 수가 있는데요.
처음에 선언했던 객체를 사용해서 iconImage, nameText, callButton 에 대한 객체를 만들었습니다.
그리고 그 다음에는 callButton 에 onClickListener를 세팅해서 이 버튼이 눌려지면 전화면호가 화면에 표시되도록 (Toast) 만들어 놨군요.

그 다음에 나오는 메소드들은 setter와 getter 메소드들 입니다.
바로 MainActivity 에서 배열에 있는 이름, 전화번호, 이미지 파일들을 이 setter들을 사용해서 세팅을 했었죠?

PersonPage.java 에서는 MainActivity.java에서 받은 내용을 가지고 person_page.xml에 정의된 Layout 에 그 내용들을 넣고 그 view들에게 리스너등을 넣어서 어떤 이벤트 등에 반응 할 수 있도록 하는 클래스 입니다.
MainActivity 는 ViewPager를 사용할 수 있도록 세팅하고 각 페이지에 들어갈 내용들을 PersonPage에 보내서 페이지를 구성할 수 있도록 하고 있구요.


여기까지 ViewPager에 대해 알아 봤습니다.



반응형

Comment

이전 1 2 3 4 5 다음