안드로이드 클래식 블루투스 개발

14356 단어
전언
이 밖에 본고는 클래식 블루투스(Classic Bluetooth)의 점대점 통신 개발만 다루고 저소모 블루투스(BLE)의 개발은 언급하지 않는다.
개발 프로세스
  • Bluetooth 설정
  • 근처 블루투스 장치 검색
  • 페어 연결
  • 커뮤니케이션
  • Bluetooth 설정
    1. 획득BluetoothAdapter:
     mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    

    2. 블루투스 오픈
  • Bluetooth 사용 권한 6.0 이하 처리
  • 1. AndroidManifest에 권한 추가:
    
    
    
    
    

    2. 블루투스 기능 오픈:
    상수 REQUESTENABLE_BT는 로컬에서 정의한 정수 (0 이상 필요) 이며, 시스템이 onActivity Result () 를 통해 응용 프로그램에 되돌아올 때 RequestCode의 매개 변수로 사용됩니다.블루투스 오픈에 성공하면 당신의 Activity는 RESULTOK 를 resultCode 로 설정합니다.Bluetooth가 성공적으로 켜지지 않은 경우(예: 사용자가 취소를 선택한 경우) ResultCode는 RESULTCANCELED
    //1、  BluetoothAdapter
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    //2、        ,         
    if(mBluetoothAdapter == null ||!mBluetoothAdapter.isEnabled()){
         Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
         startActivityForResult(enableBtIntent,REQUEST_ENABLE_BT);
    }
    

    3. 반환값 처리
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         if(requestCode == REQUEST_ENABLE_BT && resultCode == RESULT_OK){
               //   ,          
         }else if(requestCode == REQUEST_ENABLE_BT && resultCode == RESULT_CANCELED){
               //   ,    
               Toast.makeText(MainActivity.this,"     ",Toast.LENGTH_SHORT).show();  
               finish();  
         }
    }
    
  • 버전 6.0 이상의 Bluetooth 사용 권한 처리 1.AndroidManifest에 모호한 포지셔닝 권한을 추가합니다:
  • 
    
    

    2. Bluetooth 사용 권한 확인:
    if (Build.VERSION.SDK_INT >= 23) {
          //             
          if (ContextCompat.checkSelfPermission(context,
          Manifest.permission.ACCESS_COARSE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(context,
                      new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                      REQUEST_ENABLE_BT
                      );
          } else {
                //    
          }
    } else {
          //     6.0        
    }
    

    3. startActivityForResult 메서드와 같이 반환 값을 처리합니다.
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          if (requestCode == REQUEST_ENABLE_BT) {
                if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //    
                } else {
                //     
                }
          }
    }
    

    근처 블루투스 장치 검색
    1. 면맞춤된 장치 조회 및 목록 추가
    //         list
    Set paireDevices = mBluetoothAdapter.getBondedDevices();
    if(paireDevices.size()>0){
          for(BluetoothDevice device: paireDevices){
                adapter.addData(device); //adapter       
          }
    }
    

    2. 장치가 비동기적인 방법startDiscovery()을 호출하여 블루투스 장치를 검색하는 것을 발견한다.
    이 프로세스는 비동기 프로세스이며, 이 방법은 발견 작업이 성공적으로 시작되었는지 여부를 표시하는 부울 값을 즉시 되돌려줍니다.검색 프로세스는 보통 약 12초의 검색 스캔을 포함하고, 그 다음에 발견된 장치마다 블루투스 이름을 검색하기 위해 페이지를 스캔합니다.
    이 방법으로 블루투스 장치를 발견하면 방송ACTION_FOUNDIntent, 검색된 장치 정보EXTRA_DEVICE가 여기에 포함되어 있기 때문에Intent에 등록BroadcastReceiver하여 방송을 처리한다.
    //    IntentFilter
    private IntentFilter getIntentFilter(){
          IntentFilter intentFilter = new IntentFilter();    
          intentFilter.addAction(BluetoothDevice.ACTION_FOUND);         
          intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); 
          intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
          return intentFilter;
    }
    
    //  BroadcastReceiver
    private final BroadcastReceiver receiver = new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if(BluetoothDevice.ACTION_FOUND.equals(action)){
                      BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);            
                      if(!list.contains(device)){//  
                            adapter.addData(device);
                      }      
                }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){                              
                      Toast.makeText(context,"    ",Toast.LENGTH_SHORT).show();
            }
        }
    };
    
    // onCreate    
    @Override
    protected void onCreate(Bundle savedInstanceState) {        
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //  
            registerReceiver(receiver,getIntentFilter());
    }
    
    onDestroy()에서 로그아웃하는 것을 잊지 마십시오.
    unregisterReceiver(receiver);
    

    디바이스 검색을 수행하는 작업은 막대한 리소스를 소비하는 번거로운 작업입니다.일단 장치를 찾았고 연결을 하려면, 장치 검색을 중지할지 확인하십시오.연결이 완료되면 검색 동작이 연결의 속도를 현저히 낮출 수 있기 때문에 연결할 때 검색을 중단해야 합니다.cancelDiscovery () 방법으로 검색을 중지할 수 있습니다.
    쌍 연결
    두 장치 사이에 연결을 만들려면 한 장치는 서버로서 개방된 BluetoothServerSocket 를 유지하고 라인에서 호출된 accept() 연결 요청을 탐지하기 시작하고, 다른 장치는 스캔된 서버 MAC를 이용하여 연결 요청을 시작해야 한다.
    참고: 두 장치가 이전에 쌍을 이루지 않은 경우 연결 중에 Android 프레임이 자동으로 쌍 요청 알림 또는 대화 상자를 사용자에게 표시합니다.
  • 서버:
  • 호출listenUsingRfcommWithServiceRecord(String, UUID)을 통해 획득BluetoothServerSocket
  • run()에서 호출accept()을 통해 연결 요청을 감청하기 시작합니다.accept()는 호출을 막기 때문에 전문적인 라인으로 연결하는 작업이 필요합니다.
  • //        ,   ConnectedThread
    private class AcceptThread extends Thread {
          private final BluetoothServerSocket mmServerSocket;
          public AcceptThread() {
                BluetoothServerSocket tmp = null;
                try {
                      tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("YourAPPName",                        MY_UUID);
                } catch (IOException e) {
                      e.printStackTrace();
                }
                mmServerSocket = tmp;
        }    
    public void run() {
            Log.d(TAG, "BEGIN mAcceptThread" + this);
            BluetoothSocket socket = null;
            //          accept
            while (mState!=3) {
                try {
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    Log.e(TAG, "accept() failed", e);
                    break;
                }
                if (socket != null) {
                    synchronized (MainActivity.this) {
                        switch (mState) {
                            case STATE_LISTEN:
                            case STATE_CONNECTING:
                                //     
                                connected(socket);
                                break;
                            case STATE_NONE:
                            case STATE_CONNECTED:
                                // Either not ready or already connected. Terminate new socket.
                                try {
                                    socket.close();
                                } catch (IOException e) {
                                    Log.e(TAG, "Could not close unwanted socket", e);
                                }
                                break;
                        }
                    }
                }
            }
            Log.i(TAG, "END mAcceptThread");
        }
        public void cancel() {
            Log.d(TAG, "Socket cancel " + this);
            try {
                mmServerSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "Socket close() of server failed", e);
            }
        }
    }
    
    mState 현재 상태를 표시하는 변수로
    STATE_NONE = 0;       //     
    STATE_LISTEN = 1;     //     
    STATE_CONNECTING = 2; //     
    STATE_CONNECTED = 3;  //        
    
  • 클라이언트:
  • 검색된 서버측 MAC 주소로 원격 장치 제공BluetoothDevice btDev = mBluetoothAdapter.getRemoteDevice(macAddress);
  • 이 원격 장치 호출 방법createRfcommSocketToServiceRecord(UUID)은 안전한 연결을 구축한다.

  • 주: UUID00001101-0000-1000-8000-00805F9B34FB로 정의하여 휴대전화 블루투스 인터페이스의 통일된 UUID를 위한 것이다.
  • run()에서 호출conenct을 통해 연결을 만듭니다. connect()이 호출을 막기 때문에 이 연결 과정은 주Activity 스레드 이외의 스레드에서 시종일관 실행되어야 합니다.
  • //      
    private class ConnectThread extends Thread {
            private final BluetoothSocket mmSocket;
            private final BluetoothDevice mmDevice;
    
            public ConnectThread(BluetoothDevice device) {
                        mmDevice = device;
                        BluetoothSocket tmp = null;
                        try {
                            //         
                            tmp = mmDevice.createRfcommSocketToServiceRecord(MY_UUID);
                            //          
                            //tmp = mmDevice.createInsecureRfcommSocketToServiceRecord(MY_UUID);
                        } catch (IOException e) {
                            Log.i(TAG,"   BluetoothSocket  ");
                            e.printStackTrace();
                        }
                        mmSocket = tmp;
         }
        @Override
        public void run() {
            if(mBluetoothAdapter.isDiscovering()){        
                mBluetoothAdapter.cancelDiscovery();
            }
            try {
                mmSocket.connect();
            } catch (IOException e) {
                Log.i(TAG,"socket    ");
                //  Handler    
                Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
                Bundle bundle = new Bundle();
                bundle.putString(Constants.TOAST,"Socket    ");
                msg.setData(bundle);
                mHandler.sendMessage(msg);
                return;
            } 
            synchronized (MainActivity.this){
                mConnectThread = null;
            }
            //       ,        connectedThread
            connected(mmSocket);
        }
    
        public void cancel(){
            try {
                mmSocket.close();
                setState(false);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    주: connected(mmSocket)는 응용 프로그램의 허구적인 방법으로 데이터 전송에 사용할 라인을 시작합니다.
    //       ConnectedThread
    public synchronized void connected(BluetoothSocket socket){
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }
        setState(STATE_CONNECTED);
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
    }
    

    커뮤니케이션
    두 장치가 성공적으로 연결되면 각 장치마다 연결된 장치가 하나씩 있습니다BluetoothSocket.BluetoothSocket를 사용하여 데이터를 전송하는 일반적인 절차는 매우 간단합니다.
  • 획득InputStreamOutputStream, 양자는 각각 플러그인과 getInputStream()getOutputStream()를 통해 데이터 전송을 처리한다.
  • read(byte[])를 사용하여 데이터를 읽고 스트리밍에 기록합니다.

  • read(byte[])와 write(byte[]) 방법은 모두 호출이 막히기 때문에 읽기 전용 라인이 필요합니다.
    //             
    private class ConnectedThread extends Thread{
          private final BluetoothSocket mmSocket;
          private final InputStream mmInStream;
          private final OutputStream mmOutStream;
          public ConnectedThread(BluetoothSocket socket) {
                mmSocket = socket;
                InputStream tmpIn = null;
                OutputStream tmpOut = null;
                try {
                      tmpIn = socket.getInputStream();
                      tmpOut = socket.getOutputStream();
                } catch (IOException e) {
                      Log.e(TAG, "temp sockets not created", e);
                }
                mmInStream = tmpIn;
                mmOutStream = tmpOut;
          }    
          public void run() {
                Log.i(TAG, "BEGIN mConnectedThread");
                //         ,    
                while(mState == STATE_CONNECTED){
                      try {
                            //  InputStream   
                            Scanner in = new Scanner(mmInStream,"UTF-8");
                            String str = in.nextLine();
                            Log.i(TAG,"read: "+str);
                            //  handle    ,   Toast  
                            Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
                            Bundle bundle = new Bundle();
                            bundle.putString(Constants.TOAST,str);
                            msg.setData(bundle);
                            mHandler.sendMessage(msg);
                      } catch (Exception e) {
                            Log.e(TAG, "disconnected", e);
                      }
               }
          }
          public void write(byte[] buffer) {
                try {
                      mmOutStream.write(buffer);
                } catch (IOException e) {
                      e.printStackTrace();
                      Log.e(TAG, "Exception during write", e);
                }
          }
          public void cancel() {
                try {
                      mmSocket.close();
                } catch (IOException e) {
                      Log.e(TAG, "close() of connect socket failed", e);
                }
          }
    }
    
  • 호출write(byte[])으로 출력합니다.
  • 입력 흐름은 mConnectedThread.write(byte[] buffer)에서 순환적으로 읽히고 여기run() 처리 데이터 전달을 사용한다.Handler의 상량은 Constants에 대한 서로 다른 처리 방식을 대표하고 MessageConnectedThread에서 서로 다른 run()값을 사용하여 입력 흐름을 조정하는 처리 방식을 나타낸다.

  • 예에서 mHandler를 사용했습니다.obtainMessage(Constants.MESSAGE_TOAST); 대표님이 받은 데이터를 MESSAGE로...TOAST 방식으로 처리됩니다.
    //  Handler    
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
              switch(msg.what){
                    case: Constants.SomeConfig:
                    // do something
                    break;
              }
        }
    };
    

    Demo의 GitHub 링크:https://github.com/YangLuYang/android-Demo-ClassicBluetooth

    좋은 웹페이지 즐겨찾기