Two-way communication between Android Service and Activation based on serial Bluetooth module

Links to the original text: https://my.oschina.net/u/860952/blog/549214

I always want to use mobile phones to control some things, such as lights, fans and other household appliances or smart cars.

The Bluetooth driver module can be directly invoked in Activity or directly used in multi-threading, but there is a drawback: when you press the Home or Back button of your mobile phone, the program exits, and the next time you restart the software, you need to re-establish the Bluetooth link.

In order to overcome the above problems, I put the call of Bluetooth module into Service. http://tianrui-wang-163-com.iteye.com/blog/983099)

Introduction to Service
Services in Android and windows are similar things. Services generally have no user interface. They run in the system and are not easily detected by users. They can be used to develop programs such as monitoring.  

Without a visual interface, services are started, stopped, and controlled from other program components, including other Services, Activities, and Broadcast Receiver. If your application runs normally and uninterruptedly, and does not depend directly on user input, Service is your best choice.

Service life cycle

Common life cycle callbacks for services are as follows:
The onCreate() method is called only once when the service is created, and the service is created only once no matter how many times the startService() or bindService() method is called.
onDestroy() method is called when the service is terminated.  

Service objects cannot be started by themselves, and need to be started by an Activity, Service, or other Context object. There are two ways to start, Context.startService and Context.bindService(). The life cycles of the two approaches are different, as shown below.
Context.startService lifecycle:
startService -> onCreate () - > onStart () at startup
Stop Service --> on Destroy ()
Context.bindService lifecycle:
When binding, bindService - > onCreate () - > onBind ()
When unbinding, unbindService -> onUnbind () - > onDestory ()

Service implementation
Defining a Service requires only two steps:

Step 1: Inherit the Service class
Public class SMS Service extends Service {} Here you can choose the method to implement.
Step 2: Configure the service in the <application> node in the Android Manifest.xml file:
<service android:name=".SMSService" "></service>

 

Okay, let's cut the crap. Let's start with my code.

package com.lxx;  
  
  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.OutputStream;  
import java.util.UUID;  
  
import android.app.Service;  
import android.bluetooth.BluetoothAdapter;  
import android.bluetooth.BluetoothDevice;  
import android.bluetooth.BluetoothSocket;  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.os.IBinder;  
import android.util.Log;  
  
  
public class MyService extends Service{  
  
    public boolean threadFlag = true;  
    MyThread myThread;  
    CommandReceiver cmdReceiver;//Inherit from BroadcastReceiver object to get commands sent by Activity  
  
    /**************service Order *************/   
    static final int CMD_STOP_SERVICE = 0x01;  
    static final int CMD_SEND_DATA = 0x02;  
    static final int CMD_SYSTEM_EXIT =0x03;  
    static final int CMD_SHOW_TOAST =0x04;  
      
     private BluetoothAdapter mBluetoothAdapter = null;  
     private BluetoothSocket btSocket = null;  
     private OutputStream outStream = null;  
     private InputStream  inStream = null;  
     public  boolean bluetoothFlag  = true;  
     private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");  
     private static String address = "00:19:5D:EE:9B:8F"; // <== MAC address of Bluetooth device to be connected  
      
    @Override  
    public IBinder onBind(Intent intent) {  
        // TODO Auto-generated method stub  
        return null;  
    }  
  
    @Override  
    public void onCreate() {  
        // TODO Auto-generated method stub  
        super.onCreate();  
          
    }  
      
      
      
    //This method is automatically executed when the front-end Activity calls startService  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        // TODO Auto-generated method stub  
        cmdReceiver = new CommandReceiver();  
        IntentFilter filter = new IntentFilter();//Create IntentFilter objects  
        //Register a broadcast to receive commands from Activity and control the behavior of Service, such as sending data, stopping service, etc.  
        filter.addAction("android.intent.action.cmd");  
        //Register Broadcast Receiver  
        registerReceiver(cmdReceiver, filter);  
        doJob();//Invoke method to start thread  
        return super.onStartCommand(intent, flags, startId);  
  
    }  
      
      
  
    @Override  
    public void onDestroy() {  
        // TODO Auto-generated method stub  
        super.onDestroy();  
        this.unregisterReceiver(cmdReceiver);//Command Receiver Unregistered  
        threadFlag = false;  
        boolean retry = true;  
        while(retry){  
            try{   
                 myThread.join();  
                 retry = false;  
            }catch(Exception e){  
                e.printStackTrace();  
            }  
              
        }  
    }  
      
    public class MyThread extends Thread{          
        @Override  
        public void run() {  
            // TODO Auto-generated method stub  
            super.run();  
            connectDevice();//Connect Bluetooth Devices  
         while(threadFlag){  
             int value = readByte();              
            if(value != -1){                  
                DisplayToast(value + "");         
            }  
              
            try{  
                Thread.sleep(50);  
            }catch(Exception e){  
                e.printStackTrace();  
            }              
         }  
       }      
    }  
      
    public void doJob(){      
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  
        if (mBluetoothAdapter == null) {   
            DisplayToast("Bluetooth device is not available, please turn on Bluetooth!");  
            bluetoothFlag  = false;  
            return;  
        }  
  
        if (!mBluetoothAdapter.isEnabled()) {  
            DisplayToast("Please open Bluetooth and run the program again!");  
            bluetoothFlag  = false;  
            stopService();  
            showToast("Please open Bluetooth and run the program again!");  
            return;  
        }        
        showToast("Search for Bluetooth devices!");        
        threadFlag = true;    
        myThread = new MyThread();  
        myThread.start();  
          
    }  
    public  void connectDevice(){   
        DisplayToast("Attempting to connect Bluetooth device, please wait a moment.····");  
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);  
        try {  
           btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);  
        } catch (IOException e) {  
            DisplayToast("Socket creation failed!");  
            bluetoothFlag = false;  
        }  
        DisplayToast("Successful connection to Bluetooth device!");  
        mBluetoothAdapter.cancelDiscovery();  
        try {  
                btSocket.connect();  
                DisplayToast("Connections are successfully established and can be manipulated!");  
                showToast("Connections are successfully established and can be manipulated!");  
                bluetoothFlag = true;  
        } catch (IOException e) {  
               try {  
                    btSocket.close();  
                    bluetoothFlag = false;  
                } catch (IOException e2) {                          
                   DisplayToast("Connection was not established, socket could not be closed!");  
                }  
        }     
          
        if(bluetoothFlag){  
            try {  
                inStream = btSocket.getInputStream();  
              } catch (IOException e) {  
                  e.printStackTrace();  
              } //Binding Read Interface  
                
              try {  
                    outStream = btSocket.getOutputStream();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                } //Binding Write Interface  
              
        }  
    }    
      
    public void sendCmd(byte cmd, int value)//Serial Port Sending Data  
    {     
        if(!bluetoothFlag){  
            return;  
        }  
        byte[] msgBuffer = new byte[5];                                   
          msgBuffer[0] = cmd;  
          msgBuffer[1] = (byte)(value >> 0  & 0xff);  
          msgBuffer[2] = (byte)(value >> 8  & 0xff);  
          msgBuffer[3] = (byte)(value >> 16 & 0xff);  
          msgBuffer[4] = (byte)(value >> 24 & 0xff);  
            
          try {  
            outStream.write(msgBuffer, 0, 5);  
            outStream.flush();  
          } catch (IOException e) {  
              e.printStackTrace();  
          }           
    }    
      
    public int readByte(){//return -1 if no data  
        int ret = -1;     
        if(!bluetoothFlag){  
            return ret;  
        }  
        try {  
              ret = inStream.read();  
            } catch (IOException e) {  
              e.printStackTrace();  
            }              
        return ret;  
    }  
      
    public void stopService(){//Out of Service      
        threadFlag = false;//Stop thread  
        stopSelf();//Out of Service  
    }  
      
    public void showToast(String str){//Display prompt information  
        Intent intent = new Intent();  
        intent.putExtra("cmd", CMD_SHOW_TOAST);  
        intent.putExtra("str", str);  
        intent.setAction("android.intent.action.lxx");  
        sendBroadcast(intent);    
    }  
      
    public void DisplayToast(String str)  
    {  
        Log.d("Season",str);      
    }  
      
     //Receive commands from Activity  
    private class CommandReceiver extends BroadcastReceiver{  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            if(intent.getAction().equals("android.intent.action.cmd")){  
                int cmd = intent.getIntExtra("cmd", -1);//Get Extra information                              
                  if(cmd == CMD_STOP_SERVICE){  
                      stopService();  
                  }    
                    
                  if(cmd == CMD_SEND_DATA)  
                  {  
                     byte command = intent.getByteExtra("command", (byte) 0);  
                     int value =  intent.getIntExtra("value", 0);  
                      sendCmd(command,value);  
                  }  
                          
            }     
        }                          
    }  
  
}

 

The main interface Activity has only one button, that is, to send data to Service through Broadcast, Service receives data to parse commands and so on, and then calls the corresponding function, which is equivalent to calling the function in Service directly. Because Activity and Service are running in different processes, they can not communicate directly, so they must. It's only through a "bridge" that we can build a connection.

package com.lxx;  
  
import android.app.Activity;  
import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.os.Bundle;  
import android.os.IBinder;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
import android.widget.TextView;  
import android.widget.Toast;  
  
public class BroadcastActivity extends Activity {  
    /** Called when the activity is first created. */  
  
    TextView myTextView;  
    Button sendButton;  
    MyReceiver receiver;  
    IBinder serviceBinder;  
    MyService mService;  
    Intent intent;  
    int value = 0;  
      
    /**************service Order *************/   
    static final int CMD_STOP_SERVICE = 0x01;  
    static final int CMD_SEND_DATA = 0x02;  
    static final int CMD_SYSTEM_EXIT =0x03;  
    static final int CMD_SHOW_TOAST =0x04;  
      
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
          
        myTextView = (TextView)findViewById(R.id.myTextView);  
        myTextView.setText("Season");    
        sendButton = (Button)findViewById(R.id.sendButton);  
        sendButton.setOnClickListener(new SendButtonClickListener());  
                    
        intent = new Intent(BroadcastActivity.this,MyService.class);  
        startService(intent);  
    }  
      
    
    public class SendButtonClickListener implements OnClickListener{  
  
        @Override  
        public void onClick(View v) {  
            // TODO Auto-generated method stub  
            byte command = 45;  
            int value = 0x12345;  
            sendCmd(command,value);  
        }     
    }  
      
    @Override  
    protected void onDestroy() {  
        // TODO Auto-generated method stub  
        super.onDestroy();  
          
        if(receiver!=null){  
            BroadcastActivity.this.unregisterReceiver(receiver);  
        }  
    }  
  
     
  
  
    @Override  
    protected void onResume() {  
        // TODO Auto-generated method stub  
        super.onResume();  
        receiver = new MyReceiver();  
        IntentFilter filter=new IntentFilter();  
        filter.addAction("android.intent.action.lxx");  
        BroadcastActivity.this.registerReceiver(receiver,filter);  
    }  
  
    public void showToast(String str){//Display prompt information  
        Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();      
    }  
  
  
    public class MyReceiver extends BroadcastReceiver{  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        // TODO Auto-generated method stub  
        if(intent.getAction().equals("android.intent.action.lxx")){  
            Bundle bundle = intent.getExtras();  
            int cmd = bundle.getInt("cmd");  
              
            if(cmd == CMD_SHOW_TOAST){  
                String str = bundle.getString("str");  
                showToast(str);  
            }  
              
            else if(cmd == CMD_SYSTEM_EXIT){  
                System.exit(0);  
            }  
              
        }  
     }     
   }  
  
    public void sendCmd(byte command, int value){  
        Intent intent = new Intent();//Creating Intent Objects  
        intent.setAction("android.intent.action.cmd");  
        intent.putExtra("cmd", CMD_SEND_DATA);  
        intent.putExtra("command", command);  
        intent.putExtra("value", value);  
        sendBroadcast(intent);//Transmit broadcast      
    }  
      
   
}


The following is a detailed description of the code section:

1. In order to facilitate a good communication relationship between Activity and Service resume, it is necessary to interpret the commands from the data sent by them. These commands are identical between them and can read each other's data.

/**************service Order *************/   
    static final int CMD_STOP_SERVICE = 0x01;//Out of Service  
    static final int CMD_SEND_DATA = 0x02;//send data  
    static final int CMD_SYSTEM_EXIT =0x03;//Exit procedure  
    static final int CMD_SHOW_TOAST =0x04;//Display toast on the interface

  


2.Service transfers data to Activity:

To receive Broadcast, you first have a Receiver:


//Receive commands from Activity  
   private class CommandReceiver extends BroadcastReceiver{  
       @Override  
       public void onReceive(Context context, Intent intent) {  
        if(intent.getAction().equals("android.intent.action.cmd")){  
            int cmd = intent.getIntExtra("cmd", -1);//Get Extra information                              
                 if(cmd == CMD_STOP_SERVICE){  
                  stopService();  
                 }    
                   
                 if(cmd == CMD_SEND_DATA)  
                 {  
                 byte command = intent.getByteExtra("command", (byte) 0);  
                 int value =  intent.getIntExtra("value", 0);  
                  sendCmd(command,value);  
                 }  
                          
        }     
       }                          
   }


In order to receive data, the Receiver must first be registered:


public class MyReceiver extends BroadcastReceiver{  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        // TODO Auto-generated method stub  
        if(intent.getAction().equals("android.intent.action.lxx")){  
            Bundle bundle = intent.getExtras();  
            int cmd = bundle.getInt("cmd");  
              
            if(cmd == CMD_SHOW_TOAST){  
                String str = bundle.getString("str");  
                showToast(str);  
            }  
              
            else if(cmd == CMD_SYSTEM_EXIT){  
                System.exit(0);  
            }  
              
        }  
     }     
   }

 

doJob starts a thread, reads the data of Bluetooth serial port regularly, and then parses the data in it to achieve different operations. This in turn is the peripheral direct control of the mobile phone. Because this thread has been running in the background, as long as it does not end the Service, it keeps this serial port with the peripheral. Bluetooth module communication.

public class MyThread extends Thread{          
        @Override  
        public void run() {  
            // TODO Auto-generated method stub  
            super.run();  
            connectDevice();//Connect Bluetooth Devices  
         while(threadFlag){  
             int value = readByte();//Read a byte of data from Bluetooth module to interpret commands            
            if(value != -1){                  
                DisplayToast(value + "");         
            }  
              
            try{  
                Thread.sleep(50);  
            }catch(Exception e){  
                e.printStackTrace();  
            }              
         }  
       }      
    }


3.Activity transfers data to Service:

There is a button on Activity, which can send data to Bluetooth serial port module with one click. The working principle is as follows: the button click event is implemented on the interface.


public class SendButtonClickListener implements OnClickListener{  
  
    @Override  
    public void onClick(View v) {  
        // TODO Auto-generated method stub  
        byte command = 45;  
        int value = 0x12345;  
           sendCmd(command,value);  
    }     
   }


sendCmd is the key here. By setting up a broadcast message, Service is responsible for receiving it.


public void sendCmd(byte command, int value){  
        Intent intent = new Intent();//Creating Intent Objects  
        intent.setAction("android.intent.action.cmd");  
        intent.putExtra("cmd", CMD_SEND_DATA);  
        intent.putExtra("command", command);  
        intent.putExtra("value", value);  
        sendBroadcast(intent);//Transmit broadcast      
    }


Service medium

//This method is automatically executed when the front-end Activity calls startService  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        // TODO Auto-generated method stub  
        cmdReceiver = new CommandReceiver();  
        IntentFilter filter = new IntentFilter();//Create IntentFilter objects  
        //Register a broadcast to receive commands from Activity and control the behavior of Service, such as sending data, stopping service, etc.  
        filter.addAction("android.intent.action.cmd");  
        //Register Broadcast Receiver  
        registerReceiver(cmdReceiver, filter);  
        doJob();//Invoke method to start thread  
        return super.onStartCommand(intent, flags, startId);  
  
    }


Through the above steps, Bluetooth module can be established to send data to Activity, and Activity can also send data to Bluetooth module.

Reproduced in: https://my.oschina.net/u/860952/blog/549214

Tags: Android Java Mobile socket

Posted on Fri, 13 Sep 2019 03:29:06 -0700 by birdie