[출처 : http://replygun.tistory.com/136]

ListView의 내용을 드래그 앤 드롭으로 순서를 변경하고 싶을 때 사용한다.

 

안드로이드 폰의 "Music" 앱의 playlist를 보면 아이템 드래그 앤 드롭이 가능하다는 것을 확인 할 수 있다. 다만 안드로이드 기본 API로 제공되지 않기 때문에 직접 구현해야만 한다. 드래그 앤 드롭이 적용된 ListView예제는 안드로이드 기본 앱인 Music에서도 확인 가능하며 필자가 만든 "마이투두" 앱에서도 확인이 가능하다.

 

다음은 안드로이드의 Music 앱의 소스를 참고하여 만든 DndListView이다.

아래의 코드를 사용하기 위해서는 안드로이드 버전 1.5 이상부터 가능하다.

 

  1. /*
    
     * Copyright (C) 2008 The Android Open Source Project
    
     *
    
     * Licensed under the Apache License, Version 2.0 (the "License");
    
     * you may not use this file except in compliance with the License.
    
     * You may obtain a copy of the License at
    
     *
    
     *      http://www.apache.org/licenses/LICENSE-2.0
    
     *
    
     * Unless required by applicable law or agreed to in writing, software
    
     * distributed under the License is distributed on an "AS IS" BASIS,
    
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    
     * See the License for the specific language governing permissions and
    
     * limitations under the License.
    
     */
    
    
    
    package org.pyframe.tools.view;
    
    
    
    import android.content.Context;
    
    import android.graphics.Bitmap;
    
    import android.graphics.PixelFormat;
    
    import android.graphics.Rect;
    
    import android.util.AttributeSet;
    
    import android.view.GestureDetector;
    
    import android.view.Gravity;
    
    import android.view.MotionEvent;
    
    import android.view.View;
    
    import android.view.ViewConfiguration;
    
    import android.view.ViewGroup;
    
    import android.view.WindowManager;
    
    import android.widget.AdapterView;
    
    import android.widget.ImageView;
    
    import android.widget.ListView;
    
    
    
    import com.mytodo.andriod.R;
    
    
    
    public class DndListView extends ListView {
    
        
    
    	private Context mContext;
    
        private ImageView mDragView;
    
        private WindowManager mWindowManager;
    
        private WindowManager.LayoutParams mWindowParams;
    
        private int mDragPos;      // which item is being dragged
    
        private int mFirstDragPos; // where was the dragged item originally
    
        private int mDragPoint;    // at what offset inside the item did the user grab it
    
        private int mCoordOffset;  // the difference between screen coordinates and coordinates in this view
    
        private DragListener mDragListener;
    
        private DropListener mDropListener;
    
    //    private RemoveListener mRemoveListener;
    
        private int mUpperBound;
    
        private int mLowerBound;
    
        private int mHeight;
    
        private GestureDetector mGestureDetector;
    
    //    private static final int FLING = 0;
    
    //    private static final int SLIDE = 1;
    
    //    private int mRemoveMode = -1;
    
        private Rect mTempRect = new Rect();
    
        private Bitmap mDragBitmap;
    
        private final int mTouchSlop;
    
        private int mItemHeightNormal;
    
        private int mItemHeightExpanded;
    
    
    
        public DndListView(Context context, AttributeSet attrs) {
    
            super(context, attrs);
    
    //        SharedPreferences pref = context.getSharedPreferences("Music", 3);
    
    //        mRemoveMode = pref.getInt("deletemode", -1);
    
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    
            mContext = context;
    
    //        Resources res = getResources();
    
    //        mItemHeightNormal = res.getDimensionPixelSize(R.dimen.normal_height);
    
    //        mItemHeightExpanded = res.getDimensionPixelSize(R.dimen.expanded_height);
    
        }
    
        
    
        @Override
    
        public boolean onInterceptTouchEvent(MotionEvent ev) {
    
            if (mDragListener != null || mDropListener != null) {
    
                switch (ev.getAction()) {
    
                    case MotionEvent.ACTION_DOWN:
    
                        int x = (int) ev.getX();
    
                        int y = (int) ev.getY();
    
                        int itemnum = pointToPosition(x, y);
    
                        if (itemnum == AdapterView.INVALID_POSITION) {
    
                            break;
    
                        }
    
                        ViewGroup item = (ViewGroup) getChildAt(itemnum - getFirstVisiblePosition());
    
                        mDragPoint = y - item.getTop();
    
                        mCoordOffset = ((int)ev.getRawY()) - y;
    
                        View dragger = item.findViewById(R.id.dragicon);
    
                        
    
    //                    item.setBackgroundColor(Color.RED);
    
                        
    
                        Rect r = mTempRect;
    
                        dragger.getDrawingRect(r);
    
                        // The dragger icon itself is quite small, so pretend the touch area is bigger
    
                        if (x < r.right * 2) {
    
                            item.setDrawingCacheEnabled(true);
    
                            // Create a copy of the drawing cache so that it does not get recycled
    
                            // by the framework when the list tries to clean up memory
    
                            Bitmap bitmap = Bitmap.createBitmap(item.getDrawingCache());
    
                            startDragging(bitmap, y);
    
                            mDragPos = itemnum;
    
                            mFirstDragPos = mDragPos;
    
                            mHeight = getHeight();
    
                            int touchSlop = mTouchSlop;
    
                            mUpperBound = Math.min(y - touchSlop, mHeight / 3);
    
                            mLowerBound = Math.max(y + touchSlop, mHeight * 2 /3);
    
                            return false;
    
                        }
    
                        mDragView = null;
    
                        break;
    
                }
    
            }
    
            return super.onInterceptTouchEvent(ev);
    
        }
    
        
    
        /*
    
         * pointToPosition() doesn't consider invisible views, but we
    
         * need to, so implement a slightly different version.
    
         */
    
        private int myPointToPosition(int x, int y) {
    
            Rect frame = mTempRect;
    
            final int count = getChildCount();
    
            for (int i = count - 1; i >= 0; i--) {
    
                final View child = getChildAt(i);
    
                child.getHitRect(frame);
    
                if (frame.contains(x, y)) {
    
                    return getFirstVisiblePosition() + i;
    
                }
    
            }
    
            return INVALID_POSITION;
    
        }
    
        
    
        private int getItemForPosition(int y) {
    
            int adjustedy = y - mDragPoint - 32;
    
            int pos = myPointToPosition(0, adjustedy);
    
            if (pos >= 0) {
    
                if (pos <= mFirstDragPos) {
    
                    pos += 1;
    
                }
    
            } else if (adjustedy < 0) {
    
                pos = 0;
    
            }
    
            return pos;
    
        }
    
        
    
        private void adjustScrollBounds(int y) {
    
            if (y >= mHeight / 3) {
    
                mUpperBound = mHeight / 3;
    
            }
    
            if (y <= mHeight * 2 / 3) {
    
                mLowerBound = mHeight * 2 / 3;
    
            }
    
        }
    
    
    
        /*
    
         * Restore size and visibility for all listitems
    
         */
    
        private void unExpandViews(boolean deletion) {
    
            for (int i = 0;; i++) {
    
                View v = getChildAt(i);
    
                if (v == null) {
    
                    if (deletion) {
    
                        // HACK force update of mItemCount
    
                        int position = getFirstVisiblePosition();
    
                        int y = getChildAt(0).getTop();
    
                        setAdapter(getAdapter());
    
                        setSelectionFromTop(position, y);
    
                        // end hack
    
                    }
    
                    layoutChildren(); // force children to be recreated where needed
    
                    v = getChildAt(i);
    
                    if (v == null) {
    
                        break;
    
                    }
    
                }
    
                ViewGroup.LayoutParams params = v.getLayoutParams();
    
                params.height = mItemHeightNormal;
    
                v.setLayoutParams(params);
    
                v.setVisibility(View.VISIBLE);
    
            }
    
        }
    
        
    
        /* Adjust visibility and size to make it appear as though
    
         * an item is being dragged around and other items are making
    
         * room for it:
    
         * If dropping the item would result in it still being in the
    
         * same place, then make the dragged listitem's size normal,
    
         * but make the item invisible.
    
         * Otherwise, if the dragged listitem is still on screen, make
    
         * it as small as possible and expand the item below the insert
    
         * point.
    
         * If the dragged item is not on screen, only expand the item
    
         * below the current insertpoint.
    
         */
    
        private void doExpansion() {
    
            int childnum = mDragPos - getFirstVisiblePosition();
    
            if (mDragPos > mFirstDragPos) {
    
                childnum++;
    
            }
    
    
    
            View first = getChildAt(mFirstDragPos - getFirstVisiblePosition());
    
    
    
            for (int i = 0;; i++) {
    
                View vv = getChildAt(i);
    
                if (vv == null) {
    
                    break;
    
                }
    
                int height = mItemHeightNormal;
    
                int visibility = View.VISIBLE;
    
                if (vv.equals(first)) {
    
                    // processing the item that is being dragged
    
                    if (mDragPos == mFirstDragPos) {
    
                        // hovering over the original location
    
                        visibility = View.INVISIBLE;
    
                    } else {
    
                        // not hovering over it
    
                        height = 1;
    
                    }
    
                } else if (i == childnum) {
    
                    if (mDragPos < getCount() - 1) {
    
                        height = mItemHeightExpanded;
    
                    }
    
                }
    
                ViewGroup.LayoutParams params = vv.getLayoutParams();
    
                params.height = height;
    
                vv.setLayoutParams(params);
    
                vv.setVisibility(visibility);
    
            }
    
        }
    
        
    
        @Override
    
        public boolean onTouchEvent(MotionEvent ev) {
    
            if (mGestureDetector != null) {
    
                mGestureDetector.onTouchEvent(ev);
    
            }
    
            if ((mDragListener != null || mDropListener != null) && mDragView != null) {
    
                int action = ev.getAction(); 
    
                switch (action) {
    
                    case MotionEvent.ACTION_UP:
    
                    case MotionEvent.ACTION_CANCEL:
    
                        Rect r = mTempRect;
    
                        mDragView.getDrawingRect(r);
    
                        stopDragging();
    
    //                    if (mRemoveMode == SLIDE && ev.getX() > r.right * 3 / 4) {
    
    //                        if (mRemoveListener != null) {
    
    //                            mRemoveListener.remove(mFirstDragPos);
    
    //                        }
    
    //                        unExpandViews(true);
    
    //                    } else {
    
                        if (mDropListener != null && mDragPos >= 0 && mDragPos < getCount()) {
    
                            mDropListener.drop(mFirstDragPos, mDragPos);
    
                        }
    
                        unExpandViews(false);
    
    //                    }
    
                        break;
    
                        
    
                    case MotionEvent.ACTION_DOWN:
    
                    case MotionEvent.ACTION_MOVE:
    
                        int x = (int) ev.getX();
    
                        int y = (int) ev.getY();
    
                        dragView(x, y);
    
                        int itemnum = getItemForPosition(y);
    
                        if (itemnum >= 0) {
    
                            if (action == MotionEvent.ACTION_DOWN || itemnum != mDragPos) {
    
                                if (mDragListener != null) {
    
                                    mDragListener.drag(mDragPos, itemnum);
    
                                }
    
                                mDragPos = itemnum;
    
                                doExpansion();
    
                            }
    
                            int speed = 0;
    
                            adjustScrollBounds(y);
    
                            if (y > mLowerBound) {
    
                                // scroll the list up a bit
    
                                speed = y > (mHeight + mLowerBound) / 2 ? 16 : 4;
    
                            } else if (y < mUpperBound) {
    
                                // scroll the list down a bit
    
                                speed = y < mUpperBound / 2 ? -16 : -4;
    
                            }
    
                            if (speed != 0) {
    
                                int ref = pointToPosition(0, mHeight / 2);
    
                                if (ref == AdapterView.INVALID_POSITION) {
    
                                    //we hit a divider or an invisible view, check somewhere else
    
                                    ref = pointToPosition(0, mHeight / 2 + getDividerHeight() + 64);
    
                                }
    
                                View v = getChildAt(ref - getFirstVisiblePosition());
    
                                if (v!= null) {
    
                                    int pos = v.getTop();
    
                                    setSelectionFromTop(ref, pos - speed);
    
                                }
    
                            }
    
                        }
    
                        break;
    
                }
    
                return true;
    
            }
    
            return super.onTouchEvent(ev);
    
        }
    
        
    
        private void startDragging(Bitmap bm, int y) {
    
            stopDragging();
    
    
    
            mWindowParams = new WindowManager.LayoutParams();
    
            mWindowParams.gravity = Gravity.TOP;
    
            mWindowParams.x = 0;
    
            mWindowParams.y = y - mDragPoint + mCoordOffset;
    
    //
    
            mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    
            mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    
            mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
    
                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
    
            mWindowParams.format = PixelFormat.TRANSLUCENT;
    
            mWindowParams.windowAnimations = 0;
    
            
    
            ImageView v = new ImageView(mContext);
    
            int backGroundColor = mContext.getResources().getColor(R.color.dragndrop_background);
    
    //        int backGroundColor = Color.parseColor("#e0103010");
    
            v.setBackgroundColor(backGroundColor);
    
            
    
            v.setImageBitmap(bm);
    
            mDragBitmap = bm;
    
    
    
            mWindowManager = (WindowManager)mContext.getSystemService("window");
    
            mWindowManager.addView(v, mWindowParams);
    
            mDragView = v;
    
        }
    
        
    
        private void dragView(int x, int y) {
    
    //        if (mRemoveMode == SLIDE) {
    
    //            float alpha = 1.0f;
    
    //            int width = mDragView.getWidth();
    
    //            if (x > width / 2) {
    
    //                alpha = ((float)(width - x)) / (width / 2);
    
    //            }
    
    //            mWindowParams.alpha = alpha;
    
    //        }
    
            mWindowParams.y = y - mDragPoint + mCoordOffset;
    
            mWindowManager.updateViewLayout(mDragView, mWindowParams);
    
        }
    
        
    
        private void stopDragging() {
    
            if (mDragView != null) {
    
                WindowManager wm = (WindowManager)mContext.getSystemService("window");
    
                wm.removeView(mDragView);
    
                mDragView.setImageDrawable(null);
    
                mDragView = null;
    
            }
    
            if (mDragBitmap != null) {
    
                mDragBitmap.recycle();
    
                mDragBitmap = null;
    
            }
    
        }
    
        
    
        public void setDragListener(DragListener l) {
    
            mDragListener = l;
    
        }
    
        
    
        public void setDropListener(DropListener l) {
    
            mDropListener = l;
    
        }
    
        
    
    //    public void setRemoveListener(RemoveListener l) {
    
    //        mRemoveListener = l;
    
    //    }
    
    
    
        public interface DragListener {
    
            void drag(int from, int to);
    
        }
    
        public interface DropListener {
    
            void drop(int from, int to);
    
        }
    
        public interface RemoveListener {
    
            void remove(int which);
    
        }
    
    }

 

위 코드중 정상적인 컴파일을 위해서 살펴보아야 할 부분이 두군데 있다.

 

1. 위 코드에 다음과 같은 부분이 있다.

  1. View dragger = item.findViewById(R.id.dragicon);

 

R.id.dragicon 이 바로 드래그를 할 대상이 되는 View가 된다.

 

2. 또 다음과 같은 코드가 있다.

  1. int backGroundColor = mContext.getResources().getColor(R.color.dragndrop_background);

 

이 부분은 드래그 앤 드롭시 백그라운드 색상을 지정해 주는 부분이다.

 

strings.xml파일에 다음과 같은 항목을 추가해 주어야 한다.

  1. <color name="dragndrop_background">#e0103010</color>

 

 

이제 드래그 앤 드롭을 구현하기 위해서 ListActivity는 어떻게 구현해야 하는지 알아보자.

 

아래와 같이 DragListener와 DropListener를 구현하도록 ListActivity를 만든다.

  1. public class MainActivity extends ListActivity implements DragListener, DropListener {

 

onCreate메써드에서 드래그 앤 드롭을 사용한다는 정보를 입력한다.

  1. listView = (DndListView) findViewById(android.R.id.list);
  2. listView.setDragListener(this);
    listView.setDropListener(this);

 

그리고 다음과 같은 메써드를 구현한다.

  1. public void drag(int from, int to) {
  2.   // 드래그 이벤트가 발생시 구현해야 할 것들을 기술한다.
    }
  3.  
  4. public void drop(int fr, int to) {
  5.   // 드롭 이벤트 발생시 구현해야 할 것들을 기술한다.
  6. }

 

Activity의 레이아웃 파일은 다음과 같이 작성해야 한다.

  1.     <org.pyframe.tools.view.DndListView
            android:id="@android:id/list"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:drawSelectorOnTop="false"
            android:fastScrollEnabled="true"
            android:cacheColorHint="#00000000"
            android:layout_weight="1.0"
             />
 

cacheColorHint값을 주어야 드래그 앤 드롭시 선택된 아이템의 백그라운드 색상이 표시된다.

Posted by dlucky
[출처 : http://mainia.tistory.com/549]

안드로이드(Android) 단말기에서 ConnectivityManager 를 통해 실시간 네트웍 상태 파악하기

 

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

 

이 예제는 단말기에서 WIFI, 3G 가 되는지에 대한 체크를 위한 것이다.

두가지 정보를 체크할것이다하나는 네트웍을 사용할 시점에 WIFI, 3G 

사용가능한지에 대한 체크이며 다른 하나는 네트웍상태가 바뀌었을 때

상태변화에 따른 작업을 하기 위한 소스 작업이다.


(1) 네트웍을 사용할 시점에서의 체크

 

먼저 permission 을 설정한다네트웍을 사용하기 위한 permission 은 아래와같다.

이 내용을 AndroidManifest.xml 에 추가한다. INTERNET 만 추가하면 안된다.

ACCESS_NETWORK_STATE 도 추가하기 바란다.


1 <uses-permission android:name="android.permission.INTERNET">
2 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">
3 </uses-permission></uses-permission>

다음은 ConnectivityManager 객체를 통해 WIFI  3G 상태를 체크할수 있는

객체를 반환해서 상태를 체크한다
1 ConnectivityManager manager =
2     (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);

WIFI, 3G 인지 구분없이 네트웍 연결상태가 제대로 되었는지에 대한 소스는

아래와 같다. isConnectionted 함수를 써서 확인한다.
01 ConnectivityManager manager =
02    (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
03 NetworkInfo mobile = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
04 NetworkInfo wifi = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
05          
06 if (mobile.isConnected() || wifi.isConnected()){
07       // WIFI, 3G 어느곳에도 연결되지 않았을때
08       Log.d(TAG, "Network connect success");
09 }else{
10       Log.d(TAG, "Network connect fail");
11 }

현재 위의 소스를 돌여서 나온 MOBILE WIFI 의 상태값에 대한 내용이다.

인터넷선이 연결된 컴퓨터에서 테스트 했으니 당연 MOBILE 만 될것이다.

MOBILE(3G) 디버그 상태값

WIFI 상태 디버그 상태값

그럼 MOBILE 과 WIFI 둘중 어떤것에 연결되었는지에 대한 상태에 따라 구현 소스가

틀려진다면 어떻게 해야할까그것은 ConnectivityManager  getActiveNetworkInfo() 

NetworkInfo  객체를 받은후 getTypeName 으로 체크하면 된다리턴값으로 String 

반환하는데 “MOBILE”, “WIFI” 둘중 하나를 리턴한다.

1 NetworkInfo ni = manager.getActiveNetworkInfo();
2 String netname = ni.getTypeName();
3 if (netname.equals("MOBILE")) {
4     Log.d(TAG, "Network - > " + netname);
5 }else{
6     Log.d(TAG, "Network - > " + netname);
7 }

(2) 실시간 네트웍 상태 변화에 따른 값받기

 

android.content.BroadcastReceiver 클래스를 상속받아 하나 만든다.

01 import android.content.BroadcastReceiver;
02 import android.content.Context;
03 import android.content.Intent;
04 import android.net.ConnectivityManager;
05 import android.net.NetworkInfo;
06 import android.widget.Toast;
07  
08 public class ConnReceiver extends BroadcastReceiver {
09  
10     @Override
11     public void onReceive(Context context, Intent intent) {
12         String action = intent.getAction();
13          
14         // 네트웍에 변경이 일어났을때 발생하는 부분
15         if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
16             ConnectivityManager connectivityManager =
17                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
18             NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
19             NetworkInfo mobNetInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
20             Toast.makeText(context,"Active Network Type : " + activeNetInfo.getTypeName() , Toast.LENGTH_SHORT).show();
21             Toast.makeText(context,"Mobile Network Type : " + mobNetInfo.getTypeName() , Toast.LENGTH_SHORT).show();
22         }
23     }
24 }

다음은 AndroidManifest.xml 에 recevier 와 필터를 등록한다필터는

android.net.conn.CONNECTIVITY_CHANGE  등록하면 된다그리고 receiver에는

상속받아 만든 클래스를 넣는다.

Posted by dlucky
다이얼로그의 재사용성을 위해 onCreateDialog를 사용하면 설정해둔 리스트를 업뎃을 못해
난관에 봉착할 때가 있다. (바꿀수도 없고 참;;;;)

그럴 때는 onPrepareDialog에서 다시 그려야할 다이얼로그가 있을 때
removeDialog(id)를 하니까 다시 새로 그린다.

    @Override    protected void onPrepareDialog(int id, Dialog dialog){
     //해당 다이얼로그는 매번 다시 부름. 
    switch(id){
      //없에면 다시 새로 그림
      removeDialog(id);
    }}
Posted by dlucky

[출처: http://www.bloter.net/archives/43970 ]
안드로이드 2.3 진저브레드가 발표되었습니다. 새로운 기능을 정리하고 약간의 부연 설명을 곁들였습니다.

인터넷전화 (mVoIP)

진저브레드부터 안드로이드에 인터넷 전화 프로토콜이 기본으로 탑재됩니다. 사용자는 전화번호부에서 바로 SIP 계정을 가진 인터넷 전화로 전화를 걸수 있습니다. 인터넷 전화를 걸기 위해서는 SIP 계정을 따로 만들어야 하며 SIP 계정을 만드는 기능은 포함되지 않았습니다.

진저브레드에서는 따로 인터넷 전화용 앱을 깔 필요없이 기존 전화번호부에 통합되어, 인터넷 전화로 전화 걸기가 일반 전화를 거는것과 동일한 사용자 경험을 제공합니다. 이는 차츰 퍼져가고 있던 스마트폰에서의 인터넷 전화의 확산에 기름을 붇는 것입니다. 인터넷 전화 기능은 제조사나 통신사에 따라 포함되지 않을수도 있습니다. 하지만 안드로이드의 기본 기능으로 들어간 이상 통신사들도 mVoIP에 적극 대비해야 하며 인터넷 전화 회사들은 시장 확대에 따른 성장이 기대됩니다.

근거리 무선 통신 (NFC)

진저브레드에서는 NFC를 지원하여 해당 하드웨어가 탭재된 단말에서는 모바일 결제, RFID 태그 정보 습득이 가능해 집니다. NFC의 탑재로 스마트폰이 기존 신용 카드, 교통 카드의 역할을 대체할 수 있으며 양방향 통신으로 결제를 받을 수도 있어 다양한 형태의 결제가 가능하게 됩니다. RFID 태그를 읽을 수 있어 상점들의 포스터, 스티커, 광고에 붙어 있는 이벤트 관련 태그 정보를 읽어 해당 이벤트를 웹브라우저로 열수 있게 됩니다.

NFC 하드웨어를 탑재한 폰이 출시되고 있지만 여전히 상용화나 서비스 활성화가 쉽지 않은 상황에서 안드로이드에서 NFC를 공식적으로 지원하고 레퍼런스폰인 넥서스S에 탑재함으로써 시장 확대가 급물살을 타게 되었습니다.

게임 개발 기능 강화

기존 안드로이드의 게임은 다른 플랫폼에 비해 상대적으로 약합니다. 이번 진저브레드에서는 게임 개발의 향상을 위해 시스템 전체적으로 많은 변경이 있었습니다. 게임중 종종 화면의 멈춤 현상을 일으키는 가비지 컬렉션의 문제를 해결하기 위해 동시 가비지 컬렉터 (Concurrent Garbase Collector) 기술을 채택하였으며 화면 터치와 키보드 입력을 효율적으로 처리해 반응 속도를 빠르게하고 3D 비디오 드라이버를 업데이트해 전체적인 3D 그래픽 성능도 향상됩니다.

안드로이드는 일반적으로 자바(Java)언어로 앱이 개발됩니다만 성능 향상을 위해 NDK라는 도구를 통해 프로그램 일부를 Native C/C++로 작성가능합니다. 게임에서는 최적화가 중요하기 때문에 성능 향상을 이끌어내기 위해 C/C++를 많이 사용하게 됩니다. 기존에는 NDK로 접근할 수 있는 영역이 많지 않았는데 진저브레드부터 대폭 확대되어 입력, 센서, 오디오, 그래픽 관리 등을 C/C++로 직접 접근할수 있어 게임 개발에서 시스템의 성능을 최대한 이끌어낼 수 있게 되었습니다.

기존에 탑재하고 있던 OpenGL ES, OpenMAX IL에 이어 OpenSL ES, EGL 등의 Khronos의 오픈 스탠다드를 추가적으로 Native 영역에서 채택함으로써 앞으로 멀티미디어 관련 하드웨어 분야의 발전을 통한 안드로이드의 시스템 성능 향상도 기대할수 있게 되었습니다.

복잡한 기술적인 용어들은 뒤로하고 사용자 입장에서 보면 기존에 보지 못한 강력한 성능의 게임을 안드로이드에서도 할수 있게 되었다는 것을 의미합니다.

전면 카메라 지원

애플리케이션이 후면 카메라뿐 아니라 전면 카메라로도 직접 접근할수 있게 되었습니다.  이것은 비디오 채팅을 포함한 다양한 비디오 활용 앱과 서비스가 등장할 수 있다는 것을 의미합니다.

2년전 전면 카메라를 활용한 동영상 SNS로 구글 안드로이드 개발자 대회 1차를 통과했지만 2차에서 갑자기 동영상 녹화 기능이 사라졌고, 폰 출시 이후 업데이트를 통해 동영상 녹화 기능이 지원되었지만 여전히 전면 카메라를 공식적으로 지원하지 않아 해당 서비스 개발을 포기했는데 이제야 전면 카메라를 통한 동영상 녹화가 가능해져 감회가 새롭습니다.

스마트폰의 사용과 앱의 개발에 큰 영향을 줄 수 있는 새로운 기능은 위와 같습니다. 기타 변경사항과 사용자 UI의 변경에 대해서도 좀더 살펴보겠습니다.

새로운 스크린 크기와 밀도 지원

  • 엑스트라 라지 스크린 지원 : 태블릿등 화면이 큰 장치를 지원합니다.
  • 엑스트라 하이 덴서티 지원 : 아이폰4 레티나 같은 수준의 높은 밀도의 스크린 장치를 지원합니다.

향상된 멀티미디어

구글이 공개한 로열티없는 미디어 포맷인 WebM과 VP8 비디오 코덱이 안드로이드에 탑재 되어 WebM의 확산에 도움을 주게 되었습니다. AAC와 AMR WB로의 음성 인코딩을 지원해 더 깨끗한 음질의 오디오 녹음이 가능해 졌습니다. 또한 이퀄라이저, 베이스 부스트, 헤드폰 버츄얼라이제이션등 다양한 오디오 효과의 적용이 가능해져 풍부한 사운드를 즐길수 있게 되었습니다.

UI의 개선

UI는 전체 시스템에 걸쳐 쉽고 빠르게 배울수 있도록 개선되었습니다. 검은색을 채택하여 알림바와 메뉴가 좀더 선명하게 드러날 수 있도록 했으며 배터리 효율성을 강화하였습니다. 메뉴와 설정도 좀더 쉽게 장치를 다룰수 있게 변경이 되었습니다.

기존 안드로이드는 왼쪽과 같이 밝은 회색이 기본 색상이었는데 진저브레드에서는 검은색을 채택하고 전체적으로 명도를 낮춰 어두운 색상에서 전력을 적게 소모하는 AMOLED에도 적합하게 변경되었습니다.

빠른 텍스트 입력

안드로이드 스크린 키보드는 더 빠른 입력과 편집을 할수 있도록 다시 설계되었습니다. 빠른 속도로 정확하게 입력할수 있도록 모양과 위치를 변경하였고 자동 완성을위한 글자도 더 커지고 선명하게 바뀌어 읽기 좋습니다. 입력된 단어를 선택하면 해당 단어를 대체할수 있는 단어를 사전에서 찾아 제안해 오타를 쉽게 수정할 수 있습니다. 스크린 키보드에서 멀티 터치를 지원해 Shift를 누른상태로 바로 숫자를 입력할수 있습니다.


간편해진 선택, 복사, 붙여넣기 기능

문자의 입력이나 웹브라우징시에 한번에 선택, 복사, 붙여넣기가 가능해 졌습니다. 단어를 터치하면 자유 선택모드로 들어가 영역 화살표를 드래깅하여 쉽게 선택 영역의 변경이 가능합니다. 이후 선택 영역의 터치로 복사가 가능해 졌고 텍스트 입력시 커서 모드에서 영역 화살표를 드래그하여 쉽게 영역 변경이 가능하여 영역 선택에 있어 트랙볼이 굳이 필요하지 않습니다.

향상된 배터리 관리 기능

시스템이 백그라운드에서 너무 오래 실행되어 전력을 많이 소모하는 앱들의 관리를 강화하여 성능을 향상하고 배터리 사용 시간을 늘립니다. 설정에서 사용자에게 시스템 콤포넌트와 앱들에 의해 소모되는 전력을 자세히 보여줍니다. 어떤 컴포넌트와 앱이 얼마나 전력을 소모했는지 상세히 보여줍니다.

애플리케이션 관리

애플리케이션 관리 설정이 홈 스크린의 옵션 메뉴에 들어가서 애플리케이션 관리와 검사를 더 빠르게 접근할 수 있게 되었습니다. 애플리케이션 관리 화면은 탭을 사용하여 구성되며 앱이 저장된 공간과 메모리 사용량을 쉽게 확인할 수 있습니다. 폰 사용자는 각 앱에 대한 자세한 정보를 확인하고 실행을 중지할수 있으며 개발자에게 피드백을 보낼 수 있습니다.

2.2프로요에서 2.3 진저브레드로 버전이 업그레이드 되면서 0.1이라는 적은 숫자가 올라갔고 플랫폼 자체가 직접 사용자에게 주는 기능은 커보이지 않습니다. 하지만 개발자에게 주는 의미는 2.1 에서 2.2로 업그레이드된것보다 훨씬 큽니다. 개발자들이 SIP, NFC, 게임 그리고 전면 카메라를 활용하여 얼마나 새로운 애플리케이션들을 만들어 낼지 또 그것이 사용자들에게 얼마나 큰 가치를 만들어낼지 많은 기대가 됩니다.

Posted by dlucky

<출처 : http://drcarter.tistory.com/entry/Android-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C-%EC%8B%9C-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%8B%A4%ED%96%89%EC%8B%9C-%EC%A3%BC%EC%9D%98%ED%95%A0-%EC%A0%90>

안드로이드의 특징중에 하나를 꼽는다면 백그라운드 실행이라고 할 수 있겠습니다. 아이폰과 다르게 멀티테스킹이 가능한
안드로이드는 그만큼 활용도가 많다는 것입니다. 하지만 그것에 따른 다른 주의 사항이 있습니다.
그 중에 하나가 바로 wifi모드를 유지하는 것입니다.

  안드로이드는 대기 모드로 들어가게 되면 배터리 소모를 줄이기 위해서 wifi를 자동으로 꺼버리게 됩니다. 하지만 무선으로
데이터를 주고 받는 중이거나, 스티리밍을 하고 있는 중간에 대기 상태로 들어가게 되고 자동으로 wifi가 꺼지게 된다면 3G
의 무선 데이터 모드로 바뀌게 되겠지요. 그렇게 된다면 3G 데이터의 사용이 많아지게 되고 나중엔 사용자의 요금에 지대한
영향을 줄 것으로 생각됩니다.
  백그라운드 실행 중에도 wifi 상태를 유지하기 위해서는 

android.net.wifi.WifiManager.WifiLock

을 이용해서 wifi상태를 유지시켜줘야 합니다.
자세한 설명음
http://developer.android.com/reference/android/net/wifi/WifiManager.WifiLock.html
이 곳에 가시면 확인할 수 있습니다.

간단히 사용방법을 보게 된다면
WifiManager.WifiLock wifiLock = null;
//등록
if (wifiLock == null) {
                WifiManager wifiManager = (WifiManager) context.getSystemService(context.WIFI_SERVICE);
	wifiLock = wifiManager.createWifiLock("wifilock");
	wifiLock.setReferenceCounted(true);
	wifiLock.acquire();
}
//해제
if (wifiLock != null) {
                wifiLock.release();
	wifiLock = null;
}

이렇게 하면 됩니다.
대기 상태에서도 wifi를 유지하고 있게 된다면, 배터리 소모가 빠르고 많아진다는 단점이 있지만, 그만큼 3G 데이터를 사용하는것이 적어진다는 것에 대한 장점도 있게 됩니다.

  두번째로 주의할 점은, 대기상태가 오래 된다면 cpu의 활동을 정지시켜 버립니다. 이것도 배터리 소모를 줄이기 위한 것이기는 하지만, 단적인 예로 들어서 스트리밍으로 음악을 듣고 있는 중에서 화면을 꺼버린 대기 모드일 경우에 cpu를 정지 시킨다면 음악을 들을 수 없게 되겠지요. 그래서 wifi상태를 유지 시켜주기 위한 WifiLock이 있듯이 cpu상태를 활동상태로 유지시켜주는 WakeLock이 있습니다.

android.os.PowerManager.WakeLock

자세한 설명은
http://developer.android.com/reference/android/os/PowerManager.WakeLock.html
이곳에 가셔서 확인해 볼 수 있습니다.

이것을 사용하는 방법은 간단히
PowerManager.WakeLock wakeLock = null;
//등록
if (wakeLock == null) {
	PowerManager powerManager = (PowerManager) context.getSystemService(context.POWER_SERVICE);
	wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "wakelock");
	wakeLock.acquire();
}
//해제
if (wakeLock != null) {
	wakeLock.release();
	wakeLock = null;
}

이렇게 하면 됩니다.

이 두가지를 유지시켜 준다면, 백그라운드로 실행을 하면서 대기 모드로 전환이 된다고 해도, 해당 어플이 죽거나 하는 일은 없겠지만, 그만큼 배터리 소모가 많아지기 때문에 안드로이드폰을 오래 사용할 수는 없겠지요. 
Posted by dlucky
AudioManager aManager; 
aManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
    
    int mode = aManager.getRingerMode(); // 현재 모드 받아오기
    int nCurrentVolumn = aManager.getStreamVolume(AudioManager.STREAM_RING); // 현재 벨소리 크기 받아오기

    seekBar.setMax(maxValue); // 현재 프로그레스 바 최대 값 설정
    seekBar.setProgress(nCurrentVolumn); // 현재 프로그레스 바 값 설정
    seekBar.setOnSeekBarChangeListener(this);
    setCurrentValue(nCurrentVolumn);  

벨소리 크기를 설정하기 위해서는
    aManager.setStreamVolume(AudioManager.STREAM_RING, seekBar.getProgress(),    AudioManager.FLAG_PLAY_SOUND);


Posted by dlucky
AudioManager aManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
aManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); => 벨소리 모드
aManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); =>진동 모드
aManager.setRingerMode(AudioManager.RINGER_MODE_SILENT); => 무음 모드

이런 식으로 사용 해주면 됩니다.



Posted by dlucky
전공 자료/JAVA2010.11.13 02:22

<출처 : http://www.javastudy.co.kr/docs/getjava/nio.channel/14_nioSocketChannel.htm >

ServerSocketChannel 클래스와 SocketChannel 클래스에 대해 먼저 알아보자. 이들은 net패키지의 ServerSocket클래스와 Socket클래스를 채널로서 다루고자 할 때 쓰는 SelectableChannel이다. 이들 네트워크 관련 채널들은 독자적으로 소켓의 역할을 대처하지는 않는다. 대신 소켓 클래스를 내부에 가지고 있으면서 이들의 기능을 채널화하는데 적절히 이용하게 된다.

1> ServerSocketChannel 클래스

① ServerSocketChannel 클래스 생성
ServerSocketChannel을 얻으려면 open()메서드를 사용한다. 단 이때 얻는 채널은 내부의 소켓이 아직 bind되지 않은 상태이기 때문에 적절한 IP주소와 포트 번호로 binding시켜줘야 한다. 일반적으로 다음과 같은 순서로 채널을 얻고 binding 한다.

1. ServerSocketChannel 얻기.

ServerSocketChannel server=ServerSocketChannel.open();

2. 내부 소켓을 얻는다.

ServerSocket socket=server.socket();

3. binding 한다.

SocketAddress addr=new InetSocketAddress(포트번호);
socket.bind(addr);

② ServerSocketChannel 클래스 주요 메서드

ㅁ public abstract SocketChannel accept() : 이 채널의 소켓에 대한 접속을 받아들여 SocketChannel을 리턴한다. 이때 ServerSocketChannel이 Blocking I/O 모드라면 accept()는 Blocking되지만 Non-Blocking I/O 모드라면 Blocking되지 않는다. 따라서 당장 어떤 접속요구가 없다면 null을 리턴한다. 하지만 리턴된 SocketChannel은 ServerSocketChannel이 Blocking I/O 모드이든 아니든 상관없이 무조건 Blocking I/O 모드로 시작한다.

ㅁ public static ServerSocketChannel open() : ServerSocketChannel를 얻는다. 이때 리턴된 ServerSocketChannel는 아직 bind되지 않은 상태이므로 소켓의 bind 메소드를 사용해 특정의 주소에 binding을 해주어야 한다.

ㅁ public abstract ServerSocket socket() : 내부 소켓을 얻는다.

ㅁ public final int validOps() : 현재 채널이 할 수 있는 해당 동작(ops)을 리턴한다. 서버소켓채널은 SelectionKey.OP_ACCEPT 만 할 수 있다.

2> SocketChannel 클래스

① SocketChannel로 접속하기

소켓채널을 얻기 위해서는 open()메서드를 사용하면 되는데 open()에는 인자가 있는 것과 없는 것이 있다. 만약 인자 없이 open()를 사용한다면 접속이 되지 않는 소켓채널을 리턴하므로 connect()메서드를 이용해서 접속을 해주어야 한다. 인자로 SocketAddress 객체를 준다면 접속이 된 소켓채널을 얻을 수 있다. 두가지 경우를 다 보자.

* 접속된 소켓채널 얻기

SocketAddress addr=new InetSocketAddress("ip주소", 포트번호);
SocketChannel socket=SocketChannel.open(addr);

* connect() 사용해서 접속하기

SocketAddress addr=new InetSocketAddress("ip주소", 포트번호);
SocketChannel socket=SocketChannel.open(); 
socket.connect(addr);

* SocketChannel에서 Non-Blocking I/O 모드일 경우에는 open(SocketAddress addr)해도 되지만, open()해서 connect(SocketAddress addr)했을 경우에는 즉시 연결작업이 끝나지 않을 수 있어서 이 메서드가 false를 리턴하게 되므로, finishConnect()로 연결작업을 끊어줘야 한다. 만약 연결이 제대로 안되었다면 false가 리턴된다. Blocking I/O 모드일 때는 connect() 호출이 Blocking 되면 연결이 끊길때까지 지속된다.

② SocketChannel 클래스 주요 메서드

ㅁ public abstract boolean connect (SocketAddress remote) : 인자로 들어온 SocketAddress 객체 정보를 가지고 현재 채널에 소켓을 접속한다. 연결이 제대로 안되면 false를 리턴한다.

ㅁ public abstract boolean finishConnect () : 소켓채널의 접속 처리를 완료한다.

ㅁ public abstract boolean isConnected () : 채널소켓이 접속이 되었는지 유무를 리턴.

ㅁ public abstract boolean isConnectionPending () : 이 채널상에서 접속 조작이 진행중인지 어떤지를 판단. 즉 접속이 시작되고 완료하지 않은 경우, finishConnect()가 호출되고 있지 않는 경우 true를 리턴.

ㅁ public static SocketChannel open () : 접속되지 않는 소켓채널을 리턴. 
ㅁ public static SocketChannel open (SocketAddress remote)
 : 접속된 소켓채널을 리턴.

ㅁ read()류 메서드
ㅁ public abstract int read (ByteBuffer dst)
ㅁ public final long read (ByteBuffer [] dsts) 
ㅁ public abstract long read (ByteBuffer [] dsts, int offset, int length)


ㅁ write()류 메서드
ㅁ public abstract int write (ByteBuffer src)
ㅁ public final long write (ByteBuffer [] srcs) 
ㅁ public abstract long write (ByteBuffer [] srcs, int offset, int length)

3> 예제

간단한 채팅을 만들어보자. 단순하게 서버에 클라이언트가 접속, 메세지를 보내면 서버가 메세지를 읽어 출력하고 서버도 메세지를 보내는 소스이다. 우선 순서를 보자.

<< 서버측 >>

  1. 채널들을 관리할 Selector를 얻는다.

    Selector selector=Selector.open();

  2. ServerSocketChannel를 얻는다.

    ServerSocketChannel server=ServerSocketChannel.open();

  3. 내부 소켓을 얻는다.

    ServerSocket socket=server.socket();

  4. binding 한다.

    SocketAddress addr=new InetSocketAddress(포트번호);
    socket.bind(addr);

  5. ServerSocketChannel을 Selector에 등록시킨다. ServerSocket는 OP_ACCEPT동작만 할 수 있다.

    server.register(selector, SelectionKey.OP_ACCEPT);

  6. 클라이언트의 접속을 대기한다. 이때 접속이 되면 accept()에 의해 상대방 소캣과 연결된 SocketChannel의 인스턴스를 얻는다. 이 채널는 읽기(OP_READ),쓰기(OP_WRITE),접속(OP_CONNECT) 행동을 지원한다.

    SocketChannel socketChannel=serverChannel.accept();

  7. 접속된 SocketChannel를 Selector에 등록한다.

    socketChannel.register(selector, 소켓채널의 해당행동);

  8. 채널이 취할 수 있는 3가지 동작(읽기(OP_READ),쓰기(OP_WRITE),접속(OP_CONNECT) )에 대해서 검사한다. 이때는 다음과 같은 메서드를 이용해서 체크를 한다.

    isConnectable() : true이면 상대방 소켓과 새로운 연결이 됐다는 뜻이다. 이때 Non-Blocking I/O 모드일 경우에는 연결과정이 끝나지 않는 상태에서 리턴될 수도 있다. 그러므로 SocketChannel 의 isConnectionPending()메서드가 true를 리턴하는지 아닌지를 보고 true를 리턴한다면 finishConnection()를 명시적으로 불러서 연결을 마무리짓도록 한다.

    isReadable() : true이면 읽기(OP_READ) 동작이 가능하다는 뜻으로 SocketChannel로부터 데이터를 읽는 것에 관한 정보를 받을 준비가 되었다는 뜻이다. 따라서 버퍼에 읽어들이기만 하면된다.

    isWritable() : true이면 쓰기(OP_WRITE) 동작이 가능하다는 뜻으로 SocketChannel에 데이터를 쓸 준비가 되었다는 뜻이다. 따라서 버퍼의 데이터를 이 채널에 write()하면 된다.

<< 클라이언트측 >>

  1. SocketChannel를 얻어서 접속한다.

    SocketAddress addr=new InetSocketAddress("localhost", 8080);
    SocketChannel socket=SocketChannel.open(addr);

  2. 버퍼를 만들고 서버에서 들어온 데이터를 읽고 쓰기를 한다.

자, 이제 소스를 보자. 먼저 서버측 소스인 ServerSocketChannelTest1.java를 보자.

ServerSocketChannelTest1.java

import java.io.*;
import java.net.*;
import java.util.*;
import java.nio.*;
import java.nio.channels.*;

public class ServerSocketChannelTest1 implements Runnable {
Selector selector;
int port=8080;

// -> 생성자
public ServerSocketChannelTest1() throws IOException {

selector=Selector.open(); //셀렉터를 생성한다.
ServerSocketChannel server=ServerSocketChannel.open();//binding 되지 않는 ServerSocketChannel 생성
System.out.println(server);
ServerSocket socket=server.socket();// 내부소켓 생성
SocketAddress addr=new InetSocketAddress(port);
socket.bind(addr);// binding
System.out.println(server);

server.configureBlocking(false);// Non-Blocking I/O모드 설정

// 만들어진 ServerSocketChannel이 어떤 동작이 가능한지 본다.
// 결과를 보면 ServerSocketChannel은 OP_ACCEPT하나만 가능하다는 것을 알수 있다.
 
int validOps=server.validOps();
System.out.print("ServerSocketChannel.validOps() : "+validOps); 
System.out.println(", "+(validOps==SelectionKey.OP_ACCEPT));

// binding된 ServerSocketChannel을 Selector에 등록한다.
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("****************************************");
System.out.println("클라이언트의 접속을 기다리고 있습니다");
System.out.println("****************************************");
}

// --> run()
public void run() {
// SocketChannel 용 행동 변수를 미리 만들어 둔다.
int socketOps=SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE;
ByteBuffer buf=null;// 버퍼

while(true) {
try {
//현재 selector에 등록된 채널에 관심있는 동작이 하나라도 실행된 경우 그 채널들을 SelectionKey의 Set에 추가한다.
selector.select();
} catch(IOException ioe)
{
ioe.printStackTrace();
}

// 선택된 채널(위의 select()로 지정된)들의 리스트를 얻는다.
Set selectedKeys=selector.selectedKeys();
// Set에 대해 Iterator를 얻어서 하나씩 처리하게 된다.
Iterator iter=selectedKeys.iterator();

while(iter.hasNext()) {
try {
SelectionKey selected=(SelectionKey)iter.next();
iter.remove();// 현재 처리하는 SelectionKey는 Set에서 제거한다.

// channel()는 현재 동작에 관련된 채널들을 리턴하는데
// 이를 가지고 지금 현재 하는 작업이 읽기냐 쓰기냐 접속이냐를 판단한다.

SelectableChannel channel=selected.channel();
// 현재 동작중인 채널이 ServerSocketChannel이라면 accept()를 호출해서 
// 접속요청을 해온 상대방 소켓과 연결될 수 있는 SocketChannel 을 얻는다.

if (channel instanceof ServerSocketChannel) {
ServerSocketChannel serverChannel=(ServerSocketChannel)channel;
SocketChannel socketChannel=serverChannel.accept();
// 지금 서버소켓채널은 Non-Blocking I/O모드 설정되어 있다. 
// 이는 당장의 접속이 없어도 블로킹되지 않고 바로 null을 리턴하므로 이를 체크를 해주야 한다
.
if (socketChannel==null) {
System.out.println(" # null server socket");
continue;
}

System.out.println(" # socket accepted : "+socketChannel);

socketChannel.configureBlocking(false);// 얻어진 소켓은 블로킹 모드이므로 이를 Non-Blocking I/O모드 설정한다.
// 가능 행동 확인
// 결과를 보면 true를 리턴하는데 이는 소켓채널은 읽기(OP_READ),쓰기(OP_WRITE),접속(OP_CONNECT) 행동이 가능함을 알수 있다.
int validOps=socketChannel.validOps();
System.out.print("SocketChannel.validOps() : "+validOps);
System.out.println(", "+(validOps==socketOps));

// 소켓채널을 셀렉터에 등록
socketChannel.register(selector, socketOps);
} else
{
SocketChannel socketChannel=(SocketChannel)channel;
buf=ByteBuffer.allocate(20);

// 소켓채널의 가능 행동을 검사해서 맞다면 그에 대응하는 작업을 한다.
// 접속인지 아닌지
if (selected.isConnectable()) {
System.out.println(" # socket connected");
if (socketChannel.isConnectionPending()) {
System.out.println(" # Connection is pending");
socketChannel.finishConnect();
}

// 읽기 인지 아닌지
}else if (selected.isReadable())

socketChannel.read(buf); 
buf.clear();
System.out.print("# socket read :");
while(buf.hasRemaining()){
System.out.print((char)buf.get());
}
}else

// 쓰기인지 아닌지
if (selected.isWritable())

String s="Hello Client!!";
byte[] bytes=s.getBytes();
buf.put(bytes);
buf.clear();
socketChannel.write(buf);
System.out.println(" # socket write : "+s);
}
}
} catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}
}

// --> 메인메서드
public static void main(String[] args) throws IOException
{
ServerSocketChannelTest1 test=new ServerSocketChannelTest1();
new Thread(test).start();
}
}

SocketChannelTest1.java -->클라이언트측

import java.io.*;
import java.net.*;
import java.util.*;
import java.nio.*;
import java.nio.channels.*;

public class SocketChannelTest1 {
public static void main(String[] args) throws Exception {
// SocketChannel생성해서 접속한다.
SocketAddress addr=new InetSocketAddress("localhost", 8080);
SocketChannel socket=SocketChannel.open(addr);

System.out.println(socket);
System.out.println("# isBlocking() : "+socket.isBlocking());

ByteBuffer buf=ByteBuffer.allocate(20); //버퍼생성
while(true){
int read=0;
socket.read(buf);// 서버가 보내온 데이터 읽기
buf.clear();
System.out.print("# socket read :");
while(buf.hasRemaining()){
System.out.print((char)buf.get());
}
buf.clear();

// 서버에게 데이터 보내기
String msg="Hello Server!!";
byte[] bytes=msg.getBytes();
buf.put(bytes);
buf.clear();
socket.write(buf);
System.out.println("\n"+"# socket write : "+msg); 
buf.clear();

}
}

<< 실행 결과 >>

서버측


C\>java ServerSocketChannelTest1

sun.nio.ch.ServerSocketChannelImpl[unbound]
sun.nio.ch.ServerSocketChannelImpl[/0.0.0.0:8080]
ServerSocketChannel.validOps() : 16, true
*****************************************
클라이언트의 접속을 기다리고 있습니다.
*****************************************
# socket accepted : java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:1049]
SocketChannel.validOps() : 13, true
# socket write : Hello Client!!
# socket read : Hello Server!!

클라이언트측


C\>java SocketChannelTest1

java.nio.channels.SocketChannel[connected local=/127.0.0.1:1049 remote=/127.0.0.1:8080]
# isBlocking () : true
# socket read : Hello Client!!
# socket write : Hello Server!!

'전공 자료 > JAVA' 카테고리의 다른 글

[JAVA] WeakReference 와 SoftReference  (0) 2010.11.13
[Java] InetSocketAddress 클래스  (0) 2010.11.13
[JAVA] ServerSocketChannel 클래스와 SocketChannel 클래스  (0) 2010.11.13
[JAVA] 소켓 프로그래밍  (0) 2010.10.31
[JAVA] TCP/IP 설명  (0) 2010.10.31
[JAVA] Thread의 개념  (0) 2010.10.31
Posted by dlucky
smsCallReceiver = new SmsCallReceiver(this);
registerReceiver(smsCallReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));

registerReceiver를 사용하게 되면 반드시 unregisterReceiver 함수를 사용해야 메모리 누수가 생기지 않는다.

보통은 onResume에서 registerReceiver를 사용하고 onPause에서 unregisterReceiver를 사용한다.


Posted by dlucky

티스토리 툴바