본문 바로가기

수업 정리

83일차 수업정리(Android - Activity, App간 통신, 기본앱 연동)

**IoC & Component

    => IoC(제어의 역전, 제어의 역흐름): 클래스의 생성은 개발자, 인스턴스 생성및 생명주기 관리는 프레임워크 SDK 가 수행하는 것

    => 안드로이드에서 관리하는 것은 Component

        - Activity(화면), BroadcastReceiver(알림), Service(백그라운드 작업 수행), ContentProvider(데이터 공유) 4가지

    => Spring Framework가 관리하는 것은 Bean

    => IoC 사용하는 이유: 개발자는 생명주기와 관련된 작업을 직접 이해해서 할 필요없이 비지니스 로직 작성에만 집중하기 위해

 

**Activity 생성과 호출 그리고 종료

1.생성

    => Activity 클래스로부터 상속받는 클래스를 생성

 

2.호출

  1) 명시적 호출 - 클래스 이름을 기재 : 애플리케이션 내의 Activity를 호출

    => 애플리케이션 내에서는 클래스 이름을 알 수 있음

    => Intent 인텐트 = new Intent(호출하는 클래스의 인스턴스, 호출당하는 클래스의 clazz);

        - clazz 는 Class이름.class : 클래스 정보를 넘겨주면 인스턴스 생성과 관련된 모든 수명주기를 안드로이드가 책임

    => startActivity(인텐트);  //호출당하는 액티비티가 없어질 때 콜백 메소드를 호출하지 않음

        - 하위 액티비티가 종료되고 상위 액티비티에게 넘겨줄 데이터가 없을 때

    => startActivityForResult(구분하기 위한 번호, 인텐트);  //호출하는 곳에서의 콜백 메소드가 호출됨

        - 하위 액티비티가 종료되고 상위 액티비티에게 넘겨줄 데이터가 있을 때 

  2) 암시적 호출 - 별명을 사용

    => 다른 애플리케이션을 호출할 때는 클래스 이름을 사용할 수 없음

    => 스마트 폰 애플리케이션들은 샌드박스 형태로 외부에 숨겨진 채로 생성

 

3. 데이터 공유

    => public class를 만들고 static변수를 만들면 프로그램 내의 모든 곳에서 접근 가능

        (객체 지향 프로그래밍, Server 프로그래밍에서는 권장하지 않음)

    => 객체 지향에서 필요한 경우 Singleton 패턴을 이용하여 클래스를 디자인, 인스턴스 변수를 생성하여 모든 곳에서 공유하는 방법 권장

        - 스마트폰 SDK에서는 시작객체를 Singleton으로 생성, 그 Singleton에 접근 할 수 있는 방법을 제공(Android : MainActivity.this)

    => 상위 Activity -> 하위 Activity로 데이터 전달

        - Intent를 생성하고 putExtra(String key, Serializable data)를 호출하면 됨

        - 하위 Activity에서는 getIntent()를 이용하여 Intent를 찾고, 찾은 Intent를 가지고 get 자료형 Extra(String key)를 호출

        - 자료형 Extra 메소드가 없는 경우 getSerializable() 메소드가 있고 이 메소드의 리턴 값을 강제 형변환 하여 사용

    => 하위 Activity -> 상위 Activity로 데이터 전달

        - 호출하는 곳에서는 startActivityForResult(int requestCode, Intent intent)를 호출

        - requestCode는 호출하는 Activity를 구분하기 위한 코드

        - 호출당하는 곳에서는 소멸 되기 직전에

          -> Intent intent = new Intent();
               Intent.putExtra(String key, Serializale data);
               setResult(int resultCode, intent);

 

        - 호출 하는 곳에서는 

          -> void onActivityResult(int requestCode, int resultCode, Intent intent)를 재정의
              requestCode와 resultCode를 이용하여 어떤 하위 Activity가 종료되었는지 확인하고 intent를 이용하여 데이터를 가져와서 사용

 

**애플리케이션 간의 통신

    => 애플리케이션들은 다른 애플리케이션의 클래스를 사용할 수 없기 때문에 데이터를 공유하기 위해서는 통신을 이용

    => 안드로이드나 iOS는 독특하게 데이터를 공유할 수 있는 다른 방법을 제공

 

**AndroidPortFolio를 수정하여 하위 Activity로 전환

1. MainActicity.java 파일에서 List의 항목을 클릭했을 때를 처리하는 이벤트 핸들러 수정

	//항목을 선택했을 때 호출되는 이벤트 핸들러 작성
        listView.setOnItemClickListener(
                new ListView.OnItemClickListener(){
                    @Override
                    //adapterView는 이벤트가 발생한 뷰
                    //view는 선택한 항목 뷰
                    //i가 선택한 항목의 인덱스
                    //l은 선택한 항목 뷰의 id
                    public void onItemClick(
                            AdapterView<?> adapterView,
                            View view,
                            int i,
                            long l) {
                        //선택한 항목의 데이터
                        Item item = list.get(i);
                        /*
                        //토스트로 itemid를 출력
                        Toast.makeText(MainActivity.this,
                                item.itemid + "",
                                Toast.LENGTH_LONG).show();

                         */

                        //하위 Activity 출력
                        Intent intent = new Intent(
                                MainActivity.this,
                                ItemDetailActivity.class);
                        //데이터 전달하기 - itemid를 전달
                        intent.putExtra("itemid", item.itemid);
                        //액티비티 호출
                        startActivity(intent);
                    }
                });

 

2. ItemDetailActivity를 수정

  1) 레이아웃 수정

    => 뒤로가기 버튼 추가

    <Button android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="뒤로"
        android:id="@+id/backbtn"/>

 

  2) ThreadEX 클래스의 URL만드는 부분을 수정

//URL 만들기
//호출하는 인텐트 가져오기
Intent intent = getIntent();

//itemid의 값을 정수로 가져오고 없을 때 1
int itemid = intent.getIntExtra("itemid", 1);
URL url = new URL("http://192.168.0.200:8080/mysqlserver/detail?itemid="+ itemid);

 

  3) onCreate 메소드에 뒤로 버튼을 눌렀을 때 현재 Activity를 종료하는 이벤트 핸들러를 작성

        Button backbtn = (Button)findViewById(R.id.backbtn);
        backbtn.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view) {
                //현재 Activity 종료
                finish();
            }
        });

 

3. MainActivity의 onResume 메소드 수정

    => onCreate는 Activity 생성시 1번만 호출, onResume은 화면 출력시마다 호출

@Override
    public void onResume(){
        super.onResume();
        //데이터가 없을 때만 데이터를 가져오기
        if(list == null || list.size() < 1) {
            new ThreadEx().start();
            ind.setVisibility(View.VISIBLE);
        }
    }

 

4. 만들고 나면 충분한 테스트를 해야 함

 

5. 현재 작성한 프로그램 리팩토링(수정 - 에러가 있어서가 아니라 성능이나 가독성 향상 작업)

    => 하나의 클래스나 메소드 코드가 길면 별도 클래스로 분리하거나 코드를 줄이기 위해 다른 메소드를 생성하여 호출하도록 변경

    => 메인 화면에서 itemid를 넘겨서 그 itemid를 가지고 데이터를 다시 찾아오게 됨

        - 이미지를 다운로드 받는 별도의 스레드까지 생각하면 상세보기를 위해 서버에 3번 접근

        - 서버의 접근횟수를 줄이는 것도 리팩토링의 중요한 과제중 하나

 

**암시적 인텐트

    => AndroidManifest.xml 파일에 Activity를 등록 할 때 Intent-filter를 추가해야 함

        - iOS는 App의 URI를 이용하여 App을 호출하는 구조지만 Android는 App안의 화면 단위 출력이 가능

    => 다른 Application에서 호출할 수 있도록 Activity를 만들때는 아래와 같은 intent-filter를 등록

<intent-filter>
	<action android:name="아무거나 가능 - 구별만 할 수 있으면 됨"/>
    
    <category android:name="아무거나 가능"/>
</intent-filter>

    => 통상적으로 action name은 패키지명.액티비티명으로 하고, category는 DEFAULT를 많이 사용

    => Intent를 생성할 때 클래스명을 기재하지 않고, intent.setAction("action name")을 이용하여 생성시 암시적 인텐트가 됨

    => 책이나 인터넷에서는 자신의 Application에 있는 인텐트를 암시적 인텐트로 사용하는 경우가 있는 데 이것을 설명을 하기 위해서

 

**암시적 인텐트나 앱이 앱을 호출하는 것은 포트폴리오에서 유용

 

**기본 앱 연동

    => Intent의 action에 기본 앱의 이름을 설정하여 현재 앱 안에서 기본앱을 사용할 수 있도록 할 수 있음

1. 동작

    - ACTION_CALL : 통화시작

    - ACTION_EDIT

    - ACTION_MAIN : 메인을 실행

    - ACTION_VIEW : 무엇인가를 출력

    - ACTION_DIAL : 전화를 검

    - ACTION_BATTERY_LOW

    - ACTION_BATTERY_PLUG

    - ACTION_SCREEN_ON

    - ACTION_HEADSET_PLUG

    - ACTION_TIMEZONE_CHANGED

 

** 기본 앱 연동 실습

    => 주소록, 카메라, 음성인식, 지도, 브라우저, 전화앱을 실행

        - 카메라의 경우 촬영한 이미지를 ImageView에 가져와서 출력

        - 주소록 API를 이용하는 경우 제조상에 따라 안되는 경우도 있음

        - 초창기 안드로이드 폰들은 자체 API를 추가하여 기본앱을 사용할수 있었음

        - 최근에는 Google에서 기본 앱 연동에 관련된 자체 API를 사용할 수 없도록 함

        - 지도, 브라우저, 전화 앱은 데이터를 설정하여 호출

1. Application을 생성

 

2. 전화기능을 사용하기 위해서 AndroidManifest.xml 파일에 전화권한을 설정

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

 

3. 화면 디자인 - 레이아웃을 수정

    => 버튼 6개, 텍스트 뷰 1개, 이미지뷰 1개 배치

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/resultView"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_contacts"
        android:text="주소록 앱 연동"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_camera"
        android:text="카메라 앱 연동"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_voice"
        android:text="음성인식 앱 연동"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_map"
        android:text="지도 앱 연동"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_browser"
        android:text="브라우저 앱 연동"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_call"
        android:text="전화 앱 연결"/>
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/resultImageView"
        android:clickable="true"/>

</LinearLayout>

 

4. xml 파일에 디자인 한 뷰들을 사용하기 위해서 참조형 변수에 대입

  1) MainActivity.java 파일에 인스턴스 변수를 선언

    TextView resultView;
    Button btn_contact, btn_camera, btn_voice, 
            btn_browser, btn_map, btn_call;
    ImageView resultImageView;

 

  2) MainActivity.java 파일의 onCreate 메소드에 인스턴스 변수에 뷰를 찾아서 대입

 

 

 

**Activity

1. 개요

    => 애플리케이션의 기능을 갖는 단일 독립 실행 형 모듈(리눅스의 프로세스)

    => 모듈 : 독립적으로 실행 가능한 객체

        - 안드로이드에서는 Activity와 Application이 모듈

        - 리눅스에서는 이렇게 독립적으로 실행가능한 모듈을 프로세스라고 함

    => 안드로이드에서는 Activity가 화면 구성의 단위

    => View는 화면에 출력은 되지만 모듈은 아니기 때문에 독립적으로 존재할 수 없음

    => 안드로이드에서는 화면에 출력되는 애플리케이션은 반드시 한개 이상의 Activity를 소유해야 함

    => Activity 클래스는 반드시 Activity 클래스로부터 상속받는 클래스로 생성

    => 실제 화면 구성 요소는 없으므로 화면에 무엇인가를 출력하고자 하는 경우 setContentView라는 메소드 호출

        - 매개변수로는 View 객체 또는 layout의 ID를 전달

        - setContentView 메소드는 디바이스의 가로, 세로 크기를 인지하고 있다면 View의 크기를 그 크기에 맞추어서 출력

    => 전체 화면 위에 새로운 View를 추가하고자 하는 경우 addContentView라는 메소드를 이용하여 추가할 View와 크기 옵션을 설정

    => 안드로이드나 iOS가 View를 직접 출력하지 않고 Activity나 ViewController라는 개념을 도입한 이유는 View를 화면에 직접 출력시         View에 출력할 데이터를 View가 소유하고 있어야 하는데 하나의 화면에 여러개의 View가 놓일 경우 Model을 만드는 것이 어려움

    => 이벤트 관리와 데이터 관리 및 View의 배치는 Activity나 ViewController가 관리, View는 화면 출력만 담당

 

2. 안드로이드에서 Activity사용

    => 보안산의 이유로 반드시 AndroidManifest.xml 파일에 등록해야 함

        - 이전에는 직접 등록을 했지만 지금은 Activity 추가 메뉴를 이용하면 자동으로 등록

    => Activity 클래스 명 작성시 현재 애플리케이션 내의 Activity는 패키지 명을 전부 작성할 필요없이 .클래스명으로 작성

 

3. 안드로이드 애플리케이션의 실행

    => 5.0 이전까지는 달빅 가상 머신 위에서 실행

    => 5.0 부터는 가상머신을 없애고 안드로이드 런타임(ART) 시스템의 통제하에 리눅스의 프로세스로 실행

    => 디바이스의 리소스가 한게에 달하면 ART가 스스로 애플리케이션을 중지

    => 애플리케이션의 상태

        - 포그라운드 프로세스 : 현재 화면에 출력된 상태에서 사용자와의 상호작용을 하는 것

        - 가시적 프로세스 : 현재 화면에 출력은 되어있으나 사용자와의 상호작용은 하지 않음

        - 서비스 프로세스 : 화면에 출력은 되어있지 않지만 작업은 수행하고 있는 것

        - 백그라운드 프로세스 : 사용자가 볼수 없는 액티비티를 1개 이상 포함하고 있는 프로세스

        - 비어있는 프로세스 : 실행되는 애플리케이션외의 새로 실행되는 애플리케이션을 호스팅 하기 위해 메모리에 남아있는 프로세스

 

4. 안드로이드는 액티비티를 스택(stack)을 이용하여 관리

 

5. 액티비티의 상태

    => 활성 상태(Active)

    => 일시 정지 상태(Paused)

    => 정지 상태(Stopped)

    => 종료(Finish)

 

6. 수명 주기

     => 도큐먼트를 반드시 확인 : 예측하면 안됨

    => 스마트폰은 API가 수시로 변경되므로 이전에 호출되던 메소드가 새로운 API에서는 호출이 안되거나 순서가 변경될 수 있음

 

    - onCreate : Activity가 처음 만들어질 때 호출되는 메소드

        => 뷰를 초기화하고 Activity의 초기화를 수행

    - onRestart : Activity가 다시 시작될 때 호출 되는 메소드

    - onStart : onCraete, onRestart가 호출된 후 호출되는 메소드

    - onResume : Activity가 활성화될때 호출되는 메소드

        => 화면에 출력될때 호출 되는 메소드

    - onPause : Activity가 비활성화될 때 호출되는 메소드

        => 저장해야 하는 데이터가 있으면 전부 저장

        => 음악 재생 중 전화가 온 경우, 종료후 다시 돌아올때 음악을 이어서 재생하고자 하면 이 메소드에서 재생시간을 저장

        => onPause가 호출된 후, 애플리케이션은 언제든 메모리에서 삭제될 수 있음

    - onStop : onPause가 호출되고 화면에서 보이지 않게 될 때 호출되는 메소드

    - onDestroy : Activity가 파괴될 때 호출되는 메소드

        => isFinishing 속성 확인시 애플리케이션이 종료시킨 것인지, 시스템이 종료시킨 것인지 확인 가능

 

7. 하위 데이터 출력시 화면의 호출

    => 상위 Activity -> 하위 Activity

        - 상위에서 하위로 이동시 상위에서 하위를 생성

        - 하위 Activity에서는 onCreate와 onResume이 모두 호출(상위에서는 onResume만 호출)

 

8. Activity 상태 저장

    => Activity가 종료되었다가 다시 만들어지는 경우 이전 내용을 복원하기 위한 것

    => 회전이 발생하면 Activity를 새로 만듬

    => onSaveInstranceState 메소드를 오버라이딩하여 Bundle에 데이터를 저장하면 onCreate 메소드가 호출될 때 저장된 내용이 전달됨

        - 마지막 상태를 저장 했다가 액티비티가 시작될때 다시 복원하고자 하면 Bundle을 이용

        - Bundle은 Map과 유사

    => EditText는 설정하지 않아도 스스로 데이터를 저장했다가 재출력시 복원

 

9. Android의 Task관리

    => Task : 앱 실행을 위한 정보를 저장하는 공간

    => 프로세스를 앱의 물리적인 실행단위, 태스크는 앱의 논리적인 실행단위

    => 앱을 2개 실행하면 프로세스가 2개가 되고 태스크도 2개

    => 하나의 앱을 실행한 후 이 앱에서 다른 앱을 호출하게 되면 프로세스는 2개이지만 태스크는 1개

    => 안드로이드의 Activity는 실행될때 실행모드는 설정 가능

    => Activity를 등록할 때 android:launchMode에 속성을 지정하여 실행모드를 설정

        - Standard : 기본값으로 Intent를 생성할 때 마다 Activity의 인스턴스가 생성되고, 태스크 목록의 맨 위에 반복하여 올리는 구조

        - 이 모드에서는 동일한 Activity의 Intent를 2번 생성하면 2개가 Task에 쌓이게 됨

        - SingleTop : 액티비티가 최상단에 존재하면 생성하지 않는 모드

        - singleTask : 다른 애플리케이션의 액티비티를 호출한 경우 새로운 Task를 생성하는 모드

            -> 새로운 액티비티에서 이전 버튼을 눌러도 이전으로 돌아오지 않고 종료됨

        - singleInstance : 이 액티비티만 별도의 Task로 생성

    => Instant에 Flag를 설정하여 Task안의 내용을 변경하는 것도 가능

        - Intent.setFlags(Intent.FLAG_ACTIVITY_옵션)

        - 옵션에 CLERE_TOP을 설정하면 이전 Activity 정보를 모두 삭제

    

 

 

**URL & URI

    => URL은 인터넷 상에서의 자원의 위치

    => URI는 모든 자원의 위치

    => URI가 URL보다 큰 개념

    => URL을 요청하면 웹 주소를 대입하는 것이고, URI를 요청하면 웹주소가 아닐수도 있음

 

**권한 설정

    => Android 6.0 이전까지는 권한 설정을 정적으로 수행

        - AndroidManifest.xml 파일에 필요한 권한을 설정하면 앱을 설치할 때 권한 사용 여부를 묻고 앱을 설치하여 사용

    => Android 6.0 부터는 일부분의 기능은 정적으로만으로는 사용이 안되고 기능을 사용할 때 묻는 동적 설정 방식으로 변환

    => 스마트 폰 SDK에서는 이러한 변화가 와서 안드로이드 뿐 아니라 대다수의 스마트폰 SDK에서 발생

    => 안드로이드에서 동적 권한 설정 여부 묻기

if(ContextCompat.checkSelfPermission(Context context, Manifest.permisstion.권한) == 
	PackageManager.PERMISSION_GRANTED){
	권한이 부여된 경우 수행할 내용;
}else{
	권한을 요청
	ActivityCompat.requestPermisstion(Context context, new String[]{
    	Manifest.permission.권한...}, 번호);
    }

    => 외부에 파일을 저장할 때 카메라를 직접 사용할 때 이러한 동적 권한을 설정해야 함

 

**Android API 버전과 하위 호환성

    => Application을 생성할 때 설치가 되는 최하 운영체제 버전을 설정할 수 있음

        - minSdkVersion : 애플리케이션이 설치되는 최하 운영체제 버전

        - targetSdkVersion : 애플리케이션을 개발 할 때 사용한 API 버전

    => minSdkVersion, targetSdkVersion이 다른 경우, minSdkVersion에서 모든 기능을 사용할 수 있도록 하는것을 하위호환성이라고 함

    => 2개의 버전에 대한 설정은 build.gradle 파일을 수정해서도 할수 있음

 

1. Google의 support library를 이용

    => 표준 라이브러리는 아니지만 하위 호환성문제로 인해 구글이 제공하는 라이브러리

    => depencies에 설정하여 사용

        - 이전에는 com.android....로 시작, 2019년부터 androidx...로 시작

    => 안드로이드의 Activity 생성시 Activity클래스에서 상속 받아야함, 이경우 운영체제 버전별로 다른 Activity클래스를 상속

        - AppCompatActivity를 상속받으면 설치되는 운영체제 버전에따라 안드로이드 시스템이 Activity 클래스를 결정

        - 안드로이드에서 동일한 이름의 클래스가 AppComat이 붙은 상태로 존재시, 전부 하위버전 호환성 문제 해결을 위한 것

 

2. 개발자 코드에서 버전을 직접 식별하여 처리

    => 현재 디바이스의 버전 확인 : Build.VERSION.SDK.INT

    => 각각의 버전은 Build.VERSION_CODES.운영체제명 또는 약자

 

3. 오픈 소스 라이브러리를 이용

    => 다른 개발자들이 API버전에 상관없이 사용할 수 있도록 만든 API가 있으면 그 API를 사용

    => 오픈 소스 라이브러리 사용시 주의점은 마켓에서 reject 여부를 반드시 확인하고 사용

 

**Fragment

    => API Level 11(Android 3.0)에서 등장한 뷰

    => 안드로이드에서는 출력의 개념으로 2가지를 사용(Activity, View)

    => 태블릿의 등장으로 인해 Activity와 View만으로 화면 출력하는데 한계 발생

    => 하나의 화면을 2개로 분할하여 출력하고자 하는 경우