본문 바로가기

카테고리 없음

86 Android 음악재생, 연락처

3)실행 가능한 Activity 추가(SoundPlayActivity)

 

4)Service 클래스 만들기 - PlayService

public class PlayService extends Service

implements MediaPlayer.OnCompletionListener{

 

    //음원 재생 가능한 클래스의 참조형 변수

    MediaPlayer player;

 

    //서비스와의 데이터 공유에 사용할 Broadcast Receiver

    BroadcastReceiver receiver =

            new BroadcastReceiver() {

                @Override

                public void onReceive(

                        Context context, Intent intent) {

                    //전송해 준 데이터 읽기

                    //stop 이나 start 라는 문자열을 전송 - mode

                    String mode =

                            intent.getStringExtra("mode");

                    if(mode != null){

                        if(mode.equals("start")){

                            try{

                                //재생 중이라면

                                if(player != null && player.isPlaying()){

                                    //재생을 중지하고 메모리 정리

                                    player.stop();

                                    //메모리 해제

                                    player.release();

                                    //가베지 컬렉터를 호출할 수 있도록 해주는 구문

                                    player = null;

                                }

                                //새로 생성

                                player = MediaPlayer.create(

                                        getApplicationContext(), R.raw.test);

                                player.start();

                                //리시버를 호출

                                Intent aIntent = new Intent(

                                        "com.example.PLAY_TO_ACTIVITY");

                                //음원이 재생 중인지 확인해 줄 수 있도록 해주기 위한 값

                                aIntent.putExtra("mode", "start");

                                //재생 중인 위치를 알 수 있도록 해주기 위한 값

                                aIntent.putExtra("duration", player.getDuration());

                                sendBroadcast(aIntent);

                            }catch(Exception e){

                                Log.e("음원 재생 예외", e.getMessage());

                                e.printStackTrace();

                            }

                        }else if(mode.equals("stop")){

                            if(player!= null && player.isPlaying()){

                                player.stop();

                            }

                            player.release();

                            player = null;

                        }

                    }

                }

            };

 

    //MediaPlayer.OnCompletionListener 인터페이스의

    //재생이 종료되었을 때 호출되는 메소드

    @Override

    public void onCompletion(MediaPlayer mp){

        //종료 되었으므로 종료 되었다고 방송을 하고 서비스를 중지

        Intent intent = new Intent(

                "com.example.PLAY_TO_ACTIVITY");

        intent.putExtra("mode", "stop");

        sendBroadcast(intent);

        //서비스를 중지 - 이 메소드를 호출하지 않으면 서비스는 계속 살아있음

        stopSelf();

    }

 

    //서비스가 만들어질 때 호출되는 메소드

    @Override

    public void onCreate(){

        super.onCreate();

        //리시버 등록

        registerReceiver(receiver,

                new IntentFilter("com.example.PLAY_TO_SERVICE"));

    }

 

    //서비스가 종료될 때 호출되는 메소드

    @Override

    public void onDestroy(){

        //리시버 등록 해제

        unregisterReceiver(receiver);

        //파괴할 때는 상위 클래스의 메소드를 뒤에서 호출 - 소멸자

        super.onDestroy();

    }

 

    //서비스가 중지되었다가 재시작 되었을 때 호출되는 메소드

    @Override

    public int onStartCommand(

            Intent intent, int flags, int startId){

        if(player != null){

            Intent aIntent = new Intent(

                    "com.example.PLAY_TO_ACTIVITY");

            aIntent.putExtra("mode", "restart");

            aIntent.putExtra("duration",

                    player.getDuration());

            aIntent.putExtra("current",

                    player.getCurrentPosition());

            sendBroadcast(aIntent);

        }

        //리턴이 있는 메소드를 오버라이딩 할 때 메소드의 역할을 잘 모르겠으면

        //상위 클래스의 메소드를 호출해서 리턴하면 됩니다.

        return super.onStartCommand(intent, flags, startId);

    }

 

    public PlayService() {

    }

 

    @Override

    //스타트 서비스 일 때는 필요가 없고 바운드 서비스에서만 구현

    public IBinder onBind(Intent intent) {

        // TODO: Return the communication channel to the service.

        throw new UnsupportedOperationException("Not yet implemented");

    }

}

 

5)activity 레이아웃을 수정

=>android api 에 익숙해지면 xml 대신에 자바 나 코틀린 코드로 UI를 작성하는 것도 고려

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

<!-- RelativeLayout 과 ConstraintLayout은 부모 나

다른 뷰와의 관계를 이용해서 배치하는 레이아웃

안드로이드나 아이폰에서 권장하는 레이아웃

뷰 들 사이의 여백 때문 -->

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

<!-- RelativeLayout 과 ConstraintLayout은 부모 나

다른 뷰와의 관계를 이용해서 배치하는 레이아웃

안드로이드나 아이폰에서 권장하는 레이아웃

뷰 들 사이의 여백 때문 -->

 

    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"

    tools:context=".SoundPlayActivity"

    android:background="#303999">

 

    <ImageView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:src="@drawable/background"

        android:layout_alignParentTop="true"/>

 

    <ImageView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:src="@drawable/ic_play"

        android:layout_alignParentBottom="true"

        android:layout_marginBottom="36dp"

        android:layout_marginLeft="24dp"

        android:clickable="true"

        android:id="@+id/play"/>

 

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Service Test"

        android:textSize="30sp"

        android:textColor="@android:color/white"

        android:layout_alignTop="@id/play"

        android:layout_alignParentBottom="true"

        android:layout_marginLeft="16dp"

        android:layout_centerHorizontal="true"

        android:layout_centerInParent="true"

        android:layout_centerVertical="true"

        android:id="@+id/title"/>

 

    <ImageView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:src="@drawable/ic_stop"

        android:layout_alignParentBottom="true"

        android:layout_alignParentRight="true"

        android:layout_marginBottom="36dp"

        android:layout_marginRight="24dp"

        android:clickable="true"

        android:id="@+id/stop"/>

 

    <ProgressBar

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:id="@+id/progress"

        android:layout_above="@id/title"

        android:layout_marginBottom="24dp"

        android:layout_marginLeft="16dp"

        android:layout_marginRight="16dp"

        style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>

 

</RelativeLayout>

 

6)Activity 작성

=>필요한 인스턴스 변수를 선언

//화면에 보여지는 뷰 변수 

    ImageView playBtn, stopBtn;

    TextView titleView;

    ProgressBar progressBar;

    

    //스레드 동작 여부

    boolean runThread;

 

=>onCreate 메소드에서 위에서 만든 4개의 뷰 변수 찾아오기

  playBtn = (ImageView)findViewById(R.id.play);

        stopBtn = (ImageView)findViewById(R.id.stop);

        progressBar = (ProgressBar)findViewById(R.id.progress);

        titleView = (TextView)findViewById(R.id.title);

 

 

=>스레드 클래스 만들기 - 1초마다 프로그래스 바의 진행율을 재설정하고 끝까지 도달하면 스레드를 멈춤

 //프로그래스 바의 진행율을 표시할 스레드를 생성

    class ProgressThread extends Thread{

        //스레드로 동작할 메소드

        public void run(){

            while(runThread){

                progressBar.incrementProgressBy(

                        1000);

                SystemClock.sleep(1000);

                if(progressBar.getProgress() ==

                        progressBar.getMax()){

                    runThread = false;

                }

            }

        }

    }

 

 

=>리시버 만들기: start, stop, restart 메시지에 따라 프로그래스 바의 설정응ㄹ 변경하는 리시버

   //Service 와 통신하기 위한 Broadcast Receiver 생성

    //상대방이 mode에 start 와 stop 이라는 글자를 전송해주고

    //duration에 전체 재생 시간을 전송해줍니다.

    BroadcastReceiver receiver = 

            new BroadcastReceiver() {

        @Override

        public void onReceive(

                Context context, Intent intent) {

            String mode = 

                    intent.getStringExtra("mode");

            if(mode != null){

                if("start".equals(mode)){

                    //duration의 값을 정수로 가져오고 없으면 0

                    //Map은 없는 값을 가져오면 null 입니다.

                    int duration = intent.getIntExtra(

                            "duration",

                            0);

                    progressBar.setMax(duration);

                    progressBar.setProgress(0);

                    

                }else if("stop".equals(mode)){

                    runThread = false;

                }else if("restart".equals(mode)){

                    //재시작 하는 경우 전체 재생 시간과 현재 재생 위치를

                    //가져와서 프로그래스 바에 설정

                    int duration = 

                            intent.getIntExtra(

                                    "duration",

                                    0);

                    int current = 

                            intent.getIntExtra(

                                    "current", 0);

                    progressBar.setMax(duration);

                    progressBar.setProgress(current);

                    //스레드는 재시작이 안되므로 새로 생성해서 시작

                    runThread = true;

                    ProgressThread thread = new ProgressThread();

                    thread.start();

                    playBtn.setEnabled(false);

                    stopBtn.setEnabled(true);

                }

            }

        }

    };

 

=>onCreate 메소드에서 이미지의 클릭 이벤트 처리 코드를 작성

      //리시버 등록

        //리시버는 사용하는 반대편에서 등록

        registerReceiver(receiver,

                new IntentFilter(

                        "com.example.PLAY_TO_ACTIVITY"));

        //서비스 시작

        Intent intent = new Intent(

                this, PlayService.class);

        startService(intent);

 

        //이미지 클릭 처리

        playBtn.setOnClickListener(

                new ImageView.OnClickListener(){

 

                    @Override

                    public void onClick(View view) {

                        //com.example.PLAY_TO_SERVICE에게 방송

                        Intent intent =

                                new Intent(

                                        "com.example.PLAY_TO_SERVICE");

                        //필요한 데이터 작성

                        intent.putExtra("mode", "start");

                        //방송 시작

                        sendBroadcast(intent);

 

                        //진행율을 표시하기 위한 스레드 시작

                        runThread = true;

                        ProgressThread thread = new ProgressThread();

                        thread.start();

 

                        //UI 고려

                        //토글형태로 동작해야 하는 요소가 있다면 각각의 동작을 구분해 줄

                        //필요가 있습니다.

                        //숨기기, 동작하지 않도록 하기, 색상을 변경하기 등이 있습니다.

                        playBtn.setEnabled(false);

                        stopBtn.setEnabled(true);

 

                    }

                });

        //중지 버튼 클릭했을 때 이벤트 처리 

        stopBtn.setOnClickListener(

                new ImageView.OnClickListener(){

 

            @Override

            public void onClick(View view) {

                //com.example.PLAY_TO_SERVICE에게 방송

                Intent intent =

                        new Intent(

                                "com.example.PLAY_TO_SERVICE");

                //필요한 데이터 작성

                intent.putExtra("mode", "stop");

                //방송 시작

                sendBroadcast(intent);

 

                //진행율을 초기

                runThread = false;

                progressBar.setProgress(0);

 

                //UI 고려

                //토글형태로 동작해야 하는 요소가 있다면 각각의 동작을 구분해 줄

                //필요가 있습니다.

                //숨기기, 동작하지 않도록 하기, 색상을 변경하기 등이 있습니다.

                playBtn.setEnabled(true);

                stopBtn.setEnabled(false);

 

            }

        });

 

 

7)실행

=>시작 버튼 클릭: com.example.PLAY_TO_SERVICE 라는 Broadcast에게 메시지를 전송

mode라는 키로 start라는 데이터를 같이 전송

진행율을 표시하는 스레드도 시작

 

=>stop 버튼 클릭:com.example.PLAY_TO_SERVICE 라는 Broadcast에게 메시지를 전송

mode라는 키로 stop라는 데이터를 같이 전송

진행율을 표시하는 스레드를 중지

 

=>이 원리를 이용해서 다운로드 받는 과정을 출력해 줄 수 있습니다.

음악을 재생하는 부분을 다운로드 받는 코드로 변경

duraion에 음악의 전체 재생 시간을 전송했는데 다운로드 받는 스트림의 전체 사이즈를 전송하면 됩니다.

available() 이라는 메소드를 이용하면 읽을 수 있는 전체 크기(다운로드 받는 데이터의 전체 크기)를 가져올 수 있습니다.

백분율로 표시하고자 할 때는 progress 현재값 / progress 최대값 * 100 하면 됩니다.

 

**Intent Service

=>CPU를 오랜 시간 동안 사용하는 작업을 서비스로 수행하고자 할 때 사용

=>새로운 스레드를 만들어서 작업을 수행하도록 해서 작업이 다른 애플리케이션의 성능에 영향을 미치는 것을 방지할 목적으로 만드는 서비스

=>비동기식으로 동작하기 때문에 여러 개의 작업을 수행할 때 수행 순서를 알 수 없습니다.

토렌트에서 영화 다운로드 것과 유사

=>IntentService 클래스로부터 상속받아서 만들고 onHandleIntent 메소드만 재정의 하면 됩니다.

=>StartService는 반드시 종료를 직접해야 하지만 이 서비스는 자신의 작업이 전부 완료되면 자동으로 종료되기 때문에 직접 종료할 필요가 없습니다,

 

실습 - 로그를 출력하는 작업을 IntentService로 실행

1.실행 가능한 Activity - IntentServiceActivity

 

2.백그라운드 작업을 위한 IntentService 클래스를 생성

public class MyIntentService extends IntentService {

    public MyIntentService() {

        super("MyIntentService");

    }

 

    //백그라운드에서 수행할 내용 작성

    //게임같은 경우는 하나의 필드를 가져와서 게임을 진행하고 있는 경우 

    //근처의 다른 필드를 다운로드

    @Override

    protected void onHandleIntent(@Nullable Intent intent) {

        for(int i=0; i<10; i=i+1){

            SystemClock.sleep(1000);

            Log.e("TAG", "Intent Service :" + i);

        }

    }

}

 

3.Acitity 클래스의 onCreate 메소드에서 서비스를 실행하는 코드를 작성

//서비스를 시작

Intent intent = new Intent(this, MyIntentService.class);

startService(intent);

 

 

**시스템 서비스

=>안드로이드 시스템이 제공하는 서비스

=>자신만의 별도의 방법으로 서비스를 제공받아서 사용

1.생성

시스템서비스이름 변수 = (시스템서비스이름)getSystmService(서비스에 해당하는 상수);

=>LayoutInflater(xml 로 만든 레이아웃을 자바의 View로 변경해주는 서비스)

=>알림을 만들어주는 NotificationManager

=>최상위 액티비티를 확인하고자 할 때는 ActivityManager를 생성

=>앱의 설치 여부를 확인할 때는 PackageManger를 생성

 

2.AlarmManager

=>미리 지정해 놓은 시간에 이벤트를 발생시키는 장치

=>장래의 특정 시점이나 일정 시간 경과 후에 할 작업을 등록하고 싶을 때 사용

 

1)생성

AlarmManager al = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

 

 

2)알람 등록

set(int type, long triggerAtTime, PendingIntent intent): 한번만 동작

setRepeating(int type, long triggerAtTime, long interval PendingIntent intent): interval 단위로 계속 동작

 

=>type

RTC(1970년 1월 1일 자정을 0으로 해서 1/1000초 단위로 시간을 표시하는 방법)

RTC_WAKEUP - 장비를 깨움

ELAPSED_REALTIME: 부팅된 시간으로 부터 지나온 시간

ELAPSED_REALTIME_WAKEUP

 

3)PendingIntent 생성

Intent intent = new Intent(Context context, 서비스클래스.class);

PendingIntent pIntent = PendingIntent.getService(Context context, 구분할 정수, intent, PendingIntent옵션);

 

=>서비스 클래스 대신에 BroadcastReceiver를 사용해도 됩니다.

 

 

3.버튼을 누르고 10초 후에 토스트를 출력하기

1)실행 가능한 Activity 1개 생성

 

2)알람 시간이 되면 동작할 BroadcastReceiver 클래스 생성

public class AlarmReceiver extends BroadcastReceiver {

 

    @Override

    public void onReceive(Context context, Intent intent) {

        Toast.makeText(

                context, "알람", Toast.LENGTH_LONG)

                .show();

    }

}

 

3)layout에 버튼을 1개 배치

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

 

    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"

    tools:context=".AlarmActivity"

    android:orientation="vertical">

    

    <Button

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="알람"

        android:id="@+id/btn"/>

 

</LinearLayout>

 

4)Activity.java 파일의 onCreate 메소드에 버튼을 클릭했을 때 수행할 동작을 작성

       Button btn = (Button)findViewById(R.id.btn);

        btn.setOnClickListener(new Button.OnClickListener(){

 

            @Override

            public void onClick(View view) {

                //알람 시간 만들기

                //현재 시간에서 20초 후

                Calendar calendar =

                        Calendar.getInstance();

                calendar.add(Calendar.SECOND, 20);

 

                //알람등록

                Intent intent = new Intent(

                        AlarmActivity.this,

                        AlarmReceiver.class);

                PendingIntent pIntent =

                        PendingIntent.getBroadcast(

                                AlarmActivity.this,

                                0, intent, 0);

                AlarmManager am =

                        (AlarmManager)getSystemService(

                                Context.ALARM_SERVICE);

                am.set(AlarmManager.RTC_WAKEUP,

                        calendar.getTimeInMillis(), pIntent);

            }

        });

 

**Content Provider

=>안드로이드의 컴포넌트로 2개의 애플리케이션이 데이터를 공유하기 위한 개념

=>안드로이드 자체에서 연락처나 이미지 갤러리 등은 자체적으로 기능을 제공하고 있고 우리가 만든 애플리케이션끼리 데이터를 공유하기 위한 목적으로 사용

=>실제 앱 개발에서는 서버를 먼저 구성하고 업데이터 된 데이터를 서버에 업로드를 하고 다른 애플리케이션이 처음 접속할 때 서버로부터 데이터를 다운로드 받는 방식으로 구현하는 경우가 많습니다.

이렇게 구현하면 서버와 연결을 해야 하기 때문에 반드시 네트워크가 사용 가능해야 합니다.

이 대안으로 서버에 접속이 되었을 때 받아온 데이터를 로컬에 저장해서 네트워크가 되면 서버로부터 받아오고 그렇지 않으면 로컬의 데이터를 이용하는 방식으로 많이 구현

=>여기 push server를 이용해서 데이터 변경을 로컬 노티피케이션으로 알려주는 경우도 많습니다.

=>실제 애플리케이션 개발에서는 앱끼리 데이터를 공유하는 것 보다는 기본 앱의 데이터를 가져와서 사용하는 것이 훨씬 더 중요합니다.

 

1.생성

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

=>메소드 재정의

1)String getType

=>잘못된 Uri 가 왔을 때 리턴할 문자열만 설정

 

2)Cursor query(Uri uri, String[] projection, String selection, String [] selectionArgs, String sortOrder){

return Cursor;

}

=>select 구문을 수행해주는 메소드와 유사

uri는 ContentProvider의 Uri/테이블 이름

projection은 가져올 컬럼의 이름 배열 - sql에서는 select 절

selection이 가져올 조건 - SQL에서는 where 절

selectionArgs는 selection에 값을 직접 입력하지 않고 ?로 만들었을 때 실제 대입될 데이터의 배열

sortorder는 정렬할 컬럼이름

 

3)Uri insert(Uri uri, ContentValues values): 삽입할 때 사용하는 메소드

 

4)int update(Uri uri, ContentValues values, String selection, String[] selectionArgs): 갱신할 때 사용하는 메소드

 

5)int delete(Uri uri, String selection, String[] selectionArgs): 삭제에 사용할 메소드

 

6)boolean onCreate(): 객체가 처음 만들어질 때 호출되는 메소드

이 메소드에서 데이터베이스에 연결을 하고 데이터베이스 사용 객체를 생성 

 

2.AndroidManifest에 등록

=>등록 할 때 

android:readPermission=“프로바이더의 authorities.READ_DATABASE”

android:writePermission=“프로바이더의 authorities.WRITE_DATABASE”

퍼미션을 만들어 주어야 합니다.

 

3.사용하고자 하는 경우에는 AndroidManifest에 권한 설정을 해 주어야 합니다.

<permission android:name=“프로바이더의 authorities.READ_DATABASE” android:protectionLevel=“normal”/>

<permission android:name=“프로바이더의 authorities.WRITE_DATABASE” android:protectionLevel=“normal”/>

 

**시스템 앱의 데이터 공유

=>연락처나 이미지 갤러리의 데이터는 모든 앱에서 사용이 가능합니다.

대신에 권한 설정을 해야 합니다.

=>안드로이드 6.0 이상 부터는 동적인 권한을 요청해야 합니다.

이 작업이 번거로움

 

1.퍼미션 요청을 쉽게하는 라이브러리

=>이 라이브러리는 표준 라이브러리가 아니라서 외부에서 다운로드 받아야 합니다.

=>중앙 저장소에서도 없어서 직접 github에서 다운로드 받아 사용해야 합니다.

=>별도의 저장소를 지정해서 다운로드를 받아야 합니다.

=>이 라이브러리에는 AutoPermisionListener 인터페이스 가 존재해서 이 인터페이스를 구현하면 됩니다.

재정의해야 하는 메소드는 onRequestPermissionsResult, onDenied, onGranted

필요한 권한이 있으면 AutoPermissions.Companion.loadAllPermissions 만 호출하면 됩니다.

 

2.연락처에서 이름과 전화번호를 가져와서 텍스트 뷰에 출력하고 이미지 갤러리에서 이미지를 선택해서 이미지 뷰에 출력하는 예제

 

1)퍼미션을 설정

=>연락처 사용을 위한 것과 이미지가 외부 저장소에 있는 경우 외부 저장소 사용 권한

=>READ_CONTACTS, READ_EXTERNAL_STORAGE

 

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

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

 

2)동적인 퍼미션 확인을 쉽게 할 수 있는 라이브러리 의존성을 설정

=>build.gradle에서 수행 (App)

 

allprojects{
    repositories {
        maven{url "https://jitpack.io"}
    }
}

dependencies {
    implementation 'com.github.pedroSG94:AutoPermissions:1.0.3'

    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

}

 

 

 

3) 실행 가능한 Activity 추가 (BasicAppShareActivity)

 

4) 레이아웃 수정

=> 전화번호를 출력할 TextView, 이미지를

 

<?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"
    tools:context=".BasicAppShareActivity"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/lblContacts"/>
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/imgGallery"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="연락처"
            android:id="@+id/btnContacts"/>
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="연락처"
            android:id="@+id/btnGallery"/>
    </LinearLayout>
</LinearLayout>

 

 

 

 

5) Activity 클래스에 권한 동적 요청에 관련된 작업을 수행

=> Activity 클래스에 AutoPermissionListener 인터페이스를 implements

 

package com.example.android0805;

import androidx.appcompat.app.AppCompatActivity;

import android.icu.text.UnicodeSetSpanner;
import android.os.Bundle;
import android.widget.Toast;

import com.pedro.library.AutoPermissions;
import com.pedro.library.AutoPermissionsListener;

public class BasicAppShareActivity extends AppCompatActivity implements AutoPermissionsListener {
//에러에서 onDenied, onGranted 를 implements 한다.
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_basic_app_share);
    }

    //권한을 거부할 때 호출되는 메소드 : AutoPermissionListener의 메소드
    @Override
    public void onDenied(int i, String[] strings) {
        Toast.makeText(this, "권한 사용을 하지 않으면 기능을 사용 못함", Toast.LENGTH_LONG).show();
    }

    //권한을 혀용했을 때 호출되는 메소드 : AutoPermissionListener의 메소드
    @Override
    public void onGranted(int i, String[] strings) {
        Toast.makeText(this, "권한 사용을 허용 하셨습니다.", Toast.LENGTH_LONG).show();
    }


    //권한 요청을 하고 권한에 대한 응답을 했을 때 호출되는 메소드
    //Activity의 메소드
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int [] grantResults) {
        //상위 클래스의 메소드 호출
        super.onRequestPermissionsResult( requestCode, permissions, grantResults);
        //권한 요청 결과를 AutoPermission에 전송해서 메소드를 호출하도록 해줍니다.
        AutoPermissions.Companion.parsePermissions(this, requestCode, permissions, this);
    }
}

 

 

=>onCreate 메소드에서 권한을 요청하는 코드를 추가

//시작하자마자 필요한 권한을 요청
AutoPermissions.Companion.loadAllPermissions(this, 101);

 

package com.example.android0805;

import androidx.appcompat.app.AppCompatActivity;

import android.icu.text.UnicodeSetSpanner;
import android.os.Bundle;
import android.widget.Toast;

import com.pedro.library.AutoPermissions;
import com.pedro.library.AutoPermissionsListener;

public class BasicAppShareActivity extends AppCompatActivity implements AutoPermissionsListener {
//에러에서 onDenied, onGranted 를 implements 한다.
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_basic_app_share);

        //시작하자마자 필요한 권한을 요청
        AutoPermissions.Companion.loadAllPermissions(this, 101);
    }

    //권한을 거부할 때 호출되는 메소드 : AutoPermissionListener의 메소드
    @Override
    public void onDenied(int i, String[] strings) {
        Toast.makeText(this, "권한 사용을 하지 않으면 기능을 사용 못함", Toast.LENGTH_LONG).show();
    }

    //권한을 혀용했을 때 호출되는 메소드 : AutoPermissionListener의 메소드
    @Override
    public void onGranted(int i, String[] strings) {
        Toast.makeText(this, "권한 사용을 허용 하셨습니다.", Toast.LENGTH_LONG).show();
    }


    //권한 요청을 하고 권한에 대한 응답을 했을 때 호출되는 메소드
    //Activity의 메소드
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int [] grantResults) {
        //상위 클래스의 메소드 호출
        super.onRequestPermissionsResult( requestCode, permissions, grantResults);
        //권한 요청 결과를 AutoPermission에 전송해서 메소드를 호출하도록 해줍니다.
        AutoPermissions.Companion.parsePermissions(this, requestCode, permissions, this);
    }
}

 

 

6)뷰들의 참조형 변수를 인스턴스변수로 추가

TextView lblContacts;

ImageView imgGallery;

    

Button btnContacts, btnGallery;

 

7)onCreate 메소드에서 변수와 실제 디자인 한 객체를 연결

//뷰 찾아오기

        lblContacts = (TextView)findViewById(R.id.lblContacts);

        imgGallery = (ImageView)findViewById(R.id.imgGallery);

        btnContacts = (Button)findViewById(R.id.btnContacts);

        btnGallery = (Button)findViewById(R.id.btnGallery);

 

 

8)onCreate 메소드에 연락처 버튼을 눌렀을 때 수행할 내용을 작성

//연락처 버튼을 눌렀을 때 수행할 코드

        btnContacts.setOnClickListener(

                new Button.OnClickListener(){

            public void onClick(View view){

                //연락처 인텐트 생성

                Intent intent = new Intent(Intent.ACTION_PICK,

                        ContactsContract.Contacts.CONTENT_URI);

                //연락처 출력

                startActivityForResult(intent, 10);

            }

        });

 

 

9)onCreate 메소드에 이미지 버튼을 눌렀을 때 수행할 내용을 작성

    //이미지 버튼을 눌렀을 때 수행할 내용을 작성

        btnGallery.setOnClickListener(new Button.OnClickListener(){

            public void onClick(View view){

                //사진 앱을 전부 호출

                Intent intent = new Intent();

                intent.setType("image/*");

                intent.setAction(Intent.ACTION_GET_CONTENT);

                startActivityForResult(intent, 20);

            }

        });

 

 

10)호출한 인텐트가 없어질 때 호출되는 메소드를 재정의

@Override

    public void onActivityResult(int requestCode, int resultCode,

                                 Intent data){

        super.onActivityResult(requestCode, resultCode, data);

        //이미지를 선택했을 때 수행할 내용

        if(requestCode == 20 && resultCode == RESULT_OK){

            Uri fileUri = data.getData();

            ContentResolver resolver = getContentResolver();

            try{

                InputStream inputStream =

                        resolver.openInputStream(fileUri);

                Bitmap imgBitmap = BitmapFactory.decodeStream(inputStream);

                imgGallery.setImageBitmap(imgBitmap);

                inputStream.close();

            }catch(Exception e){

                Log.e("이미지 가져오기 예외", e.getMessage());

            }

 

        }

 

        //연락처가 없어졌을 때 수행할 내용

        if(requestCode == 10 && resultCode == RESULT_OK){

            try{

                //선택한 연락처 가져오기

                //선택한 항목의 id 찾아오기

                String id = Uri.parse(data.getDataString())

                        .getLastPathSegment();

                //id를 가지고 연락처 가져오기

                Cursor cursor = getContentResolver().query(

                        ContactsContract.Data.CONTENT_URI,

                        new String[]{ContactsContract.Contacts.DISPLAY_NAME,

                        ContactsContract.CommonDataKinds.Phone.NUMBER},

                        ContactsContract.Data._ID + "=" + id,

                        null, null);

                cursor.moveToNext();

                String name = cursor.getString(0);

                String phone = cursor.getString(1);

                lblContacts.setText(name + ":" + phone);

                lblContacts.setTextSize(20);

            }catch(Exception e){

                Log.e("연락처 예외", e.getMessage());

            }

        }

    }

 

 

**서비스를 이용하는 애플리케이션

=>백그라운드에서 음원 재생

=>사이즈가 큰 데이터를 다운로드 받는 애플리케이션

=>위치 정보를 계속해서 사용해야하는 애플리케이션

=>근거리 네트워크를 이용해야 하는 애플리케이션

 

**안드로이드나 아이폰에서 이미지를 출력하는 이미지 뷰나 단순한 텍스트를 출력하는 Label, TextView는 기본적으로 사용자의 인터랙션(상호 작용 - 터치)이 안됩니다.

어떤 속성의 값을 변경 해주어야만 인터랙션이 가능해집니다.

안드로이드는 clickable 이고 아이폰은 userInteractionEnabled 속성입니다.

 

 

**작업을 분류

CPU를 많이 사용하는 작업: 연산(계산)

=>안드로이드에서는 IntentService를 이용해서 수행

=>여러 개의 작업을 할 때는 서로 간의 연관성이 없어야 합니다.

=>Intent Service를 여러 개 만들면 수행 종료 시간을 알 수 없습니다.

 

CPU를 작게 사용하는 작업: 입출력

=>입출력하는 작업은 Service(백그라운드 스레드)를 만들어서 처리하는 것이 유용

 

속도를 가지고 작업을 분류

=>주기억 장치의 데이터를 이용하는 작업

=>보조기억 장치(파일을 읽고 쓰기)나 외부 와 연결(키보드, 모니터, 네트워크)에 의해 이루어지는 작업

 

**Android의 4대 컴포넌트

Activity: 화면

Service: 백그라운드 작업

Broadcast: 통신 - Activity 와 Service 간의 데이터 전송에도 사용

ContentProvider: 데이터 공유

 

=>안드로이드에서는 4대 컴포넌트는 생성만으로는 사용할 수 없고 AndroidManifest 파일에 등록을 해야만 사용할 수 있습니다.

직접 클래스를 생성한 경우에는 등록하는 코드를 작성해 주어야 합니다.

Activity는 없는 Activity를 호출하면 예외가 발생하지만 Broadcast 나 Service, ContentProvider는 없는 것을 호출하면 아무일도 하지 않습니다.

호출하는 부분에서는 예외가 발생하지 않고 작업을 하는 도중에 예외가 발생할 수 는 있습니다.

디버깅이 어렵기 때문에 Broadcast 나 Service, ContentProvider를 사용할 때는 시작하는 메소드에 로그를 출력해놓는 것이 좋습니다.

로그가 출력이 안되면 설정을 잘못하거나 잘못된 이름으로 호출한 것입니다.

 

웹 서비스를 만들 때도 클래스를 만들 때 생성자를 만들어서 로그를 출력해두는 것이 좋습니다. 로그가 출력이 안되면 어노테이션을 잘못 설정하거나 패키지 이름이 잘못된 것이거나 servlet-context.xml 파일의 Component-Scan이 잘못된 것입니다.

 

 

월요일: Oracle + MyBatis + Spring 에서 데이터 조회

화요일: MySQL + Hibernate + Spring 에서 데이터 조회

 

=>테이블 작성 - 샘플 데이터를 입력

=>검색조건, 검색어, 페이지번호, 데이터 개수를 파라미터로 전송해서 데이터를 가져오도록 

=>비밀번호가 아니라면 모두 소문자나 대문자로만 저장