'분류 전체보기'에 해당되는 글 2491건

  1. 2010.08.20 oracle null sum
  2. 2010.08.18 네트워크 내 중복 IP를 찾아보자..
  3. 2010.08.16 android listView sample 2
  4. 2010.08.16 weblogic.jdbc.extensions.ConnectionDeadSQLException
  5. 2010.08.15 Android custom Dialog
  6. 2010.08.15 Android Selected State ListView Example
  7. 2010.08.15 Custom ListView items and adapters
  8. 2010.08.12 #11. List 집중공략! - (3) Custom ArrayAdapter를 이용한 ListView
  9. 2010.08.09 SVN 용 폴더 한 번에 삭제하기
  10. 2010.08.04 android 모토로라 개발툴 다운받기
  11. 2010.08.04 [기타강좌] 버튼 눌렀을 때와 땟을때의 이미지 처리 방법
  12. 2010.08.03 네이트온 터닐링
  13. 2010.07.30 논리 DB 설계
  14. 2010.07.30 안드로이드 아파치 서버 & 클라이언트 Socket 통신에 대한 기초적 방법 1
  15. 2010.07.30 [android-beginners] Sockets with Android.
  16. 2010.07.30 [펌] 안드로이드 소켓통신
  17. 2010.07.30 > [안드로이드] 서버/클라이언트 소켓(Socket) 통신하기
  18. 2010.07.30 안드로이드 서버와 소켓통신
  19. 2010.07.30 Android에서의 TCP/IP 통신
  20. 2010.07.30 Android에서 HttpClient를 이용한 서버와의 통신
  21. 2010.07.27 android 웹서비스 구현하기 참조
  22. 2010.07.27 android 웹서비스 구현 참조
  23. 2010.07.07 자바 웹 어플리케이션에서 파일 다운로드 시 한글명 깨짐 이슈 해결법
  24. 2010.07.05 [유아독종님강좌] 구글 GMail 계정을 이용해 안드로이드에서 메일 보내기
  25. 2010.07.05 Android Cloud to Device Messaging Framework
  26. 2010.07.05 안드로이드(android) 다이얼로그 종류별 구현 방법
  27. 2010.06.25 GoogleTalkConnection을 확장한 GoogleTalkClient 제작!
  28. 2010.06.25 ListView에 동적으로 아이템 추가시 스크롤 문제!
  29. 2010.06.24 네트워크 연결 끊기 4
  30. 2010.06.24 Oracle의 문자열비교
02.Oracle/DataBase2010. 8. 20. 14:39
반응형
SELECT  NULL + 100 FROM DUAL; 의 결과는 무엇일까요?

NULL과 사직연산을 하게되면 결과 값은  NULL이 됩니다.

그래서 SELECT NVL(NULL, 0) + 100 FROM DUAL; 과 같이 NULL 공포에서
벗어 나려고 NVL 함수를 자주 사용하게 됩니다.

근본적으로 컬럼값에 NULL이 없게 NOT NULL로 설계 했다면 NVL를 남발하는 일을
없었겠죠. ㅎㅎ

그리고 또하나 NULL이 포함된 컬럼에 그룹함수( SUM() ) 를 사용하면 어떻게 될까요?

SUM를 포함한 그룹함수에서는 NULL 컬럼을 자동으로 건너띄므로 NULL 값이 있다고
해서 전체 결과가 NULL로 변해 버리지 않습니다. 즉 NVL 함수를 사용할 필요가 없다는
것입니다.

NVL() 도 함수임으로 자주 콜하게 되면 부하가 생기겠죠. 설계에서부터 NULL이 나오지
않게 하고 그룹함수는 NVL를 사용할 필요가 없으니 굳이 넣어서 부하를 줄 필요가 없겠죠. . .


출처 : http://dev4u.tistory.com/184
Posted by 1010
98..Etc/Etc...2010. 8. 18. 15:07
반응형

네트워크 내(동일 서브넷을 사용하는 네트워크)에 많은 피시들이 사용되다 보면 필수불가결적으로
나오는 문제가 있으니...

"누가 IP xxx.xxx.xxx.xxx 사용하는거야 쫑났잖아..."

정말 중요한 업무중이었다면 짜증이 확 밀려오게 된다.
이럴때는 IP를 변경하고자 하는 사람이나 또는 해당 IP를 사용하는 사람을 색출하기 위해서는
사무실을 미친년(?)처럼 헤집고 다녀야지만 찾아낼 수 있다.
(보통은 IP 쫑낸 분들은 자신들이 뭔일을 한건지 잘모른다. 그래서 아무리 자수를 권해도
나오질 않은것이다.)
이럴때는 아래와 같은 방법으로 찾아보자.

1. 도스창을 연다.

2. 아래 그림과 같이 명령어를 타이핑 한다






출처 : http://blog.happygom74.com/52사용자 삽입 이미지

Posted by 1010
04.Anddoid/listView2010. 8. 16. 17:31
반응형
Posted by 1010
02.Oracle/DataBase2010. 8. 16. 08:41
반응형
http://kr.forums.oracle.com/forums/thread.jspa?threadID=1038232

problem whle starting the SOA server.
게시일: 2010. 3. 3 오후 10:49
 
     
Hi,

I have Installed Oracle SOA suite in a server. I have created a domain and with in that domain, there is Admin server, Soa server and BAM server. They were working fine. But when I tried to re-start them recently , the Admin server was showing the warning

<Mar 3, 2010 10:42:58 PM PST> <Warning> <JDBC> <BEA-001129> <Received exception while creating connection for pool "mds-SpacesDS": ORA-28001: the password has expired>

but still I am able to access teh web logic console of the admin server.

When I try to start the SOA server it gives the message

Internal Exception: weblogic.jdbc.extensions.ConnectionDeadSQLException: weblogic.common.resourcepool.ResourceDeadException: 0:weblogic.common.ResourceException: Could not create pool connection. The DBMS driver exception was: ORA-28001: the password has expired

The same is the case with the BAM server also . The console of BAM server is showing the message

Internal Exception: weblogic.jdbc.extensions.ConnectionDeadSQLException: weblogic.common.resourcepool.ResourceDeadException: 0:weblogic.common.ResourceException: Could not create pool connection. The DBMS driver exception was: ORA-28001: the password has expired.

I have used SYSTEM user credentials while installaing the SOA-SUITE. I have changed and re-set the password for that user. But That did not help.

can any one please let me know how this issue can be resolved. Please contact me if any more info is needed.

Thanks in advance for your time,
Raj Kumar
Anuj Dwivedi, TCS

글: 2,440
등록일: 08-10-29
Re: problem whle starting the SOA server.
게시일: 2010. 3. 3 오후 11:44   Raja Kumar님의 질문에 답변 Raja Kumar님의 질문에 답변
유용함
      댓글
Hi Raj,

Have you created any connection pool with name "mds-SpacesDS"?

ORA-28001: the password has expired

Above error shows that account of username, used in the DataSource of connection pool "mds-SpacesDS", has expired and password should be changed. After changing the pasword of DB user, mention the same in your Data-Source as well. Update the DBAdapter to refresh the connection pool.

Regards,
Anuj
Raja Kumar

글: 36
등록일: 08-12-04
Re: problem whle starting the SOA server.
게시일: 2010. 3. 4 오전 5:15   Anuj Dwivedi, TCS님의 질문에 답변 Anuj Dwivedi, TCS님의 질문에 답변
 
      댓글
Anuj,

Thanks for the reply.

This connection pool seems to have got created during the installation of SOA-suite.

Can you please tell me how can I findout the username of the Databse for which the password needs to be changed, sothat I can update the pwd and reflect that change in the DB Adpater configuration and the Datasource.

Thanks,
Raj Kumar
Raja Kumar

글: 36
등록일: 08-12-04
Re: problem whle starting the SOA server.
게시일: 2010. 3. 4 오전 6:00   Raja Kumar님의 질문에 답변 Raja Kumar님의 질문에 답변
 
      댓글
Hi,

Posted by 1010
04.Anddoid/dialog2010. 8. 15. 02:32
반응형

Android provides a AlertDialog class that ease the building of Dialog windows throught it’s Builder inner class. However, it’s not possible to customize these AlertDialog windows. To give AlertDialog windows a custom Look and Feel, a solution is to create application specific AlertDialog and AlertDialog.Builder class.

Android default Dialog

Defining the Look And Feel

What we want is to turn the default AlertDialog Look and Feel into this custom Look and Feel :

Custom Android Dialog

The dialog will support the following features :

  1. Specify title from resource or String
  2. Specify content from resource, String or custom layout
  3. Set positive and negative buttons and associated listeners

Writing layout, style and theme

The dialog will use a custom layout to render it’s content. The layout defines the ids that will be used to access the title TextView, the dialog message or custom content and the buttons bar.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:minWidth="280dip"
    android:layout_height="wrap_content">
 
    <LinearLayout 
        android:orientation="vertical"
        android:background="@drawable/header"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
 
        <TextView
            style="@style/DialogText.Title"
            android:id="@+id/title"
            android:paddingRight="8dip"
            android:paddingLeft="8dip"
            android:background="@drawable/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
 
    </LinearLayout>
 
    <LinearLayout 
        android:id="@+id/content"
        android:orientation="vertical"
        android:background="@drawable/center"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
 
        <TextView
            style="@style/DialogText"
            android:id="@+id/message"
            android:padding="5dip"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"/>
 
    </LinearLayout>
 
    <LinearLayout 
        android:orientation="horizontal"
        android:background="@drawable/footer"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
 
        <Button 
            android:id="@+id/positiveButton"
            android:layout_marginTop="3dip"
            android:layout_width="0dip"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:singleLine="true"/>
 
        <Button 
            android:id="@+id/negativeButton"
            android:layout_marginTop="3dip"
            android:layout_width="0dip"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:singleLine="true"/>
 
    </LinearLayout>
 
</LinearLayout>

The root LinearLayout’s width is set to fill_parent with a minimum of 280dip so that the dialog width will always be 87,5% of the screen width.

A custom Theme should be used to declare the dialog as floating and using a custom background and a custom title view :

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <style name="Dialog" parent="android:style/Theme.Dialog">
        <item name="android:windowBackground">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
    </style>
 
</resources>

Then we need to define the appearence of our dialog title and message :

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <style name="DialogText">
        <item name="android:textColor">#FF000000</item>
        <item name="android:textSize">12sp</item>
    </style>
 
    <style name="DialogText.Title">
        <item name="android:textSize">16sp</item>
        <item name="android:textStyle">bold</item>
    </style>
 
</resources>

Writing the Dialog and Builder class

It’s preferable for our custom Builder to have the same methods that we have in the AletDialog.Builder class.

package net.androgames.blog.sample.customdialog.dialog;
 
import net.androgames.blog.sample.customdialog.R;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
 
/**
 * 
 * Create custom Dialog windows for your application
 * Custom dialogs rely on custom layouts wich allow you to 
 * create and use your own look & feel.
 * 
 * Under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
 * 
 * @author antoine vianey
 *
 */
public class CustomDialog extends Dialog {
 
    public CustomDialog(Context context, int theme) {
        super(context, theme);
    }
 
    public CustomDialog(Context context) {
        super(context);
    }
 
    /**
     * Helper class for creating a custom dialog
     */
    public static class Builder {
 
        private Context context;
        private String title;
        private String message;
        private String positiveButtonText;
        private String negativeButtonText;
        private View contentView;
 
        private DialogInterface.OnClickListener 
                        positiveButtonClickListener,
                        negativeButtonClickListener;
 
        public Builder(Context context) {
            this.context = context;
        }
 
        /**
         * Set the Dialog message from String
         * @param title
         * @return
         */
        public Builder setMessage(String message) {
            this.message = message;
            return this;
        }
 
        /**
         * Set the Dialog message from resource
         * @param title
         * @return
         */
        public Builder setMessage(int message) {
            this.message = (String) context.getText(message);
            return this;
        }
 
        /**
         * Set the Dialog title from resource
         * @param title
         * @return
         */
        public Builder setTitle(int title) {
            this.title = (String) context.getText(title);
            return this;
        }
 
        /**
         * Set the Dialog title from String
         * @param title
         * @return
         */
        public Builder setTitle(String title) {
            this.title = title;
            return this;
        }
 
        /**
         * Set a custom content view for the Dialog.
         * If a message is set, the contentView is not
         * added to the Dialog...
         * @param v
         * @return
         */
        public Builder setContentView(View v) {
            this.contentView = v;
            return this;
        }
 
        /**
         * Set the positive button resource and it's listener
         * @param positiveButtonText
         * @param listener
         * @return
         */
        public Builder setPositiveButton(int positiveButtonText,
                DialogInterface.OnClickListener listener) {
            this.positiveButtonText = (String) context
                    .getText(positiveButtonText);
            this.positiveButtonClickListener = listener;
            return this;
        }
 
        /**
         * Set the positive button text and it's listener
         * @param positiveButtonText
         * @param listener
         * @return
         */
        public Builder setPositiveButton(String positiveButtonText,
                DialogInterface.OnClickListener listener) {
            this.positiveButtonText = positiveButtonText;
            this.positiveButtonClickListener = listener;
            return this;
        }
 
        /**
         * Set the negative button resource and it's listener
         * @param negativeButtonText
         * @param listener
         * @return
         */
        public Builder setNegativeButton(int negativeButtonText,
                DialogInterface.OnClickListener listener) {
            this.negativeButtonText = (String) context
                    .getText(negativeButtonText);
            this.negativeButtonClickListener = listener;
            return this;
        }
 
        /**
         * Set the negative button text and it's listener
         * @param negativeButtonText
         * @param listener
         * @return
         */
        public Builder setNegativeButton(String negativeButtonText,
                DialogInterface.OnClickListener listener) {
            this.negativeButtonText = negativeButtonText;
            this.negativeButtonClickListener = listener;
            return this;
        }
 
        /**
         * Create the custom dialog
         */
        public CustomDialog create() {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            // instantiate the dialog with the custom Theme
            final CustomDialog dialog = new CustomDialog(context, 
            		R.style.Dialog);
            View layout = inflater.inflate(R.layout.dialog, null);
            dialog.addContentView(layout, new LayoutParams(
                    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
            // set the dialog title
            ((TextView) layout.findViewById(R.id.title)).setText(title);
            // set the confirm button
            if (positiveButtonText != null) {
                ((Button) layout.findViewById(R.id.positiveButton))
                        .setText(positiveButtonText);
                if (positiveButtonClickListener != null) {
                    ((Button) layout.findViewById(R.id.positiveButton))
                            .setOnClickListener(new View.OnClickListener() {
                                public void onClick(View v) {
                                    positiveButtonClickListener.onClick(
                                    		dialog, 
                                            DialogInterface.BUTTON_POSITIVE);
                                }
                            });
                }
            } else {
                // if no confirm button just set the visibility to GONE
                layout.findViewById(R.id.positiveButton).setVisibility(
                        View.GONE);
            }
            // set the cancel button
            if (negativeButtonText != null) {
                ((Button) layout.findViewById(R.id.negativeButton))
                        .setText(negativeButtonText);
                if (negativeButtonClickListener != null) {
                    ((Button) layout.findViewById(R.id.negativeButton))
                            .setOnClickListener(new View.OnClickListener() {
                                public void onClick(View v) {
                                    positiveButtonClickListener.onClick(
                                    		dialog, 
                                            DialogInterface.BUTTON_NEGATIVE);
                                }
                            });
                }
            } else {
                // if no confirm button just set the visibility to GONE
                layout.findViewById(R.id.negativeButton).setVisibility(
                        View.GONE);
            }
            // set the content message
            if (message != null) {
                ((TextView) layout.findViewById(
                		R.id.message)).setText(message);
            } else if (contentView != null) {
                // if no message set
                // add the contentView to the dialog body
                ((LinearLayout) layout.findViewById(R.id.content))
                        .removeAllViews();
                ((LinearLayout) layout.findViewById(R.id.content))
                        .addView(contentView, 
                                new LayoutParams(
                                        LayoutParams.WRAP_CONTENT, 
                                        LayoutParams.WRAP_CONTENT));
            }
            dialog.setContentView(layout);
            return dialog;
        }
 
    }
 
}

Using the custom Builder

Using the custom Builder is just as simple as using the default AlertDialog.Builder :

/**
 * Build the desired Dialog
 * CUSTOM or DEFAULT
 */
@Override
public Dialog onCreateDialog(int dialogId) {
    Dialog dialog = null;
    switch (dialogId) {
        case CUSTOM_DIALOG :
            CustomDialog.Builder customBuilder = new
                CustomDialog.Builder(CustomDialogActivity.this);
            customBuilder.setTitle("Custom title")
                .setMessage("Custom body")
                .setNegativeButton("Cancel", 
                        new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        CustomDialogActivity.this
                        .dismissDialog(CUSTOM_DIALOG);
                    }
                })
                .setPositiveButton("Confirm", 
                        new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
            dialog = customBuilder.create();
            break;
        case DEFAULT_DIALOG :
            AlertDialog.Builder alertBuilder = new
                AlertDialog.Builder(CustomDialogActivity.this);
            alertBuilder.setTitle("Default title")
                .setMessage("Default body")
                .setNegativeButton("Cancel", 
                		new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                })
                .setPositiveButton("Confirm", 
                		new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        CustomDialogActivity.this
                        .dismissDialog(DEFAULT_DIALOG);
                    }
                });
            dialog = alertBuilder.create();
            break;
    }
    return dialog;
}

You can browse the full source code of the Eclipse project here : SampleCustomDialog

Enjoy !

Posted by 1010
04.Anddoid/listView2010. 8. 15. 01:00
반응형

Sometimes we need to maintain the selected position in a list. This example was taken from a real-world, off-road application where users interact with the application in a bumpy environment and need a fool proof way of changing the sort order of items in a list. When an item is clicked in the list and that row becomes the selected row as indicated by a different background color. The user can then change the position of the selected row using the Move Up and Move Down buttons on the screen.

Here’s a screen-shot of this example running in the emulator.

Android Selected State List Example

Android Selected State List Example

Keeping State

Where do we keep the selected state if the view component doesn’t support this? I considered extending data objects to maintain selected state but that would only pollute data objects with view-only state information. Another thought was to extend List and keep the state there. But that would mean shoehorning view-only state info into already stable and settled Lists used throughout the application. In the end, it was decided that a custom ArrayAdapter, SelectedAdapter, was the best place to keep this state. We only care about the selected state during the lifespan of the ListView, no reason to lug around extra baggage. This turned out to be a very simple solution.

The Activity class for this example loads the view and has event handlers for list item selection and the Move Up and Move Down buttons.

package com.bestsiteinthemultiverse.selected;

import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;

public class SelectedActivity extends Activity {

	private SelectedAdapter selectedAdapter;
	private ArrayList list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.selected_example);

		// populate the model - a simple a list
		list = new ArrayList();
		list.add("Apple");
		list.add("Orange");
		list.add("Grape");

		// create our SelectedAdapter
		selectedAdapter = new SelectedAdapter(this,0,list);
		selectedAdapter.setNotifyOnChange(true);

        ListView listview = (ListView) findViewById(R.id.listExample);
        listview.setAdapter(selectedAdapter);

        listview.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView arg0, View view,
                                           int position, long id) {
				// user clicked a list item, make it "selected"
				selectedAdapter.setSelectedPosition(position);
			}
        });

        // move up event handler
        Button btnMoveUp = (Button) findViewById(R.id.btnMoveUp);
        btnMoveUp.setOnClickListener(new View.OnClickListener() {
           public void onClick(View arg0) {
        	   moveUp();
           }
        });

        // move down event handler
        Button btnMoveDown = (Button) findViewById(R.id.btnMoveDown);
        btnMoveDown.setOnClickListener(new View.OnClickListener() {
           public void onClick(View arg0) {
        	   moveDown();
           }
        });
	}

	// Move selected item "up" in the ViewList.
	private void moveUp(){
    	int selectedPos = selectedAdapter.getSelectedPosition();
    	if (selectedPos > 0 ){
    		String str = list.remove(selectedPos);
    		list.add(selectedPos-1, str);
    		// set selected position in the adapter
    		selectedAdapter.setSelectedPosition(selectedPos-1);
    	}
	}

	// Move selected item "down" in the ViewList.
	private void moveDown(){
    	int selectedPos = selectedAdapter.getSelectedPosition();
    	if (selectedPos < list.size()-1 ){
    		String str = list.remove(selectedPos);
    		list.add(selectedPos+1, str);
    		// set selected position in the adapter
    		selectedAdapter.setSelectedPosition(selectedPos+1);
    	}
	}

}

The SelectedAdapter class used in this example does three things: it extends ArrayAdapter, maintains the selected state and loads a custom row view. If that row is the selected row, a background color is applied to indicate the selected state. If you need to use something other than .toString() to describe your objects, call your method in the overridden getView(). Another thing to notice is the reference for convertView is reused from call-to-call. This saves the expense of inflating the layout every time getView() is called.

package com.bestsiteinthemultiverse.selected;

	import java.util.List;
	import android.content.Context;
	import android.graphics.Color;
	import android.view.LayoutInflater;
	import android.view.View;
	import android.view.ViewGroup;
	import android.widget.ArrayAdapter;
	import android.widget.TextView;

	public class SelectedAdapter extends ArrayAdapter{

		// used to keep selected position in ListView
		private int selectedPos = -1;	// init value for not-selected

		public SelectedAdapter(Context context, int textViewResourceId,
                           List objects) {
			super(context, textViewResourceId, objects);
		}

		public void setSelectedPosition(int pos){
			selectedPos = pos;
			// inform the view of this change
			notifyDataSetChanged();
		}

		public int getSelectedPosition(){
			return selectedPos;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
		    View v = convertView;

		    // only inflate the view if it's null
		    if (v == null) {
		        LayoutInflater vi
                            = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		        v = vi.inflate(R.layout.selected_row, null);
		    }

		    // get text view
	        TextView label = (TextView)v.findViewById(R.id.txtExample);

	        // change the row color based on selected state
	        if(selectedPos == position){
	        	label.setBackgroundColor(Color.CYAN);
	        }else{
	        	label.setBackgroundColor(Color.WHITE);
	        }

	        label.setText(this.getItem(position).toString());
	        /*
	        // to use something other than .toString()
	        MyClass myobj = (MyClass)this.getItem(position);
	        label.setText(myobj.myReturnsString());
	        */
	        return(v);
		}
	}

The layout xml used for the row view.


    <?xml version="1.0" encoding="utf-8"?> <TextView android:id="@+id/txtExample" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textColor="#000000" android:background="#FF0000" />
Posted by 1010
04.Anddoid/listView2010. 8. 15. 01:00
반응형

Android Series: Custom ListView items and adapters

Posted by admin | Posted in Mobile Development | Posted on 20-05-2009

84

This is a short tutorial on how to populate your android list view, with data downloaded from the internet or other sources, using ArrayAdapter. ListView items view is declared in a separate XML file and displayed using custom adapter class.
First things first, so go ahead and create a new project using Eclipse equipped with ADT plugin.
The project described below assumes you have a list of objects created, this can be either downloaded from the internet as XML and parsed to create ArrayList of your custom objects or anything you imagine. I will not go into details on this tutorial how to create such an ArrayList but your imagination is the limit. Parsing XML downloaded from the net will be covered in the next tutorial coming up soon.


Click File -> New -> Project and select the ‘Android Project’ wizard:

android11

Click next and fill out the next screen with the following values:

android21

Once you have filled out all the necessary data you can click finish.
Your new project has just been created. Now lets modify it a bit to display our custom made list.
Open up SoftwarePassionView.java in the eclipse editor and change the class file to the following:

1. Define necessary member variables we will use later in our class

private ProgressDialog m_ProgressDialog = null;
    private ArrayList<Order> m_orders = null;
    private OrderAdapter m_adapter;
    private Runnable viewOrders;

m_ProgressDialog is a small pop up displaying information that your data is being downloaded or retrieved other way.
m_orders is an ArrayList which will hold our data downloaded from the internet or acquired other way
m_adapter is our custom class extending ArrayAdapter
viewOrders is a runnable for downloading data from the internet in a separate thread

To import whatever you can at this point click Ctrl+Shit+O, some classes like Order or OrderAdapter are not created yet but don’t worry we will come to that point soon.
Another important note at this point is that your SoftwarePassoinView should extend ListActivity instead of simple Activity.
Your class should look more or less something like this now:

package com.softberries.lve;

import java.util.ArrayList;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.os.Bundle;

public class SoftwarePassionView extends ListActivity{
   
    private ProgressDialog m_ProgressDialog = null;
    private ArrayList<Order> m_orders = null;
    private OrderAdapter m_adapter;
    private Runnable viewOrders;
   
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Now lets create our simple Order class holding single order.
Right click on the project and and select ‘New’ -> ‘Class’, name it order and open it up in the editor.
The source code for our orders looks like this:

package com.softberries.lve;

public class Order {
   
    private String orderName;
    private String orderStatus;
   
    public String getOrderName() {
        return orderName;
    }
    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }
    public String getOrderStatus() {
        return orderStatus;
    }
    public void setOrderStatus(String orderStatus) {
        this.orderStatus = orderStatus;
    }
}

The Order class is very simple and contains only 2 strings with getters and setter generated for them
Now lets change our main.xml file to hold our custom list items:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
<ListView
    android:id="@+id/android:list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    />
<TextView
    android:id="@+id/android:empty"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:text="@string/main_no_items"/>
</LinearLayout>

This layout will display our list items if any and if the list is empty it will display ‘No orders to display’ string defined in string.xml resource file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, SoftwarePassionView!</string>
    <string name="app_name">Software Passion</string>
    <string name="main_no_items">No orders to display</string>
</resources>



Our list item (single row on the list) have a custom layout as well, defined in row.xml file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip">
    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_marginRight="6dip"
        android:src="@drawable/icon" />
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="fill_parent">
        <TextView
            android:id="@+id/toptext"
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:gravity="center_vertical"
        />
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:id="@+id/bottomtext"
            android:singleLine="true"
            android:ellipsize="marquee"
        />
    </LinearLayout>
</LinearLayout>

Single row example has been borrowed from the romain Guy website here

Ok, so we have all our layouts defined in the res folder under layout. Now its time to go back to our code and create our custom OrderAdapter class which will manage our list of orders:

private class OrderAdapter extends ArrayAdapter<Order> {

        private ArrayList<Order> items;

        public OrderAdapter(Context context, int textViewResourceId, ArrayList<Order> items) {
                super(context, textViewResourceId, items);
                this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                View v = convertView;
                if (v == null) {
                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    v = vi.inflate(R.layout.row, null);
                }
                Order o = items.get(position);
                if (o != null) {
                        TextView tt = (TextView) v.findViewById(R.id.toptext);
                        TextView bt = (TextView) v.findViewById(R.id.bottomtext);
                        if (tt != null) {
                              tt.setText("Name: "+o.getOrderName());                            }
                        if(bt != null){
                              bt.setText("Status: "+ o.getOrderStatus());
                        }
                }
                return v;
        }
}

This is a private class and should be added to our SoftwarePassionView. This is extended ListAdapter which inside overriden getView method returns our row with assigned string values to the textfields defined in row.xml.

A huge part of our application is already done. Now we have to add some modifications to the onCreate method to initialize everything properly and add a method retrieving our orders from somewhere, lets start with the latter:

private void getOrders(){
          try{
              m_orders = new ArrayList<Order>();
              Order o1 = new Order();
              o1.setOrderName("SF services");
              o1.setOrderStatus("Pending");
              Order o2 = new Order();
              o2.setOrderName("SF Advertisement");
              o2.setOrderStatus("Completed");
              m_orders.add(o1);
              m_orders.add(o2);
                 Thread.sleep(2000);
              Log.i("ARRAY", ""+ m_orders.size());
            } catch (Exception e) {
              Log.e("BACKGROUND_PROC", e.getMessage());
            }
            runOnUiThread(returnRes);
        }



Instead of creating our simple orders in the method above you could of course download them from somewhere and assign the result to the m_orders array list. The method runOnUIThread is a utility method for running tasks back on the main UI thread after the job is done on the separate thread created for long running tasks. We will call our getOrders method from a separate thread.

The returnRes runnable adds newly retrieved Order object to our custom Adapter and notifies it of the data change:

private Runnable returnRes = new Runnable() {

            @Override
            public void run() {
                if(m_orders != null && m_orders.size() > 0){
                    m_adapter.notifyDataSetChanged();
                    for(int i=0;i<m_orders.size();i++)
                    m_adapter.add(m_orders.get(i));
                }
                m_ProgressDialog.dismiss();
                m_adapter.notifyDataSetChanged();
            }
          };

Now lets move to our overriden onCreate method. We will initialize here all the member variables as well as start a new thread retrieving our orders:

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        m_orders = new ArrayList<Order>();
        this.m_adapter = new OrderAdapter(this, R.layout.row, m_orders);
                setListAdapter(this.m_adapter);
       
        viewOrders = new Runnable(){
            @Override
            public void run() {
                getOrders();
            }
        };
    Thread thread =  new Thread(null, viewOrders, "MagentoBackground");
        thread.start();
        m_ProgressDialog = ProgressDialog.show(SoftwarePassionView.this,    
              "Please wait...", "Retrieving data ...", true);
    }

After initialization, we start new thread using the viewOrders runnable and show the progress dialog which we close once the orders are retrieved.
Now you should be able to run your application. After the application starts it spawns new thread and displays the loader:

screen_load screen1

And thats it. You can add an Item Click Listener to your list to start new activities etc.
Full source code for the SoftwarePassionView below:

package com.softberries.lve;

import java.util.ArrayList;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class SoftwarePassionView extends ListActivity{
   
    private ProgressDialog m_ProgressDialog = null;
    private ArrayList<Order> m_orders = null;
    private OrderAdapter m_adapter;
    private Runnable viewOrders;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        m_orders = new ArrayList<Order>();
        this.m_adapter = new OrderAdapter(this, R.layout.row, m_orders);
        setListAdapter(this.m_adapter);
       
        viewOrders = new Runnable(){
            @Override
            public void run() {
                getOrders();
            }
        };
        Thread thread =  new Thread(null, viewOrders, "MagentoBackground");
        thread.start();
        m_ProgressDialog = ProgressDialog.show(SoftwarePassionView.this,    
              "Please wait...", "Retrieving data ...", true);
    }
    private Runnable returnRes = new Runnable() {

        @Override
        public void run() {
            if(m_orders != null && m_orders.size() > 0){
                m_adapter.notifyDataSetChanged();
                for(int i=0;i<m_orders.size();i++)
                m_adapter.add(m_orders.get(i));
            }
            m_ProgressDialog.dismiss();
            m_adapter.notifyDataSetChanged();
        }
    };
    private void getOrders(){
          try{
              m_orders = new ArrayList<Order>();
              Order o1 = new Order();
              o1.setOrderName("SF services");
              o1.setOrderStatus("Pending");
              Order o2 = new Order();
              o2.setOrderName("SF Advertisement");
              o2.setOrderStatus("Completed");
              m_orders.add(o1);
              m_orders.add(o2);
              Thread.sleep(5000);
              Log.i("ARRAY", ""+ m_orders.size());
            } catch (Exception e) {
              Log.e("BACKGROUND_PROC", e.getMessage());
            }
            runOnUiThread(returnRes);
        }
    private class OrderAdapter extends ArrayAdapter<Order> {

        private ArrayList<Order> items;

        public OrderAdapter(Context context, int textViewResourceId, ArrayList<Order> items) {
                super(context, textViewResourceId, items);
                this.items = items;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                View v = convertView;
                if (v == null) {
                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    v = vi.inflate(R.layout.row, null);
                }
                Order o = items.get(position);
                if (o != null) {
                        TextView tt = (TextView) v.findViewById(R.id.toptext);
                        TextView bt = (TextView) v.findViewById(R.id.bottomtext);
                        if (tt != null) {
                              tt.setText("Name: "+o.getOrderName());                            }
                        if(bt != null){
                              bt.setText("Status: "+ o.getOrderStatus());
                        }
                }
                return v;
        }
}
}



Enjoy!

Posted by 1010
04.Anddoid2010. 8. 12. 17:51
반응형


강좌 작성환경
SDK Version : Android SDK 1.5, release 2
ADT Version : 0.9.1

추후 SDK업데이트로 인해 글의 내용과 실제 내용간 차이가 있을 수 있습니다.

이번 강좌에서는 리스트뷰에 사용자가 원하는 레이아웃대로 항목을 표시하도록 만들어보겠습니다.

지난 강좌 (2009/06/04 - [안드로이드 이야기/안드로이드 입문] - #11. List 집중공략! - (1) 기본 다지기) 에서 설명했듯이, ListView는 ListView 하나로 이루어지는 것이 아니라 리스트뷰에 표시할 항목을 담고 있는 리스트 객체, 리스트 객체의 데이터를 리스트뷰에서 표시할 수 있게 해주는 어댑터, 최종적으로 화면에 리스트를 표시해 주는 리스트뷰(ListView)로 구성됩니다.

이 세 구성요소 중 가장 중요한 역할을 하는 것은 단연 어댑터(Adapter)라고 할 수 있습니다.
어댑터는 리스트 객체를 리스트뷰에서 표시해주는 기능, 즉 리스트 객체의 내용과 리스트 항목의 레이아웃을 연결시켜주는 역할을 합니다. 따라서, 어댑터가 없다면 일단 리스트 객체의 데이터를 리스트뷰에 표시하는 것은 불가능합니다.

그럼, 지난 강좌에서 썼던 ArrayAdapter를 한번 살펴볼까요?

1.ArrayAdapter<String> aa = new ArrayAdapter<String>(this,
2.                android.R.layout.simple_list_item_1, list);


ArrayAdapter의 생성자에서 두번째 인자, android.R.layout.simple_list_item_1은 리스트에서 각 항목을 표시할 레이아웃을 뜻합니다. 이 레이아웃은 리스트 한 줄에 한 줄의 텍스트만이 표시되도록 되어있죠.

하지만, 리스트에 표시할 항목이 하나가 아닌 여러 가지가 있는 경우가 있습니다. 당장 연락처 어플리케이션이나 설정 어플리케이션을 봐도 리스트 항목 하나에 텍스트 하나만 표시되지는 않죠. 그래소, 이번 강좌에서는 리스트 항목 하나에 두 줄의 텍스트, 그리고 하나는 큰 텍스트, 하나는 작은 텍스트로 표시되도록 만들어 볼 것입니다.

이렇게, 리스트에 사용자가 원하는 레이아웃을 표시하기 위해서는 사용자 정의 어댑터를 만들어야 합니다. ArrayAdapter는 한 줄에 하나의 텍스트만을 표시하도록 코딩되어있으므로, 우리는 이와는 달리 두 줄로, 텍스트 두 개가 표시되게끔 어댑터를 만들어야겠지요?


이번 강좌에서는 위 스크린샷처럼 두 개의 텍스트를 표시하고, 위에는 이름, 아래는 전화번호를 표시하는 어댑터를 만들어보도록 하겠습니다.

이를 구현하기 위해 이름과 전화번호 데이터를 각각 String 형식으로 저장하고 있는 Person 클래스를 만들었으며, 이 객체의 저장은 ArrayList를 이용하였습니다. 새로 만들 어댑터의 이름은 PersonAdapter로 지정하였습니다.

일단, 전체 소스 코드와 레이아웃을 살펴보도록 하겠습니다.

[소스코드 : ListExample.java]

01.package com.androidhuman.ListExample;
02.  
03.import java.util.ArrayList;
04.import android.app.ListActivity;
05.import android.content.Context;
06.import android.os.Bundle;
07.import android.view.LayoutInflater;
08.import android.view.View;
09.import android.view.ViewGroup;
10.import android.widget.ArrayAdapter;
11.import android.widget.TextView;
12.  
13.public class ListExample extends ListActivity{ // ListActivity를 상속받습니다.
14.         
15.          
16.    @Override
17.    public void onCreate(Bundle savedInstanceState) {
18.        super.onCreate(savedInstanceState);
19.        setContentView(R.layout.main);
20.        ArrayList<Person> m_orders = new ArrayList<Person>();
21.          
22.        Person p1 = new Person("안드로이드", "011-123-4567"); // 리스트에 추가할 객체입니다.
23.        Person p2 = new Person("구글", "02-123-4567"); // 리스트에 추가할 객체입니다.
24.          
25.        m_orders.add(p1); // 리스트에 객체를 추가합니다.
26.        m_orders.add(p2); // 리스트에 객체를 추가합니다.
27.          
28.        PersonAdapter m_adapter = new PersonAdapter(this, R.layout.row, m_orders); // 어댑터를 생성합니다.
29.        setListAdapter(m_adapter); // 
30.                  
31.    }
32.      
33.    private class PersonAdapter extends ArrayAdapter<Person> {
34.  
35.        private ArrayList<Person> items;
36.  
37.        public PersonAdapter(Context context, int textViewResourceId, ArrayList<Person> items) {
38.                super(context, textViewResourceId, items);
39.                this.items = items;
40.        }
41.        @Override
42.        public View getView(int position, View convertView, ViewGroup parent) {
43.                View v = convertView;
44.                if (v == null) {
45.                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
46.                    v = vi.inflate(R.layout.row, null);
47.                }
48.                Person p = items.get(position);
49.                if (p != null) {
50.                        TextView tt = (TextView) v.findViewById(R.id.toptext);
51.                        TextView bt = (TextView) v.findViewById(R.id.bottomtext);
52.                        if (tt != null){
53.                            tt.setText(p.getName());                            
54.                        }
55.                        if(bt != null){
56.                                bt.setText("전화번호: "+ p.getNumber());
57.                        }
58.                }
59.                return v;
60.        }
61.}
62.    class Person {
63.          
64.        private String Name;
65.        private String Number;
66.          
67.        public Person(String _Name, String _Number){
68.            this.Name = _Name;
69.            this.Number = _Number;
70.        }
71.          
72.        public String getName() {
73.            return Name;
74.        }
75.  
76.        public String getNumber() {
77.            return Number;
78.        }
79.  
80.    }
81.}

[ 레이아웃 : main.xml / 액티비티의 레이아웃 ]

01.<?xml version="1.0" encoding="utf-8"?>
02.<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03.   android:orientation="vertical"
04.   android:layout_width="fill_parent"
05.   android:layout_height="fill_parent"
06.   >
07.<ListView
08.    android:id="@+id/android:list"
09.    android:layout_width="fill_parent"
10.    android:layout_height="fill_parent"
11.    />
12.<TextView
13.    android:id="@+id/android:empty"
14.    android:layout_width="fill_parent"
15.    android:layout_height="fill_parent"
16.    android:text="tets"/>
17.</LinearLayout>



[레이아웃 : row.xml / 리스트 항목의 레이아웃 ]

01.<?xml version="1.0" encoding="utf-8"?>
02.<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03.    android:layout_width="fill_parent"
04.    android:layout_height="wrap_content"
05.    android:padding="6dip" android:orientation="vertical">
06.        <TextView
07.            android:id="@+id/toptext"
08.            android:layout_width="fill_parent"
09.            android:gravity="center_vertical"
10.        android:layout_height="wrap_content" android:textSize="20px"/>
11.        <TextView
12.            android:layout_width="fill_parent"
13.            android:id="@+id/bottomtext"
14.            android:singleLine="true"
15.            android:ellipsize="marquee"
16.        android:layout_height="wrap_content"/>
17.      
18.</LinearLayout>



우선, 이름과 전화번호 데이터를 저장할 수 있는 객체인 Person 클래스부터 살펴보겠습니다. 아주 간단~한 구조로 이루어져 있습니다.


01.class Person {
02.      
03.    private String Name;
04.    private String Number;
05.      
06.    public Person(String _Name, String _Number){
07.        this.Name = _Name;
08.        this.Number = _Number;
09.    }
10.      
11.    public String getName() {
12.        return Name;
13.    }
14.    public String getNumber() {
15.        return Number;
16.    }
17.}

Person 클래스 내에는 각각 이름(Name)과 전화번호(Number)를 저장하는 String형 변수가 2개 있고, 생성자를 통해 각각의 값을 초기화시키며 getName()메소드와 getNumber()를 통해 각각의 데이터를 반환하는 구조입니다.

그럼, 이번 강좌의 핵심인 어댑터, PersonAdapter를 보도록 하겠습니다.

01.private class PersonAdapter extends ArrayAdapter<Person> {
02.  
03.        private ArrayList<Person> items;
04.  
05.        public PersonAdapter(Context context, int textViewResourceId, ArrayList<Person> items) {
06.                super(context, textViewResourceId, items);
07.                this.items = items;
08.        }
09.        @Override
10.        public View getView(int position, View convertView, ViewGroup parent) {
11.                View v = convertView;
12.                if (v == null) {
13.                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
14.                    v = vi.inflate(R.layout.row, null);
15.                }
16.                Person p = items.get(position);
17.                if (p != null) {
18.                        TextView tt = (TextView) v.findViewById(R.id.toptext);
19.                        TextView bt = (TextView) v.findViewById(R.id.bottomtext);
20.                        if (tt != null){
21.                            tt.setText(p.getName());                            
22.                        }
23.                        if(bt != null){
24.                                bt.setText("전화번호: "+ p.getNumber());
25.                        }
26.                }
27.                return v;
28.        }
29.}


PersonAdapter 클래스는 ArrayAdapter 클래스를 상속하여, ArrayList의 데이터를 받아와 이를 ListView에 표시되게 해 줍니다. ArrayAdapter 클래스가 하나의 TextView만을 제공했던 것과 달리, PersonAdapter는 우리가 이 강좌에서 사용하는 Person 객체에 맞게끔 구현되어 있습니다.

그럼, PersonAdapter의 코드를 차근차근 보도록 하죠.
1.private class PersonAdapter extends ArrayAdapter<Person> {
2.    private ArrayList<Person> items;
3.    public PersonAdapter(Context context, int textViewResourceId, ArrayList<Person> items) {
4.            super(context, textViewResourceId, items);
5.            this.items = items;
6.    }

일단, PersonAdapter 클래스 내부에 우리가 리스트에 표시할 항목을 저장할 리스트객체 (ArrayList items)가 보이네요. 이는 PersonAdapter 생성자를 통해 넘어온 리스트객체의 데이터를 저장하는 역할을 합니다. 생성자 내부에서는 생성자의 인자로 넘어온 리스트 객체(ArrayList items)를 PersonAdapter 내부의 리스트 객체 (this.items)로 연결시켜주는 모습을 확인할 수 있습니다.

01.@Override
02.        public View getView(int position, View convertView, ViewGroup parent) {
03.                View v = convertView;
04.                if (v == null) {
05.                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
06.                    v = vi.inflate(R.layout.row, null);
07.                }
08.                Person p = items.get(position);
09.                if (p != null) {
10.                        TextView tt = (TextView) v.findViewById(R.id.toptext);
11.                        TextView bt = (TextView) v.findViewById(R.id.bottomtext);
12.                        if (tt != null){
13.                            tt.setText(p.getName());                            
14.                        }
15.                        if(bt != null){
16.                                bt.setText("전화번호: "+ p.getNumber());
17.                        }
18.                }
19.                return v;


getView()메소드는 PersonAdapter 클래스의 핵심이라 할 수 있습니다. 우리가 원하는 기능 (리스트 항목에 두 줄의 텍스트가 표시되도록..) 을 이곳에서 구현하고 있거든요. 우선, getView()메소드의 API부터 보도록 하죠,


public abstract View getView (int position, View convertView, ViewGroup parent)
Get a View that displays the data at the specified position in the data set. You can either create a View manually or inflate it from an XML layout file. When the View is inflated, the parent View (GridView, ListView...) will apply default layout parameters unless you use inflate(int, android.view.ViewGroup, boolean) to specify a root view and to prevent attachment to the root.

Parameters
position
  The position of the item within the adapter's data set of the item whose view we want.
convertView  The old view to reuse, if possible.
Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.
parent  The parent that this view will eventually be attached to

Returns
A View corresponding to the data at the specified position.


getView()메소드는 리스트 전체의 레이아웃을 책임지는(?) 것이 아니라, 리스트 각 항목에 대한 레이아웃만을 책임집니다. API에서도 position; 리스트 항목의 인덱스를 받아 그에 해당하는 레이아웃을 출력해주는 것을 볼 수 있습니다.


그럼, 각 항목에 대한 레이아웃은 어떻게 지정해줄까요?
아래의 코드에서 확인해보죠.

1.View v = convertView;
2.                if (v == null) {
3.                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
4.                    v = vi.inflate(R.layout.row, null);
5.                }


화면의 구성 단위인 View에 우리가 원하는 레이아웃을 적용시켜주기 위해 LayoutInflater를 사용하였습니다. LayoutInflater에 시스템 서비스를 받아온 후, inflate()메소드를 통해 레이아웃을 적용시켜주면 됩니다. inflate()메소드에 대한 API는 여기를 참조하세요.

01.Person p = items.get(position);
02.                if (p != null) {
03.                        TextView tt = (TextView) v.findViewById(R.id.toptext);
04.                        TextView bt = (TextView) v.findViewById(R.id.bottomtext);
05.                        if (tt != null){
06.                            tt.setText(p.getName());                            
07.                        }
08.                        if(bt != null){
09.                                bt.setText("전화번호: "+ p.getNumber());
10.                        }
11.                }
12.                return v;

이 부분부터는 실질적으로 Person 객체 내의 데이터를 화면에 표시해주는 역할을 해주고 있습니다.
아까 Person객체를 저장하고 있는 ArrayList를 PersonAdapter 내의 리스트에 저장했는데, 이 List로부터 리스트의 해당 인덱스의 데이터를 받아오게 됩니다. 그리고, 이 데이터가 null(데이터 없음) 이 아니라면, 우리가 지금까지 해왔던 방법과 똑같이 findViewById()메소드를 통해 레이아웃 객체를 참조하여 데이터를 화면에 표시해주게 됩니다.
데이터와 레이아웃을 연결해주는 작업이 끝나면, 최종적으로 작업이 완료된 View를 반환하여 화면에 표시하도록 합니다.


휴...
지금까지 그 어떤 강좌보다도 공부할 때에도 애를 많이 먹었고, 강좌 쓰는 시간도 많이 걸렸네요.
하지만, 그만큼 더 이것저것 많이 알게 된 계기가 되지 않않나 싶습니다 ^^;;

위의 방법을 사용해서 리스트에 이미지가 나오게도 할 수 있으니, 응용해서 한번 연습해보세요~ ㅎㅎ
다음 강좌는 일단 데이터베이스(SQLite)를 다루는 것을 목표로.... 서서히 공부해야나가야겠습니다 :)

p.s 강좌 소스 코드 첨부합니다.
Posted by 1010
90.개발관련문서2010. 8. 9. 14:32
반응형

SVN을 사용하다보면 .SVN이라는 폴더가 생성되지요.
버젼 충돌 때문이나 복사하려고 할때 이것을 지우려고 골치아픕니다. -_-;
레지스트리에 간단히 등록하고 한번에 지우는 방법이 있는데 편라하더군요.

아래 내용을  테스트 에디터로 카피 하신후 svn_folder_delete.reg 라는 이름으로 저장하시고 더블 클릭하면 레지스트리에 등록됩니다.
SVN 폴더에서 마우스 오른쪽 버튼 클릭하면 메뉴가 보입니다.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN]
@="Delete SVN Folders"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN\command]
@="cmd.exe /c \"TITLE Removing SVN Folders in %1 && COLOR 9A && FOR /r \"%1\" %%f IN (.svn) DO RD /s /q \"%%f\" \""


출처 : http://www.digipine.com/?mid=programming&document_srl=1356&page=2&sort_index=voted_count&order_type=desc

Posted by 1010
04.Anddoid2010. 8. 4. 17:17
반응형



 
모토로라 개발툴 다운받기

주요 기능

Version 1.1

Complete Development Package

통합 개발 패키지:
한 번의 인스톨로 이클립스 3.5와 안드로이드 개발 툴(ADT)이 통합된 개발 환경을 설치할 수 있으며 최신 안드로이드 SDK의 자동 다운로드와 설정을 할 수 있다.


Code Snippets
예제 코드: 제공되는 템플릿을 통해서 안정적이고 더 나은 성능의 어플리케이션을 작성할 수 있도록 자주 사용되는 코드를 제공한다.


Application Creation Wizards
어플 리케이션 생성 위자드: 기본 안드로이드 클래스를 쉽게 생성할 수 있다. 예를 들어, 브로드캐스트 리시버, 콘텐트 프로바이더, 서비스, 액티비티


Database Management
데이 터베이스 관리: 핸드셋과 에뮬레이트에서 SQLite 데이터베이스를 볼 수 있는 뷰와 에디트 기능 제공


Localization Files Editor
로컬라이제이션 파일 에디터:
지역화된 파일들을 쉽게 생성하고 관리할 수 있는 기능 제공


Handset Emulators
핸드 셋 에뮬레이터: IDE와 타겟 변경 없이 MOTODEV 스튜디오에서 통합된 안드로이드 에뮬레이터 상의 테스트 지원. 향후에는 모토로라만의 핸드셋 에뮬레이터 사용 가능


Virtual Developer Lab
가상 개발자 랩: 원격으로 호스팅된 핸드셋에서 어플리케이션 실행


Deploy Packages

디플로이 패키지:
어플리케이션을 타겟 핸드셋이나 에뮬레이터로 로딩 지원(불편한 커맨드 라인이 아닌 쉬운 인터페이스 제공)


Application Signing
어플 리케이션 인증: 어플리케이션 인증 기능 제공


Marketing Integration
마켓 통합: MOTODEV 스튜디오 내에서 안드로이드 어플리케이션 마켓 접속 가능


Target Motorola Handsets
타겟 모토로라 핸드셋: 모토로라 안드로이드 핸드셋에 연결된 상태에서 어플리케이션 디버그 및 실행 지원


Context-Sensitive Help and Integrated Documentation
컨텍 스트 중심의 도움말 및 통합된 문서: 유저 가이드 및 트러블슈팅 팁들 참고 가능

Posted by 1010
04.Anddoid2010. 8. 4. 14:13
반응형
개발 Q&A에서 가끔 보던 질문이고 저 또한 질문 했던 내용 입니다.

이미지 버튼을 사용시 버튼을 클릭했을때와 땟을때의 처리를 해결 못해서 기본 안드로이드 버튼만
사용중이었습니다.

해결을 하려고 setBackgroundResource도 생각했었습니다만...

setBackgroundResource를 사용했을경우에는 버튼을 클릭했을경우엔 이미지가 바뀌지만
땟을경우에 그이미지가 그대로 남아있어서 스레드를 돌릴까도 생각도 했었는데요...

몇주전까지만해도 구글링을 해서도 찾지 못했던 내용 이었는데 오늘 그 해답을 찾았습니다 ㅠ.ㅠ

다른 api내용 찾다가 우연히 발견하게 되었습니다.

res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:gravity="center" xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="@string/hello" />

    <ImageButton android:id="@+id/imgBtn"
    android:background="@+drawable/playbutton_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </ImageButton>
</LinearLayout>


res/drawable/playbutton_click.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="false" android:state_pressed="true"
        android:drawable="@drawable/btn1" />
    <item android:drawable="@drawable/btn" />
</selector>

drawable의폴더에는
버튼이 눌러졌을때의이미지와 버튼을 땟을때의 이미지파일이 존재하여야 합니다.




이렇게 간단하게 해결될 문제였답니다 ㅠ.ㅠ

많이 허접한 내용이지만 아직도 해결 못하신분들을위해 적어 봤습니다.
Posted by 1010
반응형
[1] 일단 터널링 프로그램부터 설치.
      네이트온이 설치 안되어있다면, 당연히 네이트온부터 설치하세요.

[2] 프로그램 실행하기

그리고 실행후, 다음과 같이 해주고


[3] 네이트온 연결(방화벽)설정 변경



이렇게 바꿔준후, 로그인하면 OK


출처 : http://tost.tistory.com/122
Posted by 1010
04.Anddoid2010. 7. 30. 15:33
반응형
  1. 이하는 물리설계 전 업무분석 및 현업화의 회의를 통해 도출해야 하는 내용들이다.
  2. 경고

    1. 어플리케이션을 반드시 고려해서 설계를 진행해야 한다.

      1. PK는 단순히 DB상에서의 의미만이 아니다. 어플리케이션의 Map이나 List의 index 및 복합키 객체를 고려해야 한다.
      2. DB컬럼터입, 관계정의, 정규화 등은 모두 Class 기준으로 생성하는게 무난하다.
    2. 코드성 데이터 파악 및 이력/상속/identity/캐싱 등을 미리 고려하여 설계해야 한다.

      1. 이들은 DB만을 봤을때 크게 고려하지 않아도 되지만 어플리케이션과는 밀접한 관계가 있다.
  3. Entity 타입과  Value타입의 구분

    1. 일반적으로 RDB에서 지원하는 타입(VChar, CLob 등)이 Vaue타입이며 Entity타입은 테이블이 된다.
    2. 하지만 ORM의 도움으로 이러한 관계를 적절히 조절 / 매핑할 수 있다. (DB만 잘 알아는것 가지고는 DB설계를 잘할 수 없다!)

      1. 예를 들자면 컴포지션 타입이 있다.  이는 DB전문가가 생각해 낼 수 없는 영역이다.
  4. 디자인 패턴

    1. 롤 모델 (Pair키)

      1. 기업 + 롤을 identity하게 연결해서 거래처 라는 롤 모델을 만든다.
      2. 기업은 고객/구매자 라는 2가지 룰을 가지며 고로 거래처는 2가지 형태가 나온다.
      3. 롤은 방대하고 갱신 가능할때만 DB에서 관리한다.
      4. 명시적인 pair키를 가지기 때문에 자료를 분명하게 나타낼수는 있지만 어플에서 관리하기가 짜증난다.
    2. 거래 모델

      1. 위의 롤 모델인 거래처를 non-Identity하게 2개 묶어서 "거래"를 만든다.
      2. 이는 N:N매핑에서 브릿지 역활을 한다.
  5. 키 설정

    1. 특별한 이유가 없다면 인조키 사용을 강력하게 권장한다.

      1. We recommend that identifiers be 'synthetic', that is, generated with no business meaning. (자세한 내용은 Hibernate-ORM 분서 참조)
      2. 경험상 자연키는 항상 말썽을 일으킨다.

        1. 자연키는 유지보수와 스키마 개선이 훨씬 힘들다.
      3. PK는 바뀔수 있는것이 되면 골치아파진다. SID같은걸 PK로 두지 말자.. 가상키가 많이 사용되는 이유중 하나이다.
      4. 가상키는 만병통치약이 아니다.. 하지만 효과는 탁월하다.
      5. 인조키다 유리할때는?

        1. 후보키(PK의 개념)가 너무 많을때, 후보키가 없거나 있더라도 확장성을 고려할 때.
        2. 후보키의 길이가 클때, 후보키가 너무 많은 수의 애트리뷰트들과 관계를 맺을 때.
        3. Hibernate등을 사용할때.
      6. 인조키의 단점은?

        1. 자연키는 그 자체로도 강력한 무결성을 보장한다. 하지만 인조키는 이들 키값들이 중복되어도 오류를 내지 않고 insert된다.
    2. 그러나 이 인조키는 Unique한 속성을 보장해주지 못함으로 나머지 대리키들에 유니크 인덱스를 주어 사실상의 키 기능을 하게 한다.
    3. 원래 개념상에서 PK와 UID(Unique ID)는 다른 개념이다. 하지만 개념만 다를뿐 실제는 같기 때문에 현업의 ERD에서(ER-Win 등의 툴 포함) UID를 PK로 표기한다.
  6. 정규화 어느것이효율적?

    1. 정규화 : 해당 파트 참고.

    2. java객체가 필요한 만큼만 정규화하라. 자동으로 정규화가 될것이다.

    3. 가볍고 많은 row 보다 무겁고 적은 row가 훨씬 다루기 쉽다.(좋은건 아니다.) 명심할것!

    4. 정규형에 해당되지 않는 "계산된 값"등은 추가하지 말것!

      1. 통계형 컬럼은 특이한 경우(계산에 시간이 오래 걸림)를 제외하면 생성하지 않는다. 실시간 계산한다.

        1. ex) 자식 컬럼들의 합계 컬럼
        2. ex) 차액(A컬럼-B컬럼 하면 단순 계산되어지는) 컬럼 등
      2. "넣을땐 힘들어도 뺄때는 편하다." 라는건 나중에 생각해도 늦지않다. (이는 다분히 SQL중심적인 사고이다. 과연 편할까?)
  7. 이력 테이블 설정

    1. 반드시 현업과 상의 및 증거를 남길것!

      1. 이력 테이블 , 기간과 데이터 범위도 포함
      2. 이력의 의도 파악

        1. 이력 내용을 조회가 가능해야 한지?
        2. 특점 이력시점으로 롤백이 가능해야 하는지?
    2. 이력 유형

      1. 스냅샷 방식 (Backup테이블 방식)

        1. 변경된 데이터가 비지니스 로직에 사용되지 않을때 사용.

          1. 웬만하면 무조건 이방법을 사용할것!!
        2. 기존 로직에서 간단히 백업로직 추가 가능 (날자 , 일련번호 등 추가)
        3. 장점 : 다른 기능을 손볼 필요 없이 생성 가능 , 예정에 없었지만 이력을 남겨야 할때 유용. 대량의 데이터에 유용.
        4. 단점 : 기존 기능과는 연계성이 떨어짐. 이력을 볼려면 별도의 API를 구축해야함.

          1. ex) 이력서 제출시 개인정보를  함께 제출한다. 도중에 개인정보가 변경등록했음에도 불구하고
            이력서를 읽으면 예전의 개인정보가 나타나야하는 경우. 이러한 경우는 사용할 수 없다.
        5. 스냅샷 종류

          1. 데이터 전체를 전부 복사 (이게 일반적)
          2. 컬럼마다 테이블을 만들어서 변경시 복사
          3. 하나의 컬럼에 String 방식으로 변경분을 나열해서 기재.
      2. 상태 코드 방식

        1. 변경된 데이터가 비지니스 로직에 사용됨.
        2. 일반적인 방법으로 삭제 or 변경시 delete가 아닌 state를 변경
        3. 수정 이력의 필요시 상위Key를 생성해서 변경 이전과의 관계를 유지함.  (A -> B로 수정됨을 알림)
        4. 장점 : 복잡하지만 변경된 객체(과거이력)도 여전히 기존 로직API로 참조 가능.
        5. 단점 : select 등의 모든 로직에 추가 where 조건이 들어가야 함. 실수 유발 가능.
      3. 문자열 방식

        1. 이력시 이력 실적테이블에 물질정보가 들어가야 한다면 -> 물질정보의 PK를 가지는 대신 물질정보 자체의 정보를 복사 / 입력한다.
        2. 이력 데이터가 롤백되지 않는, 읽기 전용이며 데이터의 크기가 크지 않을 때 사용.
        3. 역시나 비추천 방법.
      4. 참고

        1. 일반적으로 이력사항만 조회해야 한다면 스냅샷이 좋다. ㅠ
      5. 테이블 동기화 설정

        1. 실서버, 연계서버 또는 OLAP등의 도입으로 적절한 동기화를 해야 한다면 다음을 고려하자. (대부분 해당될것이다.)

        2. 상태코드 방식 : 부모 테이블마다 Update시각의 표시 => 주기적으로 배치를 돌면서 update된것만을 동기화 할 수 있다.

          1. 이는 Java 기본객체 상속으로 간단히 해결 가능하다.

        3. 백업테이블 방식 => 삭제의 경우 변경적재가 불가능 함으로 특정 테이블에 삭제 이력을 남기자. 이것을 참고해 동기화!

          1. GenericDao사용으로 간간히 해결 가능하다.

        4. SQL생성 => merge SQL을 자동생성해주는 Generator를 사용할것! 단순 복사는 PL/SQL의 효용성이 높다.

  8. Identity / NonIdentity

    1. Identity : 부모키가 자식테이블의 주키로 전이된다.  자식 테이블은 구분자로 사용될 추가 키를 가져야 한다.

      1. 강한 관계(종속관계)를 나타낸다. 부모가 없이 자식은 있을 수 없다.

      2. 자식 객체가 부모객체에 종속되며 자식을 필터링 해서 읽을 일(자식의 크기가 크지않다.)이 없다.

        1. 즉 자식이 로드되면 전량 로드된다. 자식은 독립적으로 로드될 수 없다.

      3. @CollectionOfElements로 매핑하며 타겟은 @Embeded가 된다.

      4. ex) 장바구니 객체의 자식인 ITEM 객체.

    2. NonIdentity : 부모기가 자식 테이블의 FK로 전이된다. 자식테이블의 주키는 부모키와 상관없다.

      1. 약한 관계를 나타낸다.

      2. 자식 객체가 필터링 되어 부분만(페이징 처리) 읽어야 할 수 있다.

      3. 부모와 별도로 자식이 로드 가능하다.

      4. @OneToMany로 매핑된다. 타겟은 부모와 동등한 @Entity이다.

      5. ex) 사용자 객체의 자식인 사용자 로그인(대량의 데이터) 정보.

  9. 데이터베이스 상속 설정

    1. 반드시 Java class기준으로 상속할것 .

      1. 개별 트랜잭션이 어떠한 유형을 가지냐는 보통 Table보다는 class로 알 수 있다.
      2. List<T> 의 seq는 자바와 동일하게 0부터 시작하게 설정하자. ( null초기화가 0이 안되도록 주의! 래퍼클래스 사용할것. )
    2. OneToOne Type (슈퍼타입이 개별 테이블 유지)

      1. 개별로 테이블로 트랜잭션이 형성됨

      2. 확장성이 우수하나 별개의 조인 형성

    3. Plus Type (슈퍼 + 서브를 묶어서 하나의 테이블)

      1. 하나의 도메인으로 트랜잭션이 형성됨 (추천)

      2. 데이터 베이스 다형성( Interface)

        1. Java에서 Interface를 사용하는 도메인에 해당함. (슈퍼클래스를 extend함과 동시에 interface를 구현)

          1. ex) 다양한 종류의 실적은 하나의 Interface를 구현함. 징수부과는 Interface만을 사용하며 어떤 종류의 부과금인지 알지 못해도 됨.

        2. 구현체 도메인을 부모 자식으로 나눌때 부모 테이블은 1개의 키(1개의 sequence)를 가지는 테이블로 구성한다.

          1. 이렇게 구성하면 Interface에서 도메인 Entity로의 Fk설정이 가능하다..

          2. SQL에서 각종 통계 내기 편하다.

    4. Single Type (모든 도메인을 하나의 테이블로.. ㄷㄷ)

      1. 모든 도메인의 전방위에 걸쳐 트랜잭션이 형성됨
  10. 캐싱 http://erwins.springnote.com/pages/5002681


    출처 : http://erwins.springnote.com/pages/2856434

Posted by 1010
04.Anddoid2010. 7. 30. 15:30
반응형

서버와 클라이언트의 소켓(Soket) 통신에 대해 알아보도록 합시다.

이것인 기존의 JAVA 통신에 대한 방법만 잘 알고있다면 무난히 가능한 부분입니다.

실제로 서버는 안드로이드를 통해서 돌리는 것이 아닌, 기존의 자바 프로그래밍을 이용하게됩니다.

물론 클라이언트는 안드로이브 영역이기때문에 안드로이드로 개발을 해야합니다.

*이 방법은 아파치 서버를 이용한 방법입니다.

아래는 Server 및 Client에 가장 우선적으로 필요한 api입니다.

//아래는 Server 부분

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.ServerSocket

import java.net.Socket;

//아래는 Client 부분

import java.io.BufferedInputStream;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.Socket;

import java.net.URL;

import java.net.URLConnection;



 현재 보이는 임포트문에서 java.net에 해당하는 부분이 실제 접속이 가능하게 해주는 api입니다. 위의 java.io는 데이터 전송하는 부분에 있어서 필요한 핵심입니다.

아래 보이는것 은 닷컴 레퍼런스에서 발췌한 소스입니다.


TCP SERVER 부분입니다.

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket; 


public class TCPServer implements Runnable {

    public static final int ServerPort = 9999; //소켓포트설정

    public static final String ServerIP = "xxx.xxx.xxx.xxxx"; //연결될 서버IP


    @Override

    public void run() {

        // TODO Auto-generated method stub

        try {

            System.out.println("S: Connecting...");

            ServerSocket serverSocket = new ServerSocket(ServerPort);


            while (true) {

                Socket client = serverSocket.accept();

                System.out.println("S: Receiving...");


                try {

                    BufferedReader in = new BufferedReader(

                    new InputStreamReader(client.getInputStream()));

                    String str = in.readLine();

                    System.out.println("S: Received: '" + str + "'");

                    PrintWriter out = new PrintWriter(new BufferedWriter(

                    new OutputStreamWriter(client.getOutputStream())),true);

                    out.println("Server Received " + str);

                } catch (Exception e) {

                    System.out.println("S: Error");

                    e.printStackTrace();

                } finally {

                    client.close();

                    System.out.println("S: Done.");

                }

            }

        } catch (Exception e) {

            System.out.println("S: Error");

            e.printStackTrace();

        }

    }


    public static void main(String[] args) {

        // TODO Auto-generated method stub

        Thread desktopServerThread = new Thread(new TCPServer());

        desktopServerThread.start();

    }

}



설명:

1. 자신의 port를 설정해서 socket을 엽니다.

2. 클라이언트에서 오는 정보를 받기 위해 accept()를 통해서 대기합니다.

3. 데이터가 왔다는 것이 감지되면 Reader를 통해 Stream정보를 읽어냅니다.

3. 받은 Stream정보를 다시 클라이언트로 돌려보냅니다.

위의 소스는 하나의 Client와 Connent를 통해 데이터를 주고 받는 형태입니다.



다음 보게 될 것은 닷컴레퍼런스에서 발췌한

이제 볼 부분은 안드로이드 TCP Client 부분입니다.

다음부분이 필수적으로 import가 되어있어야합니다.


package socket.client;


import java.io.BufferedInputStream;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.Socket;

import java.net.URL;

import java.net.URLConnection;


import org.apache.http.util.ByteArrayBuffer;

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

import android.widget.Toast;


public class NewClient extends Activity {

    private String html = "";

    private Handler mHandler;


    private Socket socket;

    private String name;

    private BufferedReader networkReader;

    private BufferedWriter networkWriter;

    private String ip = "xxx.xxx.xxx.xxx"; // SERVER IP를 잡습니다.

    private int port = 9999; // PORT를 설정합니다.


    @Override

    protected void onStop() {

        // TODO Auto-generated method stub

        super.onStop();

        try {

            socket.close();

        } catch (IOException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }


    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        mHandler = new Handler();


        try {

            setSocket(ip, port);

        } catch (IOException e1) {

            // TODO Auto-generated catch block

            e1.printStackTrace();

        }


        checkUpdate.start();


        final EditText et = (EditText) findViewById(R.id.EditText01);

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

        final TextView tv = (TextView) findViewById(R.id.TextView01);


        btn.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {

                if (et.getText().toString() != null

                        || !et.getText().toString().equals("")) {


                    PrintWriter out = new PrintWriter(networkWriter, true);

                    String return_msg = et.getText().toString();

                    out.println(return_msg);

                }

            }

        });

    }


    private Thread checkUpdate = new Thread() {

        public void run() {

            try {

                String line;

                Log.w("ChattingStart", "Start Thread");

                while (true) {


                    Log.w("Chatting is running", "chatting is running");

                    line = networkReader.readLine();

                    html = line;

                    mHandler.post(showUpdate);

                }


            } catch (Exception e) {

            }

        }

    };


    private Runnable showUpdate = new Runnable() {

        public void run() {

            Toast.makeText(NewClient.this, "Coming word: " + html,

                    Toast.LENGTH_SHORT).show();

        }

    };


    public void setSocket(String ip, int port) throws IOException {

        try {

            socket = new Socket(ip, port);

            networkWriter =

new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            networkReader =

new BufferedReader(new InputStreamReader(socket.getInputStream()));

           

        } catch (IOException e) {

            System.out.println(e);

            e.printStackTrace();

        }

    }

}


설명:

1. onCreate()로 되면 서버에 연결하도록 합니다.

2. textbox에 문자를 넣고 버튼을 누르면 서버로 데이터를 전송합니다.

3. 서버에 오는 데이터를 받기위해 while을 합니다.

4.받은 정보를 Toast를 이용해 표시합니다.


다음은 이어서

XML 소스입니다.

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

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

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent" >

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

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

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text="Send"/>

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>   

</LinearLayout>



설명 :

간단하게 EditText를 이용해서 거기에 들어간 문자열을 버튼을 통해 서버로 전송시키는 기능이 구현되어있습니다.


다음은 이어서

AndroidManifest.xml 소스입니다.

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

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

package="socket.client"

  android:versionCode="1"

  android:versionName="1.0">

  <application android:icon="@drawable/icon" android:label="@string/app_name">

      <activity android:name=".NewClient"

        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>

  <uses-permission android:name="android.permission.INTERNET" /> //필수 선언

</manifest>


설명:

가장 핵심이 되는 부분은

정리를 해보면 인터넷을 사용하기 위해서 다음이 꼭 추가되어야 한다는 사실입니다.


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


이렇게 하면 기본적인 통신이 가능하게 됩니다. 물론 테스트를 할려면 완성된 서버와 통신하기위한 안드로이드 에뮬레이터및 안드로이드 OS가 탑제된 폰이 필요합니다.

실제 구동여부는 서버의 미완성으로 확인을 못했지만 교과서 적으로 쓰이는 방법이기 때문에 소스를 분석하고 정리해서 올려봅니다. 많은 도움이 되었으면 좋겠습니다.


출처 : http://arkroid.tistory.com/34

Posted by 1010
04.Anddoid2010. 7. 30. 14:29
반응형

[android-beginners] Sockets with Android.

Android_n00b
Fri, 02 Oct 2009 08:59:43 -0700

Hi
I'm implementing a program which uses sockets to communicate between
the client and server. I am getting this to work fine with just a
message. However, I want to have an EditText field in my application,
which when I hit a 'Send' button, sends the text from the field to my
server and logs it. I am trying to implement this, but my program
always crashes. Any help would be appreciated. Here is my code:

Android Client:
public class SocketTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Thread sThread = new Thread(new TCPServer());
        Thread cThread = new Thread(new TCPClient());
        sThread.start();
        try {
               Thread.sleep(1000);
          } catch (InterruptedException e) { }

          cThread.start();
    }
}

TCP Server:
public class TCPServer implements Runnable{

    public static final String SERVERIP = "127.0.0.1";
    public static final int SERVERPORT = 8888;
    public void run() {
         try {
              Log.d("TCP", "S: Connecting...");

              ServerSocket serverSocket = new ServerSocket
(SERVERPORT);
              while (true) {
                 Socket client = serverSocket.accept();
                 Log.d("TCP", "S: Receiving...");
                 try {
                      BufferedReader in = new BufferedReader(new
InputStreamReader(client.getInputStream()));
                      String str = in.readLine();
                      Log.d("TCP", "S: Received: '" + str + "'");
                    } catch(Exception e) {
                        Log.e("TCP", "S: Error", e);
                    } finally {
                         client.close();
                         Log.d("TCP", "S: Done.");
                    }

              }

         } catch (Exception e) {
           Log.e("TCP", "S: Error", e);
         }
    }
}

TCP Client:
 public class TCPClient extends Activity implements Runnable {

    final EditText msg = (EditText) findViewById(R.id.message);
    final Button sendButton = (Button) findViewById(R.id.sendserver);

    public void run() {
        sendButton.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                                // TODO Auto-generated method stub
                                try {

                                   InetAddress serverAddr = 
InetAddress.getLocalHost();
                                   Log.d("TCP", "C: Connecting...");
                                   Socket socket = new Socket(serverAddr,
TCPServer.SERVERPORT);
                                   String message = msg.toString();
                                       try {
                                        Log.d("TCP", "C: Sending: '" + message 
+ "'");
                                        PrintWriter out = new PrintWriter( new
BufferedWriter( new OutputStreamWriter(socket.getOutputStream
())),true);

                                        out.println(message);
                                        Log.d("TCP", "C: Sent.");
                                          Log.d("TCP", "C: Done.");

                                     } catch(Exception e) {
                                         Log.e("TCP", "S: Error", e);
                                        } finally {
                                          socket.close();
                                        }
                                 } catch (Exception e) {
                                      Log.e("TCP", "C: Error", e);
                                 }
                        }
                }) ;

    }
}
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Android Beginners" group.
To post to this group, send email to android-beginners@googlegroups.com
To unsubscribe from this group, send email to
android-beginners-unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-beginners?hl=en
-~----------~----~----~----~------~----~------~--~---
Posted by 1010
04.Anddoid2010. 7. 30. 14:21
반응형
출처는 명확하지 않음-_-;; 원작성자 분에게는 죄송합니다;
앞의 2009/03/10 - [Computer Science/Android] - [펌]Android에서의 TCP/IP 통신 글의 코드와 매우 유사하긴 하지만.. 뭔가 더 간단하고 예제는 많을 수록 좋으므로;(-_-)

SocketTest.java

Java:

public class SocketTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
       
        Thread sThread = new Thread(new TCPServer());
        Thread cThread = new Thread(new TCPClient());
       
        sThread.start();
        try {
               Thread.sleep(1000);
          } catch (InterruptedException e) { }
       
          cThread.start();
    }
}


TCPServer.java

Java:

public class TCPServer implements Runnable{
     
    public static final String SERVERIP = "127.0.0.1";
    public static final int SERVERPORT = 4444;
         
    public void run() {
         try {
              Log.d("TCP", "S: Connecting...");
             
              ServerSocket serverSocket = new ServerSocket(SERVERPORT);
              while (true) {
                 Socket client = serverSocket.accept();
                 Log.d("TCP", "S: Receiving...");
                 try {
                      BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                      String str = in.readLine();
                      Log.d("TCP", "S: Received: '" + str + "'");
                    } catch(Exception e) {
                        Log.e("TCP", "S: Error", e);
                    } finally {
                         client.close();
                         Log.d("TCP", "S: Done.");
                    }

              }
             
         } catch (Exception e) {
           Log.e("TCP", "S: Error", e);
         }
    }
}


TCPClient.java

Java:

public class TCPClient implements Runnable {

     
    public void run() {
         try {
           
           InetAddress serverAddr = InetAddress.getByName(UDPServer.SERVERIP);
           
           Log.d("TCP", "C: Connecting...");
           Socket socket = new Socket(serverAddr, TCPServer.SERVERPORT);
           String message = "Hello from Client";
               try {
                Log.d("TCP", "C: Sending: '" + message + "'");
                PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())),true);
               
                out.println(message);
                Log.d("TCP", "C: Sent.");
                  Log.d("TCP", "C: Done.");
               
             } catch(Exception e) {
                 Log.e("TCP", "S: Error", e);
                } finally {
                  socket.close();
                }
         } catch (Exception e) {
              Log.e("TCP", "C: Error", e);
         }
    }
}




AndroidManifest.xml

XML:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.e2esp.socket.test">

    <application android:icon="@drawable/icon">
        <activity class=".SocketTest" android:label="@string/app_name">
            <intent-filter>
                <action android:value="android.intent.action.MAIN" />
                <category android:value="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>



출처 : http://plucky.tistory.com/13
Posted by 1010
카테고리 없음2010. 7. 30. 14:20
반응형

<목표> [안드로이드] 서버/클라이언트 소켓(Socket) 통신하기

     

   

   오늘은  서버, 클라이언트의 소켓(Soket) 통신에 대해서 알아보겠습니다. 기존의 많은 안드로이드 어플리케이션이 각각의 서버를 이용하여 정보를 주고 받습니다. 아무래도 기기 내에서 만으로 서비스하기에는 한계가 있기 때문이죠. 정보를 저장하고, 서버에서 처리하여 결과를 주고, 클라이언트는 그 결과를 받아서 어플리케이션에 알맞은 동작을 취하도록 합니다. 트위터 서비스나 스마트폰을 이용해서 공짜 문자(통신료 제외)를 주고 받을 수 있는 것도 서비스를 제공하는 곳에서 서버를 두기 때문입니다. 그 덕분에 핸드폰을 벗어나 더 많은 정보를 처리할 수 있도록 할 수 있습니다.


   서버/클라이언트 소켓 통신은 기존의 자바를 이용해서 소켓 통신을 해보신 분들이라면 어렵지 않게 사용하실 수 있습니다. 서버의 소스 자체는 완전히 자바 소스로 이루어지기 때문이지요. 실제로 서버는 안드로이드를 통해서 돌리는 것이 아니라, 기존의 자바 프로그래밍을 이용하여 수행합니다. 클라이언트는 당연히 안드로이드로 개발을 해야겠지요. 오늘 보여드릴 예제 소스는 인터넷에 돌아다니는 간단한 서버 – 클라이언트 소켓 통신을 가지고 와서 나름대로 수정을 해본 것입니다. 기존의 샘플 코드가 하나의 메시지를 서버로 보내고 난 뒤에, 바로 클라이언트에서 기다리면서 데이터가 오기를 기다리는 형태로 제공되었습니다. 그렇기 때문에 클라이언트가 계속 데이터를 기다리면서 블록킹 되어 있는 상태에서만 프로그래밍이 돌아갔습니다. 이러한 부분을 수정하여 쓰레드를 이용하여 백그라운드에서 돌아가게 하여, 기존의 클라이언트에서는 원래의 프로그램이 수행되고, 서버에서 오는 정보를 받는 부분은 쓰레드를 통해 해결했습니다.


    먼저 서버를 만들 자바 코드를 알아보고, 뒤에는 안드로이드에서 소켓 통신을 위한 설정과정을 간단한 예제를 통해서 알아보겠습니다.

     

     

     

STEP 1  Java Source Code

     

   자바 코드는 두 가지를 다루게 됩니다. 처음은 서버를 돌리는 데 필요한 자버 코드를 알아보고, 두 번째는 안드로이드 클라이언트 코드를 알아보겠습니다.



  [[ 서버 ]]   TCP Server Java Code


import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;


public class TCPServer implements Runnable {

    public static final int ServerPort = 9999;

    public static final String ServerIP = "xxx.xxx.xxx.xxxx";


    @Override

    public void run() {

        // TODO Auto-generated method stub

        try {

            System.out.println("S: Connecting...");

            ServerSocket serverSocket = new ServerSocket(ServerPort);


            while (true) {

                Socket client = serverSocket.accept();

                System.out.println("S: Receiving...");


                try {

                    BufferedReader in = new BufferedReader(

                    new InputStreamReader(client.getInputStream()));

                    String str = in.readLine();

                    System.out.println("S: Received: '" + str + "'");

                    PrintWriter out = new PrintWriter(new BufferedWriter(

                    new OutputStreamWriter(client.getOutputStream())),true);

                    out.println("Server Received " + str);

                } catch (Exception e) {

                    System.out.println("S: Error");

                    e.printStackTrace();

                } finally {

                    client.close();

                    System.out.println("S: Done.");

                }

            }

        } catch (Exception e) {

            System.out.println("S: Error");

            e.printStackTrace();

        }

    }


    public static void main(String[] args) {

        // TODO Auto-generated method stub

        Thread desktopServerThread = new Thread(new TCPServer());

        desktopServerThread.start();

    }

}


   서버의 기능은 클라이언트에서 오는 데이터를 받아들이는 게 핵심입니다. 자신의 포트를 세팅하여 소켓을 여는 것부터 시작하여, 클라이언트에서 오는 정보를 받기 위해서 accept() 를 통해서 기다립니다. 그리고 데이터가 왔다는 신호가 오면 리더를 통해서 스트림을 읽어냅니다. 그리고 자신이 받은 스트림 정보를 다시 돌려보내는 역할을 합니다. 현재는 하나의 클라이언트와 컨넥트를 통해 데이터를 주고 받는 형식입니다. 많은 클라이언트와 통신하고 싶으면 클라이언트에 대한 정보를 저장하고, 여러 클라이언트에게 적절하게 보내는 기능을 추가하시면 되겠습니다.



   [[ 클라이언트 ]]   TCP Client Java Code



package socket.client;


import java.io.BufferedInputStream;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.net.Socket;

import java.net.URL;

import java.net.URLConnection;


import org.apache.http.util.ByteArrayBuffer;

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

import android.widget.Toast;


public class NewClient extends Activity {

    private String html = "";

    private Handler mHandler;


    private Socket socket;

    private String name;

    private BufferedReader networkReader;

    private BufferedWriter networkWriter;

    private String ip = "xxx.xxx.xxx.xxx"; // IP

    private int port = 9999; // PORT번호


    @Override

    protected void onStop() {

        // TODO Auto-generated method stub

        super.onStop();

        try {

            socket.close();

        } catch (IOException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }


    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        mHandler = new Handler();


        try {

            setSocket(ip, port);

        } catch (IOException e1) {

            // TODO Auto-generated catch block

            e1.printStackTrace();

        }


        checkUpdate.start();


        final EditText et = (EditText) findViewById(R.id.EditText01);

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

        final TextView tv = (TextView) findViewById(R.id.TextView01);


        btn.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {

                if (et.getText().toString() != null

                        || !et.getText().toString().equals("")) {


                    PrintWriter out = new PrintWriter(networkWriter, true);

                    String return_msg = et.getText().toString();

                    out.println(return_msg);

                }

            }

        });

    }


    private Thread checkUpdate = new Thread() {

        public void run() {

            try {

                String line;

                Log.w("ChattingStart", "Start Thread");

                while (true) {


                    Log.w("Chatting is running", "chatting is running");

                    line = networkReader.readLine();

                    html = line;

                    mHandler.post(showUpdate);

                }


            } catch (Exception e) {

            }

        }

    };


    private Runnable showUpdate = new Runnable() {

        public void run() {

            Toast.makeText(NewClient.this, "Coming word: " + html,

                    Toast.LENGTH_SHORT).show();

        }

    };


    public void setSocket(String ip, int port) throws IOException {

        try {

            socket = new Socket(ip, port);

            networkWriter =

new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            networkReader =

new BufferedReader(new InputStreamReader(socket.getInputStream()));

           

        } catch (IOException e) {

            System.out.println(e);

            e.printStackTrace();

        }

    }

}



   이 번에 살펴볼 코드는 안드로이드 클라이언트 프로그램입니다. 안드로이드 클라이언트 코드에서는 서버에 소켓을 연결하고 받은 정보를 이용하여 프로그램을 수행시키는 것을 다룹니다. 먼저 onCreate()가 되면 서버에 연결하도록 설계되어 있습니다. IP 주소와 포트 번호를 알맞게 설정해주시고, 소켓을 연결합니다. 그리고 데이터를 주고 받기 위해서 리드, 라이터를 설정하여 둡니다. 예제에서는 간단히 텍스트 박스에 문자를 적고 버튼을 누르면, 서버로 데이터를 보냅니다. 그리고 서버에서 오는 데이터를 계속 받기 위해서 while을 쓰레드를 통해서 실행합니다. 여기서 받은 데이터 정보를 토스트 기능을 통해 출력합니다. 여기서 UI에 대한 접근을 핸들러를 통해서 하고 있는 것을 확인할 수 있습니다. 이렇게 하는 이유는 다른 포스트에 올리겠습니다. 일단 UI에 대한 접근은 핸들러를 통해서 수행한다고 생각하시고 프로그램을 수행해야한다는 것만 기억하시고 계시면 될 것 같습니다.

       


STEP 2  Xml Code

       

   Xml 코드에는 간단히 EditText에서 넣은 문자열을 버튼을 통해 서버로 데이터를 보내는 기본적인 기능만 추가하시면 됩니다.


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

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

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent" >

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>

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

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text="Send"/>

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

    android:layout_height="wrap_content"

    android:layout_width="fill_parent"/>   

</LinearLayout>


        

     

STEP 3  AndroidManifest.xml Code

     

   메니페스트에는 소켓을 이용하기위해 인터넷을 사용해야 하므로, 인터넷을 사용하겠다는 퍼미션만 추가해주시면 됩니다.

       

AndroidManifest.xml 에 추가해야할 인터넷 사용 허가권

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


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

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

package="socket.client"

  android:versionCode="1"

  android:versionName="1.0">

  <application android:icon="@drawable/icon" android:label="@string/app_name">

      <activity android:name=".NewClient"

        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>

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

</manifest>

     

     

     

 < 마무리 >  서버/클라이언트 소켓(Socket) 통신하기

     


  서버와 클라이언트의 소켓 통신에 대해서 알아보았습니다. 핸드폰 내에서의 기능과 정보만으로는 어플리케이션이 한정적일 수 밖에 없습니다. 그렇기 때문에 통신을 통해서 좀 더 폭넓은 서비스를 제공하기 위해서 서버와 클라이언트가 서로 통신할 수 있도록 소켓에 대해서 알아볼 필요가 있었습니다. 하나의 서버가 여러 곳의 클라이언트들에 대해서 서비스를 해줘야 할 때는 클라이언트에 대한 정보를 가지고 있으면서 소켓을 각각 연결시켜주어야 합니다. 안드로이드 클라이언트 부분에서도 현재 수행되고 있는 동작에 영향을 받지 않으면서 백그라운드에서 쓰레드가 돌면서 서버에서 오는 정보를 수시로 받을 수 있도록 해야합니다. 소켓 통신에 대한 기존 개념을 알고 계신 분들이라면 크게 어렵지 않게 이해하실 수 있으셨으리라 봅니다. 처음 접하시는 분들은 데이터를 주고 받는 부분에서 자신이 수행하고 싶은 기능만 추가해주시면 간단히 소켓 통신을 하실 수 있으리라 생각합니다.

   

   

   

[참고자료]  

Posted by 1010
04.Anddoid2010. 7. 30. 14:19
반응형

소스는 humaneer.net 의 도움을 99.99999999999% 이상 받았습니다.. 운영자님께 감솨.. (__)


웹서버 구성하고 안드로이드로 서버쪽 데이터 전송하는 방법말고 어떤게 있나 찾던 중.. 위 블로그 통해 tcp 소켓통신으로 서버와 데이터 주고 받을 수 있음을 알았다..(흠.. 구글링은 역쉬...라는 감탄..)


소스는 위 블로그 링크 참조..


main.xm


 

Activity 클레스

 

Server쪽 클레스


 

결과는..



 이상 오늘 깨작거린 내용..


내일은...

1. 클라이언트에서 입력받은 값을 서버로 전송.

2. 서버쪽 DB접속해 입력데이터에 적합한 Resultset을 클라이언트에 전송

3. 클라이언트는 서버에서 받은 Resultset을 리스트 형태로 뿌려...

마치 조회조건으로 검색후 결과를 화면에 뿌린것 같은.. 그런걸 해봐야겠음..


출처 : http://motpool.textcube.com/17

Posted by 1010
04.Anddoid2010. 7. 30. 14:17
반응형
안드로이드 에뮬레이터와 데스크탑 서버간 TCP/IP통신을 하는 간단한 코드이다.
보통 이런 테스트는 loopback을 이용하면 되지 않을까? 라고 착각하는 사람들이 굉장히 많을 것이다.
그래서 서버의 주소를 getLocalHost()를 통해 가져오게 되면 완전 삽질이다.
왜냐? 안드로이드 에뮬레이터의 localhost는 안드로이드 에뮬레이터의 네트웍이지 데스크탑의 네트웍이 아니기 때문이다. 따라서 아래의 소스코드는 일단 네트웍이 돌아가는 컴퓨터에서만 작동이 가능하다.


- Android Client의 메인부분
01.package android.SocketTest;
02.import android.app.Activity;
03.import android.os.Bundle;
04.  
05.public class SocketTest extends Activity {
06.  
07.    @Override
08.    public void onCreate(Bundle icicle) {
09.        super.onCreate(icicle);
10.        setContentView(R.layout.main);
11.        Thread cThread = new Thread(new TCPClient());
12.        cThread.start();
13.    }
14.}



- Client의 실제코드

01.package android.SocketTest;
02.  
03.import java.io.BufferedWriter;
04.import java.io.OutputStreamWriter;
05.import java.io.PrintWriter;
06.import java.net.InetAddress;
07.import java.net.Socket;
08.  
09.import android.TCPDesktopServer.TCPDesktopServer;
10.import android.util.Log;
11.  
12.public class TCPClient implements Runnable {
13.    public void run() {
14.         try {
15.             // 서버의 주소를 로컬호스트라고 127.0.0.1로 하는 삽질은 하지말것 -_-;
16.             InetAddress serverAddr = InetAddress.getByName(TCPDesktopServer.SERVERIP);
17.       
18.             Log.d("TCP", "C: Connecting...");
19.             Socket socket = new Socket(serverAddr, 4444);
20.               
21.             String message = "Hello from Client";
22.             try {
23.                 Log.d("TCP", "C: Sending: '" + message + "'");
24.                 PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())),true);
25.                   
26.                 out.println(message);
27.                 Log.d("TCP", "C: Sent.");
28.                 Log.d("TCP", "C: Done.");
29.                   
30.             } catch(Exception e) {
31.                 Log.e("TCP", "S: Error", e);
32.              } finally {
33.                socket.close();
34.              }
35.         } catch (Exception e) {
36.              Log.e("TCP", "C: Error", e);
37.         }
38.    }
39.}



- 데스크탑 서버 코드
01.package android.TCPDesktopServer;
02.import java.io.BufferedReader;
03.import java.io.InputStreamReader;
04.import java.net.ServerSocket;
05.import java.net.Socket;
06.  
07.public class TCPDesktopServer implements Runnable{
08.    public static final int SERVERPORT = 4444;
09.    public static final String SERVERIP = "192.168.0.16";
10.      
11.    public void run() {
12.        try {
13.            System.out.println("S: Connecting...");
14.            ServerSocket serverSocket = new ServerSocket(SERVERPORT);
15.  
16.            while (true) {
17.                Socket client = serverSocket.accept();
18.                System.out.println("S: Receiving...");
19.  
20.                try {
21.                    BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
22.                    String str = in.readLine();
23.                    System.out.println("S: Received: '" + str + "'");
24.                } catch(Exception e) {
25.                    System.out.println("S: Error");
26.                    e.printStackTrace();
27.                } finally {
28.                    client.close();
29.                    System.out.println("S: Done.");
30.                }
31.            }
32.  
33.        } catch (Exception e) {
34.            System.out.println("S: Error");
35.            e.printStackTrace();
36.        }
37.    }
38.  
39.    public static void main (String a[]) {
40.        Thread desktopServerThread = new Thread(new TCPDesktopServer());
41.        desktopServerThread.start();
42.    }
43.}
Posted by 1010
04.Anddoid2010. 7. 30. 14:17
반응형




Unblocking Android RetuarantFinder 에서는 서버에 xml 파일을 요청하여 받은 그 파일을 SAX를 이용하여 Parsing 하는 방법을 제공하고 있다.





그러나 서버에서 xml 파일을 가져오는 방법이 단순히 URL Class를 생성하여 파서에서 InputStream을 읽어 파싱하는 방식으로 하기 때문에

URL url = new URL(this.query);
xr.parse(new InputSource(url.openStream()));
http Connection 시 좀더 세밀한 동작을 하거나 제한을 둘때에는 (예를 들면 일정시간 정도 서버로 부터 응답이 없을 경우에는 Timeout Exception 발생하는 경우) 이러한 방법으로는 해결할 수가 없다.

대안으로는HttpClient를 이용하여 해결 할 수가 있다.
관련 Site는

http://hc.apache.org/httpcomponents-client/tutorial/html/ 를 참조하면 되는 데

목차를 보면

○ Http Request
○ Http Reponse
○ Http Entity
○ Http Content
○ Exception Handling
○ Http Parameter Setting
○ Connection Manager
○ Cookie
○ Http Authentication

으로 되어 있다.
내 생각에는 실제로 사용하려면 HttpClient를 사용해야 되지 않을까 하는 생각이 든다.
어쩌든 내 경우에는 Http Paramter를 Setting하여 Timeout을 주고자 하는 목적이기
때문에 HttpClient를 생성하고 Setting하고 실행하여 InputStream을 가져오는 로직으로 수정하여다. (아래 Flow 참조)


 
SAX Paser 정의 및 Parsing을 위한 Handler Setting

   public ArrayList<MCTH> getReviews() {
        long startTime = System.currentTimeMillis();
        ArrayList<MCTH> results = null;    
           
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp;
   try {
    sp = spf.newSAXParser();
             XMLReader xr = sp.getXMLReader();

             mcthHandler handler = new mcthHandler();
             xr.setContentHandler(handler);

             //xr.parse(new InputSource(url.openStream()));
             xr.parse(new InputSource(getInputStreamFromURI()));
             // after parsed, get record
             results = handler.getReviews();        
   
   } catch (ParserConfigurationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (SAXException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }  

        long duration = System.currentTimeMillis() - startTime;
        Log.v(TAG," call and parse duration - " + duration);
        return results;
    }


  HttpClient를 이용하여 서버로 부터 XML 파일의 InputStream을 가져오는 부분
  public InputStream getInputStreamFromURI() {
     InputStream result = null;
     URI uri = null;    
       
  try {
     uri = URIUtils.createURI(URI_SCHEME, URI_HOST, URI_PORT, URI_PATH, URIQuery, null);       
        
         // Http parameter setting
         HttpParams hp = new BasicHttpParams();
         // Http Timeout
         HttpConnectionParams.setConnectionTimeout(hp,10000);
        
         // Create an instance of HttpClient.
         HttpClient hc = new DefaultHttpClient(hp);
        
         // Socket Timeout
         HttpConnectionParams.setSoTimeout(hp,10000);
        
         // Create a method instance.
         HttpGet hm = new HttpGet(uri);
        
         Log.v(TAG,"URI ==> " + hm.getURI());
        
         // Create an instance HttpResponse
         HttpResponse hr = null;
        
         //Execute the request
         hr = hc.execute(hm);
                  
        // Examine the response status
            Log.i(TAG,"hc ===> " + hr.getStatusLine().toString());
           
            // Get hold of the response entity
            HttpEntity he = hr.getEntity();
        
            if (he != null)
            {
             result = he.getContent();    
             Log.v(TAG,"xml file size is " + Long.toString(he.getContentLength()));
            }     
           
  } catch (URISyntaxException e1) {
   // TODO Auto-generated catch block
   Log.v(TAG,"getInputStreamFromURI URISyntaxException");
   e1.printStackTrace();
  } catch (ClientProtocolException e) {
   // TODO Auto-generated catch block
   Log.v(TAG,"getInputStreamFromURI ClientProtocolException");
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   Log.v(TAG,"getInputStreamFromURI IOException" + e.getMessage());
   e.printStackTrace();
  }             
     return result;    
   }



출처 : http://blog.naver.com/kippee/130068924726


Posted by 1010
04.Anddoid2010. 7. 27. 18:04
반응형
http://www.ibm.com/developerworks/kr/library/tutorial/j-dw-java-rest-i.html
RESTful한 웹 서비스 만들기

http://www.ibm.com/developerworks/kr/library/ws-pojo-springcxf2/index.html
스프링과 아파치 CXF를 이용해 POJO 웹 서비스를 디자인하고 구현하기, Part 2: RESTful 웹 서비스 만들기

http://www.ibm.com/developerworks/kr/library/j-jws12.html
Java 웹 서비스: CXF 소개

http://www.ibm.com/developerworks/kr/library/j-jws9.html
Java 웹 서비스: Metro 소개

http://www.ibm.com/developerworks/kr/library/tutorial/ws-dw-ws-eclipse-javase1.html
Eclipse와 Java SE 6을 사용하여 독립형 웹 서비스 개발하기, Part 1: 웹 서비스 서버 애플리케이션

http://www.ibm.com/developerworks/kr/library/tutorial/x-dw-x-restatompp.html
REST 서비스 작성하기

http://www.ibm.com/developerworks/kr/library/ws-soa-axis2-1/index.html
Axis2에서의 SOA 개발, Part 1: Axis2 기초 이해하기 (한글)

http://www.ibm.com/developerworks/kr/library/ws-wsajax2/index.html
Ajax로 SOAP 웹 서비스 호출하기, Part 2: 웹 서비스 클라이언트 확장하기 (한글)

http://www.ibm.com/developerworks/kr/library/ws-wsajax/index.html
Ajax로 SOAP 웹 서비스 호출하기, Part 1: 웹 서비스 클라이언트 구현하기 (한글)

http://www.ibm.com/developerworks/kr/library/wa-ajaxarch/index.html
Ajax와 REST, Part 1 (한글)

http://www.ibm.com/developerworks/kr/library/ws-soaphandler/index.html
SOAP/HTTP 웹 서비스를 위해 신뢰성 있는 클라이언트 구현하기

http://www.ibm.com/developerworks/kr/library/ws-sqzsoap.html
SOAP 압축하기 (한글)

http://www.ibm.com/developerworks/kr/library/ws-dw-ws-understand-web-services1.html
웹 서비스 이해하기, Part 1: SOAP

http://www.ibm.com/developerworks/kr/library/ws-whichwsdl/index.html
WSDL 고르기 (한글)

http://www.ibm.com/developerworks/kr/library/x-tippass.html
Tip: 웹 서비스에 파일 전달하기

http://www.ibm.com/developerworks/kr/library/ws-security.html
WS-Security 구현

http://www.ibm.com/developerworks/kr/library/ws-soapenc/index.html
SOAP 인코딩이 웹 서비스 퍼포먼스에 미치는 영향

http://www.ibm.com/developerworks/kr/library/ws-sec1.html
웹 서비스 보안, Part I
2010/06/23 20:15 2010/06/23 20:15
Posted by 1010
04.Anddoid2010. 7. 27. 17:50
반응형

웹 서비스의 정의

웹 서비스 (일반적으로 서비스 라 불리는) 는 개방형 프로그램적 인터페이스를 갖는 약간의 컨텐트, 또는 약간의 프로세스, 또는 둘 다로 구성되어 있습니다. 간단한 사례: 통화 변환기, 주식 시세, 사전. 보다 복잡한 사례: 여행 입안자, 조달 워크플로우 시스템. 서비스는 다음과 같은 특징을 갖고 있습니다:

  • 특정 작업을 수행하고 표준 사양으로 컴파일하는 인터넷-기반 애플리케이션.
  • XML 및 XML 메시징을 통해 표현되면서 액세스되는 실행 파일.
  • 분산 컴퓨팅 환경에서 동적으로 퍼블리싱 (publishing), 디스커버리 (discovery) 그리고 호출 등이 가능합니다.
  • 플랫폼 및 언어에 독립적입니다.

웹 서비스는 메시징 프로토콜, 프로그래밍 표준, 네트워크 등록 그리고 디스커버리 기능 등을 통해 모든 웹-연결 장비에서 액세스할 수 있는 소프트웨어 컴포넌트로서 비연속 (discrete) 비즈니스 프로세스를 캡슐화합니다:

  • WSDL (Web Services Description Language) 에서 기능 및 속성을 내보이고 기술을 합니다.
  • 애플리케이션 및 다른 서비스들이 UDDI (Universal Description, Discovery, and Integration) 레지스트리를 사용하여 웹 상에 그것을 위치시키게 해줍니다.
  • 원격 애플리케이션 및 서비스들이 SOAP (Simple Object Access Protocol) 와 같은 표준 인터넷 프로토콜을 사용하여 그것을 호출할 수 있게 해줍니다.
  • 동일한 프로토콜 상에서 요청 애플리케이션에게 응답을 반환합니다.

일반적으로, 웹 서비스는 세 가지 특징을 갖고 있습니다:

  • EJBs, PL/SQL 프로시저, 다른 웹/데이타베이스 컨텐트 또는 애플리케이션 등과 같은 한 개 이상의 정보 소스를 캡슐화합니다.
  • 이벤트, 변형, 그리고 조건부 로직 등을 처리할 수 있는 실행 플로우를 갖고 있습니다
  • 일정한 XML 인터페이스를 통해 액세스가 가능합니다.

다음은 웹 서비스에 대한 디스커버리 및 액세스의 고급 프로토콜 예입니다. 작업은 다음과 같이 진행됩니다:

  1. 웹 서비스 개발자는 서비스를 생성한 후 그것을 서버로 배포합니다. 서비스의 위치는 엔드포인트 (endpoint) 라고 불립니다. 개발의 일부로서 또는 개발 툴을 통해 WSDL 의 형태로 서비스 기술 (description) 이 생성됩니다.
  2. 엔드포인트와 WSDL 위치 등을 포함하는 웹 서비스 정보는 선택적으로 UDDI 디렉토리에 퍼블리싱될 수 있습니다.
  3. 웹 서비스 클라이언트는 WSDL 사양 기반의 툴을 통해 구축되거나 생성될 수 있습니다. 클라이언트는 SOAP 메시지를 엔드포인트로 보내고, 응답을 처리합니다.

웹 서비스 기반구조의 두 가지 주요 부분은 웹 서비스의 기술 언어인 WSDL; 그리고 웹 서비스의 통신 프로토콜인 SAOP 입니다.


WSDL 의 정의

Web Services Description Language (WSDL) 는 웹 서비스 인터페이스와 위치의 구문 (syntax) 을 기술하는 XML 언어입니다. WSDL 사양 은 그것을 “네트워크 서비스를 문서-중심 또는 프로시저-중심 정보를 포함하는 메시지에서 동작하는 일련의 엔드포인트로 기술하기 위한 XML 포맷” 이라고 부릅니다. 그 컨텍스트를 통해, 아래 다이어그램은 WSDL 문서에 나타나는 요소들을 설명하면서 그것들이 어떻게 관련되었는지를 보여주고 있습니다.

WSDL element hierarchy.

프로그래머 또는 자동화된 개발 툴은 서비스를 기술하는 WSDL 파일들을 생성할 수 있고, 인터넷 상에서 기술을 할 수도 있습니다. 클라이언트-사이드 프로그래머와 개발 툴은 사용 가능한 웹 서비스 정보를 얻을 수 있고, 또한 사용 가능한 서비스에 액세스하는 프록시 또는 프로그램 템플릿 등을 구축하고 생성하는 퍼블리싱된 WSDL 기술을 사용할 수도 있습니다.

WSDL 문서는 다음 테이블에서 기술된 바와 같이 유형, 메시지, portypes 바인딩, 그리고 서비스 요소 등을 포함하는 정의 요소를 보유하고 있습니다.

정의

한 개 이상의 서비스를 정의합니다. 정의 요소는 다음 속성들을 지원합니다:

  • 이름은 선택 사항입니다.
  • targetNamespace 는 이 서비스에 대한 정보의 논리적 네임스페이스 (namespace) 입니다. WSDL 문서는 다른 WSDL 문서를 임포트할 수 있고, targetNamespace 를 고유 값으로 설정하는 것은 네임스페이스가 손상되지 않는다는 것을 보증합니다.
  • Xmlns 는 WSDL 문서의 디폴트 네임스페이스 이고, http://schemas.xmlsoap.org/wsdl/ 로 설정이 됩니다. <definitions>, <types> 그리고 <message> 등과 같은 모든 WSDL 요소들은 네임스페이스에 속하게 됩니다.
  • xmlns:xsd xmlns:soap 는 데이타 유형과 SOAP-특정 정보의 지정을 위해 사용되는 표준 네임스페이스 정의입니다.
  • xmlns:tns this namespace 를 나타냅니다. "this namespace" (tns) 접두사는 현재 문서를 언급하기 위한 규약으로 사용됩니다.
유형 WSDL 문서에서 사용하는 복합 데이타 유형의 정보를 제공합니다. 간단한 유형이 사용될 때에는 WSDL 문서가 이 섹션을 필요로 하지 않습니다.
메시지 통신되는 데이타의 추상적 정의.
운영 서비스가 지원하는 작업의 추상적 기술.
portType 한 개 이상의 엔드포인트가 지원하는 작업들의 추상적 집합.
바인딩 작업과 메시지를 위한 명확한 프로토콜과 데이타 포맷 사양을 지정하여 작업이 호출되는 방식을 기술합니다.
포트 단일 통신 엔드포인트를 정의하면서, 단일 엔드포인트를 바인딩을 위한 주소로 지정합니다.
서비스 바인딩의 포트 주소를 지정합니다. 서비스는 네트워크 엔드포인트 또는 포트의 모음입니다.

다음 목록은 BC4J-VSM 가 사용하는 Credit Card Web Service 의 WSDL 코드를 보여주고 있습니다.

<?xml version = '1.0' encoding = 'UTF-8'?>
<!--Generated by the Oracle9i JDeveloper Web Services WSDL Generator-->
<!--Date Created: Fri Oct 25 15:05:09 IST 2002-->
<definitions
name="oracle.otnsamples.vsm.services.CreditCardServiceWS"
targetNamespace="http://oracle/otnsamples/vsm/services/CreditCardServiceWS.wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://oracle/otnsamples/vsm/services/CreditCardServiceWS.wsdl"
xmlns:ns1="http://oracle.otnsamples.vsm.services/ICreditCardServiceWS.xsd">
<types>
<schema
targetNamespace="http://oracle.otnsamples.vsm.services/ICreditCardServiceWS.xsd"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"/>
</types>
<message name="validateCard0Request">
<part name="provider" type="xsd:string"/>
<part name="ccNum" type="xsd:string"/>
</message>
<message name="validateCard0Response">
<part name="return" type="xsd:string"/>
</message>
<portType name="CreditCardServiceWSPortType">
<operation name="validateCard">
<input name="validateCard0Request" message="tns:validateCard0Request"/>
<output name="validateCard0Response" message="tns:validateCard0Response"/>
</operation>
</portType>
<binding name="CreditCardServiceWSBinding" type="tns:CreditCardServiceWSPortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="validateCard">
<soap:operation soapAction="" style="rpc"/>
<input name="validateCard0Request">
<soap:body use="encoded" namespace="oracle.otnsamples.vsm.services.CreditCardServiceWS" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output name="validateCard0Response">
<soap:body use="encoded" namespace="oracle.otnsamples.vsm.services.CreditCardServiceWS" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="oracle.otnsamples.vsm.services.CreditCardServiceWS">
<port name="CreditCardServiceWSPort" binding="tns:CreditCardServiceWSBinding">
<soap:address location="http://localhost:8888/VSM-BC4J-VSM-BC4J-context-root/ oracle.otnsamples.vsm.services.CreditCardServiceWS"/>
</port>
</service>
</definitions>



SOAP 의 정의

Simple Object Access Protocol (SOAP) 는 분산 환경에서 정보를 교환하기 위한 경량의 XML-기반 프로토콜로서, 다음과 같은 정보 교환의 유형들을 지원하고 있습니다:

  • 엔드포인트가 프로시저-지향 메시지를 받은 다음, 상호 관련된 응답 메시지로 대답하는 곳에서 요청-응답 프로세싱을 허용하는 Remote Procedure Call style (RPC)
  • 메시지가 전송은 되지만 전송자가 즉각적인 응답은 기대하거나 기다리지 않는 곳에서 비즈니스 또는 다른 유형의 문서들을 교환할 필요가 있는 구성 및 애플리케이션들을 지원하는 메시지-지향 정보 교환.

SOAP 는 다음과 같은 특징을 갖고 있습니다:

  • 프로토콜 독립성
  • 언어 독립성
  • 플랫폼 및 운영 체제 독립성
  • (멀티파트 MIME 구조를 사용하여) 첨부를 통합하는 SOAP XML 메시지 지원

SOAP 메시지는 두 개의 데이타 구조, SOAP 헤더, SOAP 본문 그리고 그것들을 정의하기 위해 사용되는 이름 공간의 정보 등을 포함하는 SOAP 봉투 (envelope) 로 구성되어 있습니다. 헤더는 선택 사항입니다; 그것이 나타날 때에는 SOAP 본문에서 정의한 요청 정보를 전달하게 됩니다. 예를 들면, 헤더는 트랜잭션, 보안, 컨텍스트 또는 사용자 프로파일 정보 등을 포함할 수 있습니다. 본문은 웹 서비스 요청을 포함하거나 요청을 XML 포맷으로 응답을 합니다. 다음 그림은 SOAP 메시지의 고급 구조를 나타내고 있습니다.

SOAP message structure.

SOAP 메시지가 웹 서비스 요청 및 응답의 전달을 위해 사용될 때에는 사용 가능한 웹 서비스의 WSDL 정의를 따를 수 있고, WSDL 은 웹 서비스 액세스를 위해 사용되는 SOAP 메시지, SOAP 메시지가 교환 가능한 프로토콜, 그리고 이 웹 서비스를 액세스할 수 있는 인터넷 장소 등을 정의할 수 있습니다. WSDL 디스크립터 (descriptor) 는 UDDI 또는 다른 디렉토리 서비스 내에 위치하면서, 구성 또는 SOAP 요청의 본문과 같이 다른 수단을 통해 제공될 수도 있습니다.

SOAP 사양은 요청 및 응답을 암호화하는 표준 방식을 제공하고 있습니다. 그리고, XML 스키마를 사용하여 메시지 페이로드 (payload) 의 구조 및 데이타 유형도 기술하고 있습니다. 다음은 웹 서비스의 메시지 및 응답을 위하여 SOAP 이 사용되고 있는 방식입니다:

  • 서비스 응답은 다시 SOAP 프로토콜을 사용하여 SOAP 서버로 반환이 되고, 이 메시지는 원래 SOAP 클라이언트에게 반환이 됩니다.

SOAP 은 XML 에 대한 산업 투자의 활용 방법을 제공하고 있습니다. 그리고, 일반적으로 SOAP 가 HTTP 와 SMTP 등과 같이 “방화벽에 친숙한” 프로토콜에 대하여 정의되어 있기 때문에, 방화벽 기술에 대한 산업 투자도 잘 활용할 수 있습니다. 또한 산업계는 SOAP 을 웹 서비스의 필수 요소로 정의하여 다른 전략 보다도 웹 서비스의 볼륨 생산을 적절히 사용하고 있습니다.



출처: http://blog.naver.com/kjs_3047/50003016500

Posted by 1010
01.JAVA/Java2010. 7. 7. 17:21
반응형

여기 이슈는 Browser에 따라 다른 파일 다운로드 & 저장 메커니즘을 웹 서버에서 어떻게 조절하느냐를 얘기한다.


여기 언급하는 이슈 외에도 다른 여러가지 이슈가 있기 때문에

(이를테면 Jeus가 WAS일 경우 Jeus가 response header의 content disposition 값에 직접 관여하므로 이를 고려해야 하며, 이외에도 잘 모른 채 끼어들어가는 솔루션이나 개발자의 실수 등 여러 이슈가 가능)

여기 나온 해법이 전부가 아님을 일단 일러둔다.


... 코드는 다음과 같은 방식으로 구현되어야 한다.


// 빈 간, 특수문자, 한글에 대한 테스트

String filename = "바+보 는=바보.-똥개.txt";

String disposition = getDisposition(filename, "UTF-8", getBrowser(request););

char[] content = 적당히......;

 

response.addHeader("Content-disposition", disposition);

 

// 2009. 2. 19 추가

if ("Opera".equals(getBrowser(request))){

    response.setContentType("application/octet-stream;charset=UTF-8");

}


Writer out = response.getWriter();
out.write(content); // <-- 자기 방식으로 넣으세요.. 대충 쓴 거니..
out.flush();



... 위의 코드에서 호출하는 메소드들이다.


 

            private String getBrowser(HttpServletRequest request) {
                String header = request.getHeader("User-Agent");
                if (header.indexOf("MSIE") > -1) {
                    return "MSIE";
                } else if (header.indexOf("Chrome") > -1) {
                    return "Chrome";
                } else if (header.indexOf("Opera") > -1) {
                    return "Opera";
                }
                return "Firefox";
            }

            private String getDisposition(String filename, String browser)

                                                                            throws Exception {
                String dispositionPrefix = "attachment;filename=";
                String encodedFilename = null;
                if (browser.equals("MSIE")) {
                    encodedFilename = URLEncoder.encode(filename, "UTF-8")
                            .replaceAll("\\+", "%20");
                } else if (browser.equals("Firefox")) {
                    encodedFilename =

                 "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
                } else if (browser.equals("Opera")) {
                    encodedFilename =

                 "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
                } else if (browser.equals("Chrome")) {
                    StringBuffer sb = new StringBuffer();
                    for (int i = 0; i < filename.length(); i++) {
                        char c = filename.charAt(i);
                        if (c > '~') {
                            sb.append(URLEncoder.encode("" + c, "UTF-8"));
                        } else {
                            sb.append(c);
                        }
                    }
                    encodedFilename = sb.toString();
                } else {
                    throw new RuntimeException("Not supported browser");
                }

                return dispositionPrefix + encodedFilename;
            }

간단히 설명하자면,

1. MSIE는 URLEncoder.encode 를 이용해 UTF-8로 인코드해 주기만 하면 된다.

    (아, 공백 문제는 해결해야 함)

    보통 new String(filename.getBytes("MS949"), "8859_1")  를 이용하는데,

    이건 철저하게 국내용이다. 한글 Windows에서 실행한 IE/FF 라면 보통 이게 먹히겠지만

    외국 로케일을 적용한 경우에는 여지없이 파일명이 깨지므로

    가능하다면 어떤 경우에든 먹히는 해법을 시도하는 것이 좋다.

    이게 의심된다면 자신이 만든 어플리케이션을 유닉스에서 LANG 환경변수를 바꿔가면서

    시도해 보자.

    (혹은 Windows라면 언어 설정을 일본어나 다른 언어로 바꾸고 시도해 보자 - 09.01.13 추가)

    (이에 대한 부가설명이 http://blog.naver.com/anabaral/130042610256 에 있다 - 09.02.15 추가)


2. Firefox는 URLEncoder로 인코드한 문자열을 받아들이지 못한다.

    대신 UTF-8 혹은 브라우저가 실행되는 인코딩으로 인코드된 바이너리는 받아들이므로

    일반적으로 통용되는 방식을 사용한다.

    new String(filename.getBytes("UTF-8"), "8859_1") 

    보통은 IE에서와 마찬가지로 위에 MS949를 쓰지만, 고맙게도(?) Firefox는

    위의 인코딩을 UTF-8로만 설정해도 알아서 디코드 해 준다.

    아마도 '브라우저 인코딩' 혹은 '기본 인코딩'으로 시도하고 인식이 안되면 UTF-8을 시도하는 듯.

    다만 공백이 있으면 공백 뒤가 무시되어 잘리므로 앞뒤로 따옴표 " " 를 적용해야 한다.


3. Opera는 상당히 독특한데, Opera는 브라우저 인코딩에 맞추어 파일명을 디코드해 저장한다.

    게다가 URLEncoder 로 인코드한 내용도 안 먹힌다.

    그래서 이 경우는 정답이 없다.

    다만 사용자가 별다른 설정을 하지 않는다면 브라우저 인코딩은 그 전까지 접했던 페이지의

    인코딩을 따라가므로 우리가 일관된 인코딩 정책을 가져가고 그에 맞춰 세팅해 주면 된다.

    위의 예제에서는 그 표준이 UTF-8 이었다.

    최근에(2009.2.19)에 테스트한 바로는 Opera는 다운로드 당시의 ContentType 헤더에 정해준

    charset에 민감하다. 그래서 Content-Type 의 charset과 문자열 인코딩의 charset을 일치

    시켜주면 다운로드에 문제가 없다.

    (그래서 다른 브라우저에서의 UTF-8과는 의미가 다르다)


4. 구글 Chrome도 써보았는데, 이건 URLEncoder 인코드 문자열을 받아들이지만 뭔가 부족하다.

    몇몇 글자들(+, = 등)이 깨지기 때문이다.

    그래서 ASCII 문자(0x00 ~ 0x7e)로 간주되는 부분들은 encode하지 않는 방향으로 진행한다.


5. Safari는 지금까지는 답이 없다..

   무조건 latin1(8859_1)으로 인식하니..


이 예제의 테스트는

Internet Explorer 6,

Internet Explorer 7,

Firefox 2.0,

Firefox 3.0,

Opera 9.62,

Chrome 1.0

( 실패했지만 Safari 3.2.1)

에서 진행했다.


위의 테스트로 빈 칸, 특수문자, 한글에 대한 테스트가 가능했다.

테스트에 협조해 주신 liquidbird 님께 심심한 감사를 표한다.


Posted by 1010
04.Anddoid2010. 7. 5. 15:45
반응형

구글의 GMail POP3, IMAP, SMTP 를 지원한다. POP3 가 된다는 말은 클라이언트에서

메일을 읽어올수 있으며, SMTP 를 사용하여 메일을 보낼수 있다. 그래서 아래에 설명될

안드로이드에서 메일을 보내기는 GMail SMTP 를 이용해 보내는 예제이다.

 

전제조건이 있다. 먼저 GMail 계정이 있어야 한다. 가입하고 등록된 계정으로 테스트

하도록 한다. 그리고 메일보내기 기능은 안드로이드 SDK 로는 해결이 안되므로 필요한

3가지 jar 파일이 필요하다.

 

1. 자바메일을 보내기 위한 jar 다운로드와 패스설정


위에서 말했듯이 기본적으로 제공되는 jar 에서는 메일 보내기를 할수 없다.

그래서 다음 3가지를 다운받아야 한다.

 

Download them from :

http://javamail-android.googlecode.com/files/mail.jar

http://javamail-android.googlecode.com/files/activation.jar

http://javamail-android.googlecode.com/files/additionnal.jar

 

다운받은 파일을 프로젝트내에 적당한 폴더를 만들고 복사한다.

그리고 Build path 를 걸어주기 위해 eclipse > Preferences > Java Build Path  화면으로

이동한다. Libraries 탭을 클릭해서 화면을 열고 다운받아 복사한 폴더에 있는 3 가지 jar

링크하면 된다. path 를 걸어 주는 방법은 몇가지가 있다. 간단하게 Add External JARS…

메뉴를 통해 바로 파일을 선택하고 링크하는 방법이 있다.



설정이 끝나고 나서 왼쪽 Explorer 창을 보게 되면 Referenced Libraries 폴더가 생긴 것을

볼수 있다. 이것은 외부 라이브러리를 참조하려고 위와 같이 패스를 설정하면 자동으로

생기게 된다. 기본 구글 API 로 해결하지 못하는 기능이나 개발 패키지를 jar로 묶어

사용하고자 할 때 유용하게 쓰일수 있다.  



Build path 를 설정하는 방법은 이외에도 Add Library.. 버튼을 클릭해 사용자 정의

라이브러리를 등록할수도 있다.



2. AndroidManifest.xml permission 등록


인터넷사용을 위한 permission android.permission.INTERNET 을 등록한다.

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

 

3. 간략한 소스 분석


메일 소스는 gamil smtp를 이용해 보내기 때문에 gmail 로그인 계정이 필요하다. 이것을

넣는 소스가 있다. 상속받은 javax.mail.Authenticator 클래스의 함수를 오버로딩해서

유저와 패스워드를 셋팅하고 넘겨준다.

 

protected PasswordAuthentication getPasswordAuthentication() { 

        return new PasswordAuthentication(user, password); 

}

 

클래스는 2개이며 Activity 화면을 구현한 것과 메일 보내기 기능만 있는 클래스 하나다.

일반 java 메일 보내기와 거의 동일하므로 소스 분석하는 일은 그렇게 어렵지 않을 것이다.

아직 다양하게 방법들을 테스트 해보지 않았다. 내가 당장 하고 싶은건 이미지 파일을 동봉해서

보내는 것인데 아직 구현하지 못했다.

 

환경설정을 위한 정보는 Properties 클래스를 사용하였다. 그리고 그 Properties session

에 저장하고 공통적으로 사용한다.

Properties props = new Properties(); 

props.setProperty("mail.transport.protocol", "smtp"); 

props.setProperty("mail.host", mailhost); 

props.put("mail.smtp.auth", "true"); 

props.put("mail.smtp.port", "465"); 

props.put("mail.smtp.socketFactory.port", "465");

 

4. 결과 확인


Activity
를 실행하고 화면에서 메일 보내기를 클릭한다. 그러면 설정해 놓은 GMail 계정으로

메일을 보냈다.



메일 사이트로 가 결과를 확인한다.


4. 전체 소스

<<<   GMailSender.class >>>>

package com.success.sample;

import java.io.ByteArrayInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.security.Security; 
import java.util.Properties; 
 
import javax.activation.DataHandler; 
import javax.activation.DataSource; 
import javax.mail.Message; 
import javax.mail.PasswordAuthentication; 
import javax.mail.Session; 
import javax.mail.Transport; 
import javax.mail.internet.InternetAddress; 
import javax.mail.internet.MimeMessage; 
 
public class GMailSender extends javax.mail.Authenticator { 
    private String mailhost = "smtp.gmail.com"; 
    private String user; 
    private String password; 
    private Session session; 
 
    public GMailSender(String user, String password) { 
        this.user = user; 
        this.password = password; 
 
        Properties props = new Properties(); 
        props.setProperty("mail.transport.protocol", "smtp"); 
        props.setProperty("mail.host", mailhost); 
        props.put("mail.smtp.auth", "true"); 
        props.put("mail.smtp.port", "465"); 
        props.put("mail.smtp.socketFactory.port", "465"); 
        props.put("mail.smtp.socketFactory.class", 
                "javax.net.ssl.SSLSocketFactory"); 
        props.put("mail.smtp.socketFactory.fallback", "false"); 
        props.setProperty("mail.smtp.quitwait", "false"); 
 
        session = Session.getDefaultInstance(props, this); 
    } 
 
    /* (non-Javadoc)
     * @see javax.mail.Authenticator#getPasswordAuthentication()
     */
    protected PasswordAuthentication getPasswordAuthentication() { 
        return new PasswordAuthentication(user, password); 
    } 
 
    public synchronized void sendMail(String subject, String body, String sender, String recipients) throws Exception { 
        MimeMessage message = new MimeMessage(session); 
        DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain")); 
        message.setSender(new InternetAddress(sender)); 
        message.setSubject(subject); 
        message.setDataHandler(handler); 
        if (recipients.indexOf(',') > 0) 
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients)); 
        else 
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients)); 
        Transport.send(message); 
    } 
 
    public class ByteArrayDataSource implements DataSource { 
        private byte[] data; 
        private String type; 
 
        public ByteArrayDataSource(byte[] data, String type) { 
            super(); 
            this.data = data; 
            this.type = type; 
        } 
 
        public ByteArrayDataSource(byte[] data) { 
            super(); 
            this.data = data; 
        } 
 
        public void setType(String type) { 
            this.type = type; 
        } 
 
        public String getContentType() { 
            if (type == null) 
                return "application/octet-stream"; 
            else 
                return type; 
        } 
 
        public InputStream getInputStream() throws IOException { 
            return new ByteArrayInputStream(data); 
        } 
 
        public String getName() { 
            return "ByteArrayDataSource"; 
        } 
 
        public OutputStream getOutputStream() throws IOException { 
            throw new IOException("Not Supported"); 
        } 
    } 
}

<<<   MailSend.class >>>>

package com.success.sample;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.success.R;
 
public class SendMail extends Activity { 
    /**
     * Called with the activity is first created.
     */ 
    @Override 
    public void onCreate(Bundle icicle) { 
        super.onCreate(icicle); 
        setContentView(R.layout.send_mail); 
        final Button send = (Button) this.findViewById(R.id.send); 
         
        send.setOnClickListener(new View.OnClickListener() { 
            public void onClick(View view) { 
            GMailSender sender = new GMailSender("id@gmail.com","storm200"); // SUBSTITUTE HERE                   
                try { 
                    sender.sendMail( 
                            "메일제목 !!",   //subject.getText().toString(),  
                            "메일 본문입니다..~~ ",           //body.getText().toString(),  
                            "id@gmail.com",          //from.getText().toString(), 
                            "id@naver.com"            //to.getText().toString() 
                            ); 
                } catch (Exception e) { 
                    Log.e("SendMail", e.getMessage(), e); 
                } 
            } 
        }); 
    } 
}

<<<< send_mail.xml >>>>

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"  > 
 <TextView   
     android:layout_width="fill_parent"  
     android:layout_height="wrap_content"  
     android:text="안녕하세요 "/>        
     <Button 
        android:id="@+id/send"       
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="center_horizontal"   
        android:text="메일 보내기" /> 

</LinearLayout>  

Posted by 1010
04.Anddoid2010. 7. 5. 15:43
반응형

Android Cloud to Device Messaging Framework

Android Cloud to Device Messaging (C2DM) is a service that helps developers send data from servers to their applications on Android devices. The service provides a simple, lightweight mechanism that servers can use to tell mobile applications to contact the server directly, to fetch updated application or user data. The C2DM service handles all aspects of queueing of messages and delivery to the target application running on the target device.

Note: Android C2DM will ultimately be available to all developers. However, it is currently launched in Labs, and not universally available. If you're interested in using C2DM with your Android applications, go to the signup page to request access. Google will contact you when you've been granted access.

Contents

  1. Introduction
  2. Architectural Overview
    1. Lifecycle Flow
    2. What Does the User See?
  3. Writing Android Applications that use C2DM
    1. Creating the Manifest
    2. Registering for C2DM
    3. Unregistering from C2DM
    4. Handling Registration Results
    5. Handling Received Data
    6. Developing and Testing Your Applications
  4. Role of the Third-Party Application Server
    1. How the Application Server Sends Messages
  5. Examples
  6. Limitations

Introduction

Here are the primary characteristics of Android Cloud to Device Messaging (C2DM):

  • It allows third-party application servers to send lightweight messages to their Android applications. The messaging service is not designed for sending a lot of user content via the messages. Rather, it should be used to tell the application that there is new data on the server, so that the application can fetch it.
  • C2DM makes no guarantees about delivery or the order of messages. So, for example, while you might use this feature to tell an instant messaging application that the user has new messages, you probably would not use it to pass the actual messages.
  • An application on an Android device doesn’t need to be running to receive messages. The system will wake up the application via Intent broadcast when the the message arrives, as long as the application is set up with the proper broadcast receiver and permissions.
  • It does not provide any built-in user interface or other handling for message data. C2DM simply passes raw message data received straight to the application, which has full control of how to handle it. For example, the application might post a notification, display a custom user interface, or silently sync data.
  • It requires devices running Android 2.2 or higher that also have the Market application installed. However, you are not limited to deploying your applications through Market.
  • It uses an existing connection for Google services. This requires users to set up their Google account on their mobile devices.

Architectural Overview

This section gives an overview of how C2DM works.

This table summarizes the key terms and concepts involved in C2DM. It is divided into these categories:

  • Components — The physical entities that play a role in C2DM.
  • Credentials — The IDs and tokens that are used in different stages of C2DM to ensure that all parties have been authenticated, and that the message is going to the correct place.
Components
Mobile Device The device that is running an Android application that uses C2DM. This must be a 2.2 Android device that has Market installed, and it must have at least one logged in Google account.
Third-Party Application Server An application server that developers set up as part of implementing C2DM in their applications. The third-party application server sends data to an Android application on the device via the C2DM server.
C2DM Servers The Google servers involved in taking messages from the third-party application server and sending them to the device.
Credentials
Sender ID An email account associated with the application's developer. The sender ID is used in the registration process to identify a Android application that is permitted to send messages to the device. This ID is typically role-based rather than being a personal account—- for example, my-app@gmail.com.
Application ID The application that is registering to receive messages. The application is identified by the package name from the manifest. This ensures that the messages are targeted to the correct application.
Registration ID An ID issued by the C2DM servers to the Android application that allows it to receive messages. Once the application has the registration ID, it sends it to the third-party application server, which uses it to identify each device that has registered to receive messages for a given application. In other words, a registration ID is tied to a particular application running on a particular device.
Google User Account For C2DM to work, the mobile device must include at least one logged in Google account.
Sender Auth Token A ClientLogin Auth token that is saved on the third-party application server that gives the application server authorized access to Google services. The token is included in the header of POST requests that send messages. For more discussion of ClientLogin Auth tokens, see ClientLogin for Installed Applications.

Lifecycle Flow

Here are the primary processes involved in cloud-to-device messaging:

  • Enabling C2DM. An Android application running on a mobile device registers to receive messages.
  • Sending a message. A third-party application server sends messages to the device.
  • Receiving a message. An Android application receives a message from a C2DM server.

These processes are described in more detail below.

Enabling C2DM

This is the sequence of events that occurs when an Android application running on a mobile device registers to receive messages:

  1. The first time the application needs to use the messaging service, it fires off a registration Intent to a C2DM server.

    This registration Intent (com.google.android.c2dm.intent.REGISTER) includes the sender ID (that is, the account authorized to send messages to the application, which is typically the email address of an account set up by the application's developer), and the application ID.

  2. If the registration is successful, the C2DM server broadcasts a REGISTRATION Intent which gives the application a registration ID.

    The application should store this ID for later use. Note that Google may periodically refresh the registration ID, so you should design your application with the understanding that the REGISTRATION Intent may be called multiple times. Your application needs to be able to respond accordingly.

  3. To complete the registration, the application sends the registration ID to the application server. The application server typically stores the registration ID in a database.

The registration ID lasts until the application explicitly unregisters itself, or until Google refreshes the registration ID for your application.

Sending a Message

For an application server to send a message, the following things must be in place:

  • The application has a registration ID that allows it to receive messages for a particular device.
  • The third-party application server has stored the registration ID.

There is one more thing that needs to be in place for the application server to send messages: a ClientLogin authorization token. This is something that the developer must have already set up on the application server for the application (for more discussion, see Role of the Third-Party Application Server). Now it will get used to send messages to the device.

The ClientLogin token authorizes the application server to send messages to a particular Android application. An application server has one ClientLogin token for a particular 3rd party app, and multiple registration IDs. Each registration ID represents a particular device that has registered to use the messaging service for a particular 3rd party app.

Here is the sequence of events that occurs when the application server sends a message:

  1. The application server sends a message to C2DM servers.
  2. Google enqueues and stores the message in case the device is inactive.
  3. When the device is online, Google sends the message to the device.
  4. On the device, the system broadcasts the message to the specified application via Intent broadcast with proper permissions, so that only the targeted application gets the message. This wakes the application up. The application does not need to be running beforehand to receive the message.
  5. The application processes the message. If the application is doing non-trivial processing, you may want to grab a wake lock and do any processing in a Service.

An application can unregister C2DM if it no longer wants to receive messages.

Receiving a Message

This is the sequence of events that occurs when an Android application running on a mobile device receives a message:

  1. The system receives the incoming message and extracts the raw key/value pairs from the message payload.
  2. The system passes the key/value pairs to the targeted Android application in a com.google.android.c2dm.intent.RECEIVE Intent as a set of extras.
  3. The Android application extracts the raw data from the RECEIVE Intent by key and processes the data.

What Does the User See?

When mobile device users install an applications that include C2DM, they will get a permission from Android Market informing them that the application includes C2DM. They must approve the use of this feature to install the application. Depending on the implementation of the application, it may offer users the option of unregistering to receive messages. Uninstalling the application also has the effect of unregistering.

Writing Android Applications that Use C2DM

To write Android applications that use C2DM, you must have an application server that can perform the tasks described in Role of the Third-Party Application Server. This section describes the steps you take to create a client application that uses C2DM.

Remember that there is no user interface associated with the C2DM Framework. However you choose to process messages in your application is up to you.

There are two primary steps involved in writing a client application:

  • Creating a manifest that contains the permissions the application needs to use C2DM.
  • Implementing your Java code. To use C2DM, this implementation must include:
    • Code to start and stop the registration service.
    • Receivers for com.google.android.c2dm.intent.C2D_MESSAGE and com.google.android.c2dm.intent.REGISTRATION.

Creating the Manifest

Every application must have an AndroidManifest.xml file (with precisely that name) in its root directory. The manifest presents essential information about the application to the Android system, information the system must have before it can run any of the application's code (for more discussion of the manifest file, see the Android Developers Guide). To use the C2DM feature, the manifest must include the following:

  • com.google.android.c2dm.permission.RECEIVE states that the application has permission register and receive messages.
  • android.permission.INTERNET states that the application has permission to send the receiver key to the 3rd party server.
  • applicationPackage + ".permission.C2D_MESSAGE prevents other applications from registering and receiving the application's messages.
  • Receivers for com.google.android.c2dm.intent.RECEIVE and com.google.android.c2dm.intent.REGISTRATION, with the category set as applicationPackage. The receiver should require the com.google.android.c2dm.SEND permission, so that only the C2DM Framework can send it the message. Note that both registration and the receiving of messages are implemented as Intents.
  • If the C2DM feature is critical to the application's function, be sure to set android:minSdkVersion="8" in the manifest. This ensures that the application cannot be installed in an environment in which it could not run properly.

Received C2D_MESSAGE Intents have all key/value pairs sent by the 3rd party server as extras. One special key is collapse_key, which is specified by the sender to allow handling of messages waiting for an off-line device.

Here is an example of a manifest that supports C2DM:

<manifest package="com.example.myapp" ...> 
 
<!-- Only this application can receive the messages and registration result -->  
<permission android:name="com.example.myapp.permission.C2D_MESSAGE" android:protectionLevel="signature" /> 
<uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE" /> 
 
<!-- This app has permission to register and receive message --> 
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> 
 
<!-- Send the registration id to the server --> 
<uses-permission android:name="android.permission.INTERNET" /> 
 
<!-- Only C2DM servers can send messages for the app. If permission is not set - any other app can generate it -->  
<receiver android:name=".C2DMReceiver" android:permission="com.google.android.c2dm.permission.SEND"> 
    <!-- Receive the actual message --> 
    <intent-filter> 
        <action android:name="com.google.android.c2dm.intent.RECEIVE" /> 
        <category android:name="com.example.myapp" /> 
    </intent-filter> 
    <!-- Receive the registration id --> 
    <intent-filter> 
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> 
        <category android:name="com.example.myapp" /> 
    </intent-filter> 
</receiver>

Registering for C2DM

An Android application needs to register with C2DM servers before receiving any message. To register it needs to send an Intent (com.google.android.c2dm.intent.REGISTER), with 2 extra parameters:

  • sender is the ID of the account authorized to send messages to the application, typically the email address of an account set up by the application's developer.
  • app is the application's ID, set with a PendingIntent to allow the registration service to extract application information.

For example:

Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER"); 
registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0); // boilerplate 
registrationIntent.putExtra("sender", emailOfSender); 
startService(registrationIntent);

Registration is not complete until the application sends the registration ID to the third-party application server. The application server uses the registration ID to send messages that are targeted to the application running on that particular device.

Unregistering from C2DM

To unregister from C2DM:

Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER"); 
unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0)); 
startService(unregIntent);

Handling Registration Results

As discussed in Creating the Manifest, the manifest defines a receiver for com.google.android.c2dm.intent.REGISTRATION. It also defines a receiver for com.google.android.c2dm.intent.RECEIVE. Note that both registration and the receiving of messages are implemented as Intents.

The main use of REGISTRATION is to allow the application to receive the registration ID. The Intent can be sent at any time. Google may periodically refresh the receiver ID. An application receiving this Intent with a registration_id parameter must ensure that the third-party application server receives the registration ID. It may do so by saving the registration ID and sending it to the server. If the network is down or there are errors, the application should retry sending the registration ID when the network is up again or the next time it starts. An application should keep track of its registration status and attempt to register again if the process is not fully completed.

The REGISTRATION Intent is generated with an error parameter if the registration couldn't be completed. If that happens, the application should try again later with exponential back off. When the application unregisters, the REGISTRATION Intent will be sent with an unregistered extra parameter.

Here are the possible error codes for the REGISTRATION Intent:

Error Code Description
SERVICE_NOT_AVAILABLE The device can't read the response, or there was a 500/503 from the server that can be retried later. The application should use exponential back off and retry.
ACCOUNT_MISSING There is no Google account on the phone. The application should ask the user to open the account manager and add a Google account. Fix on the device side.
AUTHENTICATION_FAILED Bad password. The application should ask the user to enter his/her password, and let user retry manually later. Fix on the device side.
TOO_MANY_REGISTRATIONS The user has too many applications registered. The application should tell the user to uninstall some other applications, let user retry manually. Fix on the device side.
INVALID_SENDER The sender account is not recognized.
PHONE_REGISTRATION_ERROR Incorrect phone registration with Google. This phone doesn't currently support C2DM.

The application receives a REGISTRATION Intent broadcast whenever the application server attempts to send a message to it. But registration IDs can be nonexistent or invalid for a variety of reasons:

  • If an application is running for the first time, it has no registration ID yet.
  • If the application unregistered, it has no registration ID.
  • The C2DM server periodically refreshes registration IDs.

An application must be prepared to handle every case. For example:

public void onReceive(Context context, Intent intent) { 
    if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) { 
        handleRegistration(context, intent); 
    } else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { 
        handleMessage(context, intent); 
     } 
 } 
 
private void handleRegistration(Context context, Intent intent) { 
    String registration = intent.getStringExtra("registration_id");  
    if (intent.getStringExtra("error") != null) { 
        // Registration failed, should try again later. 
    } else if (intent.getStringExtra("unregistered") != null) { 
        // unregistration done, new messages from the authorized sender will be rejected 
    } else if (registration != null) { 
       // Send the registration ID to the 3rd party site that is sending the messages. 
       // This should be done in a separate thread. 
       // When done, remember that all registration is done.  
    } 
}

Handling Received Data

When the C2DM server receives a message from the third-party application server, C2DM extracts the raw key/value pairs from the message payload and passes them to the Android application in the com.google.android.c2dm.intent.RECEIVE Intent as a set of extras. The application extracts the data by key and processes it, whatever that means for that application.

Here is an example:

protected void onReceive(Context context, Intent intent) { 
    String accountName = intent.getExtras().getString(Config.C2DM_ACCOUNT_EXTRA); 
    String message = intent.getExtras().getString(Config.C2DM_MESSAGE_EXTRA); 
    if (Config.C2DM_MESSAGE_SYNC.equals(message)) { 
        if (accountName != null) { 
            if (Log.isLoggable(TAG, Log.DEBUG)) { 
                Log.d(TAG, "Messaging request received for account " + accountName); 
            } 
             
            ContentResolver.requestSync( 
                new Account(accountName, SyncAdapter.GOOGLE_ACCOUNT_TYPE), 
                JumpNoteContract.AUTHORITY, new Bundle()); 
        } 
    } 
}

Developing and Testing Your Applications

Here are some guidelines for developing and testing an Android application that uses the C2DM feature:

  • To develop and test your C2DM applications, you need to run and debug the applications on an Android 2.2 system image that includes the necessary underlying Google services.
  • To develop and debug on an actual device, you need a device running an Android 2.2 system image that includes the Market application.
  • To develop and test on the Android Emulator, you need to download the Android 2.2 version of the Google APIs Add-On into your SDK using the Android SDK and AVD Manager. Specifically, you need to download the component named "Google APIs by Google Inc, Android API 8". Then, you need to set up an AVD that uses that system image.
  • If the C2DM feature is critical to the application's function, be sure to set android:minSdkVersion="8" in the manifest. This ensures that the application cannot be installed in an environment in which it could not run properly.

Role of the Third-Party Application Server

Before you can write client applications that use the C2DM feature, you must have an HTTPS application server that meets the following criteria:

  • Able to communicate with your client.
  • Able to fire off HTTP requests to the C2DM server.
  • Able to handle requests and queue data as needed. For example, it should be able to perform exponential back off.
  • Able to store the ClientLogin Auth token and client registration IDs. The ClientLogin Auth token is included in the header of POST requests that send messages. For more discussion of this topic, see ClientLogin for Installed Applications. The server should store the token and have a policy to refresh it periodically.

How the Application Server Sends Messages

This section describes how the third-party application server sends messages to a 3rd party client application running on a mobile device.

Before the third-party application server can send a message to an application, it must have received a registration ID from it.

To send a message, the application server issues a POST request to https://android.apis.google.com/c2dm/send that includes the following:

Field Description
registration_id The registration ID retrieved from the Android application on the phone. Required.
collapse_key An arbitrary string that is used to collapse a group of like messages when the device is offline, so that only the last message gets sent to the client. This is intended to avoid sending too many messages to the phone when it comes back online. Note that since there is no guarantee of the order in which messages get sent, the "last" message may not actually be the last message sent by the application server. Required.
data.<key> Payload data, expressed as key-value pairs. If present, it will be included in the Intent as application data, with the <key>. There is no limit on the number of key/value pairs, though there is a limit on the total size of the message. Optional.
delay_while_idle If included, indicates that the message should not be sent immediately if the device is idle. The server will wait for the device to become active, and then only the last message for each collapse_key value will be sent. Optional.
Authorization: GoogleLogin auth=[AUTH_TOKEN] Header with a ClientLogin Auth token. The cookie must be associated with the ac2dm service. Required.

This table lists the possible response codes:

Response Description
200

Includes body containing:

  • id=[ID of sent message]
  • Error=[error code]
    • QuotaExceeded — Too many messages sent by the sender. Retry after a while.
    • DeviceQuotaExceeded — Too many messages sent by the sender to a specific device. Retry after a while.
    • InvalidRegistration — Missing or bad registration_id. Sender should stop sending messages to this device.
    • NotRegistered — The registration_id is no longer valid, for example user has uninstalled the application or turned off notifications. Sender should stop sending messages to this device.
    • MessageTooBig — The payload of the message is too big, see the limitations. Reduce the size of the message.
    • MissingCollapseKey — Collapse key is required. Include collapse key in the request.
503 Indicates that the server is temporarily unavailable (i.e., because of timeouts, etc ). Sender must retry later, honoring any Retry-After header included in the response. Application servers must implement exponential back off. Senders that create problems risk being blacklisted.
401 Indicates that the ClientLogin AUTH_TOKEN used to validate the sender is invalid.

Examples

Here are a few complete examples to get you started:

  • JumpNote. JumpNote is a sample two-way notes application, with auto-sync. JumpNote shows how to use the Android Cloud to Device Messaging Framework, as well as the Android Sync framework. JumpNote runs on top of Google App Engine, and it uses GWT for the web user interface.
  • Google Chrome to Phone Extension. "Chrome to Phone" lets users send content from the Chrome browser to their mobile device.

To view the source code, open the Source tab and click Browse. From there, you can navigate through the source tree.

Limitations

C2DM imposes the following limitations:

  • The message size limit is 1024 bytes.
  • Google limits the number of messages a sender sends in aggregate, and the number of messages a sender sends to a specific device
Posted by 1010
04.Anddoid2010. 7. 5. 15:39
반응형

안드로이드(android) 다이얼로그 종류별 구현 방법

 

개발환경 : JDK 1.5, eclipse-galileo, android GoogleAPI 2.1, window XP

 

(1) 여러 개의 멀티선택 옵션으로 표현해 주기

 

다중선택을 위한 다이얼로그 창을 띄운다. 리스트에는 제목과 radio button

이 있다. AlertDialog.Builder 클래스로 구현하며 리스트중 특정행을 클릭했을 때

이벤트는 setSingleChoiceItems 에 등록한다. Ok 버튼클릭 이벤트는

setPositiveButton , Cancel 버튼클릭 이벤트는 setNegativeButton 에 등록하고

기능을 구현하면 된다.


 

01 private void DialogSelectOption() {
02     final String items[] = { "item1", "item2", "item3" };
03     AlertDialog.Builder ab = new AlertDialog.Builder(DialogSample.this);
04     ab.setTitle("Title");
05     ab.setSingleChoiceItems(items, 0,
06         new DialogInterface.OnClickListener() {
07         public void onClick(DialogInterface dialog, int whichButton) {
08             // 각 리스트를 선택했을때 
09         }
10         }).setPositiveButton("Ok",
11         new DialogInterface.OnClickListener() {
12         public void onClick(DialogInterface dialog, int whichButton) {
13             // OK 버튼 클릭시 , 여기서 선택한 값을 메인 Activity 로 넘기면 된다.
14         }
15         }).setNegativeButton("Cancel",
16         new DialogInterface.OnClickListener() {
17         public void onClick(DialogInterface dialog, int whichButton) {
18             // Cancel 버튼 클릭시
19         }
20         });
21     ab.show();
22 }
 

 

(2) html 로 구현한 text 넣기

 

다이얼로그 창을 띄울 때 공지나 경고성 창이라면 내용이 들어갈것이다. 이 내용을

HTML 태그를 적용해 넣을 수가 있다. 색깔은 제대로 바뀌는 것 같은데 <strong>, <b>

등 글자에 대한 bold 처리가 한거나 안한거나 똑같이 보여서 제대로 표현되는지는

좀더 테스트 해봐야할것같다.

 
 
1 private void DialogHtmlView(){
2 AlertDialog.Builder ab=new AlertDialog.Builder(DialogSample.this);
3     ab.setMessage(Html.fromHtml("<STRONG><FONT color=#ff0000> " +
4         "Html 표현여부 " +"</FONT></STRONG><BR>HTML 이 제대로 표현되는지 본다."));
5         ab.setPositiveButton("ok", null);
6         ab.show();
7 }

 

(3) 프로그레시브(Progress) 다이얼로그 구현

 

안드로이드에서 프로그레시브바를 구현할수 있다. 이것을 구현하기 위한 클래스는

ProgressDialog 를 사용해야 한다. 다이얼로그 창의 중지는 ProgressDialog

dismiss 를 쓰면된다.

 
 
1 private void DialogProgress(){
2        ProgressDialog dialog = ProgressDialog.show(DialogSample.this, "",
3                         "잠시만 기다려 주세요 ...", true);
4       // 창을 내린다.
5       // dialog.dismiss();
6 }

aaa

 

(4) Radio 버튼을 포함한 다중선택 다이얼로그 창

 

1번 예제와 비슷하게 Radio 버튼이 추가되어있는 다중선택 다이얼로그 창이다.

차이가 있다면 창 타이틀에 setIcon 을 써서 아이콘을 집어넣은것과

선택한 행 번호를 알아와 Toast 로 화면에 문자열을 표시해주는 내용이다.

창을 닫고 싶다면 onclick 함수에 넘어온 DialogInterface 객체로 cancel 함수를

호출해 닫으면 된다.

 
 
01 private void DialogRadio(){
02 final CharSequence[] PhoneModels = {"iPhone", "Nokia", "Android"};
03         AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
04         alt_bld.setIcon(R.drawable.icon);
05         alt_bld.setTitle("Select a Phone Model");
06         alt_bld.setSingleChoiceItems(PhoneModels, -1, new DialogInterface.OnClickListener() {
07             public void onClick(DialogInterface dialog, int item) {
08                 Toast.makeText(getApplicationContext(), "Phone Model = "+PhoneModels[item], Toast.LENGTH_SHORT).show();
09                 // dialog.cancel();
10             }
11         });
12         AlertDialog alert = alt_bld.create();
13         alert.show();
14 }

 

(5) 선택에 따른 로직구현을 위한 다이얼로그 창 구현

 

예를 들어 Yes/No 중 하나를 선택함으로서 특정 기능을 구현해주고자 할 때

쓰일만한 예제이다. 샘플에서는 Yes/No 일때를 구분하여 코드를 구현할수

있도록 나누어져 있다. 우리가 흔히 보는 대표적인 다이얼로그 창일것이다.

 
 
01 private void DialogSimple(){
02     AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
03     alt_bld.setMessage("Do you want to close this window ?").setCancelable(
04         false).setPositiveButton("Yes",
05         new DialogInterface.OnClickListener() {
06         public void onClick(DialogInterface dialog, int id) {
07             // Action for 'Yes' Button
08         }
09         }).setNegativeButton("No",
10         new DialogInterface.OnClickListener() {
11         public void onClick(DialogInterface dialog, int id) {
12             // Action for 'NO' Button
13             dialog.cancel();
14         }
15         });
16     AlertDialog alert = alt_bld.create();
17     // Title for AlertDialog
18     alert.setTitle("Title");
19     // Icon for AlertDialog
20     alert.setIcon(R.drawable.icon);
21     alert.show();
22 }

 

(6) Time Picker 시간선택 컨트롤을 다이얼로그에 구현

 

Time Picker 컨트롤을 다이얼로그 창에 구현한 예제이다. 그리고 다이얼로그에

구현되어있는 Time Picker 에서 선택한 값을 부모 화면에서 받아 그 값을 Toast

로 보여준다

 
 
01 private void DialogTimePicker(){
02     TimePickerDialog.OnTimeSetListener mTimeSetListener = 
03     new TimePickerDialog.OnTimeSetListener() {
04         public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
05             Toast.makeText(DialogSample.this,
06             "Time is=" + hourOfDay + ":" + minute, Toast.LENGTH_SHORT)
07             .show();
08         }
09     };
10     TimePickerDialog alert = new TimePickerDialog(this
11 mTimeSetListener, 0, 0, false);
12     alert.show();
13 }

 

(7) Date Picker 날짜선택 컨트롤을 다이얼로그에 구현

 

Date Picker 컨트롤을 다이얼로그 창에 구현한 예제이다. 그리고 다이얼로그에

구현되어있는 Date Picker 에서 선택한 값을 부모 화면에서 받아 그 값을 Toast

로 보여준다. 창을 띄우기 전에 현재 날짜정보를 넘겨주고 Date Picker 에서는

그것을 받아 표시해 준다.

 
 
01 private void DialogDatePicker(){
02     Calendar c = Calendar.getInstance();
03     int cyear = c.get(Calendar.YEAR);
04     int cmonth = c.get(Calendar.MONTH);
05     int cday = c.get(Calendar.DAY_OF_MONTH);
06       
07     DatePickerDialog.OnDateSetListener mDateSetListener = 
08     new DatePickerDialog.OnDateSetListener() {
09     // onDateSet method
10     public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
11          String date_selected = String.valueOf(monthOfYear+1)+
12                 " /"+String.valueOf(dayOfMonth)+" /"+String.valueOf(year);
13          Toast.makeText(DialogSample.this
14         "Selected Date is ="+date_selected, Toast.LENGTH_SHORT).show();
15     }
16      };
17      DatePickerDialog alert = new DatePickerDialog(this,  mDateSetListener,  
18      cyear, cmonth, cday);
19      alert.show();
20 }

 

(8) 전체 소스

 

 
001 import java.util.ArrayList;
002 import java.util.Calendar;
003 import java.util.HashMap;
004 import java.util.List;
005 import java.util.Map;
006   
007 import android.app.AlertDialog;
008 import android.app.DatePickerDialog;
009 import android.app.ListActivity;
010 import android.app.ProgressDialog;
011 import android.app.TimePickerDialog;
012 import android.content.DialogInterface;
013 import android.os.Bundle;
014 import android.text.Html;
015 import android.util.Log;
016 import android.view.View;
017 import android.widget.DatePicker;
018 import android.widget.ListView;
019 import android.widget.SimpleAdapter;
020 import android.widget.TimePicker;
021 import android.widget.Toast;
022   
023 import com.sample.R;
024   
025 public class DialogSample extends ListActivity {
026   
027     private String[] mMenuText;
028     private String[] mMenuSummary;
029   
030     private String keyName = "name";
031     private String keyDesc = "desc";
032     private String TAG;
033       
034     /** Called when the activity is first created. */
035     @Override
036     public void onCreate(Bundle savedInstanceState) {
037         super.onCreate(savedInstanceState);
038         TAG = getClass().getName();
039   
040         int length = 7;
041         mMenuText = new String[length];
042         mMenuSummary = new String[length];
043   
044         mMenuText[0] = "다중선택 새창";
045         mMenuSummary[0] = "다중선택을 할수 있는 Alert 예제구현이다.";
046         mMenuText[1] = "HTML 적용 새창";
047         mMenuSummary[1] = "Text 에 HTML 을 적용하는 Alert 예제구현이다.";
048         mMenuText[2] = "프로그레시브바 새창";
049         mMenuSummary[2] = "진행중을 나타내는 프로그레시브바 Alert 예제구현다.";
050         mMenuText[3] = "Radio 버튼 새창";
051         mMenuSummary[3] = "Radio 버튼이 들어간 새창 이며 선택하면 창이 닫힌다. ";
052         mMenuText[4] = "Simple Dialog";
053         mMenuSummary[4] = "선택에 따른 로직구현을 위한 다이얼로그 창 구현";
054         mMenuText[5] = "Time Picker";
055         mMenuSummary[5] = "Time Picker 시간선택 컨트롤을 다이얼로그에 구현";
056         mMenuText[6] = "Date Picker";
057         mMenuSummary[6] = "Date Picker 날짜선택 컨트롤을 다이얼로그에 구현";
058   
059         setListAdapter(new SimpleAdapter(this, getListValues(),
060                 android.R.layout.simple_list_item_2, new String[] { keyName,
061                         keyDesc }, new int[] { android.R.id.text1,
062                         android.R.id.text2 }));
063     }
064   
065     private List<MAP<STRING, String>> getListValues() {
066         List<MAP<STRING, String>> values = new ArrayList<MAP<STRING, String>>();
067         int length = mMenuText.length;
068         for (int i = 0; i < length; i++) {
069             Map<STRING, String> v = new HashMap<STRING, String>();
070             v.put(keyName, mMenuText[i]);
071             v.put(keyDesc, mMenuSummary[i]);
072             values.add(v);
073         }
074         return values;
075     }
076   
077     @Override
078     protected void onListItemClick(ListView l, View v, int position, long id) {
079         super.onListItemClick(l, v, position, id);
080         Log.d(TAG, "id : " + id + ", position : " + position);
081         switch (position) {
082         case 0:
083             // 다중선택 옵션창 
084             this.DialogSelectOption();
085             break;
086         case 1:
087             // HTML 구현 
088             this.DialogHtmlView();
089             break;
090         case 2:
091             // 프로그레시브바 구현
092             this.DialogProgress();
093             break;
094         case 3:
095             //  Radio 버튼이 추가된 다중선택 창
096             this.DialogRadio();
097             break;
098         case 4:
099             // 가장 일반적인 Yes/NO기능구현 Dialog
100             this.DialogSimple();
101             break;
102         case 5:
103             this.DialogTimePicker();
104             break;
105         case 6:
106             // 날짜 선택 Dialog 구현 
107             this.DialogDatePicker();
108             break;
109         default:
110             break;
111         }
112     }
113   
114     private void DialogSelectOption() {
115         final String items[] = { "item1", "item2", "item3" };
116         AlertDialog.Builder ab = new AlertDialog.Builder(DialogSample.this);
117         ab.setTitle("Title");
118         ab.setSingleChoiceItems(items, 0,
119             new DialogInterface.OnClickListener() {
120                 public void onClick(DialogInterface dialog, int whichButton) {
121                     // 각 리스트를 선택했을때 
122                 }
123             }).setPositiveButton("Ok",
124             new DialogInterface.OnClickListener() {
125                 public void onClick(DialogInterface dialog, int whichButton) {
126                     // OK 버튼 클릭시 , 여기서 선택한 값을 메인 Activity 로 넘기면 된다.
127                 }
128             }).setNegativeButton("Cancel",
129             new DialogInterface.OnClickListener() {
130                 public void onClick(DialogInterface dialog, int whichButton) {
131                     // Cancel 버튼 클릭시
132                 }
133             });
134         ab.show();
135     }
136       
137     private void DialogHtmlView(){
138         AlertDialog.Builder ab=new AlertDialog.Builder(DialogSample.this);
139         ab.setMessage(Html.fromHtml("<STRONG><FONT color=#ff0000> " +
140                 "Html 표현여부 " +"</FONT></STRONG><BR>
141 HTML 이 제대로 표현되는지 본다."));
142             ab.setPositiveButton("ok", null);
143             ab.show();
144     }
145       
146     private void DialogProgress(){
147         ProgressDialog dialog = ProgressDialog.show(DialogSample.this, "",
148                                 "잠시만 기다려 주세요 ...", true);
149                 
150         // 창을 내린다.
151         // dialog.dismiss();
152     }
153       
154     private void DialogRadio(){
155         final CharSequence[] PhoneModels = {"iPhone", "Nokia", "Android"};
156         AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
157         alt_bld.setIcon(R.drawable.icon);
158         alt_bld.setTitle("Select a Phone Model");
159         alt_bld.setSingleChoiceItems(PhoneModels, -1, new DialogInterface.OnClickListener() {
160             public void onClick(DialogInterface dialog, int item) {
161                 Toast.makeText(getApplicationContext(), "Phone Model = "+PhoneModels[item], Toast.LENGTH_SHORT).show();
162                 dialog.cancel();
163             }
164         });
165         AlertDialog alert = alt_bld.create();
166         alert.show();
167     }
168       
169     private void DialogSimple(){
170         AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
171         alt_bld.setMessage("Do you want to close this window ?").setCancelable(
172                 false).setPositiveButton("Yes",
173                 new DialogInterface.OnClickListener() {
174                     public void onClick(DialogInterface dialog, int id) {
175                         // Action for 'Yes' Button
176                     }
177                 }).setNegativeButton("No",
178                 new DialogInterface.OnClickListener() {
179                     public void onClick(DialogInterface dialog, int id) {
180                         // Action for 'NO' Button
181                         dialog.cancel();
182                     }
183                 });
184         AlertDialog alert = alt_bld.create();
185         // Title for AlertDialog
186         alert.setTitle("Title");
187         // Icon for AlertDialog
188         alert.setIcon(R.drawable.icon);
189         alert.show();
190     }
191       
192     private void DialogTimePicker(){
193         TimePickerDialog.OnTimeSetListener mTimeSetListener = 
194             new TimePickerDialog.OnTimeSetListener() {
195                 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
196                     Toast.makeText(DialogSample.this,
197                         "Time is=" + hourOfDay + ":" + minute, Toast.LENGTH_SHORT)
198                         .show();
199                 }
200         };
201         TimePickerDialog alert = new TimePickerDialog(this, mTimeSetListener, 0, 0, false);
202         alert.show();
203     }
204       
205     private void DialogDatePicker(){
206         Calendar c = Calendar.getInstance();
207         int cyear = c.get(Calendar.YEAR);
208         int cmonth = c.get(Calendar.MONTH);
209         int cday = c.get(Calendar.DAY_OF_MONTH);
210           
211         DatePickerDialog.OnDateSetListener mDateSetListener = 
212             new DatePickerDialog.OnDateSetListener() {
213                 // onDateSet method
214                 public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
215                     String date_selected = String.valueOf(monthOfYear+1)+
216                         " /"+String.valueOf(dayOfMonth)+" /"+String.valueOf(year);
217                     Toast.makeText(DialogSample.this
218                             "Selected Date is ="+date_selected, Toast.LENGTH_SHORT).show();
219                 }
220         };
221         DatePickerDialog alert = new DatePickerDialog(this,  mDateSetListener,  cyear, cmonth, cday);
222         alert.show();
223     }
224   
225 }


 



package com.DialogSample;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.app.TimePickerDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.View;
import android.widget.DatePicker;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TimePicker;
import android.widget.Toast;

public class DialogSample extends ListActivity {

 private String[] mMenuText;
 private String[] mMenuSummary;

 private String keyName = "name";
 private String keyDesc = "desc";
 private String TAG;

 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  TAG = getClass().getName();

  int length = 7;
  mMenuText = new String[length];
  mMenuSummary = new String[length];

  mMenuText[0] = "다중선택 새창";
  mMenuSummary[0] = "다중선택을 할수 있는 Alert 예제구현이다.";
  mMenuText[1] = "HTML 적용 새창";
  mMenuSummary[1] = "Text 에 HTML 을 적용하는 Alert 예제구현이다.";
  mMenuText[2] = "프로그레시브바 새창";
  mMenuSummary[2] = "진행중을 나타내는 프로그레시브바 Alert 예제구현다.";
  mMenuText[3] = "Radio 버튼 새창";
  mMenuSummary[3] = "Radio 버튼이 들어간 새창 이며 선택하면 창이 닫힌다. ";
  mMenuText[4] = "Simple Dialog";
  mMenuSummary[4] = "선택에 따른 로직구현을 위한 다이얼로그 창 구현";
  mMenuText[5] = "Time Picker";
  mMenuSummary[5] = "Time Picker 시간선택 컨트롤을 다이얼로그에 구현";
  mMenuText[6] = "Date Picker";
  mMenuSummary[6] = "Date Picker 날짜선택 컨트롤을 다이얼로그에 구현";

  setListAdapter(new SimpleAdapter(this, getListValues(), android.R.layout.simple_list_item_2, new String[] {
    keyName, keyDesc }, new int[] { android.R.id.text1, android.R.id.text2 }));
 }

 private List<Map<String, String>> getListValues() {
  List<Map<String, String>> values = new ArrayList<Map<String, String>>();
  int length = mMenuText.length;
  for (int i = 0; i < length; i++) {
   Map<String, String> v = new HashMap<String, String>();
   v.put(keyName, mMenuText[i]);
   v.put(keyDesc, mMenuSummary[i]);
   values.add(v);
  }
  return values;
 }

 @Override
 protected void onListItemClick(ListView l, View v, int position, long id) {
  super.onListItemClick(l, v, position, id);
  Log.d(TAG, "id : " + id + ", position : " + position);
  switch (position) {
  case 0:
   // 다중선택 옵션창
   this.DialogSelectOption();
   break;
  case 1:
   // HTML 구현
   this.DialogHtmlView();
   break;
  case 2:
   // 프로그레시브바 구현
   this.DialogProgress();
   break;
  case 3:
   // Radio 버튼이 추가된 다중선택 창
   this.DialogRadio();
   break;
  case 4:
   // 가장 일반적인 Yes/NO기능구현 Dialog
   this.DialogSimple();
   break;
  case 5:
   this.DialogTimePicker();
   break;
  case 6:
   // 날짜 선택 Dialog 구현
   this.DialogDatePicker();
   break;
  default:
   break;
  }
 }

 private void DialogSelectOption() {
  final String items[] = { "item1", "item2", "item3" };
  AlertDialog.Builder ab = new AlertDialog.Builder(DialogSample.this);
  ab.setTitle("Title");
  ab.setSingleChoiceItems(items, 0, new DialogInterface.OnClickListener() {
   public void onClick(DialogInterface dialog, int whichButton) {
    // 각 리스트를 선택했을때
   }
  }).setPositiveButton("Ok", new DialogInterface.OnClickListener() {
   public void onClick(DialogInterface dialog, int whichButton) {
    // OK 버튼 클릭시 , 여기서 선택한 값을 메인 Activity 로 넘기면 된다.
   }
  }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
   public void onClick(DialogInterface dialog, int whichButton) {
    // Cancel 버튼 클릭시
   }
  });
  ab.show();
 }

 private void DialogHtmlView() {
  AlertDialog.Builder ab = new AlertDialog.Builder(DialogSample.this);
  ab.setMessage(Html.fromHtml("<STRONG><FONT color=#ff0000> " + "Html 표현여부 "
    + "</FONT></STRONG><BR>  HTML 이 제대로 표현되는지 본다."));
  ab.setPositiveButton("ok", null);
  ab.show();
 }

 private void DialogProgress() {
  ProgressDialog dialog = ProgressDialog.show(DialogSample.this, "", "잠시만 기다려 주세요 ...", true);

  // 창을 내린다.
  dialog.dismiss();
 }

 private void DialogRadio() {
  final CharSequence[] PhoneModels = { "iPhone", "Nokia", "Android" };
  AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
  alt_bld.setIcon(R.drawable.icon);
  alt_bld.setTitle("Select a Phone Model");
  alt_bld.setSingleChoiceItems(PhoneModels, -1, new DialogInterface.OnClickListener() {
   public void onClick(DialogInterface dialog, int item) {
    Toast.makeText(getApplicationContext(), "Phone Model = " + PhoneModels[item],
      Toast.LENGTH_SHORT).show();
    dialog.cancel();
   }
  });
  AlertDialog alert = alt_bld.create();
  alert.show();
 }

 private void DialogSimple() {
  AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
  alt_bld.setMessage("Do you want to close this window ?").setCancelable(false).setPositiveButton("Yes",
    new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialog, int id) {
      // Action for 'Yes' Button
     }
    }).setNegativeButton("No", new DialogInterface.OnClickListener() {
   public void onClick(DialogInterface dialog, int id) {
    // Action for 'NO' Button
    dialog.cancel();
   }
  });
  AlertDialog alert = alt_bld.create();
  // Title for AlertDialog
  alert.setTitle("Title");
  // Icon for AlertDialog
  alert.setIcon(R.drawable.icon);
  alert.show();
 }

 private void DialogTimePicker() {
  TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {
   public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
    Toast.makeText(DialogSample.this, "Time is=" + hourOfDay + ":" + minute, Toast.LENGTH_SHORT)
      .show();
   }
  };
  TimePickerDialog alert = new TimePickerDialog(this, mTimeSetListener, 0, 0, false);
  alert.show();
 }

 private void DialogDatePicker() {
  Calendar c = Calendar.getInstance();
  int cyear = c.get(Calendar.YEAR);
  int cmonth = c.get(Calendar.MONTH);
  int cday = c.get(Calendar.DAY_OF_MONTH);

  DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {
   // onDateSet method
   public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
    String date_selected = String.valueOf(monthOfYear + 1) + " /" + String.valueOf(dayOfMonth)
      + " /" + String.valueOf(year);
    Toast.makeText(DialogSample.this, "Selected Date is =" + date_selected, Toast.LENGTH_SHORT)
      .show();
   }
  };
  DatePickerDialog alert = new DatePickerDialog(this, mDateSetListener, cyear, cmonth, cday);
  alert.show();
 }

}




Posted by 1010
04.Anddoid2010. 6. 25. 14:37
반응형
저번에 XMPPConnection를 상속받아서 GoogleTalkConnection을 만들어보왔는데 그모든기능을 하나의 객체안에서 쓰기 편하게 하기 위해서 GoogleTalkClient를 제작을 해보았다. 코드는 아직 초보라서 잘하지는 못했지만 간단한 기능은 제공한다고 생각해서 올려본다.
 

* GoogleTalkConnection 소스를 보실려면 여기 ▶ XMPPConnection 소스보기

001 import org.jivesoftware.smack.*;
002 import org.jivesoftware.smack.filter.PacketFilter;
003   
004 /**
005  * @author GoogleTalk Client 프로그램으로서 XMPP통신으로 채팅을 지원하는 객체
006  * smack.jar 파일이 잇어야함!
007  * has-a : GoogleTalkConnection, MessageListener;
008  *  
009  */
010 public class GoogleTalkClient {
011       
012     private final GoogleTalkConnection connection;
013     private final String username;
014     private final String password;
015     private String recipientsName;
016     private MessageListener listener = null;
017       
018     /**
019      * GoogleTalkClient 생성자!
020      * @param username 접속자의 Gmail 주소 입니다.
021      * @param password Gmail의 비밀번호 입니다.
022      * @throws XMPPException 접속과 로그인에서의 예외처리입니다.
023      */
024     public GoogleTalkClient(String username, String password) throws XMPPException {
025          connection = new GoogleTalkConnection();
026          connection.login(username, password);
027          this.username = username;
028          this.password = password;
029     }
030       
031     /**
032      * public void open() throws XMPPException {
033      
034      * XMPPConnection 의 접속및 로그인을 합니다. 
035      * @throws XMPPException
036      */
037     public void open() throws XMPPException {
038         if (isConnected()) {
039             connection.connect();
040             connection.login(username, password);
041         }
042     }
043       
044     /**
045      * public void close(){ 
046      
047      * 커넥션 접속 해제를 합니다.
048      */
049     public void close() {
050         if (!isConnected()){
051             connection.disconnect();
052         }
053     }
054       
055     /**
056      * public boolean isConnected()
057      
058      * 커넥션의 접속된 정보를 표현합니다.
059      * @return 커넥션접속정보!
060      */
061     public boolean isConnected() {
062         return connection.isConnected();
063     }
064       
065     /**
066      * public void addMessageListener(MessageListener listener)
067      
068      * 상대방의 보낸메세지를 담당하는 MessageLisener 추가 메서드
069      * @param listener MessageListener
070      */
071     public void addMessageListener(MessageListener listener) {
072         this.listener = listener;
073     }
074       
075     /**
076      * public void addWriterListener(PackteListener listener)
077      
078      * 내가 보낸메세지를 담당하는 추가메서드
079      * @param listener PacketListener
080      */
081     public void addWriterListener(PacketListener listener) {
082         addWriterListener(listener, null);
083     }
084       
085     /**
086      * public void addWriterListener(PacketListener listener, PacketFilter filter) {
087      
088      * 내가 보낸메세지를 담당하는 추가메서드
089      * @param listener PacketListener
090      * @param filter PacketFilter
091      */
092     public void addWriterListener(PacketListener listener, PacketFilter filter) {
093         connection.addPacketWriterListener(listener, filter);
094     }
095       
096     /**
097      * public String getUserName(){
098      
099      * 사용자의 gmail 주소를 리턴합니다.
100      * @return 사용자의 gmail 주소
101      */
102     public String getUserName() {
103         return username;
104     }
105       
106     /**
107      * public String getPassWord(){ 
108      
109      * 사용자의 비밀번호를 리턴합니다.  
110      * @return 사용자의 비밀번호 리턴
111      */
112     public String getPassWord(){
113         return password;
114     }
115       
116     /**
117      * public void setRecipientsName(String name) {
118      
119      * 받는사람의 Gmail 주소를 등록한다.
120      * @param 받는사람
121      */
122     public void setRecipientsName(String name) {
123         this.recipientsName = name;
124     }
125       
126     /**
127      * public String getRecipientsName(){
128      
129      * 받는사람의 Gmail 주소를 리턴합니다.
130      * @return 받는사람
131      */
132     public String getRecipientsName() {
133         return recipientsName;
134     }
135       
136     /**
137      * public void sendMessage(String message) throws XMPPException {
138      
139      * setRecipientsName을 통해 미리 등록이 되있어야만 쪽지를 보낼수있다. 
140      * @param message 보낼 쪽지를 의미 합니다.
141      * @throws XMPPException 
142      */
143     public void sendMessage(String message) throws XMPPException {
144         sendMessage(recipientsName, message);
145     }
146       
147     /**
148      * public void sendMessage(String to, String message) throws XMPPException {
149      
150      * 상대방에게 메세지를 보냅니다.
151      
152      * @param to 상대방의 Gmail 아이디 입니다.
153      * @param message  상대방에게 보낼 메세지입니다.
154      * @throws XMPPException XMPPException입니다.
155      */
156     public void sendMessage(String to, String message) throws XMPPException {
157         Chat chat = connection.getChatManager().createChat(to, this.listener);
158         chat.sendMessage(message);
159     }
Posted by 1010
04.Anddoid2010. 6. 25. 14:35
반응형
안드로이드에서 자주 사용하는 기법인 ListView에 동적으로 아이템을 추가시 새로추가된 아이템으로 스크롤이 이동이 되지 않는 현상에 대해서 간단하게 알아보겠습니다.

일단 간단한 상황을 예를 들어서 풀어보도록 하겠습니다.

상황 : 현재의 Activity는 ListView와 EditText로 구성이 되어있고 EditText에 내용을 입력하고 Enter키를 입력시 ListView에 동적으로 추가된다.

위의 상황을 코드로 표현을 해보겠습니다.

01 public class TranscriptDemo extends ListActivity implements OnKeyListener {
02     private ArrayAdapter<STRING> adapter;
03     private ArrayList<STRING> list = new ArrayList<STRING>();
04     private EditText editText;
05        
06     public void onCreate(Bundle bundle){
07         super.onCreate(bundle);
08         setContentView(R.layout.transcript);
09           
10         editText = (EditText)findViewById(R.id.EditText01);
11         editText.setOnKeyListener(this);
12         adapter = new ArrayAdapter<STRING>(this, android.R.layout.simple_list_item_1, list);
13         setListAdapter(adapter);
14     }
15   
16     @Override
17     public boolean onKey(View v, int keyCode, KeyEvent event) {
18         if (event.getAction() == KeyEvent.ACTION_DOWN) {
19             switch (keyCode) {
20                 case KeyEvent.KEYCODE_ENTER:
21                     setText();
22                     return true;
23             }
24         }
25         return false;
26     }
27       
28     private void setText() { 
29         adapter.add(editText.getText().toString());
30         editText.setText(null);
31     }
32 }

코드를 정상적으로 작성을 하였으면 밑에 화면과 같이 정상적으로 동작이 됩니다.



하지만 여기서 문제가 있는게 위와같이 게속 아이템을 등록을 한다면 스크롤바가 생기게 될텐데 그 포커스가 밑에 그림과 같이 처음의 포커스에 맞춰져 잇다는점 입니다.



이 문제를 해결하기 위해서 저는 예전에 새로운 아이템을 추가할 시에 마지막 아이템을 선택 하기 조건을 줬습니다.

1 getListView().setSelection(list.size() - 1);

하지만 안드로이드 내에서 ListView에 제공하는 속성인 android:transcriptMode를 이용하여 더욱더 간단하게 처리를 할수 있습니다.
android:transcriptMode에 대해서 알아보자.

android:transcriptMode ?


Sets the transcript mode for the list. In transcript mode, the list scrolls to the bottom to make new items visible when they are added.

Must be one of the following constant values.


 Constant  Value  Description
 disabled  0  Disables transcript mode. This is the default value.
 normal  1  The list will automatically scroll to the bottom when a data set change notification is received and only if the last item is already visible on screen.
 alwaysScroll  2  The list will automatically scroll to the bottom, no matter what items are currently visible.

위에 자료는 안드로이드 API에 있는 내용이다. 간단하게 설명을하자면
transcriptMode 속성은 3가지의 값을 정의해줄수 있고 각각의 설명은 아래와 같다.  

1. disabled : 스크롤이 되지 않음
2. nomal : 현재의 포커스에서 추가된만큼의 포커스가 이동
3. alwaysScroll : 마지막에 추가된곳으로 포커스 이동

제가 만들프로그램은 마지막에 추가된곳으로 포커스가 이동을 해야하기 때문에 alwaysScroll을 줘야한다.

1 getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

위와같이 Java단에서 설정을 해줘도 되고 xml에서 설정을 해줘도 된다.
설정이 올바르게 끝났으면 정상적으로 밑에 화면과 같이 작동을 할것이다.



출처 : http://dmh11.tistory.com/entry/ListView에-동적으로-아이템-추가시-스크롤-문제
Posted by 1010
90.개발관련문서2010. 6. 24. 17:05
반응형
 

윈도우를 쓰다보면 공유폴더로 접근하였다가

특정계정만 접근이 되는 디렉토리로 접근을 하려고 하는데

기존의 계정접속 연결이 끊기지 않아 아이디와 패스워드 인증없이

이전에 인증하였던 공유폴더만 보일것이다.


이럴경우 기존의 접속연결을 끊는 방법을 소개합니다.

1. 먼저 윈도우 cmd창을 연다.

2. net use 명령을 실행하여 어떤것들이 연결되었는지 확인한다.

3. 전체 연결 끊기 : net use * /delete 명령으로 전체 연결을 끊는다.
  
   부분 공유 삭제 : net use 공유명 /delete

                       net use /delete \\[ ip ]      // 해당 ip 연결 끊기

5. \\아이피 를 입력하여 새롭게 아이디와 패스워드 인증을 받는다.

==========================================
실제 실행화면
C:\Documents and Settings\hubgo>net use
새 연결 정보가 저장되지 않습니다.


상태         로컬      원격                      네트워크

-------------------------------------------------------------------------------
OK                     
\\192.38.134.122\Unit2 개인
                                                 Microsoft Windows 네트워크
OK                     
\\192.38.134.122\Unit2 공용
                                                 Microsoft Windows 네트워크
명령을 잘 실행했습니다.


C:\Documents and Settings\hubgo>net use * /delete
다음 원격 연결이 있습니다.

                    \\192.38.134.122\Unit2 개인폴더
                    
\\192.38.134.122\Unit2 공용폴더
계속하면 연결이 취소됩니다.

이 작업을 계속하시겠습니까? (Y/N) [N]: y
명령을 잘 실행했습니다.


C:\Documents and Settings\hubgo>net use
새 연결 정보가 저장되지 않습니다.

목록에 항목이 없습니다.

Posted by 1010
02.Oracle/DataBase2010. 6. 24. 09:44
반응형
ORACLE에서 문자열을 비교하기 위해 Blank-Padded Comparison Semantics 과Nonpadded Comparison Semantics 이라는 방법을 사용한다. 여기에서 주의할 점은 Blank-Padded Comparison Semantics과 Nonpadded Comparison semantics의 결과가 항상 일치하는 것이 아니라는 점이다.

그러면 사례를 통해 두 비교방식에 대하여 알아보도록 하자.

Blank Padded Comparison
Nonpadded Comparison

'ab' > 'aa'
'ab' > 'aa'

'ab' > 'a '
'ab' > 'a '

'ab' > 'a'
'ab' > 'a'

'ab' = 'ab'
'ab' = 'ab'

'a ' = 'a'
'a ' > 'a'


위의 사례를 보면 비교되는 문자열이 Blank를 포함하고 있으면 padding과 non padding이 다른 결과를 나타내고 있다.

1.Blank-Padded Comparison Semantics

비교되는 두 값이 서로 다른 길이를 가질 경우에 길이가 짧은 쪽에 공백을 추가하여 길이를 동일하게 한다.
서로 다른 문자 값이 나타날 때까지 문자 단위로 비교를 수행한다.
서로 다른 값이 나타나면 문자 값이 큰 컬럼이 크다고 판단하고 비교를 종료한다. 그러나 서로 다른 값이 나타나지 않으면 중간에 어떤 유효한 값이 나타날 지를 알 수가 없기 때문에 끝까지 비교를 하게 된다. 만약 끝까지 비교를 하고도 다른 문자가 나타나지 않으면 같다고 판단한다.
Oracle에서 이 비교방식을 사용하는 경우는 다음과 같다.

양쪽 모두가 CHAR 혹은 NCHAR 타입인 경우
양쪽 모두가 문자타입의 상수일 경우
사용자 함수에서 반환된 값
2.Nonpadded Comparison Semantics

서로 다른 문자 값이 나타날 때까지 문자 단위로 비교를 수행한다.
서로 다른 값이 나타나면 문자 값이 큰 컬럼이 크다고 판단하고 비교를 종료한다. 만약 값이 같다면 길이가 짧은 컬럼만큼만 비교한 후 각 컬럼의 길이를 비교하여 길이가 긴 컬럼이 크다고 판단한다.
Oracle에서 이 비교방식을 사용하는 경우는 다음과 같다.

비교되는 값 중에 VARCHAR2 혹은 NVARCHAR2가 있을 경우 

[출처] Oracle의 문자열비교|작성자 짱가

Posted by 1010