관리 메뉴

HAMA 블로그

안드로이드 와이파이 다이렉트(WI-FI Direct) 프로그래밍(3-1) 본문

안드로이드

안드로이드 와이파이 다이렉트(WI-FI Direct) 프로그래밍(3-1)

[하마] 이승현 (wowlsh93@gmail.com) 2015. 6. 21. 17:12



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();

                        }

                    }

                }

            }


        }

    }

}



Comments