반응형

인텐트(Intent)를 이용하여 안드로이드 시스템 액티비티들(스마트폰 개발회사가 미리 설치해 놓은 어플들)을 호출하는 예제입니다.


많은 시스템액티비티들 중에서 주로 많이 이용되고 있는 sms 문자보내기전화걸기이메일보내기 등의 어플들을 실행시켜보는 예제 소스입니다. 




메인화면                                        Dial Activity(전화번호 입력 액티비티)          Call Activity(전화걸기 액티비티)

    





SMS Activity(sms문자보내기 액티비티)      Web Page Activity(웹브라우져 액티비티)

   


 

Layout 파일

 activity_main.xml

 

<LinearLayout 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"

    android:orientation="vertical" >

    

    <Button android:id="@+id/btn_dial"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Start Dial Activity"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_call"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Start Call Activity"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_sms"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Start SMS Activity"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_webpage"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Start Web Page Activity"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_email"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Start E-Mail Activity"

        android:onClick="mOnClick"/>   


</LinearLayout>




소스파일

 

 MainActivity.java

 public class MainActivity extends Activity {


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

    //Button 클릭시에 자동으로 호출되는 callback Method...

    public void mOnClick(View v){

    

     Intent i; //시스템액티비티를 부를 Intent 참조변수

     Uri uri;  //시스템 액티비티의 세부 종류를 구분하는 Data는 Uri객체로 제공. 이를 위한 참조변수

    

     switch( v.getId() ){

    

     case R.id.btn_dial://전화번호 Dial 입력 액티비티 

    

     uri= Uri.parse("tel:01012345678"); //전화와 관련된 Data는 'Tel:'으로 시작. 이후는 전화번호

     i= new Intent(Intent.ACTION_DIAL,uri); //시스템 액티비티인 Dial Activity의 action값

     startActivity(i);//액티비티 실행   

    

     break;

    

     case R.id.btn_call://전화걸기 화면 액티비티

    

     uri= Uri.parse("tel:01012345678"); //전화와 관련된 Data는 'Tel:'으로 시작. 이후는 전화번호

     i= new Intent(Intent.ACTION_CALL,uri); //시스템 액티비티인 Dial Activity의 action값

     startActivity(i);//액티비티 실행  

     //전화걸기 액티비티는 전화요금과 관련된 것이기에 반드시 퍼미션(permission)을 받아야 합니다.

     //메니페스트 파일에 <uses-permission android:name="android.permission.CALL_PHONE"/> 추가.

    

     break;

     case R.id.btn_sms://문자보내기 액티비티

    

     uri= Uri.parse("smsto:01012345678"); //sms 문자와 관련된 Data는 'smsto:'로 시작. 이후는 문자를 받는 사람의 전화번호

     i= new Intent(Intent.ACTION_SENDTO,uri); //시스템 액티비티인 SMS문자보내기 Activity의 action값

     i.putExtra("sms_body", "Hello...");  //보낼 문자내용을 추가로 전송, key값은 반드시 'sms_body'

     startActivity(i);//액티비티 실행   

    

     break;

    

     case R.id.btn_webpage://웹브라우저 실행

    

     uri= Uri.parse("http://www.naver.com"); //웹페이지와 관련된 Data는 'http:'으로 시작. 이후는 사이트의 URL

     i= new Intent(Intent.ACTION_VIEW, uri); //시스템 액티비티인 Dial Activity의 action값

     startActivity(i);//액티비티 실행  

     //웹페이지 액티비티는 데이터요금과 관련된 것이기에 퍼미션(permission)을 받도록 합니다.

     //메니페스트 파일에 <uses-permission android:name="android.permission.INTERNET"/> 추가.

    

     break;


     case R.id.btn_email://이메일 보내기

    

     uri= Uri.parse("mailto:abcd@naver.com"); //이메일과 관련된 Data는 'mailto:'으로 시작. 이후는 이메일 주소

     i= new Intent(Intent.ACTION_SENDTO, uri); //시스템 액티비티인 Dial Activity의 action값

     startActivity(i);//액티비티 실행  

     //GenyMotion 무료버전에서는 실행오류. 실제 디바이스에서는 동작됩니다.

    

     break;   

    

     }

    

    }

}

 



 전화걸기와 네트워크를 사용하기 위해서는 사용 권한(uses-permission)이 필요합니다.

여러분 프로젝트의 메니페스트 파일에 퍼미션을 추가합니다.


 AndroidManifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.kitesoft.systemintent"

    android:versionCode="1"

    android:versionName="1.0" >


    <uses-sdk

        android:minSdkVersion="14"

        android:targetSdkVersion="16" />

    

    <!-- 전화걸기 Call 액티비티 실행 퍼미션 -->

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

    

    <!-- 네트워크 사용 액티비티들에 대한 퍼미션 -->

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


    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>


</manifest>


 



기타 시스템 액티비티들


// 구글맵 띄우기

Uri uri = Uri.parse("geo:38.899533,-77.036476");

Intent it = new Intent(Intent.Action_VIEW,uri);

startActivity(it);

 

 

// 구글 길찾기 띄우기

Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=출발지주소&daddr=도착지주소&hl=ko");

Intent it = new Intent(Intent.ACTION_VIEW,URI);

startActivity(it);  

 

// SMS/MMS 발송

Intent it = new Intent(Intent.ACTION_VIEW);   

it.putExtra("sms_body", "The SMS text");   

it.setType("vnd.android-dir/mms-sms");   

startActivity(it); 

 

// MMS 발송

Uri uri = Uri.parse("content://media/external/images/media/23");   

Intent it = new Intent(Intent.ACTION_SEND);   

it.putExtra("sms_body", "some text");   

it.putExtra(Intent.EXTRA_STREAM, uri);   

it.setType("image/png");   

startActivity(it);

 

 

// 이메일 발송 

Intent it = new Intent(Intent.ACTION_SEND);   

it.putExtra(Intent.EXTRA_EMAIL, "me@abc.com");   

it.putExtra(Intent.EXTRA_TEXT, "The email body text");   

it.setType("text/plain");   

startActivity(Intent.createChooser(it, "Choose Email Client")); 

 

 

Intent it = new Intent(Intent.ACTION_SEND);     

String[] tos = {"me@abc.com"};     

String[] ccs = {"you@abc.com"};     

it.putExtra(Intent.EXTRA_EMAIL, tos);     

it.putExtra(Intent.EXTRA_CC, ccs);     

it.putExtra(Intent.EXTRA_TEXT, "The email body text");     

it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");     

it.setType("message/rfc822");     

startActivity(Intent.createChooser(it, "Choose Email Client"));  

 

 

// extra 추가하기

Intent it = new Intent(Intent.ACTION_SEND);   

it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");   

it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/mysong.mp3");   

sendIntent.setType("audio/mp3");   

startActivity(Intent.createChooser(it, "Choose Email Client"));

 

 

// 미디어파일 플레이 하기

Intent it = new Intent(Intent.ACTION_VIEW);

Uri uri = Uri.parse("file:///sdcard/song.mp3");

it.setDataAndType(uri, "audio/mp3");

startActivity(it);

 

 

Uri uri = Uri.withAppendedPath(

  MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");   

Intent it = new Intent(Intent.ACTION_VIEW, uri);   

startActivity(it); 

 

 

// 설치 어플 제거

Uri uri = Uri.fromParts("package", strPackageName, null);   

Intent it = new Intent(Intent.ACTION_DELETE, uri);   

startActivity(it);

 

 

// APK파일을 통해 제거하기

Uri uninstallUri = Uri.fromParts("package", "xxx", null);

returnIt = new Intent(Intent.ACTION_DELETE, uninstallUri);

 

 

// APK파일 설치

Uri installUri = Uri.fromParts("package", "xxx", null);

returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);

 

 

// 음악 파일 재생

Uri playUri = Uri.parse("file:///sdcard/download/everything.mp3");

returnIt = new Intent(Intent.ACTION_VIEW, playUri);

 

 

// 첨부파일을 추가하여 메일 보내기

Intent it = new Intent(Intent.ACTION_SEND);  

it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");  

it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3");  

sendIntent.setType("audio/mp3");  

startActivity(Intent.createChooser(it, "Choose Email Client"));

 

 

// 마켓에서 어플리케이션 검색

Uri uri = Uri.parse("market://search?q=pname:pkg_name");  

Intent it = new Intent(Intent.ACTION_VIEW, uri);  

startActivity(it);  

// 패키지명은 어플리케이션의 전체 패키지명을 입력해야 합니다.

 

 

// 마켓 어플리케이션 상세 화면

Uri uri = Uri.parse("market://details?id=어플리케이션아이디");  

Intent it = new Intent(Intent.ACTION_VIEW, uri);  

startActivity(it);

// 아이디의 경우 마켓 퍼블리싱사이트의 어플을 선택후에 URL을 확인해보면 알 수 있습니다.

 

 

// 구글 검색

Intent intent = new Intent();

intent.setAction(Intent.ACTION_WEB_SEARCH);

intent.putExtra(SearchManager.QUERY,"searchString")

startActivity(intent);



[출처. 인터넷 검색(어딘지 기억이 안나네요. 문제되면 댓글달아 주세요~)]


반응형
반응형

이미지를 로딩하거나 외부데이터베이스의 데이터를 로딩하는 등의 오래걸리는 작업을 수행할 때 사용자가 앱이 멈춘것이 아니라는 것을 인지시키기 위해 ProgressDialog를 사용하게 됩니다. 이 ProgressDialog에는 두가지 모양의 Progress가 가능합니다.


1. 로딩할 데이터의 양이 얼마인지 몰라 현재 얼마만큼 작업이 수행된건지 측정할 수 없을 때 사용하는 둥근 형태의 SPINNER 스타일.

2. 로딩할 데이터의 양이 얼마인지 알고있어서 현재 작업수행정도를 표시할 수 있을 때 사용하는 막대바 형태의 HORIZONTAL 스타일.


이 중 막대바형태의 ProgressDialog 를 진행시키기 위해서는 별도의 Thread를 사용해야만 합니다. 이때 별도의 Thread에서 Progress를 진행하는 것은 어렵지 않지만 주의할 점은 화면(UI)은 오로지 main(UI) Thread 만 변경이 가능하다는 것입니다. 그렇기에 별도의 작업스레드에서 진행상황을 변경하는 명령을 수행할 수 없어서 runOnUiThread() 메소드를 호출하거나 Handler를 이용하여 UI를 변경하는 작업을 수행해야 합니다. 이는 다소 불편하게 느껴질수 있습니다. 그래서 별도의 Thread 작업과 UI 작업을 한번에 할 수 있는 AsyncTask 클래스를 이용해서 막대바 형태의 ProgressDialog를 실행시키는 앱을 소개합니다.



버튼을 누르면 0.1초(100ms) 간격으로 ProgressDialog가 진행되고 끝마치면 Toast를 표시하는 예제 앱 소스입니다.


메인화면                                       버튼클릭시 나타나는 ProgressDialog         Progress 종료후 Toast 완료 메세지























Layout 파일

 activity_main.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="${relativePackage}.${activityClass}" >


    <Button android:id="@+id/button"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Progress Dialog by AsyncTask"

        android:onClick="mOnClick"/>


</RelativeLayout>


소스파일 

 MainActivity.java

 public class MainActivity extends Activity {

ProgressDialog dialog;   //ProgressDialog 참조변수

int pos_dilaog=0;         //ProgressDialog의 진행 위치


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }

    

    //Button 클릭시에 자동으로 호출되는 callback Method...

    public void mOnClick(View v){

    

     switch( v.getId() ){

    

     case R.id.button:

    

     if(dialog!=null) return;  //ProgressDialog가 현재 보여지고 있다면 아무작업도 하지 않음.

    

     new AsyncProgressDialog().execute(100); // 다이얼로그의 max 값으로 100 전달

    

    

     break;

     }

    

    }

    

    

    //AsyncTask를 상속한 클래스선언

    //AsyncTask 클래스의 콜백 메소드들 중 doInBackground를 제외하고는 기본적으로 main(UI) Thread에서 실행됨.

    //doInBackground를 제외한 다른 메소드에서는 어디서든 UI 작업 가능 

    

    //AsyncTask< 작업스레드가 처리할 데이터 타입 , 다이얼로그의 설정 데이터 타입, 작업의 결과로 리턴할 데이터 타입 >

    //쉽게 소개하면

    //첫번재 데이터 타입은 doInBackground() 메소드의 파라미터 타입을 지정

    //두번재 파라미터의 타입은 onProgressUpdate() 메소드의 파라미터 타입을 지정

    //세번째 파라미터의 타입은 onPostExecute() 메소드의 파라미터 타입을 지정

    class AsyncProgressDialog extends AsyncTask<Integer, Integer, String>{

    

    

     //작업이 시작되기 전에 호출되는 메소드로서 일반적으로 이곳에서 ProgressDialog 객체를 생성.    

     @Override

     protected void onPreExecute() {

     // TODO Auto-generated method stub

     super.onPreExecute();

    

     dialog= new ProgressDialog(MainActivity.this); //ProgressDialog 객체 생성

     dialog.setTitle("Progress");                   //ProgressDialog 제목

     dialog.setMessage("Loading.....");             //ProgressDialog 메세지

     dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); //막대형태의 ProgressDialog 스타일 설정

     dialog.setCanceledOnTouchOutside(false); //ProgressDialog가 진행되는 동안 dialog의 바깥쪽을 눌러 종료하는 것을 금지

    

     dialog.show(); //ProgressDialog 보여주기    

     }    

    

     //excute() 메소드에 의해 실행되는 작업 스레드의 메소드  ( excute()호출 시에 전달한 값 params에서 받음 )

@Override

protected String doInBackground(Integer... params) { 

     // TODO Auto-generated method stub

while( pos_dilaog < params[0]){ //현재 ProgessDialog의 위치가 100보다 작은가? 작으면 계속 Progress

pos_dilaog++;

//ProgressDialog에 변경된 위치 적용 ..

publishProgress(pos_dilaog);  //onProgressUpdate()메소드 호출.

try {

Thread.sleep(100); //0.1초간 스레드 대기 ..예외처리 필수

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}//while

//while을 끝마치고 여기까지 오면 Progress가 종료되었다는 것을 의미함.

pos_dilaog=0; //다음 프로세스를 위해 위치 초기화

return "Complete Load"; // AsyncTask 의 작덥종료 후 "Complete Load" String 결과 리턴

}

//publishProgress()에 의해 호출되는 메소드

@Override

protected void onProgressUpdate(Integer... values) {

// TODO Auto-generated method stub

super.onProgressUpdate(values);

dialog.setProgress(values[0]); //전달받은 pos_dialog값으로 ProgressDialog에 변경된 위치 적용

}

//doInBackground() 메소드 종료 후 자동으로 호출되는 콜백 메소드

//doInBackground() 메소드로부터 리턴된 결과를 파라미터로 받음

@Override

protected void onPostExecute(String result) {

// TODO Auto-generated method stub

super.onPostExecute(result);

dialog.dismiss(); //ProgressDialog 보이지 않게 하기

dialog=null;      //참조변수 초기화

//doInBackground() 메소드로부터 리턴된 결과 "Complete Load" string Toast로 화면에 표시

Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();

}

    

    }//AsyncProgressDailog class..


}//MainActivity class..


반응형
반응형

 이전의 두 포스트는 여러분의 앱에서 다른 앱의 액티비티를 시작하는 방법에 대한 측면에 초첨이 맞추어져 있었습니다. 하지만 만약 여러분의 앱이 다른 앱에게 유용한 action 을 수행할 수 있다면, 여러분의 앱은 다른 앱으로부터 요청된 action에 응답할 수 있도록 미리 준비가 되어 있어야 합니다. 실예로, 만약 여러분이 사용자의 친구들과 메세지나 사진들을 공유할 수 있는 소셜(social) 앱을 만든다면,ACTION_SEND 인텐트를 지원하도록 하는 것이 가장 좋습니다. 그렇게 하면 사용자는 다른 앱으로부터 "공유(share)" action 을 시작되게 할 수 있으며 그 action을 수행할 수 있도록 여러분의 액티비티를 실행할 수 있습니다.


 다른 앱이 여러분의 액티비티를 시작하도록 하기 위해서는 앱의 메니페스트(manifest) 파일안에 해당하는 <activity> 요소에 대해<intent-filter> 요소를 추가할 필요가 있습니다.


 여러분의 앱이 디바이스에 설치되어있을 때, 시스템은 여러분의 인텐트 필터를 식별하며 모든 설치된 앱들에 의해 지원되는 인텐트들의 내부 카테고리에 정보를 추가합니다. 하나의 앱이 묵시적(implicit) 인텐트(Intent)로 startActivity() 또는 startActivityForResult() 메소드를 호출할 때, 시스템은 어떤 액티비티가 이 인텐트(Intent)에 응답할 수 있는지 찾게 됩니다.



인텐트 필터 추가하기(Add an Intent Filter)

---------------------------------

여러분의 액티비티가 처리할 수 있는 인텐트(Intent)들을 적절하게 정의하기 위해서는, 여러분이 추가한 인텐트 필터는 액티비티가 허용하는 action 과 data 의 타입 측면에서 가능한 명확하게 해야 합니다.


시스템은 액티비티가 가지고 있는 인텐트 필터가 아래에 있는 Intent 객체의 기준에 충족하면 주어진 Intent 를 액티비티에 전달 합니다.


Action

  수행하는 action 의 이름을 지정하는 문자열(String). 일반적으로 ACTION_SEND 또는 ACTION_VIEW 와 같은 값을 정의한 플랫폼 중 하나입니다.


 액티비티의 인텐트 필터안에 <action> 요소를 통해 지정합니다. 이 요소안에 지정하는 값은 API 상수을 대시하여 action에 대한 전체 문자열 이름으로 되어 있어야 합니다.(아래 예를 볼 수 있습니다.) 



Data

  인텐트와 함께 조합된 데이터의 세부사항


  액티비티의 인텐트 필터안에 <data> 요소를 통해 지정합니다. 이 요소안에 하나 또는 그 이상의 속성을 사용합니다. 여러분은 단지MIME타입만 저장하거나, URI 스키마 하나만 지정할 수도 있고, 또는 이것들과 허용되는 data 타입을 나타내는 다른 것들과 조합하여 지정할 수 있습니다.


Note : 만약 여러분이 Uri 데이터에 대해 세부사항을 선언할 필요가 없다면( 여러분의 액티비티가 URI를 대신해서 다른 종류의 "extra" 데이터를 처리할 때와 같이), 여러분은 text/plain 또는 image/jpeg 과 같이 여러분의 액티비티가 처리할 data의 타입을 선언하기 위해 오직android:mimeType 속성만을 지정하셔야만 합니다.



Category

   보통 사용자 제스쳐 또는 시작된 것으로 부터의 위치에 관련된 Intent 를 처리하는 액티비티를 특정하기 위해 추가적인 방법을 제공합니다. 시스템에 의해 지원되어지는 서로 다른 다양한 카테고리가 있습니다. 하지만 대부분은 드물게 사용되어 집니다. 어쨋든, 모든 묵시적(implicit) 인텐트(Intent)들은 디폴트로 CAREGORY_DEFAULT로 정의되어 집니다.


 액티비티의 인텐트 필터안에 <category> 요소를 통해 지정합니다.



인텐트 필터안에서, 여러분은 <intent-filter>요소에 자리한 해당 XML 요소와 그들 각각을 선언하여 액티비티가 허용하는 기준을 선언할 수 있습니다. 


예를들어 보겠습니다. 아래는 text 또는 image의 data 타입일 때 ACTION_SEND 인텐트를 처리하는 인텐트 필터를 가진 액티비티의 예가 있습니다.

 <activity android:name="ShareActivity">

    <intent-filter>

        <action android:name="android.intent.action.SEND"/>

        <category android:name="android.intent.category.DEFAULT"/>

        <data android:mimeType="text/plain"/>

        <data android:mimeType="image/*"/>

    </intent-filter>

</activity>


각각의 들어오는 인텐트는 오직 하나의 action 과 하나의 data 타입을 지정합니다. 하지만 각각의 <intent-filter>요소안에는 <action>,<category>, 그리고 <data> 요소의 인스턴스를 여러개 선언해도 OK 입니다.


만약 action과 data로 된 2개의 쌍이 상호 배타적으로 동작한다면, 여러분은 data 타입에 부합되는 action 을 받아들일 수 있도록 지정하기 위해 별도로 구분된 인텐트 필터를 만들어야만 합니다.


예를 들어, 여러분의 액티비티가 ACTION_SEND 와 ACTION_SENDTO 인텐트 모두에서 text 와 image 를 모두 처리하도록 처리할 수 있도록 지원할 수 있습니다. 이런 경우, 여러분은 반드시 2개의 action 에 대해 별개의 구분된 인텐트 필터(Intent filter)를 정의해야만 합니다. 왜냐하면, ACTION_SENDTO 인텐트는 send 와 sendto URI 스키마(scheme)의 수신자 주소를 지정하는데 반드시 Uri data 를 사용해야만 하기 때문입니다. 예:

 <activity android:name="ShareActivity">

    <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->

    <intent-filter>

        <action android:name="android.intent.action.SENDTO"/>

        <category android:name="android.intent.category.DEFAULT"/>

        <data android:scheme="sms" />

        <data android:scheme="smsto" />

    </intent-filter>


    <!-- filter for sending text or images; accepts SEND action and text or image data -->

    <intent-filter>

        <action android:name="android.intent.action.SEND"/>

        <category android:name="android.intent.category.DEFAULT"/>

        <data android:mimeType="image/*"/>

        <data android:mimeType="text/plain"/>

    </intent-filter>

</activity>

Note : 묵시적(implicit) 인텐트를 수신하기 위해서는, 반드시 인텐트 필터(Intent filter)안에 category 로 CATEGORY_DEFAULT 를 포함해야만 합니다. 카테고리에 CATEGORY_DEFAULT 로 선언함으로서 startActivity()  startActivityForResult() 메소드들은 선언된 인텐트들을 처리합니다. 만약 여러분이 인텐트필터안에 이 것을 선언하지 않는다면 묵시적 인텐트는 여러분의 액티비티를 처리하지 않을 겁니다.


소셜(social) 공유를 작업을 수행하기 위한 ACTION_SEND 인텐트를 전송하고 수신받는 것에 대한 추가 정보를 알고싶다면 개발자 사이트의 Receiving Simple Data from Other Apps 를 참조하시기 바랍니다.




액티비티에서 Intent 처리하기 (Handle the Intent in Your Activity)

--------------------------------------------------

여러분의 액티비티가 가지고 있는 action 이 무엇인지 결정하기 위해서 여러분은 액티비티를 시작하는데 사용한 Intent 객체를 읽을 수 있습니다.


여러분의 액티비티가 시작됨으로서, 액티비트를 실행시킨 Intent 객체를 복구하기 위해 getIntent() 메소드를 호출합니다. 여러분은 액티비티의 라이프사이클(lifecycle) 동안 어떤 순간에서든 이 작업을 할 수 있습니다. 하지만 일반적으로는 onCreate() 또는 onStart() 와 같이 콜백메소드 중에 가장 빨리 호출되는 메소드안에서 이 작업을 하는 것이 좋습니다.


예:

 @Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);


    setContentView(R.layout.main);


    // Get the intent that started this activity

    Intent intent = getIntent();

    Uri data = intent.getData();


    // Figure out what to do based on the intent type

    if (intent.getType().indexOf("image/") != -1) {

        // Handle intents with image data ...

    } else if (intent.getType().equals("text/plain")) {

        // Handle intents with text ...

    }

}



결과 리턴하기(Return a Result)

------------------------

만약 여러분의 액티비티를 발동시킨 액티비티로 결과(result)를 리턴하길 원한다면, 간단하게 setResult() 메소드를 호출하여 결과코드(result code) 와 결과 인텐트(result Intent) 를 지정할 수 있습니다. 액티비티가 여러분의 명령을 끝마쳤거나 사용자가 원래의 액티비티로 되돌아가길 원할 때, finish() 메소들를 호출하여 여러분의 액티비티를 정지(그리고 파괴Destroy) 시킵니다. 예:

 // Create intent to deliver some kind of result data

 Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");

 setResult(Activity.RESULT_OK, result);

 finish();


여러분은 항상 결과(result)와 함께 결과코드(result code)를 지정해야만 합니다. 일반적으로 RESULT_OK 이거나 RESULT_CANCEL 을 사용합니다. 여러분은 필요하다면 Intent 안에 data를 추가적으로 제공할 수 있습니다.


Note : 결과(result)는 기본적으로 RESULT_CANCEL로 설정되어 있습니다. 그래서 action 을 완료하기 전에 사용자가 'Back' 버튼을 눌러서 결과(result)를 설정하기 전이라면 원래의 오리지널 액티비티는 "canceled" 결과를 받게 됩니다.


만약 다양한 결과(result) 옵션중 하나을 나타내는 정수형(integer) 결과를 리턴할 필요가 있다면, 여러분은 0 보다 큰 값으로 결과코드(result code)를 설정할 수 있습니다. 만약 여러분이 하나의 정수형(integer)를 전달하기 위해 result code 를 사용한다면 여러분은 Intent를 포함할 필요가 없습니다. 여러분은 setResult() 메소드를 호출하며 오직 result code 만 전달할 수 잇습니다. 예:


 setResult(RESULT_COLOR_RED);

 finish();


Note : 여러분은 여러분의 액티비티가 startActivity() 로 실행된건지 startActivityForResult()로 실행된건지 체크할 필요가 없습니다. 인텐트에 의해 시작된 여러분의 액티비티가 결과(result)를 기대한다면 간단하게 setResult()를 호출합니다. 만약 원래의 액티비티가startActivityforResult()를 호출햇다면, 시스템은 setResult()로 제공한 결과(result)를 전달할겁니다. 만약 그렇지 않다면 그 result는 무시될 겁니다.

반응형
반응형

 다른 액티비티를 실행하는 것은 한가지 방법만 있는 것은 아닙니다. 여러분은 다른 액티비티를 실행하고 그 액티비티로부터 결과를 되돌려 받을 수도 있습니다. 결과를 받기 위해서는 startActivity() 를 대신해서 startActivityForResult() 메소드로 다른 액티비티를 실행해야 합니다.


 예를 들어보면, 여러분의 앱이 카메라 앱을 실행해서 촬영된 사진을 결과로 받을 수 있습니다. 또는 사용자가 연락처를 선택하기 위해People App( 예전 버전에서의 연락처 앱)을 실행하고 그 결과로 연락처의 상세내역을 받도록 할 수도 있습니다.


 물론, 응답하도록 되어 있는 액티비티는 반드시 결과(Result)를 리턴하도록 디자인 되어 있어야 합니다. 그렇게 되어 있을 때, 그 앱은 또다른 Intent 객체를 이용해서 결과를 보내줍니다. 여러분의 액티비티는 리턴된 결과를 onActivityResult() 콜백(callback) 메소드를 통해 받을 수 있습니다.


Note : 여러분은 startActivityForResult() 메소드를 호출할 때 명시적(explicit) 인텐트 또는 묵시적(implicit) 인텐트를 사용할 수 있습니다. 결과를 받기 위해 여러분의 앱안에 있는 액티비티들 중 하나를 실행할 경우에는 여러분이 기대하는 결과를 받을 수 있도록 보장하기 위해 명시적(explicit) 인텐트(intent)를 사용하는 것이 좋습니다.




액티비티 시작 (Start the Activity)

---------------------------

 결과(result)를 받기 위해 액티비티를 실행할 때 사용하는 Intent 객체는 특별할 것이 없습니다. 다만 startActivityForResult() 메소들 호출할 때 정수형(integer) 매개변수를 추가적으로 전달할 필요가 있습니다.


이 정수형(integer) 인자는 "요청코드(request code)"로서 여러분의 요청을 식별하는 용도입니다. 여러분이 결과(result) 인텐트(Intent)를 받을 때, onActivityResult() 콜백(callback) 메소드는 여러분이 결과 액티비티를 호출할 때 전달했던 것과 같은 요청코드(request code)를 제공합니다. 이로 인해서 여러분의 앱은 그 결과가 적절한 것인지 확인할 수 있고 그 것을 어떻게 처리할 것인지 결정할 수 있습니다.


아래는 사용자가 연락처를 선택하고 얻어오도록 하는 액티비티를 실행하는 방법의 예제입니다.

 static final int PICK_CONTACT_REQUEST = 1;  // The request code

 ...

 private void pickContact() {

     Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));

     pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers

     startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);

 }

 



결과 받기 (Receive the Result)

-------------------------

 사용자가 다음 액티비티을 수행하고 다시 현재 액티비티로 되돌아 왔을 때, 시스템은 여러분의 액티비티의 onActivityResult() 메소드를 자동으로 호출합니다. 이 메소드는 3개의 인자(argument)를 포함하고 있습니다.


° startActivityForResult() 메소드를 호출할 때 전달했던 요청코드(request code)

° 두번째 액티비티에 의해 지정되어지는 결과코드(result code). 이 코드는 명령이 성공했을 때의 RESULT_OK 와 어떤 이유에 의해 명령이 실패하거나 사용자가 뒤로가기(back)을 통해 되돌아 올때의 RESULT_CANCELED 가 있을 수 있습니다.

° 결과(result) 데이터를 가지고 있는 Intent 객체


아래는 "연락처 얻기" 인텐트(Intent)에 대한 결과를 처리할 수 있는 방법에 대한 예제입니다.

 @Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    // Check which request we're responding to

    if (requestCode == PICK_CONTACT_REQUEST) {

        // Make sure the request was successful

        if (resultCode == RESULT_OK) {

            // The user picked a contact.

            // The Intent's data Uri identifies which contact was selected.


            // Do something with the contact here (bigger example below)

        }

    }

}

이 예제에서, Android의 연락처 앱(Contract 또는 People)에 의해 리턴된 결과(resultIntent 는 사용자가 선택한 연락처를 식별하는Content  Uri 객체를 제공합니다.


 이 결과(Result)를 성공적으로 처리하기 위해서는, 여러분은 반드시 결과가 될 Intent 의 형식이 무엇인지 이해하고 있어야 합니다. 여러분의 앱이 가지고 있는 액티비티들 중 하나로부터 결과를 되돌려 받을 때는 그 결과를 이해하는 것이 쉽습니다. 앱들은 Android 플랫폼이 특정 결과 데이터를 기대할 수있는 그들의 API 들을 포함하고 있습니다. 예를 들어, 연락처 앱(People) 앱은 항상 선택된 연락처를 식별할 수 있는 Content  Uri 를 결과로 돌려줍니다. 그리고 카메라 앱 같은 경우에는 Intent의 extra "data" 안에 Bitmap 객체를 리턴합니다(개발자 사이트의 Capturing Photos 에 대한 클래스를 참고하세요).



추가설명 : 연락처 데이터 읽기 ( Bonus : Read the contact data )

-----------------------------------------------------

 People 앱으로부터 결과를 받기 위한 방법을 보여주는 위의 코드는 결과(result)로부터 실제 데이터를 읽어들이는 방법에 대해서는 세부적으로서 소개하고 있지는 않습니다. 왜냐하면, 이 작업을 하려면 Content Provider 에대한 좀더 향샹된 설명이 요구되기 때문입니다. 어쨋든, 만약 여러분이 이에 대해 관심이 있다면, 아래의 코드를 보시기 바랍니다. 아래의 몇가지 추가된 코드는 선택된 연락처로부터 전화번호를 얻기위해 결과 데이터(result data)를 요청하는 방법을 보여주고 있습니다.

 @Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    // Check which request it is that we're responding to

    if (requestCode == PICK_CONTACT_REQUEST) {

        // Make sure the request was successful

        if (resultCode == RESULT_OK) {

            // Get the URI that points to the selected contact

            Uri contactUri = data.getData();

            // We only need the NUMBER column, because there will be only one row in the result

            String[] projection = {Phone.NUMBER};


            // Perform the query on the contact to get the NUMBER column

            // We don't need a selection or sort order (there's only one result for the given URI)

            // CAUTION: The query() method should be called from a separate thread to avoid blocking

            // your app's UI thread. (For simplicity of the sample, this code doesn't do that.)

            // Consider using CursorLoader to perform the query.

            Cursor cursor = getContentResolver()

                    .query(contactUri, projection, null, null, null);

            cursor.moveToFirst();


            // Retrieve the phone number from the NUMBER column

            int column = cursor.getColumnIndex(Phone.NUMBER);

            String number = cursor.getString(column);


            // Do something with the phone number...

        }

    }

}


Note : Android 2.3( API level 9) 이전에는 Contacts Provider 의 요청을 수행하기위해(위에 보여젔던 것과 같이) 여러분의 앱에READ_CONTACTS 퍼미션(permission) 을 선언하도록 요구했었습니다. 어쨋든, Android 2.3 부터는 Contacts/People 에 대해 여러분이 하나의 결과를 리턴할 때 Contacts Provider 로부터 읽어들일 수 있는 임시 퍼미션을 여러분의 앱에 허가합니다. 임시 퍼미션은 오직 요청된 특정 연락처(contact) 만을 제공합니다. 그래서 여러분은 READ_CONTACTS 퍼미션 선언 없이 intent 의 Uri에 의해 지정된 하나 이상의 연락처를 요청할 수 없습니다.

반응형
반응형

이 앱은 자바언어를 학습하실 때 해봤던 소켓통신을 안드로이드에 적용한 예제입니다. 

소켓통신으로 단말기 끼리 1:1 채팅을 하는 예제로 서로 간단한 메세지를 주고받는 정도만 가능합니다.

정식 채팅 앱은 나중에 따로 포스트 할 예정입니다.


 한 대는 서버로 동작하고 한 대는 클라이언트가 되는 앱입니다. 서로 역할을 바꿀 수도 있습니다.


우선 단말기 간 테스트를 할 수 있도록 Genymotion의 에뮬레이터를 2대 준비합니다. 



프로그램을 Run 시킬때 아래처럼 연결되어 있는 디바이스가 2개 보이셔야 합니다.(각 단말기의 IP번호도 확인가능합니다.)




아래 소개될 소스코드를 2번 Run 해서 각각의 에뮬레이터에 동일한 앱을 설치합니다.


서버 (ip주소 : 192.168.0.101)

 

























클라이언트(ip주소 : 192.168.0.102)

 

























동일한 앱을 설치한 2대의 에뮬레이터를 각 각 서버와 클라이언트로 접속합니다.

그러면 서버 액티비티 화면과 클라이언트 액티비티 화면으로 전환합니다.


  서버

















 클라이언트




 서버와 클라이언트가 준비되면 먼저 서버쪽 부터 START 버튼을 클릭해서 서버를 구축합니다. 에러가 발생하지 않으면 잘 만들어진 겁니다. 서버에 에러가 없었다면 클라이언트쪽의 CONNECT SERVER 버튼을 클릭해서 서버에 접속합니다. 아래의 EditText 의 IP주소를 변경해서 서버의 역할을 바꿀 수도 있습니다.


서버 구축                                                서버에 접속

       






 에러가 없다면 접속이 잘 된 것입니다. 이제 각 각의 EditText View에 원하는 메세지를 작성하고 SEND 버튼을 클릭하면 서로에게 메세지가 전달되는 것을 확인할 수 있습니다.


 



 


















            








































Main 화면 Layout 파일

 activity_main.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="${relativePackage}.${activityClass}" >


    <Button

        android:id="@+id/btn_server"

        android:layout_width="200dp"

        android:layout_height="50dp"

        android:layout_centerHorizontal="true"

        android:layout_marginTop="200dp"

        android:gravity="center"

        android:text="Server"

        android:textSize="25sp"

        android:onClick="mOnClick"/>

    

    <Button

        android:id="@+id/btn_client"

        android:layout_width="200dp"

        android:layout_height="50dp"

        android:layout_centerHorizontal="true"

        android:layout_alignParentBottom="true"

        android:layout_marginBottom="200dp"

        android:gravity="center"

        android:text="Client"

        android:textSize="25sp"

        android:onClick="mOnClick"/>


</RelativeLayout>



Main 화면 소스파일

 

 MainActivity.java

public class MainActivity extends Activity {


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

//Button을 클릭했을 때 호출되는 callback 메소드

public void mOnClick(View v){

Intent i;

switch(v.getId()){

case R.id.btn_server: //서버 화면

i= new Intent(this, ServerActivity.class);

startActivity(i);

break;

case R.id.btn_client: //클라이언트 화면

i= new Intent(this, ClientActivity.class);

startActivity(i);

break;

}

}

}






Server 화면 Layout 파일

 activity_server.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"

    android:gravity="start"

    tools:context="${relativePackage}.${activityClass}" >


    <Button android:id="@+id/btn_start_server"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_marginTop="20dp"

        android:text="START"

        android:textStyle="bold"

        android:onClick="mOnClick"/>

    

    <TextView android:id="@+id/label_message_from_client" 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_below="@id/btn_start_server"

        android:gravity="center"

        android:text="The Message from the client"

        android:textSize="20sp"

        android:textStyle="bold"

        android:textColor="#000000"

        android:layout_marginTop="50dp"/>

    

    <TextView android:id="@+id/text_massage_from_client"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_below="@id/label_message_from_client"

        android:text="text"

        android:gravity="center"

        android:layout_marginTop="20dp"/>

    

    <TextView android:id="@+id/label_message_to_client" 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_below="@id/text_massage_from_client"

        android:gravity="center"

        android:text="The Message to the client"

        android:textSize="20sp"

        android:textStyle="bold"

        android:textColor="#000000"

        android:layout_marginTop="50dp"/>

    

    <EditText android:id="@+id/edit_message_to_client"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_below="@id/label_message_to_client"

        android:layout_marginTop="20sp"

        android:gravity="center"

        android:hint="Enter the message"/>

    

    <Button android:id="@+id/btn_send_server"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_below="@id/edit_message_to_client"

        android:layout_marginTop="50dp"

        android:text="SEND"

        android:onClick="mOnClick"/>


</RelativeLayout>



Server 화면 소스파일

 

 ServerActivity.java

public class ServerActivity extends Activity {

static final int PORT=10001;

ServerSocket serversocket;

Socket socket;

DataInputStream is;

DataOutputStream os;

TextView text_msg; //클라이언트로부터 받을 메세지를 표시하는 TextView

EditText edit_msg; //클라이언트로 전송할 메세지를 작성하는 EditText

String msg="";

boolean isConnected=true;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_server);

text_msg= (TextView)findViewById(R.id.text_massage_from_client);

edit_msg= (EditText)findViewById(R.id.edit_message_to_client);

}

//Button 클릭시 자동으로 호출되는 callback 메소드

public void mOnClick(View v){

switch(v.getId()){

case R.id.btn_start_server: //채팅 서버 구축 및 클라이언트로 부터 메세지 받기

//Android API14버전이상 부터 네트워크 작업은 무조건 별도의 Thread에서 실행 해야함.

new Thread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

try {

//서버소켓 생성.

serversocket=new ServerSocket(PORT);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

try {

      //서버에 접속하는 클라이언트 소켓 얻어오기(클라이언트가 접속하면 클라이언트 소켓 리턴)

socket= serversocket.accept(); //서버는 클라이언트가 접속할 때까지 여기서 대기...

//여기 까지 왔다는 것은 클라이언트가 접속했다는 것을 의미하므로

//클라이언트와 데이터를 주고 받기 위한 통로구축..

is= new DataInputStream(socket.getInputStream()); //클라이언트로 부터 메세지를 받기 위한 통로

os= new DataOutputStream(socket.getOutputStream()); //클라이언트로 메세지를 보내기 위한 통로

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//클라이언트가 접속을 끊을 때까지 무한반복하면서 클라이언트의 메세지 수신

while(isConnected){

try {

​                                     //클라이언트로 부터 메세지가 전송되면 이를 UTF형식으로 읽어서 String 으로 리턴​

msg=is.readUTF(); 

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//클라이언트로부터 읽어들인 메시지msg를 TextView에 출력..

//안드로이드는 오직 main Thread 만이 UI를 변경할 수 있기에

//네트워크 작업을 하는 이 Thread에서는 TextView의 글씨를 직접 변경할 수 없음.

//runOnUiThread()는 별도의 Thread가 main Thread에게 UI 작업을 요청하는 메소드임.

runOnUiThread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

text_msg.setText(msg);

}

});

/////////////////////////////////////////////////////////////////////////////

}//while..

}//run method...

}).start(); //Thread 실행..

break;

case R.id.btn_send_server: // 클라이언트로 메세지 전송하기.

if(os==null) return; //클라이언트와 연결되어 있지 않다면 전송불가..

//네트워크 작업이므로 Thread 생성

new Thread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

//클라이언트로 보낼 메세지 EditText로 부터 얻어오기

String msg= edit_msg.getText().toString();

try {

os.writeUTF(msg); //클라이언트로 메세지 보내기.UTF 방식으로(한글 전송가능...)

os.flush();   //다음 메세지 전송을 위해 연결통로의 버퍼를 지워주는 메소드..    

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}).start(); //Thread 실행..

break;

}

}

}

   



 

Client 화면 Layout 파일

 activity_client.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="${relativePackage}.${activityClass}" >


    <Button android:id="@+id/btn_connectserver"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_marginTop="20dp"

        android:text="CONNECT SERVER"

        android:textStyle="bold"

        android:onClick="mOnClick"/>

    

    <TextView android:id="@+id/label_message_from_server" 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_below="@id/btn_connectserver"

        android:gravity="center"

        android:text="The Message from the server"

        android:textSize="20sp"

        android:textStyle="bold"

        android:textColor="#000000"

        android:layout_marginTop="50dp"/>

    

    <TextView android:id="@+id/text_massage_from_server"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_below="@id/label_message_from_server"

        android:text="text"

        android:gravity="center"

        android:layout_marginTop="20dp"/>

    

    <TextView android:id="@+id/label_message_to_server" 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_below="@id/text_massage_from_server"

        android:gravity="center"

        android:text="The Message to the server"

        android:textSize="20sp"

        android:textStyle="bold"

        android:textColor="#000000"

        android:layout_marginTop="50dp"/>

    

    <EditText android:id="@+id/edit_message_to_server"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_below="@id/label_message_to_server"

        android:layout_marginTop="20sp"

        android:gravity="center"

        android:hint="Enter the message"/>

    

    <Button android:id="@+id/btn_send_client"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerHorizontal="true"

        android:layout_below="@id/edit_message_to_server"

        android:layout_marginTop="50dp"

        android:text="SEND"

        android:onClick="mOnClick"/>

    

    <EditText android:id="@+id/edit_addressofserver"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:hint="Enter the address of server"

        android:layout_alignParentBottom="true"

        android:layout_marginBottom="20dp"

        android:gravity="center"/>


</RelativeLayout>



Client 화면 소스파일

 

 ClientActivity.java

public class ClientActivity extends Activity {

private static final int PORT = 10001; //서버에서 설정한 PORT 번호

String ip="192.168.56.101"; //서버 단말기의 IP주소..

//본 예제는 Genymotion 에뮬레이터 2대로 테스한 예제입니다.

//Genymotion을 실행하면 각 에뮬레이터의 IP를 확인할 수 있습니다.

Socket socket;     //클라이언트의 소켓

DataInputStream is;

DataOutputStream os;

TextView text_msg;  //서버로 부터 받은 메세지를 보여주는 TextView

EditText edit_msg;  //서버로 전송할 메세지를 작성하는 EditText

EditText edit_ip;   //서버의 IP를 작성할 수 있는 EditText

String msg="";

boolean isConnected=true;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_client);

text_msg=(TextView)findViewById(R.id.text_massage_from_server);

edit_msg=(EditText)findViewById(R.id.edit_message_to_server);

edit_ip=(EditText)findViewById(R.id.edit_addressofserver);

edit_ip.setText(ip);

}

//Button 클릭시 자동으로 호출되는 callback 메소드

public void mOnClick(View v){

switch(v.getId()){

case R.id.btn_connectserver://서버에 접속하고 서버로 부터 메세지 수신하기

//Android API14버전이상 부터 네트워크 작업은 무조건 별도의 Thread에서 실행 해야함.

new Thread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

try {

ip= edit_ip.getText().toString();//IP 주소가 작성되어 있는 EditText에서 서버 IP 얻어오기

//서버와 연결하는 소켓 생성..

socket= new Socket(InetAddress.getByName(ip), PORT );

//여기까지 왔다는 것을 예외가 발생하지 않았다는 것이므로 소켓 연결 성공..

//서버와 메세지를 주고받을 통로 구축

is=new DataInputStream(socket.getInputStream());

os=new DataOutputStream(socket.getOutputStream());

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//서버와 접속이 끊길 때까지 무한반복하면서 서버의 메세지 수신

while(true){

try {

msg= is.readUTF(); //서버 부터 메세지가 전송되면 이를 UTF형식으로 읽어서 String 으로 리

//서버로부터 읽어들인 메시지msg를 TextView에 출력..

//안드로이드는 오직 main Thread 만이 UI를 변경할 수 있기에

//네트워크 작업을 하는 이 Thread에서는 TextView의 글씨를 직접 변경할 수 없음.

//runOnUiThread()는 별도의 Thread가 main Thread에게 UI 작업을 요청하는 메소드임.

runOnUiThread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

text_msg.setText(msg);

}

});

//////////////////////////////////////////////////////////////////////////

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}//while

}//run method...

}).start();//Thread 실행..

break;

case R.id.btn_send_client: //서버로 메세지 전송하기...

if(os==null) return;   //서버와 연결되어 있지 않다면 전송불가..

//네트워크 작업이므로 Thread 생성

new Thread(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

//서버로 보낼 메세지 EditText로 부터 얻어오기

String msg= edit_msg.getText().toString();

try {

os.writeUTF(msg);  //서버로 메세지 보내기.UTF 방식으로(한글 전송가능...)

os.flush();        //다음 메세지 전송을 위해 연결통로의 버퍼를 지워주는 메소드..

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}//run method..

}).start(); //Thread 실행..

break;

}

}

}

   



네트워크 사용하기 위해서는 사용 권한(uses-permission)이 필요합니다.

여러분 프로젝트의 메니페스트 파일에 퍼미션을 추가합니다.


 AndroidManifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.kitesoft.socket"

    android:versionCode="1"

    android:versionName="1.0" >


    <uses-sdk

        android:minSdkVersion="8"

        android:targetSdkVersion="16" />

    

    <!-- 네트워크 작업을 위한 permission -->

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


    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

        

        <activity

            android:name=".ServerActivity"

            android:label="@string/title_activity_server" >

        </activity>

        

        <activity

            android:name=".ClientActivity"

            android:label="@string/title_activity_client" >

        </activity>

        

    </application>


</manifest>

 

반응형
반응형

인텐트를 받을 앱이 있는지 확인하기(Verify There is an App to Receive the Intent)

-------------------------------------------------------------------

비록 안드로이드 플랫폼이 여러분의 인텐트에 대해 내장된 앱(전화걸기, 이메일, 또는 달력 앱 등)중에서 하나가 확실하게 사용할 수 있다는 것을 보증하더라도, 여러분은 항상 인텐트를 발동하기 전에 인텐트에 대응하는 앱이 있는지 확인 단계를 포함하시는 것이 좋습니다.


Caution : 만약 인텐트가 발동 되었는데 디바이스(device)에 인텐트를 처리할 수 있는 앱이 없다면 여러분의 앱은 충돌을 일으킵니다.


 인텐트에 반응할 수 있는 액티비티가 있는지 확인하기 위해서 여러분은 queryIntentActivities() 메소드를 호출하여 그 Intent 를 처리하는 능력을 가진 액티비티들의 리스트(list)를 얻을 수 있습니다. 만약 이 메소드로부터 리턴된 List 인스턴스가 비어있지 않다면, 여러분은 안전하게 인텐트를 사용할 수 있습니다. 예:

 PackageManager packageManager = getPackageManager();

 List activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);

 boolean isIntentSafe = activities.size() > 0;


 만약 isIntentSafe 변수의 값이 true 면, 인텐트에 응답할 앱이 최소한 1개 이상이라는 것입니다. 만약 이 값이 false 라면, 그 인텐트를 처리할 어떤 앱도 없다는 것입니다.


Note :  사용자가 인텐트를 사용려고 하기 전에 이를 사용하는 기능을 해제 해야할 필요가 있는 경우에는 여러분의 액티비티가 처음 시작될 때 이 체크를 수행 해야 합니다. 만약 여러분이 인텐트를 처리할 특정 앱을 알고 있다면, 여러분은 또한 앱을 다운로드 하기 위한 사용자에 대해 링크(link)를 제공할 수 있습니다.( 개발자 사이트의 "link to your product on Google Play" 의 방법을 참고하세요)




Intent 로 Activity 실행하기 (Start an Activity with the Intent)

------------------------------------------------

 여러분이 Intent 를 만들고 extra 정보를 설정했다면, startActivity() 메소들 호출해서 시스템에게 인텐트를 전송합니다. 만약 시스템이 그 인텐트를 처리 할 수 있는 액티비티들이 하나 이상으로 식별한다면, 그림 1에 보여진 것 처럼 사용자에게 어떤 앱을 사용할 것이지 선택하도록 하기위한 다이얼로그(dialog)를 표시합니다. 만약 인텐트를 처리할 수 있는 액티비티가 오직 하나라면 시스템은 즉시 그 앱을 실행합니다.

  startActivity(intent);



그림 1. 인텐트를 처리할 수 있는 앱이 하나 이상일때 나타나는 선택 다이얼로그의 예.



 아래는 지도보기를 위한 인텐트를 만드는 방법과 그 인텐트를 처리할 수 있는 앱이 존재하는지 확인하고 난후에 그 앱을 실행하는 완성된 예제입니다.

 // Build the intent

 Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");

 Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);


 // Verify it resolves

 PackageManager packageManager = getPackageManager();

 List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);

 boolean isIntentSafe = activities.size() > 0;


 // Start an activity if it's safe

 if (isIntentSafe) {

     startActivity(mapIntent);

 }




Chooser 앱 보여주기 (Show an App Chooser)

-------------------------------------

  위에서 설명했듯이 여러분이 startActivity()에 Intent를 전달해서 하나의 액티비티를 시작할 때 그 인텐트에 응답하는 앱이 하나 이상이면 어떤 앱을 사용할 지 선택하도록 하는데 이 때 여러분은 기본으로 실행될 앱을 선택할 수 있습니다.( 그림 1의 다이얼로그에서 아래 바닥에 있는 체크박스(checkbox)를 선택함으로서). 이 것은 웹 페이지(web page)를 열 때(사용자가 가능한 하나의 웹브라우져(web browser)를 사용한다고 가정하고) 또는 사진 촬영(사용자가 하나의 카메라를 선호한다고 가정하고)과 같이 , 하나의 액션을 수행할 때 사용자가 일반적으로 매 순간 같은 앱을 사용하도록 한다면 매우 좋은 방법입니다.


어쨋든, 만약 수행되어지는 action이 여러 앱들에서 처리되어지고 사용자가 매 순간 다른 앱을 선호할 수 도 있다면( 사용자가 하나의 아이템을 여러 앱들을 통해 공유할 수도 있는 "공유(share)" action 같은) 여러분은 그림 2에서 보여진 Chooser 다이얼로그(Chooser Dialog)를 명시적(explicit)으로 보이도록 하는 것이 좋습니다. Chooser Dialog는 사용자가 매 순간 action에 대해 어떤 앱을 사용할 지 선택할 수 있도록 합니다. (이 다이얼로그는 사용자가 action에 대한 기본 앱을 선택할 수 없습니다.)



 















그림 2. A Chooser Dialog



 Chooser를 보기 위해서는 createChooser() 메소드를 사용하여 Intent 만들고 startActivity()에 이 인텐트를 전달해야 합니다.

예:

 Intent intent = new Intent(Intent.ACTION_SEND);

 ...


 // Always use string resources for UI text.

 // This says something like "Share this photo with"

 String title = getResources().getString(R.string.chooser_title);

 // Create intent to show chooser

 Intent chooser = Intent.createChooser(intent, title);


 // Verify the intent will resolve to at least one activity

 if (intent.resolveActivity(getPackageManager()) != null) {

     startActivity(chooser);

 }


 Chooser는 createChooser() 메소드에 전달된 인텐트에 응답하는 앱의 목록을 다이얼로그에 표시합니다. 그리고 Dialog의 제목에는 제공되어진 Text 를 사용합니다.

반응형
반응형

 안드로이드의 가장 중요한 기능중 하나는 실행하기를 원하는 "action" 을 기반으로 다른 앱으로 사용자를 보내는 능력(다른 앱을 실행하는 능력)입니다. 예를 들어, 만약 여러분 앱이 지도에 보여주고 싶은 업체 주소를 가지고있는데, 여러분의 앱안에는 지도를 보여주는 액티비티가 없다면 이를 대신하여 여러분은 Intent를 사용하여 맵에 주소를 보여주도록 요청할 수 있습니다. 그렇게 하면 안드로이드 시스템은 맵 위에 해당 주소를 보여줄 수 있는 앱을 시작합니다.


 이 게시판의 첫번째 포스트 "첫번째 앱 만들기(Building Your First App)"에서 설명했듯이, 여러분은 여러분의 앱이 가지고 있는 액티비티들 사이를 이동하기 위해서는 Intent 를 사용해야만 합니다. 여러분은 일반적으로 여러분이 시작하기를 원하는 확실한 컴포넌트의 클래스 이름을 정의한 명시적(explicit) 인텐트를 통해 이 작업을 합니다. 어쨋든, 여러분이 "지도보기"와 같이 하나의 action을 수행하는 별도의 다른 앱을 실행하고자 한다면 여러분은 반드시 암시적(implicit) 인텐트를 사용해야만 합니다.


 이번 시간에는 여러분에게 특정 action 에 대한 암시적 인텐트를 만드는 방법과 다른 앱안에서 이 액션을 수행하는 액티비티를 실행하도록 이 인텐트를 사용하는 방법을 소개하도록 하겠습니다. 



암시적 인텐트 만들기 (Build an Implicit Intent)

--------------------------------------

암시적 인텐트는 시작될 컴포넌트의 클래스 이름을 선언하지 않습니다. 대신 실행 할 action을 선언합니다. action 은 무엇인가를 보여주고, 만들고, 전송하고, 얻어오는 등의 여러분이 동작하길 원하는 것을 지정합니다. Intent는 또한 여러분이 지도에 보여주길 원하는 주소, 또는 전송하고 싶은 이메일(email) 같은 action 과 조합된 데이터를 포함하고 있습니다. 이 데이터는 여러분이 만들기를 원하는 Intent에 따라, Uri 객체가 될수도 있고, 또 다른 다양한 데이터 타입 중 하나일 수도 있으며 또는 어떤 데이터도 필요하지 않을 수도있습니다.


만약 여러분의 데이터가 Uri 타입이라면, action 과 data 를 정의하는데 사용할 수 있는 간단한 Intent() 생성자(constructor)가 있습니다.


예들 들어보겠습니다. 아래는 전화 번호를 지정한 Uri 데이터를 사용하여 전화 걸기를 초기화하는 Intent를 만드는 방법을 보여주고 있습니다.

 Uri number = Uri.parse("tel:5551234");

 Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

여러분의 앱이 startActivity() 메소드를 호출해서 이 인텐트를 발동시기면, 전화 앱이 시작되고 이 앱은 주어진 전화번호로 초기화합니다.


아래는 action 과 Uri data 쌍의 다른 인텐트들의 예입니다.


지도보기:

 // Map point based on address

 Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");

 // Or map point based on latitude/longitude

 // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level

 Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);



웹뷰(WebView) 보기:

 Uri webpage = Uri.parse("http://www.android.com");

 Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);



 암시적(implicit) 인텐트의 다른 종류들은 String과 같이 다른 데이터 타입을 제공하는 "extra"데이터를 요구합니다. 여러분은 다양한putExtra() 메소드를 사용해서 하나 또는 그 이상의 extra 데이터 추가할 수 있습니다.



기본적으로 시스템은 포함된 Uri 를 기반으로 Intent에 의해 요구되는 적절한 MIME(Multipurpose Internet Mail Extensions) 타입을 결정합니다. 만약 여러분이 Intent 안에 Uri 을 포함시키지 않았다면, 일반적으로 인텐트와 조합된 데이터의 타입을 지정하기 위한setType() 메소드을 사용해야 합니다. MIME 타입을 설정하는 것은 나중에 인텐트를 받아야 하는 액티비티들의 종류를 지정하는 것입니다.



아래는 여러분이 원하는 action을 지정하기 위한 extra 데이터를 추가한 Intent 들의 몇가지 예시입니다.


첨부파일과 함께 이메일 보내기

 Intent emailIntent = new Intent(Intent.ACTION_SEND);

 // The intent does not have a URI, so declare the "text/plain" MIME type

 emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);

 emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // recipients

 emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");

 emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");

 emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));

 // You can also attach multiple items by passing an ArrayList of Uris


달력(Calendar) 이벤트 생성하기

 Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);

 Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);

 Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);

 calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());

 calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());

 calendarIntent.putExtra(Events.TITLE, "Ninja class");

 calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");

Note : Calendar 이벤트에 대한 이 인텐트(intent)는 API 14 버전 이상에서만 제공되어집니다. 


Note : 가능한 명확하게 여러분의 Intent 를 정의하는 것을 중요합니다. 예를 들어, 만약 여러분이 ACTION_VIEW 인텐트를 사용하여 이미지를 보여주기를 원함다면 여러분은 image/* 로 MIME 타입을 지정하셔야만 합니다. 이것은 인텐트에 의해 앱이 시작될 때"view" 할 수 있는 다른 데이터 타입( 지도보기 처럼)의 앱이 실행되는 것을 방지합니다.

반응형
반응형

 안드로이드 앱은 일반적으로 다양한 액티비티들을 가지고 있습니다. 각각의 액티비티는 사용자가 특정 작업( 지도보기 또는 사진촬영 같은)을 수행시킬 수 있는 사용자 인터페이스(user Interface : UI )를 보여줍니다. 사용자가 하나의 액티비티에서 다른 액티비티로 전환하기 위해서는, 여러분은 반드시 무엇인가를 하려는 앱의 "의도(intent)" 를 정의하기 위해 Intent 클래스를 사용해야만 합니다.startActivity() 메소드를 통해 시스템에게 Intent를 전달할 때, 시스템은 적절한 앱 컴포넌트(component)를 식별하고 이를 시작하기 위해 Intent 인스턴스를 사용합니다. Intent를 사용하는 것은 심지어 별도의 다른 앱안에 포함되어 있는 액티비티도 시작하게 할 수 있습니다.


Intent 는 특정 컴포넌트를 지정하여 실행하도록 하는 명시적(explicit) 인텐트와 컴포넌트를 지정하지 않고 의도된 동작(마치 사진캡쳐와 같은)을 처리할 수 있는 어떤 컴포넌트를 적절히 실행하기 위한 암시적(implicit) 인텐트가 있습니다.


 이번 장에서는 여러분에게 다른 앱 실행하기, 앱으로부터 결과 받기, 여러분의 앱을 다른 앱으로부터의 Intent에 응답할 수 있도록 하는 등, 몇가지 다른앱과의 기본적인 상호작용을 수행하기 위해 Intent 를 사용하는 방법을 소개하도록 하겠습니다.


 

Lessons

---------------------------------------

다른 앱으로 사용자 보내기- 다른 앱 실행하기

(Sending the User to Another App)

  액션(action)을 수행할 수 있는 다른 앱을 시작하기 위한 암시적(implicit) 인텐트를 만드는 방법을 소개합니다. 


액티비티로부터 결과 얻어오기 (Getting a Result from an Activity)

  다른 앱을 실행하고 그 액티비티로 부터 결과(result)를 받는 방법을 소개합니다.


다른 앱들이 여러분의 앱을 실행하도록 하기 

(Allowing Other Apps to Start Your Activity)

  여러분의 앱이 암시적 인텐트를 받을 수 있도록 선언된 인텐트 필터(filter)를 정의함으로서 다른 앱에 의해 여러분의 앱안에 있는 액티비티를 열 수 있도록 만드는 방법을 소개합니다.

반응형
반응형

 데이터베이트(database)에 데이터를 저장하는 것은 마치 주소록 정보같이, 반복되고 구조화된 데이터에 가장 이상적인 방법입니다.

이번 시간은 여러분이 일반적인 SQL 데이터베이스를 잘 알고 있다고 가정하고 설명할 것이며 안드로이드에서의 SQLite 데이터베이스(database)를 시작하는 데 도움이 되고자 합니다. 여러분이 Android 에서 데이터베이스(database)를 사용하는데 필요한 API 들은android.database.sqlite 패키지안에 있습니다.



스키마(Schema) 정의와 Contract (Define a Schema and Contract)

-----------------------------------------------------

 SQL 데이터베이스의 주요 법칙중 하나는 스키마(schema)입니다. 스키마(schema)는 데이터 베이스가 어떻게 구성되어 있는가에 대한 공식적인 선언입니다. 스키마는 여러분이 여러분의 데이터베이스(database)를 생성하는데 사용하는 SQL 문을 나타냅니다. 여러분은 명시적이고 자기 문서화 하는 방법으로 스키마의 레이아웃을 지정하는 Contract 클래스로 알려진 companion 클래스을 생성하는데 유용한 것을 찾게 될 겁니다. 


contract 클래스는 URIs, 테이블, 컬룸(Column)에 대한 이름을 정의한 상수에 대한 컨테이너(container) 입니다. contract 클래스는 같은 패키지 안에 있는 모든 다른 클래스를 통틀어 같은 상수를 사용하도록 합니다. 이것은 여러분인 하나의 장소(위치)에서 컬룸(Column)의 이름을 변경하도록 하며, 그것을 가지고 있는 있는 여러분의 코드 전체에 변경 사항이 반영되도록 합니다.


contract 클래스를 조직하는 좋은 방법은 클래스의 루트(root)레벨에 여러분의 전체 데이터베이스(database)를 전역(global)으로 정의해 놓는 것입니다. 그런 다음 컬룸(column)을 열거한 각 테이블에 대한 이너 클래스(inner class)을 만듭니다.


Note : BaseColumns 인터페이스(interface)를 구현함으로서, 여러분의 이너클래스(inner class)는 cursor adapter 같은 일부 안드로이드 클래스들이 가지고 있을 것으로 기대하고 있는 _ID라고 불려지는 주요 키(프라이머리 키 : primary key)를 상속할 수 있습니다. 이것이 반드시 필요하지는 않지만, 이를 통해 여러분이 Android 프레임워크(framework)와 조화롭게 데이터베이스를 작업할 수 있도록 도와 줍니다.



아래는 단일 테이블에 대해 테이블 이름과 컬룸(column) 이름을 정의하는 부분의 예제입니다.

 public final class FeedReaderContract {

    // To prevent someone from accidentally instantiating the contract class,

    // give it an empty constructor.

    public FeedReaderContract() {}


    /* Inner class that defines the table contents */

    public static abstract class FeedEntry implements BaseColumns {

        public static final String TABLE_NAME = "entry";

        public static final String COLUMN_NAME_ENTRY_ID = "entryid";

        public static final String COLUMN_NAME_TITLE = "title";

        public static final String COLUMN_NAME_SUBTITLE = "subtitle";

        ...

    }

}




SQL Helper를 사용하여 데이터베이스 생성하기

(Create a Database Using a SQL Helper)

--------------------------------------

여러분이 데이터베이스의 모양을 정의할 때, 여러분은 데이터베이스(database)와 테이블(table)을 생성하고 유지하는 메소드들을 구현해야만 합니다. 아래 테이블을 만들고 삭제하는 몇가지 전형적인 명세서(statment)이 있습니다.

 private static final String TEXT_TYPE = " TEXT";

private static final String COMMA_SEP = ",";

private static final String SQL_CREATE_ENTRIES =

    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +

    FeedEntry._ID + " INTEGER PRIMARY KEY," +

    FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +

    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +

    ... // Any other options for the CREATE command

    " )";


private static final String SQL_DELETE_ENTRIES =

    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;


 여러분이 단말기의 내부저장소(internal storage)에 저장하는 파일처럼, 안드로이드는 여러분의 데이터베이스(database)를 어플과 관련된 프라이빗(private) 디스크(disk)의 공간에 저장합니다. 여러분의 데이터는 안전합니다. 왜냐하면, 기본적으로 이 영역은 다른 어플들이 접근할 수 없기 때문입니다.



유용한 API들은 SQLiteOpenHelper 클래스에서 사용할 수 있습니니다. 여러분이 데이터베이스(database)를 참조하는 인스턴스를 얻기위해 이 클래스를 사용할때, 시스템은 데이터베이스를 생성하고 업데이트하는 명령같이 오래 걸리는 명령들을 수행할 수도 있기에 오직 데이터베이스가 필요할 때만 사용되며 startup하는 동안에는 수행하지 않습니다. 여러분이 해야할 모든 것은 getWirteableDatabase() 또는 getReadableDatabase()를 호출하는 것입니다.


Note : getWriteableDatabase() 또는 getReadableDatabase() 역시 오래 걸릴 수 있게 때문에, AsyncTask 또는IntentService 와 같이 백그라운드 스레드(Background Thread)안에서 호출해야 합니다.


SQLiteOpenHelper 를 사용하기 위해, onCreate()onUpgrade() 그리고 onOpen() 콜백 메소드들을 오버라이드(override)한 하위클래스(subclass)를 생성합니다. 여러분이 onDowngrade() 를 구현할지도 모르지만, 꼭 요구 되지는 않습니다.



아래는 위에 소개된 명령 중 일부를 사용하는 SQLiteOpenHelper의 구현에 대한 예제입니다.

 public class FeedReaderDbHelper extends SQLiteOpenHelper {

    // If you change the database schema, you must increment the database version.

    public static final int DATABASE_VERSION = 1;

    public static final String DATABASE_NAME = "FeedReader.db";


    public FeedReaderDbHelper(Context context) {

        super(context, DATABASE_NAME, null, DATABASE_VERSION);

    }

    public void onCreate(SQLiteDatabase db) {

        db.execSQL(SQL_CREATE_ENTRIES);

    }

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        // This database is only a cache for online data, so its upgrade policy is

        // to simply to discard the data and start over

        db.execSQL(SQL_DELETE_ENTRIES);

        onCreate(db);

    }

    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        onUpgrade(db, oldVersion, newVersion);

    }

}


여러분의 데이터베이스에 접속하기 위해서는, SQLiteOpenHelper의 하위클래스(subclass)을 인스턴스화 합니다.

  FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());




데이터베이스에 정보 넣기( Put Information into a Database)

-------------------------------------------------

ContentVauses 객체를 insert()메소드의 매개변수로 전달함으로서 데이터베이스안에 데이터를 삽입합니다.

 // Gets the data repository in write mode

SQLiteDatabase db = mDbHelper.getWritableDatabase();


// Create a new map of values, where column names are the keys

ContentValues values = new ContentValues();

values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);

values.put(FeedEntry.COLUMN_NAME_TITLE, title);

values.put(FeedEntry.COLUMN_NAME_CONTENT, content);


// Insert the new row, returning the primary key value of the new row

long newRowId;

newRowId = db.insert(

         FeedEntry.TABLE_NAME,

         FeedEntry.COLUMN_NAME_NULLABLE,

         values);


insert() 메소드의 첫번째 인자는 간단한 테이블 이름입니다. 두번째 인자는 컬룸(Column)의 이름을 제공합니다. 프레임워크(framework)는 ContentValues 가 비어있는 상황에서는 이곳에 NULL을 삽입할 수 있습니다.(만약 여러분이 넣을 값이 없어서, 이 곳을 null 로 대신하면 프레임워크(framework)는 row를 삽입하지 않습니다.)




데이터베이스에서 정보 읽기( Read Information from a Database)

-----------------------------------------------------

데이터 베이스로부터 정보를 읽기위해 query() 메소드를 사용하며, 이것에 여러분이 읽기를 원하는 컬룸(column)과 기준 selection을 전달 합니다. 이 메소드는 insert()  update()의 요소를 혼합한 것으로, 여러분이 삽입했던 데이터에 비해서 여러분이 원하는 데이터만 얻어오기 위해 컬룸(column) 목록중 원하지 않는 나머지는 제외합니다. query()의 결과는 Cursor 객체로 여러분에게 리턴됩니다.


 SQLiteDatabase db = mDbHelper.getReadableDatabase();


// Define a projection that specifies which columns from the database

// you will actually use after this query.

String[] projection = {

    FeedEntry._ID,

    FeedEntry.COLUMN_NAME_TITLE,

    FeedEntry.COLUMN_NAME_UPDATED,

    ...

    };


// How you want the results sorted in the resulting Cursor

String sortOrder =

    FeedEntry.COLUMN_NAME_UPDATED + " DESC";


Cursor c = db.query(

    FeedEntry.TABLE_NAME,  // The table to query

    projection,                              // The columns to return

    selection,                               // The columns for the WHERE clause

    selectionArgs,                          // The values for the WHERE clause

    null,                                      // don't group the rows

    null,                                      // don't filter by row groups

    sortOrder                                 // The sort order

    );


Cursor 안에 있는 row 를 보기위해서는 항상 값을 읽기 시작하기 전에 Cursor 이동 메소드중 하나를 호출해야만 합니다. 일반적으로 여러분은 query의 결과안에 있는 첫번째 데이터의 "읽기 위치"에 Cursor가 놓여지도록 moveToFirst() 메소드를 호출하여 시작하도록 합니다. 각 각의 row에 대해, 여러분은 Cursor의 get 메소드들 중 하나(getString() 또는 getLong() 같은 )을 호출함으로서 컬룸(Column)의 값을 읽을 수 있습니다. get 메소드들 각각을 사용할 때, 여러분은 반드시 여러분이 원하는 column의 인덱스(index) 위치를 전달해야만 합니다. 컬룸(Column)의 인덱스(index)는 getColumnIndex() 또는 getColumnIndexOrThrow()를 호출해서 얻어낼 수 있습니다. 예:

 cursor.moveToFirst();

long itemId = cursor.getLong(

    cursor.getColumnIndexOrThrow(FeedEntry._ID)

);



데이터베이스로부터 정보 삭제하기(Delete Information from a Database)

-----------------------------------------------------------

DB테이블로부터 row들을 삭제하기 위해서는, row를 식별할 수 있는 기준 selection(일종의 검색 조건)을 제공해야만 합니다. 데이터베이스 API는 SQL 인젝션(injection :SQL문의 취약성)을 방지하기 위해 기준 seletion 을 생성하는 것에 대한 메커니즘(mechanism)을 제공합니다. 이 메커니즘은 selection을 selection 절(clause)과 selection 인자들(arguments)로 특성을 분할합니다. selection 절은 형태를 보면 Column들을 정의하고 또한 Column 테스트와 결합할 수 있습니다. selection 인자들은seletion 절에 연결되어 테스트할(검색) 조건에 대응되는 값입니다. 그 결과는 정규 SQL 문과 같이 처리되지 않기 때문에 SQL 인젝션(injection)에 대해 영향을 받지 않습니다.


 // Define 'where' part of query.

String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";

// Specify arguments in placeholder order.

String[] selectionArgs = { String.valueOf(rowId) };

// Issue SQL statement.

db.delete(table_name, selection, selectionArgs);




데이터베이스 갱신하기( Update a Database)

------------------------------------

여러분이 데이터베이스의 값중 일부를 수정해야 하는 경우에는 Update() 메소드를 사용합니다.


DB테이블을 업데이트 하는 것은 insert()의 컨텐츠 값(contents value)구문과 delete()의 where구문을 결합한 것입니다.

 SQLiteDatabase db = mDbHelper.getReadableDatabase();


// New value for one column

ContentValues values = new ContentValues();

values.put(FeedEntry.COLUMN_NAME_TITLE, title);


// Which row to update, based on the ID

String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";

String[] selectionArgs = { String.valueOf(rowId) };


int count = db.update(

    FeedReaderDbHelper.FeedEntry.TABLE_NAME,

    values,

    selection,

    selectionArgs);


반응형
반응형

내부 저장장치에 파일 저장하기(Save a File on Internal Storage)

---------------------------------------------------

 내부 저장소(Internal Storage)에 파일을 저장 할 때, 여러분은 아래의 두개 메소드중 하나를 호출함으로서 파일을 저장할 알맞은 디렉토리를 가지고 있는 File 인스턴스를 얻을 수 있습니다.


getFileDir()

   여러분의 앱이 저장된 내부 저장소의 디렉토리(internal directory)를 나타내고 있는 File 인스턴스(instance)를 리턴합니다.


getCacheDir()

   앱의 임시 캐시(cache) 파일을 저장하고 있는 내부 저장소의 디렉토리를 나타내고 있는 File 인스턴스를 리턴합니다. 파일이 더 이상 필요하지 않으면 각 파일을 삭제해야 하며 1MB와 같이, 주어진 시간에 사용되는 메모리의 총양에 대한 합리적인 크기 제한을 구현합니다. 만약 시스템이 저장공간이 부족해 지기 시작하면, 어떠한 경고도 없이 여러분의 캐시(cache) 파일을 제거 할 수도 있습니다.


 이들 디렉토리중 하나에 새로운 파일을 생성하기 위해서는, File() 생성자함수를 호출하면서 파라미터로 여러분의 내부 저장 디렉토리를 지정하는 위의 메소드중 하나를 사용해서 File 객체를 전달합니다. 예:

 File file = new File(context.getFilesDir(), filename);


 위를 대체해서, 여러분은 앱의 내부 저장 디렉토리의 파일에 쓰기(저장)을 하도록 하는 FileOutputStream객체를 openFileOutput()을 호출하여 사용할 수도 있습니다. 아래는 파일에 몇가지 텍스트를 쓰는 방법의 예제입니다.

 String filename = "myfile";

 String string = "Hello world!";

 FileOutputStream outputStream;


 try {

   outputStream = openFileOutput(filename, Context.MODE_PRIVATE);

   outputStream.write(string.getBytes());

   outputStream.close();

 } catch (Exception e) {

   e.printStackTrace();

 }


또한, 만약 일부의 파일들을 캐시메모리에 저장할 필요가 있다면, 여러분은 createTempFile()로 대신하여 사용해야 합니다. 예를 들어, 아래의 매소드는 URL 로 부터 파일 이름을 추출하여.앱의 내부 캐시 디렉토리안에 추출된 이름으로 파일을 만듭니다. 

 public File getTempFile(Context context, String url) {

    File file;

    try {

        String fileName = Uri.parse(url).getLastPathSegment();

        file = File.createTempFile(fileName, null, context.getCacheDir());

    catch (IOException e) {

        // Error while creating file

    }

    return file;

}


Note : 앱이 저장되는 내부 저장 디렉토리는 안드로이드 파일 시스템의 특정 지역안에 앱의 패키지 이름으로 지정됩니다. 기술적으로, 다른 앱은 여러분이 읽을 수 있도록 설정하기만 했다면 여러분 앱의 내부 저장 파일을 읽을 수 있습니다. 어쨋든, 다른 앱 역시 여러분 앱의 패키지 이름과 파일 이름을 알아야만 합니다. 다른 앱들은 여러분이 명시적으로 여러분의 파일을 읽고 쓸 수 있도록 설정하지 않은 상태에서 여러분의 내부 디렉토리를 탐색하거나 읽고 쓸수 없습니다. 그래서 여러분이 내부 저장소에 있는 여러분의 파일에 대해MODE_PRIVATE로 하기만하면, 다른 앱들은 절대로 여러분의 파일에 접속할 수 없습니다.




외부 저장장치에 파일 저장하기(Save a File on External Storage)

---------------------------------------------------

 사용자가 PC에 있는 저장장치를 사용하거나 외부 저장장치(External Storage)로 제공된 SD card 를 제거햇을 때와 같이 외부 저장장치를 사용할 수 없을 지도 모르게 때문에 여러분은 장치에 접속하지 전에 저장장치가 사용할 수 있는지 항상 점검해야만 합니다. 여러분은 getExternalStorageState() 메소드를 호출함으로서 외부 저장장치(External Storage)의 상태를 요청할 수 있습니다. 만약 이 메소드로부터 리턴된 상태가 MEDIA_MOUNTED 라면, 여러분은 파일을 이 장치에 읽고 쓸 수 있습니다. 예를 들어, 아래의 메소드들은 저장장치의 사용가능여부를 알아내는데 매우 유용합니다.

 /* Checks if external storage is available for read and write */

public boolean isExternalStorageWritable() {

    String state = Environment.getExternalStorageState();

    if (Environment.MEDIA_MOUNTED.equals(state)) {

        return true;

    }

    return false;

}


/* Checks if external storage is available to at least read */

public boolean isExternalStorageReadable() {

    String state = Environment.getExternalStorageState();

    if (Environment.MEDIA_MOUNTED.equals(state) ||

        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {

        return true;

    }

    return false;

}



 비록 외부 저장장치가 사용자와 다른앱들에 의해 수정될 수도 있지만, 이곳에 저장 하는 파일들은 두 종류가 있습니다.


Public files

  다른 앱들과 사용자에게 자유롭게 사용할 수 있도록 되어 있는 파일입니다. 사용자가 앱을 언인스톨(uninstall)할 때, 이 파일들은 여전히 사용자가 사용할 수 있는 상태로 남아 있습니다.


 예: 앱에 의한 사진 캡쳐 또는 또다른 다운로드 된 파일들



Private files

  사용자가 앱을 제거할 때 당연하게 앱에 속하며 삭제되어야 할 파일입니다. 비록 기술적으로는 이 파일들이 외부저장장치에 있기 때문에 다른 앱들과 사용자에 의해 접속될 수 있어야 하지만, 실제로는 여러분의 앱 밖으로 값을 제공하지 않는 파일입니다. 사용자가 여러분의 앱을 언인스톨(uninstall)할 때, 시스템은 앱의 외부 프라이빗(Private) 디렉토리안에 있는 모든 파일들을 삭제합니다. 


 예: 앱에 의해 다운로드된 추가적인 리소스들 또는 임시 미디어(Media) 파일들


  

만약 여러분이 외부 저장장치(External Storage)에 파일을 저장하길 원한다면, getExternalStoragePublicDirectory() 메소드를 사용하여 외부 저장장치의 적절한 디렉토리를 나타내는 File 인스턴스(instance)를 얻어야 합니다. 이 메소드는 여러분이 저장하길 원하는 파일의 타입(Type)을 지정하는 인자(argument)를 가집니다( DIRECTORY_MUSIC 또는 DIRECTORY_PICTURE 등). 그래서 다른public 파일들과 디렉토리안에서 논리적으로 조직되어 집니다. 예 :

 public File getAlbumStorageDir(String albumName) {

    // Get the directory for the user's public pictures directory. 

    File file = new File(Environment.getExternalStoragePublicDirectory(

            Environment.DIRECTORY_PICTURES), albumName);

    if (!file.mkdirs()) {

        Log.e(LOG_TAG, "Directory not created");

    }

    return file;

}


만약 미리 지정된 하위 디렉토리(sub directory)중에 여러분의 파일에 알맞은 이름이 없다면, 위의 메소드를 대신해서 여러분은getExternalFilesDir()의 호출로 대신하고 null 을 전달합니다. 이렇게 하면 외부 저장소에서 여러분 앱의 프라이빗(Private) 디렉토리에 대한 루트(root) 디렉토리을 리턴합니다.


 getExternalFilesDir()로 받아온 디렉토리안에 만든 디렉토리는 사용자가 앱을 언인스톨할 때 자동으로 지워진다는 것을 기억하시기 바랍니다. 만약 여러분이 저장한 파일이 사용자가 앱을 언인스톨한 후에도 여전히 남아있기를 원한다면( 여러분의 앱이 카메라이고 사용자가 사진을 유지하고 싶어할때와 같은 경우) 여러분은 반드시 getExternalStoragePublicDirectory()를 사용하셔야 합니다.


 여러분이 getExternalStoragePublicDirectory()을 사용하여 공유된 파일이든 또는 getExternalFilesDir()를 사용하여 프라이빗하고 사용하는 앱의 파일이든 상관없이, 디렉토리의 이름은 DIRECTORY_PICTURE 처럼 API 상수에 의해 제공되어진 것이라는 것이 중요합니다. 이 디렉토리의 이름들은 파일이 시스템에 의해 제대로 처리된다는 것을 보장합니다. DIRECTORY_RINGTONES 안에 저장된 음악 파일들은 시스템미디어 스캐너에 의해 음악대신 벨소리로 분류되어 집니다.



자유 공간 요청하기(Query Free Space)

-------------------------------

만약 여러분이 저장하는 데이터의 양이 얼마인지 미리 알고 있다면, 여러분은 getFreeSpace() 또는 getTotalSpace()를 호출하여IOException없이 충분한 공간을 사용할 수 있는지 확인할 수 있습니다. 이 메소드들은 각 각 저장장치의 현재 사용가능 공간과 전체 공간을 알려줍니다. 이 정보는 일정 임계치 값 이상의 저장공간이 꽉 차는 것을 회피하는데 유용합니다.


Note : 여러분에게 여러분의 파일을 저장하기 전에 사용할 수 있는 공간의 총양에 대한 체크할 필요가 없습니다. 만약 여러분이 사용해야 하는 공간이 정확히 얼마인지 알지 못할 때는 이렇게 하는 것이 필요할 수 도 있습니다. 예를들어, 만약 PNG 이미지를 JPEG으로 변경함으로서 여러분이 이전에 저장한 파일의 인코딩(encoding)이 변경되어 진다면, 여러분은 사전에 파일의 사이즈를 알지 못합니다.




파일 삭제하기(Delete a File)

-----------------------

여러분은 파일이 더이상 필요하지 않으면 항상 삭제해야만 합니다. 파일을 지우는 가장 정확한 방법은 파일을 열었던 참조변수에게delete()를 호출하도록 하는 것입니다.


 myFile.delete();


만약 파일을 내부 저장장치에 저장했다면, 여러분은 Context 에게 위치를 요청할 수 있으며 deleteFile()을 호출함으로서 파일을 삭제할 수 있습니다.


 myContext.deleteFile(fileName);




Note : 사용자가 앱을 언인스톨 할때, 안드로이드 시스템은 아래의 항목을 삭제합니다.

° 내부 저장장치에 있는 앱의 모든 파일들

° getExternalFilesDir() 를 사용해서 외부 저장장치에 저장한 앱의 모든 파일들


어쨋든 여러분은 getCacheDir()로 만든 모든 캐시된 파일들은 수동으로 직접 삭제 해야만 하며, 정기적으로 더 이상 필요하지 않은 다른 파일들을 삭제해야 합니다. 

반응형

+ Recent posts