2016년 8월 2일 화요일

Android 이론 - Service 사용 시 고려사항


서비스는 항상 Background 에서 동작

Background에서 지속적으로 NW 통신을 하게 된다는건 불합리

날씨를 가져다가 갱신하는 앱 위젯인데

화면이 Off 된 상태에서는 필요 없는 통신이므로

screen on receiver 에 반응해서 통신을 시작 하도록 설계하는것이 중요

동적으로 manifest에 등록 하거나 등록 해제 하도록 설계해야 함

Android Studio - Android IPC인 AIDL


RPC(Remote Procedure Call)
 : 서로다른 네트워크 또는 프로세스에 존재하는 Procedure(함수)를 호출하기 위한 기술

안드로이드에서는 AIDL(Android Interface Definition Language)로 제공 하고 있다.

IPC를 하는 Service와 Activity 간에 AIDL 이라는 인터페이스를 공유해서
Activity - Service 가 Bind Connect 된 이후에 Activity 에서 Service에서 구현 된 AIDL 인터페이스를 활용할 수 있도록 하는 기술이다.


1. AIDL 서비스 앱 생성
2. AIDL Client 앱 생성
 

[AIDL 서비스 앱]
1. res/raw 에 mp3 파일 준비
2. aidl 폴더, 패키지 및 aidl 파일 생성

IPlayService.aidl

// IPlayService.aidlpackage com.example.ch3_aidl;

// Declare any non-default types here with import statements
interface IPlayService {
    int currentPosition();
    int getMaxDuration();
    void start();
    void stop();
    int getMediaStatus();
}

3. Service 생성

PlayService.java

package com.example.ch3_aidl;

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

public class PlayService extends Service {
    public PlayService() {
    }

    @Override    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.        throw new UnsupportedOperationException("Not yet implemented");
    }
}

4. manifest 수정

자동 생성된 service 태그 아래

<service    android:name=".PlayService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.multi.ACTION_PLAY"/>
    </intent-filter>
</service>

*5. 빌드 한번 하기

Build -> Make Module 'AIDL모듈명'
진행 해야 현 소스코드에서 해당 AIDL 인지 가능


6. 서비스 구현

package com.example.ch3_aidl;

public class CommonProperties {
   public static final int MEDIA_STATUS_STOP = 0;
   public static final int MEDIA_STATUS_RUNNING = 1;
   public static final int MEDIA_STATUS_COMPLETED = 2;
}


package com.example.ch3_aidl;

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

public class PlayService extends Service {

    MediaPlayer player;
    int status = 0;

    public PlayService() {
    }

    @Override    public void onCreate() {
        super.onCreate();
        player = new MediaPlayer();
    }

    @Override    public void onDestroy() {
        super.onDestroy();
        player.release();
    }

    @Override    public IBinder onBind(Intent intent) {
        // aidl 파일을 구현한 stub 객체(실 업무 객체가 넘어가는게 아니라)를 리턴        return new IPlayService.Stub(){// Make Module xxx 작업을 해서 에러가 발생하지 않음
            @Override            public int currentPosition() throws RemoteException {
                if(player.isPlaying()){
                    return player.getCurrentPosition();
                }else{
                    return 0;
                }
            }

            @Override            public int getMaxDuration() throws RemoteException {
                if(player.isPlaying()){
                    return player.getDuration();
                }else{
                    return 0;
                }
            }

            @Override            public void start() throws RemoteException {
                if(!player.isPlaying()){
                    player = MediaPlayer.create(PlayService.this, R.raw.music);
                    player.start();

                    player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                        @Override                        public void onCompletion(MediaPlayer mp) {
                            status = CommonProperties.MEDIA_STATUS_COMPLETED;
                        }
                    });
                }

                status = CommonProperties.MEDIA_STATUS_RUNNING;
            }

            @Override            public void stop() throws RemoteException {
                if(player.isPlaying())
                    player.stop();
                status = CommonProperties.MEDIA_STATUS_STOP;
            }

            @Override            public int getMediaStatus() throws RemoteException {
                return status;
            }
        };
    }
}




[AIDL Client 앱]

1. aidl 폴더, 패키지 생성 및 aidl 파일 복사

2. 빌드 한번 하기

Build -> Make Module 'AIDL모듈명'
진행 해야 현 소스코드에서 해당 AIDL 인지 가능

3. MainActivity

package com.example.ch3_aidl_client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ProgressBar;

import com.example.ch3_aidl.IPlayService;


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    IPlayService pService;
    ImageButton start;
    ImageButton stop;
    ProgressBar mProgress;
    boolean isRunning = true;
    ProgressThread pt;
    Handler handler;
    Intent intent;


    //--add 1---------------    // bindService callback함수를 가지는 interface구현 클래스    ServiceConnection connection = new ServiceConnection() {
        // bindService에 의해 bind객체가 넘어온 순간 호출        // 두번째 객체가 bind 객체 stub 객체가 넘어 옴        @Override        public void onServiceConnected(ComponentName name, IBinder service) {
            pService = IPlayService.Stub.asInterface(service);
            start.setEnabled(true);
            checkService();
        }

        @Override        public void onServiceDisconnected(ComponentName name) {
            pService = null;
        }
    };
   //------------------------
    @Override    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        start = (ImageButton) findViewById(R.id.start);
        stop = (ImageButton) findViewById(R.id.stop);
        mProgress = (ProgressBar) findViewById(R.id.pb);
        mProgress.setProgress(0);

        start.setOnClickListener(this);
        stop.setOnClickListener(this);
        start.setEnabled(false);
        stop.setEnabled(false);

        handler = new Handler() {
            // Activity ANR - Thread에서 sendMessage하는 순간 UI Thread에 의해 자동 호출            @Override            public void handleMessage(Message msg) {
                switch(msg.what) {
                    case 1:    // stopped media                        start.setEnabled(true);
                        stop.setEnabled(false);
                        mProgress.setProgress(0);
                        break;
                }
                super.handleMessage(msg);
            }
        };

      //add2-------------------------------------        // service 구동을 위한 intent 준비        intent = new Intent("com.multi.ACTION_PLAY");
        // 이전버전에서는 그냥 intent를 실행. lollipop부터는 bindService의 경우 package명을 명시        intent.setPackage("com.example.ch3_aidl");
      //--------------------------------------    }

    private void checkService() {
        if(pService != null) {
            try {
                if(pService.getMediaStatus() == CommonProperties.MEDIA_STATUS_STOP) {
                    Log.d("kkang", "MEDIA_STATUS_STOP");
                    stop.setEnabled(false);
                } else if(pService.getMediaStatus() == CommonProperties.MEDIA_STATUS_RUNNING) {
                    Log.d("kkang", "MEDIA_STATUS_RUNNING");
                    start.setEnabled(false);
                    isRunning = true;
                    pt = new ProgressThread();
                    pt.start();
                }
            } catch (RemoteException e) {

            }
        }

    }

    //add3-------------------------------
    @Override    protected void onResume() {
        super.onResume();
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
        checkService();
    }

    @Override    protected void onPause() {
        super.onPause();
        unbindService(connection);
        isRunning = false;
    }
    //---------------------------------
    @Override    public void onClick(View v) {
        if(v == start) {
            try {
                pService.start();
                mProgress.setMax(pService.getMaxDuration());
                isRunning = true;
                pt = new ProgressThread();
                pt.start();

                start.setEnabled(false);
                stop.setEnabled(true);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block                e.printStackTrace();
            }
        } else if(v == stop) {
            try {
                pService.stop();
                isRunning = false;
                start.setEnabled(true);
                stop.setEnabled(false);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block                e.printStackTrace();
            }
        }

    }


    class ProgressThread extends Thread {
        @Override        public void run() {
            while(isRunning) {
                try {

                    if(pService.getMediaStatus() == CommonProperties.MEDIA_STATUS_COMPLETED) {
                        handler.sendEmptyMessage(1);
                        break;
                    } else {
                        mProgress.setProgress(pService.currentPosition());
                        SystemClock.sleep(1000);
                    }
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block                    e.printStackTrace();
                }
            }
        }
    }
}

Android 이론 - Time out 문제 (5초, 10초 룰)


안드로이드에는 2가지 TIMEOUT 제약이 있다.

5초룰과 10초룰

5초룰로 더 유명한 에러는 ANR이라고 일컬어 지는 에러이다.
ANR 이란 Activity Not Response 의 약자 이다.

사전적 의미는 Activity 응답 없음

개발 하면서 고려 해야 할 것은 화면 event 발생으부터 logic 처리 완료까지 5초 안에 끝내야 한다는 것이다.
대부분의 네트워킹 관련 로직이 있으면 내포하고 있는 문제이다.
안드로이드 개발자라면 반드시 고려하고 구현해야 하는 부분이다.

[솔루션]
1. Thread로 처리 하면 문제 없음
 -> android 개발자라면 NW 처리 이벤트는 무조건 Thread 처리

BUT, Thread로 처리하면 생기는 문제

사용자 정의 Thread 는 UI Thread 에서 생성할텐데
문제는 개발자 정의 Thread 가 View 객체에 접근이 불가하다는 것이다.(Run time error)
View 객체는 UI Thread 에서만 제어 가능하다.
 -> Handler 로 해결 가능, Thread 에서 Handler 를 이용하면 의뢰 형식으로 간접제어 가능

2. IntentService

IntentService 는 서비스와 거의 동일하지만,
해당 작업이 완료 되면 알아서 종료 (Service 는 스스로 종료 불가)
완료된 작업은 ResultReceiver 이용


p.s.
10초룰
 : Broadcast receiver 가 logic 처리 완료까지의 시간 10초


Android Studio - Tab 화면을 ViewPager 와 연동 하자


1. main_activity.xml 에 TabLayout 추가
2. MainActivity.java 에 TabLayout 변수를 선언하여 컨트롤
3. ViewPager와 연결


[main_activity.xml 에 TabLayout 추가]

<Toolbar> 태그 밑에다가 추가

<android.support.design.widget.TabLayout

    android:id="@+id/tabs"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_below="@id/toolbar"
    android:layout_gravity="bottom"
    app:layout_collapseMode="parallax"
    app:tabGravity="center"
    app:tabMode="scrollable">
</android.support.design.widget.TabLayout>


[MainActivity.java 에 TabLayout 변수를 선언하여 컨트롤]

1. TabLayout 의 이벤트 처리를 해당 클래스에서 처리

tabLayout 의 이벤트 처리를 MainActivity 에서 하도록, eventlistner를 implement 하고

public class MainActivity extends AppCompatActivity        
      implements TabLayout.OnTabSelectedListener

추상함수 구현
Tab을 선택할 경우 ViewPager를 이동 시키는 것은 개발자가 수동으로 해줘야 함.

@Overridepublic void onTabSelected(TabLayout.Tab tab) {
    viewPager.setCurrentItem(tab.getPosition());
}

@Overridepublic void onTabUnselected(TabLayout.Tab tab) {

}

@Overridepublic void onTabReselected(TabLayout.Tab tab) {

}


2. onCreate 함수 안에 아래 코드를 추가

// TabLayout
TabLayout tabLayout = (TabLayout)findViewById(R.id.tabs);
// 아래의 코드로 유저가 viewPager화면 조정시 tab도 자동조정
// 반대로는 개발자가 이벤트 처리로
// tab button의 문자열은 이곳에서 직접줄수도 있고
// 아래 코드에 의해 viewPager의 문자열을 그대로 적용할 수도 있다.
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this);


[ViewPager와 연결]

MyPagerAdapter 내부클래스에 Tab의 title 정보를 추가 및 getter 추가

class MyPagerAdapter extends FragmentStatePagerAdapter {

    ....
    // viewPager를 위한 PagerTitleStrip, PagerTabStrip에 나올 문자열    // 우리의 경우에는 TabLayout에서 이용한다.    String titles[] = new String[]{"TAB1", "TAB2", "TAB3"};

    @Override    public CharSequence getPageTitle(int position) {
        return titles[position];
    }
    
    ....

}




2016년 8월 1일 월요일

Android Studio - ListView 의 업그레이드 버전인 RecyclerView


기존 ListView 에서는 개발자가 임의로 관여할 수 있는 기회가 적었던 반면,
RecyclerView 는 그 안에다가 효과, 레이아웃의 변경이 가능하다.

ex) LinearLayoutManager / GridLayoutManager / StaggeredGridLayoutManager (높이가 불규칙한 Grid)


1. Graddle 에 dependency 추가
프로젝트 익스플로러에서 '모듈 우클릭' -> 'open module setting' -> 'dependency 탭 선택' -> '우측의 + 버튼 클릭'

  library : 이미 다운 받아진 구글의 라이브러리
  file : 개발자가 다운 받은 임의의 jar 파일
  module : 현재 개발중인 다른 모듈의 라이브러리

com.android.support:recyclerview-v7:24.1.1 추가

Graddle에 추가된 것 확인

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'    
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:recyclerview-v7:24.1.1'
}


2. recycler view xml 파일 준비

fragment_one.xml

<?xml version="1.0" encoding="utf-8"?><android.support.v7.widget.RecyclerView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:id="@+id/recyclerView"  />

recycler view 멤버 변수 추가

OneFragment.java

RecyclerView recyclerView;


2. Holder 패턴



findViewById 의 퍼포먼스 문제로 인해서 한번 가져 온 viewId를 지니고 있다가 활용

OneFragment.java
// findViewId의 성능 이슈부분 솔루션
// 내부적으로 holder 객체는 메모리에 누적되어 한번만 view find
class MyViewHolder extends RecyclerView.ViewHolder{
    public TextView title;  // Adapter에서 획득하고자 하는 view
    // 매개변수는 항목 layout.xml 초기화 view 계층의 root 객체이다.
    // Adapter가 전달할 것임.
    public MyViewHolder(View itemView){
        super(itemView);
        title = (TextView)itemView.findViewById(android.R.id.text1);
    }
}



3. Adapter

OneFragment.java

class MyAdapter extends RecyclerView.Adapter<MyViewHolder>{
    final List<String> list;
    // 항목구성 집합 데이터.. activity가 전달할것임
    public MyAdapter(List<String> list){
        this.list = list;
    }

    // 하나의 항목을 위해 최초에 수행될 부분
    // 항목 layout 초기화.. Holder 준비
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 항목 layout 초기화
        View view = LayoutInflater.from(parent.getContext())
          .inflate(android.R.layout.simple_list_item_1, parent, false);
        return new MyViewHolder(view);
    // holder 리턴 시키면 내부적으로 holder 객체를 메모리에 유지 
    }

    // 항목 하나를 구성하기 위해서 호출
    // 항목구성 데이터, 이벤트 알고리즘이 구성되는 곳
    // onCreateViewHolder에서 리턴시킨 holder가 매개변수로
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        String text = list.get(position);
        holder.title.setText(text);
    }

    @Override    public int getItemCount() {
        return list.size();
    }
}

4. Decoration (optional)

OneFragment.java

class MyDecoration extends RecyclerView.ItemDecoration{
    // RecyclerView Adapter 작업에 의해 항목이 추가된 후에 호출
    // 항목위에 추가 drawing 작업이 필요시 이용
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, 
                           RecyclerView.State state) {
        super.onDrawOver(c, parent, state);

        int width = parent.getWidth();
        int height = parent.getHeight();

        Drawable dr = getActivity().getResources()
         .getDrawable(R.drawable.kbo);
        int drWidth = dr.getIntrinsicWidth();
        int drHeight = dr.getIntrinsicHeight();

        int left = width/2 - drWidth/2;
        int top = height/2 - drHeight/2;

        c.drawBitmap(
                BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.kbo),
                left, top, null);
    }

    // 항목 하나하나를 추가하기 위해서
    @Override
    public void getItemOffsets(Rect outRect, View view, 
                  RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

        // 항목의 index
        int index = parent.getChildAdapterPosition(view)+1;

        if(index % 3==0){
            outRect.set(20,20,20,60);
        }else{
            outRect.set(20,20,20,20);
        }

        view.setBackgroundColor(0xFFECE9E9);
        ViewCompat.setElevation(view, 20.0f);
    // 하위버전 호완을 위해서
    }
}

4. Fragment에 적용

OneFragment.java

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    recyclerView = (RecyclerView)inflater.inflate(R.layout.fragment_one, container, false);

    // 항목구성 데이터 준비    List<String> list = new ArrayList<>();

    for(int i=0; i<20; i++){
        list.add("item="+i);
    }

    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    recyclerView.setAdapter(new MyAdapter(list));
    recyclerView.addItemDecoration(new MyDecoration());

    return recyclerView;
}

Android Studio - 간단한 Navigation Drawer 적용


[Navigation Drawer]

1. android design support 라이브러리 활용 (Graddle 에 dependency 추가 필요 함)
프로젝트 익스플로러에서 '모듈 우클릭' -> 'open module setting' -> 'dependency 탭 선택' -> '우측의 + 버튼 클릭'

  library : 이미 다운 받아진 구글의 라이브러리
  file : 개발자가 다운 받은 임의의 jar 파일
  module : 현재 개발중인 다른 모듈의 라이브러리

com.android.support:design:24.1.1 추가

Graddle에 추가된 것 확인

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'    
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:design:24.1.1'
}


2. activity_main.xml 에다가 navigation tag 추가 (최상위 루트에다가 추가)
최상위와 최 하위에다가 아래 추가

<android.support.v4.widget.DrawerLayout>
블라블라~~
</android.support.v4.widget.DrawerLayout>

ALT+CTRL+L 단축키로 인덴테이션 맞추고, 루트태그 속성을 아래와 같이 카피해다가 붙인다.

<android.support.v4.widget.DrawerLayout

    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    >

(fitsSystemWindows : 시스템 상태바 가리지 않게 하기 위해서)

근데 이렇게 하면 에러가 나게 된다.
사용하는 view가 표준라이브러리의 view라면 그곳의 속성도 표준속성이다.
표준속성은 android라는 이름의 namespace로 등록되어있음
  => xmlns:android="http://schemas.android.com/apk/res/android"
But, 우리가 쓰고자 하는 NavigationView는 표준라이브러리가 아니다.
'android:' 을 사용할 수가 없다.
그래서 별도의 namespace를 등록해서 적용해야 한다.

xmlns:app="http://schemas.android.com/apk/res-auto"

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    >


Navigation view를 기존 layout 밑에다가 추가 해 줌.

<!-- navigation drawer에 의해서 나오는 부분 --><android.support.design.widget.NavigationView

    android:id="@+id/main_drawer_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/navigation_header"
    app:menu="@menu/main_drawer">
</android.support.design.widget.NavigationView>


3. navigation header xml, navigation menu xml 세팅

layout/navigation_header.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="172dp"
    android:background="#FF0000">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="i am header"
        android:textColor="#FFFFFF"
        android:gravity="bottom"
        android:textSize="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginBottom="20dp"
        />
</LinearLayout>

menu/main_drawer.xml

<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/navigation_item_1"
            android:icon="@android:drawable/ic_menu_manage"
            android:title="Menu1"/>
        <item
            android:id="@+id/navigation_item_2"
            android:icon="@android:drawable/ic_menu_search"
            android:title="menu2"/>
    </group>
</menu>


4. MainActivity에 추가 및 이벤트 적용

컨트롤을 하기 위해서 변수를 선언

DrawerLayout drawer;
ActionBarDrawerToggle toggle;
boolean isDrawerOpened;

onCreate 함수안에 아래 추가

// navigation drawerdrawer = (DrawerLayout) findViewById(R.id.drawer);
// 생성자에 인자로 들어간 문자열은 화면 출력 문자열이 아니라// 상태 표현을 위한 접근성 차원에서 제공하는 문자열toggle = new ActionBarDrawerToggle(this, drawer,
        R.string.drawer_open,
        R.string.drawer_close) {
    // 이벤트 추가, 필수는 아님
    @Override    public void onDrawerOpened(View drawerView) {
        super.onDrawerOpened(drawerView);
        isDrawerOpened = true;
    }

    @Override    public void onDrawerClosed(View drawerView) {
        super.onDrawerClosed(drawerView);
        isDrawerOpened = false;
    }
};
drawer.addDrawerListener(toggle);   // 이벤트 핸들러인 toggle을 적용
// 액션바 아이콘을 업 네비게이션 형태로 표시
getSupportActionBar().setDisplayHomeAsUpEnabled(true); NavigationView navigationView = (NavigationView) findViewById(R.id.main_drawer_view); navigationView.setNavigationItemSelectedListener( new NavigationView.OnNavigationItemSelectedListener() { // navigation view의 항목 선택 시 이벤트 콜백함수 @Override public boolean onNavigationItemSelected(MenuItem item) { Log.d("mulcam", "navigation view: " + item.getItemId()); return false; } }
);

그리고 잔여 이벤트 처리를 위해서

@Overridepublic void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
    super.onPostCreate(savedInstanceState, persistentState);
    toggle.syncState();
}

@Overridepublic boolean onOptionsItemSelected(MenuItem item) {
    if(toggle.onOptionsItemSelected(item)){
        // 발생한 메뉴 이벤트가 navigation view 에서의 이벤트인 경우 후속처리 x        return true;
    }
    return super.onOptionsItemSelected(item);
}

// navigation drawer가 화면에 보이고있다.// 그 상황에서 유저가 back 버튼을 누르면// back 버튼 이벤트를 제어해서 drawer를 닫아준다.// 모든 key event onKeyDown(){}의 함수를 사용하는데// back button만을 제어할 목적이라면 onBackPressed 함수를 이용하면 편하다@Overridepublic void onBackPressed() {
    if(isDrawerOpened){
        drawer.closeDrawers();
    }else{
        super.onBackPressed();
    }
}