WIFIDirectStart.zip


"기적을 부르는 안드로이드 통신 프로그래밍" 으로 공부한 내용 정리입니다.


1. 와이파이 다이렉트란 ? 

2. 와이파이 개발 일반 

  2-1) 와이파이 스캔 

  2-2) 와이파이 환경 설정

  2-3) 와이파이 네트워크 연결

 

3. 와이파이 다이렉트 개발 

  3-1) 일반

  3-2) 서비스 검색 

  3-3) 와이파이 락 과 멀티캐스트 설정 




와이파이 작업단계


1. 단말기 검색 

  단말기끼리 서로 연결하기 전에 먼저 서로 단말기를 검색하고 연결하고자 하는 단말기를 서로 인식해야한다. 

  이때 데이터 링크 계층의 "프로브" 라는 프레임을 사용한다. 

  예를들어 단말기 설정내 "WI-FI Direct" 를 누르면, 단말기는 프로브라는 요청 프레임으로 주변에 뿌려 와이파이 다이렉트와 관련된 정보를 

  요청한다.

  요청 프레임과 응답 프레임 모두 기본 서비스 집합(SSID) 를 "DIRECT" 란 이름을 사용한다. 단말기들은 요청 프레임과 응답 프레임으로 

  단말기 이름이나 와이파이 다이렉트 모드등의 기본적인 정보를 교환한다.


2. 그룹 생성과 오너 결정

  네트워크 그룹을 형성한 단말기들 가운데 하나의 단말기를 선정하여 소프트웨어 무선 접속 장치(soft-AP) 로 만든다.
  이 무선 접속 장치가 다른 단말기들과 연결하고 통신의 시작과 종료의 역할을 수행하는 그룹 오너가 된다. 

  그룹오너는 무선 접속 장치의 기능을 수행하며, 네트웤 그룹을 통제하는 역할을 한다.

  따라서 와이파이 다이렉트의 기능을 만들려면, 반드시 다른 와이파이 다이렉트 장치와 협상하여 그룹 오너를 형성하는 기능을 

  제공해야한다. 이러한 기능을 영어로 "Group Owner Negotiation procedure' 이라 한다.

  이 외에도 검색 및 전원 관리 매커니즘을 반드시 지원해야한다.

  다음 두가지 방법으로 단말기들간의 그룹을 형성하고 오너를 선정한다.

  

  첫번째. 시스템에 위임 

  두번째. 직접 그룹 생성


3. 사용자 인증  

  일단 단말기는 네트워크 그룹에 가입하려면, 무선 접속 장치에 연결하는 방법과 동일하게 그룹 오너에 연결 요청을 수행한다. 

  그리고 그에 대해 그룹 오너는 대부분 별도 사용자 인증 작업을 위한 패스워드를 요구하기 보다 승인 버튼을 사용하여 인증한다.

  이러한 기능을 PBC(Push Button Configuration) 이라 표현한다.


4. IP 주소 배정 

  와이파이는 블루투스와 달리 IP 주소를 사용하여 , 클라이언트와 통신한다. 따와이파이 다이렉트에서 그룹 오너가 결정되면 

  일반 무선 접속 장치처럼 자신에 접속한 클라이언트에 IP 주소를 배포한다. 

  그룹 오너는 클라이언트와 일대 다수로 연결되기 때문에 대부분 자신이 서버소켓을 생성한다.


아래 코드에 대한 시나리오는 다음과 같다.

  

 1) WIFIP2pManager 클래스의 인스턴스 생성

 2) 와이파이 다이렉트 초기화

 3) 와이파이 다이렉트 디바이스의 검색

 4) 브로드캐스트 리시버 등록

 5) 검색한 와이파이 다이렉트 디바이스 얻기

 6) 와이파이 다이렉트 연결 요청 

 7) 서버소켓이나 소켓 객체 생성 

 8) 비지니스 로직 

 9) 와이파이 다이렉트 연결 종료 






전반적인 작동 순서에 대한 모식도를 그려보았다.




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

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

    package="com.example.android.wifidirect"

    android:versionCode="1" android:versionName="1.0">


    <uses-sdk android:minSdkVersion="14" />

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

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

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

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

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

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

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


    <!-- Google Play filtering --> 

    <uses-feature android:name="android.hardware.wifi.direct" android:required="true"/>


    <application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@android:style/Theme.Holo">

        <activity

            android:name=".WiFiDirectActivity"

            android:label="@string/app_name" android:launchMode="singleTask">

            <intent-filter>

                <action

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

                <category

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

            </intent-filter>

        </activity>


        <!-- Used for transferring files  after a successful connection -->

        <service android:enabled="true" android:name=".FileTransferService" />


    </application>

</manifest>



WiFiDirectActivity 

import com.example.android.wifidirect.DeviceListFragment.DeviceActionListener;

public class WiFiDirectActivity extends Activity implements ChannelListener, DeviceActionListener {


    public static final String TAG = "wifidirectdemo";

    private WifiP2pManager manager;

    private boolean isWifiP2pEnabled = false;

    private boolean retryChannel = false;


    private final IntentFilter intentFilter = new IntentFilter();

    private Channel channel;

    private BroadcastReceiver receiver = null;


    /**

     * @param isWifiP2pEnabled the isWifiP2pEnabled to set

     */

    public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) {

        this.isWifiP2pEnabled = isWifiP2pEnabled;

    }


    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);


        // add necessary intent values to be matched.

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);

        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);


        manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);

        channel = manager.initialize(this, getMainLooper(), null);

    }


    /** register the BroadcastReceiver with the intent values to be matched */

    @Override

    public void onResume() {

        super.onResume();

        receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);

        registerReceiver(receiver, intentFilter);

    }


    @Override

    public void onPause() {

        super.onPause();

        unregisterReceiver(receiver);

    }


    /**

     * Remove all peers and clear all fields. This is called on

     * BroadcastReceiver receiving a state change event.

     */

    public void resetData() {

        DeviceListFragment fragmentList = (DeviceListFragment) getFragmentManager()

                .findFragmentById(R.id.frag_list);

        DeviceDetailFragment fragmentDetails = (DeviceDetailFragment) getFragmentManager()

                .findFragmentById(R.id.frag_detail);

        if (fragmentList != null) {

            fragmentList.clearPeers();

        }

        if (fragmentDetails != null) {

            fragmentDetails.resetViews();

        }

    }


    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

        MenuInflater inflater = getMenuInflater();

        inflater.inflate(R.menu.action_items, menu);

        return true;

    }


    /*

     * (non-Javadoc)

     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)

     */

    @Override

    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {

            case R.id.atn_direct_enable:

                if (manager != null && channel != null) {

                    // Since this is the system wireless settings activity, it's

                    // not going to send us a result. We will be notified by

                    // WiFiDeviceBroadcastReceiver instead.

                    startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));

                } else {

                    Log.e(TAG, "channel or manager is null");

                }

                return true;


            case R.id.atn_direct_discover:

                if (!isWifiP2pEnabled) {

                    Toast.makeText(WiFiDirectActivity.this, R.string.p2p_off_warning,

                            Toast.LENGTH_SHORT).show();

                    return true;

                }

                final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()

                        .findFragmentById(R.id.frag_list);

                fragment.onInitiateDiscovery();

                manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {


                    @Override

                    public void onSuccess() {

                        Toast.makeText(WiFiDirectActivity.this, "Discovery Initiated",

                                Toast.LENGTH_SHORT).show();

                    }


                    @Override

                    public void onFailure(int reasonCode) {

                        Toast.makeText(WiFiDirectActivity.this, "Discovery Failed : " + reasonCode,

                                Toast.LENGTH_SHORT).show();

                    }

                });

                return true;

            default:

                return super.onOptionsItemSelected(item);

        }

    }


    @Override

    public void showDetails(WifiP2pDevice device) {

        DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()

                .findFragmentById(R.id.frag_detail);

        fragment.showDetails(device);


    }


    @Override

    public void connect(WifiP2pConfig config) {

        manager.connect(channel, config, new ActionListener() {

            @Override

            public void onSuccess() {

                // WiFiDirectBroadcastReceiver will notify us. Ignore for now.

            }


            @Override

            public void onFailure(int reason) {

                Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",

                        Toast.LENGTH_SHORT).show();

            }

        });

    }


    @Override

    public void disconnect() {

        final DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()

                .findFragmentById(R.id.frag_detail);

        fragment.resetViews();

        manager.removeGroup(channel, new ActionListener() {


            @Override

            public void onFailure(int reasonCode) {

                Log.d(TAG, "Disconnect failed. Reason :" + reasonCode);

            }


            @Override

            public void onSuccess() {

                fragment.getView().setVisibility(View.GONE);

            }

        });

    }


    @Override

    public void onChannelDisconnected() {

        // we will try once more

        if (manager != null && !retryChannel) {

            Toast.makeText(this, "Channel lost. Trying again", Toast.LENGTH_LONG).show();

            resetData();

            retryChannel = true;

            manager.initialize(this, getMainLooper(), this);

        } else {

            Toast.makeText(this,

                    "Severe! Channel is probably lost premanently. Try Disable/Re-Enable P2P.",

                    Toast.LENGTH_LONG).show();

        }

    }


    @Override

    public void cancelDisconnect() {


        if (manager != null) {

            final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()

                    .findFragmentById(R.id.frag_list);

            if (fragment.getDevice() == null

                    || fragment.getDevice().status == WifiP2pDevice.CONNECTED) {

                disconnect();

            } else if (fragment.getDevice().status == WifiP2pDevice.AVAILABLE

                    || fragment.getDevice().status == WifiP2pDevice.INVITED) {


                manager.cancelConnect(channel, new ActionListener() {


                    @Override

                    public void onSuccess() {

                        Toast.makeText(WiFiDirectActivity.this, "Aborting connection",

                                Toast.LENGTH_SHORT).show();

                    }


                    @Override

                    public void onFailure(int reasonCode) {

                        Toast.makeText(WiFiDirectActivity.this,

                                "Connect abort request failed. Reason Code: " + reasonCode,

                                Toast.LENGTH_SHORT).show();

                    }

                });

            }

        }


    }

}



WiFiDirectBroadcastReceiver 



public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

    private WifiP2pManager manager;
    private Channel channel;
    private WiFiDirectActivity activity;

  
    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
            WiFiDirectActivity activity) {
        super();
        this.manager = manager;
        this.channel = channel;
        this.activity = activity;
    }


    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {

            // UI update to indicate wifi p2p status.
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                // Wifi Direct mode is enabled
                activity.setIsWifiP2pEnabled(true);
            } else {
                activity.setIsWifiP2pEnabled(false);
                activity.resetData();

            }
            Log.d(WiFiDirectActivity.TAG, "P2P state changed - " + state);
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

            // request available peers from the wifi p2p manager. This is an
            // asynchronous call and the calling activity is notified with a
            // callback on PeerListListener.onPeersAvailable()
            if (manager != null) {
                manager.requestPeers(channel, (PeerListListener) activity.getFragmentManager()
                        .findFragmentById(R.id.frag_list));
            }
            Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

            if (manager == null) {
                return;
            }

            NetworkInfo networkInfo = (NetworkInfo) intent
                    .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);

            if (networkInfo.isConnected()) {

                // we are connected with the other device, request connection
                // info to find group owner IP

                DeviceDetailFragment fragment = (DeviceDetailFragment) activity
                        .getFragmentManager().findFragmentById(R.id.frag_detail);
                manager.requestConnectionInfo(channel, fragment);
            } else {
                // It's a disconnect
                activity.resetData();
            }
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
                    .findFragmentById(R.id.frag_list);
            fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
                    WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));

        }
    }
}


DeviceListFragment 


public class DeviceListFragment extends ListFragment implements PeerListListener {


    private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();

    ProgressDialog progressDialog = null;

    View mContentView = null;

    private WifiP2pDevice device;


    @Override

    public void onActivityCreated(Bundle savedInstanceState) {

        super.onActivityCreated(savedInstanceState);

        this.setListAdapter(new WiFiPeerListAdapter(getActivity(), R.layout.row_devices, peers));

    }


    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        mContentView = inflater.inflate(R.layout.device_list, null);

        return mContentView;

    }

    public WifiP2pDevice getDevice() {

        return device;

    }


    private static String getDeviceStatus(int deviceStatus) {

        Log.d(WiFiDirectActivity.TAG, "Peer status :" + deviceStatus);

        switch (deviceStatus) {

            case WifiP2pDevice.AVAILABLE:

                return "Available";

            case WifiP2pDevice.INVITED:

                return "Invited";

            case WifiP2pDevice.CONNECTED:

                return "Connected";

            case WifiP2pDevice.FAILED:

                return "Failed";

            case WifiP2pDevice.UNAVAILABLE:

                return "Unavailable";

            default:

                return "Unknown";


        }

    }


    @Override

    public void onListItemClick(ListView l, View v, int position, long id) {

        WifiP2pDevice device = (WifiP2pDevice) getListAdapter().getItem(position);

        ((DeviceActionListener) getActivity()).showDetails(device);

    }


    private class WiFiPeerListAdapter extends ArrayAdapter<WifiP2pDevice> {


        private List<WifiP2pDevice> items;

        public WiFiPeerListAdapter(Context context, int textViewResourceId,

                List<WifiP2pDevice> objects) {

            super(context, textViewResourceId, objects);

            items = objects;


        }


        @Override

        public View getView(int position, View convertView, ViewGroup parent) {

            View v = convertView;

            if (v == null) {

                LayoutInflater vi = (LayoutInflater) getActivity().getSystemService(

                        Context.LAYOUT_INFLATER_SERVICE);

                v = vi.inflate(R.layout.row_devices, null);

            }

            WifiP2pDevice device = items.get(position);

            if (device != null) {

                TextView top = (TextView) v.findViewById(R.id.device_name);

                TextView bottom = (TextView) v.findViewById(R.id.device_details);

                if (top != null) {

                    top.setText(device.deviceName);

                }

                if (bottom != null) {

                    bottom.setText(getDeviceStatus(device.status));

                }

            }


            return v;


        }

    }


    public void updateThisDevice(WifiP2pDevice device) {

        this.device = device;

        TextView view = (TextView) mContentView.findViewById(R.id.my_name);

        view.setText(device.deviceName);

        view = (TextView) mContentView.findViewById(R.id.my_status);

        view.setText(getDeviceStatus(device.status));

    }


    @Override

    public void onPeersAvailable(WifiP2pDeviceList peerList) {

        if (progressDialog != null && progressDialog.isShowing()) {

            progressDialog.dismiss();

        }

        peers.clear();

        peers.addAll(peerList.getDeviceList());

        ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();

        if (peers.size() == 0) {

            Log.d(WiFiDirectActivity.TAG, "No devices found");

            return;

        }


    }


    public void clearPeers() {

        peers.clear();

        ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();

    }


    public void onInitiateDiscovery() {

        if (progressDialog != null && progressDialog.isShowing()) {

            progressDialog.dismiss();

        }

        progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel", "finding peers", true,

                true, new DialogInterface.OnCancelListener() {


                    @Override

                    public void onCancel(DialogInterface dialog) {

                        

                    }

                });

    }

    public interface DeviceActionListener {


        void showDetails(WifiP2pDevice device);


        void cancelDisconnect();


        void connect(WifiP2pConfig config);


        void disconnect();

    }


}



DeviceDetailFragment


public class DeviceDetailFragment extends Fragment implements ConnectionInfoListener {


    protected static final int CHOOSE_FILE_RESULT_CODE = 20;

    private View mContentView = null;

    private WifiP2pDevice device;

    private WifiP2pInfo info;

    ProgressDialog progressDialog = null;


    @Override

    public void onActivityCreated(Bundle savedInstanceState) {

        super.onActivityCreated(savedInstanceState);

    }


    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        mContentView = inflater.inflate(R.layout.device_detail, null);

        Button b = (Button) mContentView.findViewById(R.id.btn_connect);

        b.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                WifiP2pConfig config = new WifiP2pConfig();

                config.deviceAddress = device.deviceAddress;

                config.wps.setup = WpsInfo.PBC;

                if (progressDialog != null && progressDialog.isShowing()) {

                    progressDialog.dismiss();

                }

                progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel",

                        "Connecting to :" + device.deviceAddress, true, true

//                        new DialogInterface.OnCancelListener() {

//

//                            @Override

//                            public void onCancel(DialogInterface dialog) {

//                                ((DeviceActionListener) getActivity()).cancelDisconnect();

//                            }

//                        }

                        );

                ((DeviceActionListener) getActivity()).connect(config);


            }

        });


        mContentView.findViewById(R.id.btn_disconnect).setOnClickListener(

                new View.OnClickListener() {

                    @Override

                    public void onClick(View v) {

                        ((DeviceActionListener) getActivity()).disconnect();

                    }

                });


        mContentView.findViewById(R.id.btn_start_client).setOnClickListener(

                new View.OnClickListener() {

                    @Override

                    public void onClick(View v) {

                        // Allow user to pick an image from Gallery or other

                        // registered apps

                     ACTION_GET_CONTENT 설명:   http://androidhuman.tistory.com/269

                        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

                        intent.setType("image/*");

                        startActivityForResult(intent, CHOOSE_FILE_RESULT_CODE);

                    }

                });


        return mContentView;

    }


   onActivityResult 설명 :   http://blog.naver.com/hisukdory/50088038280

    @Override

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

        Uri uri = data.getData();

        TextView statusText = (TextView) mContentView.findViewById(R.id.status_text);

        statusText.setText("Sending: " + uri);

        Log.d(WiFiDirectActivity.TAG, "Intent----------- " + uri);

        Intent serviceIntent = new Intent(getActivity(), FileTransferService.class);

        serviceIntent.setAction(FileTransferService.ACTION_SEND_FILE);

        serviceIntent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString());

        serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS,

                info.groupOwnerAddress.getHostAddress());

        serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8988);

        getActivity().startService(serviceIntent);

    }


    @Override

    public void onConnectionInfoAvailable(final WifiP2pInfo info) {

        if (progressDialog != null && progressDialog.isShowing()) {

            progressDialog.dismiss();

        }

        this.info = info;

        this.getView().setVisibility(View.VISIBLE);


        // The owner IP is now known.

        TextView view = (TextView) mContentView.findViewById(R.id.group_owner);

        view.setText(getResources().getString(R.string.group_owner_text)

                + ((info.isGroupOwner == true) ? getResources().getString(R.string.yes)

                        : getResources().getString(R.string.no)));


        // InetAddress from WifiP2pInfo struct.

        view = (TextView) mContentView.findViewById(R.id.device_info);

        view.setText("Group Owner IP - " + info.groupOwnerAddress.getHostAddress());


        // After the group negotiation, we assign the group owner as the file

        // server. The file server is single threaded, single connection server

        // socket.

      TextView v = (TextView) mContentView.findViewById(R.id.status_text);

        if (info.groupFormed && info.isGroupOwner) {

            new FileServerAsyncTask(getActivity(), v).execute();

        } else if (info.groupFormed) {

            // The other device acts as the client. In this case, we enable the get file button.

            mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);

            v.setText(getResources().getString(R.string.client_text));

        }


        // hide the connect button

        mContentView.findViewById(R.id.btn_connect).setVisibility(View.GONE);

    }


    public void showDetails(WifiP2pDevice device) {

        this.device = device;

        this.getView().setVisibility(View.VISIBLE);

        TextView view = (TextView) mContentView.findViewById(R.id.device_address);

        view.setText(device.deviceAddress);

        view = (TextView) mContentView.findViewById(R.id.device_info);

        view.setText(device.toString());


    }


    public void resetViews() {

        mContentView.findViewById(R.id.btn_connect).setVisibility(View.VISIBLE);

        TextView view = (TextView) mContentView.findViewById(R.id.device_address);

        view.setText(R.string.empty);

        view = (TextView) mContentView.findViewById(R.id.device_info);

        view.setText(R.string.empty);

        view = (TextView) mContentView.findViewById(R.id.group_owner);

        view.setText(R.string.empty);

        view = (TextView) mContentView.findViewById(R.id.status_text);

        view.setText(R.string.empty);

        mContentView.findViewById(R.id.btn_start_client).setVisibility(View.GONE);

        this.getView().setVisibility(View.GONE);

    }


AsyncTask

 1. 비동기 처리를 하기 위해서는 별도의 Thread 를 생성하여 public void run()  메소드를 구현하면 되지만 안드로이드에서는 

 직접 Thread 를 생성하기 보다는 AsyncTask 를 사용하길 권장합니다.

 AsyncTask 내부에는 자체 ThreadPool 이 있어 Thread 가 무한정 늘어나 메모리에 부담을 주지 않도록 관리 하고 있기 때문에

 따로 ThreadPool 을 관리하지 않는다면 AsyncTask 를 사용하는게 무난할 것 같습니다.

 doInBackground 메소드는 기존의 Thread 에서의 run() 메소드라고 보시면 됩니다.

 

 2.안드로이드는 UI Thread 에서 일정 시간동안 유저의 반응에 응답하지 못하면 ANR(Application Not Responding) 오류를

 발생시킨다. 하지만 네트워크를 통한 데이터 전송 등 계속적인 작업이 실행되어야 할 경우 AsyncTask를 상속받은 

 클래스를 통해서 작업을 수행하는 것으로 ANR 오류를 방지할 수 있다.

    

     public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> {


        private final Context context;

        private final TextView statusText;


        public FileServerAsyncTask(Context context, View statusText) {

            this.context = context;

            this.statusText = (TextView) statusText;

        }


        @Override

        protected String doInBackground(Void... params) {

            try {

                ServerSocket serverSocket = new ServerSocket(8988);

                Log.d(WiFiDirectActivity.TAG, "Server: Socket opened");

                Socket client = serverSocket.accept();

                Log.d(WiFiDirectActivity.TAG, "Server: connection done");

                final File f = new File(Environment.getExternalStorageDirectory() + "/"

                        + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis()

                        + ".jpg");


                File dirs = new File(f.getParent());

                if (!dirs.exists())

                    dirs.mkdirs();

                f.createNewFile();


                Log.d(WiFiDirectActivity.TAG, "server: copying files " + f.toString());

                InputStream inputstream = client.getInputStream();

                copyFile(inputstream, new FileOutputStream(f));

                serverSocket.close();

                return f.getAbsolutePath();

            } catch (IOException e) {

                Log.e(WiFiDirectActivity.TAG, e.getMessage());

                return null;

            }

        }


        @Override

        protected void onPostExecute(String result) {

            if (result != null) {

                statusText.setText("File copied - " + result);

                Intent intent = new Intent();

                intent.setAction(android.content.Intent.ACTION_VIEW);

                intent.setDataAndType(Uri.parse("file://" + result), "image/*");

                context.startActivity(intent);

            }


        }


        @Override

        protected void onPreExecute() {

            statusText.setText("Opening a server socket");

        }


    }


    public static boolean copyFile(InputStream inputStream, OutputStream out) {

        byte buf[] = new byte[1024];

        int len;

        try {

            while ((len = inputStream.read(buf)) != -1) {

                out.write(buf, 0, len);


            }

            out.close();

            inputStream.close();

        } catch (IOException e) {

            Log.d(WiFiDirectActivity.TAG, e.toString());

            return false;

        }

        return true;

    }


}



FileTransferService


IntentService

이 클래스는 Service 클래스의 서브 클래스이며, 하나의 워커 쓰레드를 만들어서 요청들을 한번에 하나씩 처리하도록 합니다. 만약 여러개의 요청을 동시에 처리해야만 하는 상황이 아니라면, 이 클래스를 사용하는 것이 가장 좋은 선택일 것입니다. 이 클래스를 상속받아 확장할 때는 onHandleIntent()만 구현하면 되는데, 이 콜백 메소드는 워커 쓰레드에서 실행되기 때문에, 매개변수로 인텐트를 받아서 그에 해당하는 요청을 처리하는 것만 구현해 주면 됩니다. 

IntentService는 아래와 같은 일을 합니다:

워커 쓰레드를 만든 후, onStartCommand()를 통해 전달받은 인텐트들을 워커 쓰레드에서 차근차근 실행합니다.

작업큐를 만들어서 전달받은 인텐트들을 넣어놨다가 한번에 하나씩 onHandleIntent()에게 넘겨주기 때문에, 멀티쓰레드 구현에 

대한 고민을 하지 않아도 됩니다.

모든 요청에 대한 처리가 끝나면 종료되도록 이미 구현되어 있기 때문에, 확장한 클래스에서 stopSelf()를 호출할 필요가 없습니다.

onBind() 콜백 메소드는 기본적으로 null을 리턴하도록 되어 있습니다.

onStartCommand() 콜백 메소드는 기본적으로 전달 받은 인텐트를 작업큐에 넣는 일을 합니다.

이러한 사실들에 비춰봤을때, 확장 클래스에서는 onHandleIntent()만 구현하면 됩니다.

http://arabiannight.tistory.com/247


public class FileTransferService extends IntentService {

    private static final int SOCKET_TIMEOUT = 5000;

    public static final String ACTION_SEND_FILE = "com.example.android.wifidirect.SEND_FILE";

    public static final String EXTRAS_FILE_PATH = "file_url";

    public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";

    public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";


    public FileTransferService(String name) {

        super(name);

    }


    public FileTransferService() {

        super("FileTransferService");

    }


    @Override

    protected void onHandleIntent(Intent intent) {


        Context context = getApplicationContext();

        if (intent.getAction().equals(ACTION_SEND_FILE)) {

            String fileUri = intent.getExtras().getString(EXTRAS_FILE_PATH);

            String host = intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);

            Socket socket = new Socket();

            int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);


            try {

                Log.d(WiFiDirectActivity.TAG, "Opening client socket - ");

                socket.bind(null);

                socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);


                Log.d(WiFiDirectActivity.TAG, "Client socket - " + socket.isConnected());

                OutputStream stream = socket.getOutputStream();

                ContentResolver cr = context.getContentResolver();

                InputStream is = null;

                try {

                    is = cr.openInputStream(Uri.parse(fileUri));

                } catch (FileNotFoundException e) {

                    Log.d(WiFiDirectActivity.TAG, e.toString());

                }

                DeviceDetailFragment.copyFile(is, stream);

                Log.d(WiFiDirectActivity.TAG, "Client: Data written");

            } catch (IOException e) {

                Log.e(WiFiDirectActivity.TAG, e.getMessage());

            } finally {

                if (socket != null) {

                    if (socket.isConnected()) {

                        try {

                            socket.close();

                        } catch (IOException e) {

                            // Give up

                            e.printStackTrace();

                        }

                    }

                }

            }


        }

    }

}




"기적을 부르는 안드로이드 통신 프로그래밍" 으로 공부한 내용 정리입니다.


1. 와이파이 다이렉트란 ? 

2. 와이파이 개발 일반 

  2-1) 와이파이 스캔 

  2-2) 와이파이 환경 설정

  2-3) 와이파이 네트워크 연결

 

3. 와이파이 다이렉트 개발 

  3-1) 일반

  3-2) 서비스 검색 

  3-3) 와이파이 락 과 멀티캐스트 설정 

  




와이파이 스캔 


- 무선 접속 장치

   무선접속 장치에 접속하고자 하는 단말기들을 위해 ,무선 접속 장치는 비콘 메세지를 주기적으로 주변에 뿌린다.

   비콘 메세지는 무선접속장치의 BSSID,SSID,무선접속장치의 속도 그리고 암호화 정보로 구성된다.

- 클라이언트

   수동적으로 비콘 메세지를 수신하여 주변의 무선접속 장치의 존재를 확인하는 방법

    능동적으로  단말기에서 직접 주변에 프로브 메세지를 브로드캐스트하고, 수신한 무선접속장치로부터 프로브 응답메세지 받는 방법.



다음은 코드로 확인해보자.

간략하게 설명하면 첫번째 액티비티에서 버튼을 클릭하여 1.스캔을 시작하고 2. 리시버를 등록한다

리시버에서 와이파이정보들을 받은후에 두번째 액티비티로 넘긴후 리스트로 보여준다.


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

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

  package="com.example.wifi" 

  android:versionCode="1" 

  android:versionName="1.0">

  <application android:icon="@drawable/icon" 

    android:label="@string/app_name"

      android:theme="@android:style/Theme.Light">

     <activity android:name=".WiFiDemo" 

         android:label="@string/app_name">

      <intent-filter>

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

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

      </intent-filter>

    </activity>

    <activity android:name=".ArrayAdapterActivity"/>

   </application>

  <uses-sdk android:minSdkVersion="6" />

  // 와이파이 디바이스에 대한 정보를 엑세스 할수있는 / 상태를 변경할수있는 권한을 취득합니다.

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

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

</manifest>



package com.example.wifi;


import android.app.Activity;

import android.content.ActivityNotFoundException;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.content.IntentFilter;

import android.net.wifi.ScanResult;

import android.net.wifi.WifiConfiguration;

import android.net.wifi.WifiInfo;

import android.net.wifi.WifiManager;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;


import java.util.ArrayList;

import java.util.List;


public class WiFiDemo extends Activity {

WifiManager wifi;

BroadcastReceiver receiver;


TextView textStatus;

Button buttonScan;


/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);


// Setup UI

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

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

buttonScan.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View view) {

wifi.startScan();  // 와이파이 스캔을 시작합니다.

}

});


// Setup WiFi

wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);


if (!wifi.isWifiEnabled())

    wifi.setWifiEnabled(true); 


// Get WiFi status

WifiInfo info = wifi.getConnectionInfo();

textStatus.append("\n\nWiFi Status: " + info.toString());


// List available networks

List<WifiConfiguration> configs = wifi.getConfiguredNetworks();

for (WifiConfiguration config : configs) {

textStatus.append("\n\n" + config.toString());


// 브로드 캐스트 리시버를 등록합니다.

               // 블루투스와 동일하게 와이파이를 스캔하여 취득한 무선 접속 장치의 정보는 안드로이드 시스템에서 

               // SCAN_RESULTS_AVAILABLE_ACTION 액션과 함께 브로드캐스트 인텐트로 모든 애플리케이션에 뿌리집니다.

receiver = new WiFiScanReceiver();

registerReceiver(receiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

}


@Override

public void onDestroy() {

unregisterReceiver(receiver);

}

public class WiFiScanReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context c, Intent intent) {

List<ScanResult> results = wifi.getScanResults();

ArrayList<String> data = new ArrayList<String>(); 

       

// 가장 강력한 신호를 가진것을 기록해서 밑에서 토스트로 사용자에게 보여줍니다.

ScanResult bestSignal = null;

for (ScanResult result : results) {

data.add(result.toString());

if (bestSignal == null || WifiManager.compareSignalLevel(bestSignal.level, result.level) < 0)

bestSignal = result;

}


// 검색된 와이파이 정보를 컬렉션에 넣고, startActivity 액티비티 호출

Intent i = new Intent();

                        i.setClass(WiFiDemo.this, ArrayAdapterActivity.class);

                        i.putStringArrayListExtra("data", data);  

            

                    try {

                    startActivity(i);

   } catch (ActivityNotFoundException e) {

         Log.w("ExplicitIntent", "ForwardTarget.class 를 발견할수없습니다.");

   }


                   String message = String.format("%s networks found. %s is the strongest.",

results.size(), bestSignal.SSID);

  Toast.makeText(WiFiDemo.this, message, Toast.LENGTH_LONG).show(); 

}

}

}



package com.example.wifi;


import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;

import android.widget.AdapterView.OnItemClickListener;

import android.widget.ArrayAdapter;

import android.widget.ListView;

import android.widget.TextView;

import android.widget.Toast;


import java.util.List;


public class ArrayAdapterActivity extends Activity {

ArrayAdapter<String> btArrayAdapter;

ListView listDevicesFound;

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.list);

        listDevicesFound = (ListView)findViewById(R.id.devicesfound);

        

        Intent i = getIntent();

        List<String> data = i.getStringArrayListExtra("data");        

 

        btArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);

        listDevicesFound.setAdapter(btArrayAdapter);

        

        listDevicesFound.setOnItemClickListener(new OnItemClickListener() {

            @Override

public void onItemClick(AdapterView<?> l, View v, int position, long id) {    

                                Toast.makeText(getApplicationContext(), ((TextView) v).getText(),

                                Toast.LENGTH_SHORT).show();

            }

        });

    }

}






1. WI-FI ?  와이파이 다이렉트란 ? 

2. 와이파이 개발 일반 

  2-1) 와이파이 스캔 

  2-2) 와이파이 환경 설정

  2-3) 와이파이 네트워크 연결

 

3. 와이파이 다이렉트 개발 

  3-1) 일반

  3-2) 서비스 검색 

  3-3) 와이파이 락과 멀티캐스트 설정 



WI-FI


전파거리 : 직선거리 200미터 /  벽으로 막힌곳에서는 대략 3~4미터 (1층~2층) 

전송속도 : 최대 300Mbps ( 802.11n) 

단점     :  블루투스에 비해서 많은 전력소모 /  전자레인지등과 같은 대역대라서 전파간섭의 가능성 

다른 무선 통신 방법들 : 지그비 or Z-Wave 등 


보통 가정에서 무선공유기를 주축으로 와이파이가 연결되어져서 사용된다. 무선공유기같은 AP 없이 

기기들끼리 서로 제어하고 데이터를 전송할수있는 기술을 와이파이 다이렉트라고 한다. 


와이파이 다이렉트 기술은 와이파이가 무선 접속 장치를 거치지 않고 기기간에 직접 접속하여 데이터를 주고받을수 있다는 

점에서 초기에는 WI-FI Peer-to-Peer 라고 불리워 왔다. 



무선랜이란 ? "전통적이니 와이파이 망을 무선랜이라고 부른다" 


와이파이 규격 

- IEEE 802.11n

 802.11n 은 최근 상용화된 전송 규격으로 2.4Ghz 대역과 5Ghz 대역을 사용할 수 있으며 이론적으로 최대 300Mbps 까지의 

 속도를 지원할 수있다. 우리나라에서는 기술 규격 내 주파수 점유 대역폭의 문제로 인하여 135~144Mbps 로 속도가 

 제한되었으나 최근에 전파연구소의 기술기준고시로 300Mbps 까지 사용할수있게 되었다.


http://blog.skbroadband.com/543 의 와이파이 다이렉트 소개 

와이파이 다이렉트는 와이파이 기술의 새 버전으로, AP(Access Point)를 통한 인터넷 망과의 연결 없이도 무선으로 여러 기기를 와이파이로 연결, 데이터를 빠르고 신속하게 전송할 수 있는 기술을 뜻합니다. Wi-Fi의 장점과 블루투스의 장점을 합친 기술로써, 블루투스보다 속도가 빠른 Wi-Fi의 통신방식을 활용하면서 블루투스의 기능을 띄고 있습니다. 

보통의 와이파이는 무선으로 유선망을 사용하기 때문에 유선망과 연결된 AP가 하나는 존재해야만 사용이 가능한 반면, 와이파이 다이렉트는 해당 기능을 지원하는 기기만 2개가 있다면 블루투스처럼 쉽게 상호 통신이 가능하므로 별도의 AP는 필요 없게 됩니다와이파이 다이렉트는 블루투스처럼 기기들간의 통신을 위한 기술이라는 점. 이해가 가시겠죠?^^

                                                          유무선 공유기 & 와이파이 를 이용한 인터넷 공유 

                                     무선 접속 장치는 자신과 연결된 단말기 모두를 같은 네트워크 대역으로 연결시킨다.



                                                                            WI-FI Direct 모식도 



안드로이드에서 와이파이 다이렉트 

와이파이와 와이파이 다이렉트는 안드로이드 프레임워크가 독자적으로 젝오하는 기능이 아니다. 리눅스의 wpa_supplicant 에 의해 제공.


WiFi  와   TCP/IP 의 차이점은 ?



           

위에서 눈여겨 볼것은 TCP/IP 는 OSI 계층의 3,4 단계에 위치해있는 소프트웨어 레벨 프로토콜이라는 점~

WI-FI 는 이더넷과 같은 1,2 계층에 위치한 물리적 계층이라는 점~~

WiFi is one way to implment OSI Layers 1 and 2 .It does that using radio technology. Ethernet is another way to implment Layers 1 and 2 (using wires or fibre). Once you have a Layer 1 and 2 infrastructure, you then have a local network (LAN) that can interconnect a small number of hosts (usually a few to a few hundred) that are typically managed by a single entity. You can then run any Level 3 internetworking protocol, such as IP, IPX, AppleTalk, etc. over the LAN, which allows those hosts to talk not only to each other but also to hosts anywhere else on the internet (provided you have an appropriate router with a physical connection to an ISP). If your Level 3 protocol is IP, then you can run any of several Level 4 protocols on top of that, such as TCP or UDP. The Level 4 protocols transfer data between specified "ports" (basically, just numerical addresses) on the hosts at the two ends, and they can do it very reliably, though with high overhead (TCP), not reliably, but with low overhead (UDP), or in various other ways. ## Bluetooth networks are kind of like LANs, but are not normally thought of in this model, because they are only used for point-to-point communication between devices close together (10 m). Bluetooth is really just a wireless replacement for a serial cable.



zigbee 와 z-wave 프로토콜 스택 

           


지그비와 Z-Wave 는 어플리케이션레벨 개발에 대한 라이브러리가 지원되고있다. 칩에 핸들링 코드를 C 등으로 코딩하여 

호스트-타켓간에 통신할수있겠다. Z-WAY(http://www.z-wave.me/index.php?id=22)  같은 기술을 이용하여 손쉽게 개발할수도 있다. 


2 편 부터는  코드와 함께  와이파이 다이렉트 개발 에 대해서 알아보도록 하자.



기존에 스위치를 켜고/끄는 IOT 용 솔루션이 모바일웹으로 만들어져있었다. 

서버로 1번이나 2번스위치 ID 를 입력하여 스위치 조작을 할수있었다.

근데 매번 ID 를 입력하기는 귀찮은 일이었다.

따라서 전력공급원(콘센트)에 QR 코드를 붙혀놓고,자동으로 읽어서 자동으로 스위치를 켜주려했다.

하지만 모바일웹에서는 카메라를 조작하여 QR 코드를 읽을수 없었다.

그래서 하이브리드앱을 만들어야했다.

하이브리드 앱은 보통 2가지로 하나는 아이오닉(폰갭) 같은것을 이용하는것인데

이것의 목적은 하나의 앱을 개발해서 다양한 플랫폼에서 사용하기 위함이고

다른 종류의 하이브리드앱은 다음이나 네이버앱처럼 네이티브기능을 쓰면서

기존의 모바일웹 방식의 (자주 업데이트가 생기는 컨텐트) 를 그대로 가져다 사용하기 위함이다.

2번째의 하이브리드앱 형태가 다양한 앱개발 형태중에서 내가 만들 앱에 맞다고 판단하여

안드로이드 네이티브 앱을 이용하여 기존 모바일웹은 웹뷰로 대체하고 QR 스캐너를 부착하였다.

다음은 웹뷰와 QR 스캐닝의 간단한 예를 보여준다.


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/loadButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" 열기 "
android:padding="4dp"
android:textSize="20dp"
/>
<EditText
android:id="@+id/urlInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:inputType="textUri"
/>
</LinearLayout>

<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

</LinearLayout>

                                                      res/layout/activity_main.xml



public class MainActivity extends ActionBarActivity {

private static final String TAG = "MainActivity";

private WebView webview;


private Button loadButton;


private Handler mHandler = new Handler();

@SuppressLint("JavascriptInterface")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// 웹뷰 객체 참조
webview = (WebView) findViewById(R.id.webview);

// 웹뷰 설정 정보
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);

webview.setWebChromeClient(new WebBrowserClient());
webview.addJavascriptInterface(new JavaScriptMethods(), "sample");

// assets 폴더에 있는 메인 페이지 로딩
webview.loadUrl("file:///android_asset/www/sample.html");

final EditText urlInput = (EditText) findViewById(R.id.urlInput);

// 버튼 이벤트 처리
loadButton = (Button) findViewById(R.id.loadButton);
loadButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 입력한 URL의 페이지 로딩
webview.loadUrl(urlInput.getText().toString());
}
});

}

/**
* 자바스크립트 함수를 호출하기 위한 클래스 정의
*/
public class JavaScriptMethods {

JavaScriptMethods() {

}

@android.webkit.JavascriptInterface
public void clickOnFace() {
mHandler.post(new Runnable() {
public void run() {
// 버튼의 텍스트 변경
loadButton.setText("클릭후열기");
// 자바스크립트 함수 호출
webview.loadUrl("javascript:changeFace()");
}
});

}
}

final class WebBrowserClient extends WebChromeClient {
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(TAG, message);
result.confirm();

return true;
}
}


...
}

                                                               MainActivity.java


웹뷰관련 링크 

http://blog.acronym.co.kr/528

http://kimhs20.com/wp/?p=334

https://opentutorials.org/module/1234/8081



QR 스캐너 


public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View view)
{
// QR코드/바코드 스캐너를 구동합니다.
IntentIntegrator.initiateScan(MainActivity.this);
}
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
// QR코드/바코드를 스캔한 결과 값을 가져옵니다.
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);

// 결과값 출력
new AlertDialog.Builder(this)
.setTitle(R.string.app_name)
.setMessage(result.getContents() + " [" + result.getFormatName() + "]")
.setPositiveButton("확인", new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
})
.show();
}
}

QR 스캐너 관련 링크

http://hs36.tistory.com/36  

http://ocean.kisti.re.kr/downfile/volume/dpm/DJTJBT/2014/v12n1/DJTJBT_2014_v12n1_333.pdf


위 논문은 아래와 같은 모식도를 갖는다.


               



안드로이드 애플리케이션에서 JNI - USB to UART - Zigbee 동글이 - Zigbee 센서 이렇게 연결이 된다. 

 장점은 게이트웨이를 스마트폰과 함께 가지고 다닐수있다는것이고, 인터넷이 필요없다.

 단점은 센싱데이터를 꾸준히 빅데이터/클라우드로 보내는것은 불가능하다. 스마트폰을 놔두고 다닐수는 없으니

 어디에서나 온도상태등을 확인할수없다. 사람이 가서 검침해야한다. ㅎㅎ


 보통 이런경우의 모식도는 아래와 같다. 


 


    고정된 게이트웨이를 중간에 놔두고 , 게이트웨이는 인터넷과 연결이 되있어야하며 게이트웨이에 집약된 데이터가 

    클라우드로 올라간후에  스마트폰에서는 어디에서나 클라우드에 접속해서 현재 상태를 확인하게된다.

  

    이 경우의 장점은 어디서나 상태를 모니터링할수있다는것이고 

    단점은 게이트웨이에서 인터넷에 접근해야하는 문제가 생긴다. 게이트웨이가 있는곳에 항상 와이파이/랜케이블이 있다고 

    보장못하니깐~


   따라서 적재적소에 잘 활용하는것이 중요하겠다.

 


1. 안드로이드 구조 

                                                     안드로이드 소프트웨어 스택 


리눅스 커널 

   - 안드로이드는 리눅스를 사용하여 보안,메모리관리,프로세스 관리, 네트워크, 장치 드라이버 같은 시스템 서비스를 제공

   - 커널은 하드웨어와 상위 레이어들 사이의 추상화 계층으로 동작한다. 


안드로이드 런타임 

   - 안드로이드 런타임은 애플리케이션을 실행시키기 위한 최소한의 환경을 제공한다. 

   - 자바의 코어 라이브러리와 달빅 가상머신으로 구성된다.

   - 안드로이드 애플리케이션은 리눅스의 하나의 프로세스로 실행되며, 각 프로세스 마다 자신만의 달빅가상머신을 가진다.

   - 여러개의 가상머신이 동시에 실행될수있으므로 달빅가상머신은 효율적으로 실행될수있도록 설계되었다. 

   - 달빅가상머신은 레지스터 기반이며 자바 컴파일러에 의하여 컴파일된 클래스들을 .dex 형식으로 변환후에 실행한다.


라이브러리 

   - 안드로이드는 다양한 C/C++ 라이브러리를 표함한다. 애플리케이션 프레임워크를 통하여 이들 라이브러리를 사용할수있다.

   - 시스템 C 라이브러리 

   - 미디어 라이브러리

   - Surface manager

   - LibWebCore

   - SGL

   - 3D 

   - FreeType

   - SQLIte 


애플리케이션 프레임워크 

  - 개발자들을 위한 API 제공

  - 개발자는 이 프레임워크를 통해서 하드웨어장치, 위치정보접근, 병렬처리, 알람설정, 상태표시줄알림등의 기능을 사용

  - 컴포넌트의 재사용을 쉽게 할수있게 설계됨. 모든 어플리케이션은 자신의 기능을 다른 어플리케이션에 개방할수있다. 

 -  풍부하고 확장가능한 뷰를 제공한다.

 - 컨텐트 제공자

 - 리소스 관리자

 - 통지 관리자

 - 액티비티 관리자 


애플리케이션

  - 안드로이드는 전화,메세지,달력,지도,이메일,브라우저,전화번호부와 같은 핵심 애플리케이션들을 기본으로 탑재하고있다. 

  - 모든 애플리케이션은 자바 프로그래밍언어를 사용하여 개발된다.

 

2. 애플리케이션의 기초개념 


                                                        자바 소스파일이  JVM 과 달빅VM 위에 어떻게 올라가나? 


                                                                    달빅 VM  모식도 

                                              


                                                            .apk 패캐지가 만들어지기까지

        안드로이드 애플리케이션은 자바언어로 작성된다. 안드로이드 SDK 도구를 이용하면 자바코드를 컴파일하고 

            리소스와 결합하 여 안드로이드 패키지(.apk ) 로 만들수있다. 하나의 .apk 파일 안에 있는 모든 코드는 하나의 

            애플리케이션으로 간주된다. apk 파일은 모바일 장치로 다운로드외어서 애플리케이션으로 설치된다. 

     

             일단 장치에 설치되면 자신의 보안 샌드박스안에서만 동작된다.

             - 각 앱은 리눅스 운영 체제 상에서 서로 다른 사용자가 된다.

             - 운영 체제는 각 앱에게 사용자 아이디를 부여하고 앱에 속하는 파일은 다른 앱이 접근할수없다.

             - 각 앱은 자신의 가상 머신 위에서 실행된다. 따라서 다른 앱과는 완벽하게 분리된다.

                                     

  

  3.애플리케이션 컴포넌트 


      액티비티 

            

           - 사용자 인터페이스 화면을 가지고 , 하나의 작업을 담당하는 컴포넌트.

           - 하나의 애플리케이션은 여러개의 액티비티를 가질수있다.

           - 액티비티는 Activity 클래스를 상속받아서 구현된다.


     서비스

               


          - 백그라운드에서 실행되는 컴포넌트이다. (인터페이스화면을 가지지 않는다)

             - 오랫동안 실행되는 작업이나 원격 프로세스를 위한 작업을 할때 사용된다.

             - 예를들어 다른 앱이 실행되는동안 배경음악을 연주하는데 사용된다.

             - 서비스는 다른 컴포넌트에 의해서 실행되며 Service 클래스를 상속받아서 구현된다.


     방송 수신자

           


        - 화면이 꺼졌다, 배터리가 낮은 상태, 사진이 촬영되었다라는것을 알리기위해 방송을 사용한다.

        - 애플리케이션 자신도 방송을 송출할수도있다. (데이터가 다운로드되어 사용 가능하게 되었다를 다른 앱에게 알리려고)


     컨텐트 제공자 

                 



 -  다른 앱에게 데이터를 제공하는 컴포넌트이다. 

 -  컨텐트 제공자를 통하여 다른 애플리케이션은 데이터를 쿼리하거나 변경할수있다.

 - 전화번호부 앱은 많은 사람들의 이름과 전번이 데이터베이스의 형태로 저장되어있고 이 데이터는 컨텐트 제공자를 통하여      다른 앱에 제공될수있다. 즉 데이터를 다른 앱과 공유할수있는것이다. 


다른 애플리케이션의 컴포넌트 실행 


   - 안드로이드앱은 PC에서 실행되는 프로그램과 매우 다르다. 일반적으로 pc 에서 하나의 프로그램은 다른 프로그램안에 들어있는 코드를 사용할수없다. 그러나 안드로이드의 앱은 상당히 다르다.

  - 허가만 얻을수있다면 여러개의 앱들의 특정 컴포너트를 공유하여 사용할수있다.

 - 예를들어서 카메라 어플리케이션 / QR 코드 어플리케이션의 결과물을 우리의 어플리케이션에서 사용할수있다.

  - 안드로이드 시스템에게 우리의 인텐트가 포함된 메세지를 보내서 요청한후 승인되면 실행한다.  



인텐트 

 


    - 비동기적인 메세지이다.

    -  컴포넌트가 필요로하는 요청 내용을 가지고 있다. 

    - 액티비티와 서비스의 경우에는 인텐트 객체 안에 필요한 액션의 이름과 사용할 데이터의 URL 를 가지고있다. 

    - 예를들어 이미지를 화면에 표시하거나 웹 페이지를 오픈하는 요청을 인텐트에 기술할수있다. 

    - 어떤 경우에는 인텐트를 통하여 결과를 반환 받기도 한다.


매니페스트 파일 

   - 앱안의모든 컴포넌트들은 메니페스트 파일안에 선언되어있어야한다.

   - 인터넷 접근과 같이 앱이 요청하는 권한을 나열한다.

   - 앱이 필요한 최소한의 api 레펠을 선언한다.

   - 앱이 필요로하는 하드웨어 사용을 선언한다.

   

   


4.애플리케이션 모습들 

     




레퍼런스:

http://www.kyobobook.co.kr/product/detailViewKor.laf?barcode=9788970507392

+ Recent posts