Android-как внедрить клиент TCP-ip внутри intentservice

#android #tcp #nullpointerexception #android-intentservice #android-geofence

#Android #tcp #исключение nullpointerexception #android-intentservice #android-геозона

Вопрос:

я создал простой TCP-клиент, и он отлично работает, когда я использую простое действие и кнопку для отправки сообщения

теперь я реализую приложение geofence, используя тот же пример геозоны Google здесь

 public class GeofenceTransitionsIntentService extends IntentService {

protected static final String TAG = "GeofenceTransitionsIS";

/**
 * This constructor is required, and calls the super IntentService(String)
 * constructor with the name for a worker thread.
 */
public GeofenceTransitionsIntentService() {
    // Use the TAG to name the worker thread.
    super(TAG);
}

@Override
public void onCreate() {
    super.onCreate();
}

/**
 * Handles incoming intents.
 * @param intent sent by Location Services. This Intent is provided to Location
 *               Services (inside a PendingIntent) when addGeofences() is called.
 */
@Override
protected void onHandleIntent(Intent intent) {
    GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
    if (geofencingEvent.hasError()) {
        String errorMessage = GeofenceErrorMessages.getErrorString(this,
                geofencingEvent.getErrorCode());
        Log.e(TAG, errorMessage);
        return;
    }

    // Get the transition type.
    int geofenceTransition = geofencingEvent.getGeofenceTransition();

    // Test that the reported transition was of interest.
    if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
            geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

        // Get the geofences that were triggered. A single event can trigger multiple geofences.
        List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

        // Get the transition details as a String.
        String geofenceTransitionDetails = getGeofenceTransitionDetails(
                this,
                geofenceTransition,
                triggeringGeofences
        );

        // Send notification and log the transition details.
        sendNotification(geofenceTransitionDetails);
        Log.i(TAG, geofenceTransitionDetails);
    } else {
        // Log the error.
        Log.e(TAG, getString(R.string.geofence_transition_invalid_type, geofenceTransition));
    }
}

/**
 * Gets transition details and returns them as a formatted string.
 *
 * @param context               The app context.
 * @param geofenceTransition    The ID of the geofence transition.
 * @param triggeringGeofences   The geofence(s) triggered.
 * @return                      The transition details formatted as String.
 */
private String getGeofenceTransitionDetails(
        Context context,
        int geofenceTransition,
        List<Geofence> triggeringGeofences) {

    String geofenceTransitionString = getTransitionString(geofenceTransition);

    // Get the Ids of each geofence that was triggered.
    ArrayList triggeringGeofencesIdsList = new ArrayList();
    for (Geofence geofence : triggeringGeofences) {
        triggeringGeofencesIdsList.add(geofence.getRequestId());
    }
    String triggeringGeofencesIdsString = TextUtils.join(", ",  triggeringGeofencesIdsList);

    return geofenceTransitionString   ": "   triggeringGeofencesIdsString;
}

/**
 * Posts a notification in the notification bar when a transition is detected.
 * If the user clicks the notification, control goes to the MainActivity.
 */
private void sendNotification(String notificationDetails) {
    // Create an explicit content Intent that starts the main Activity.
    Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);

    // Construct a task stack.
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

    // Add the main Activity to the task stack as the parent.
    stackBuilder.addParentStack(MainActivity.class);

    // Push the content Intent onto the stack.
    stackBuilder.addNextIntent(notificationIntent);

    // Get a PendingIntent containing the entire back stack.
    PendingIntent notificationPendingIntent =
            stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

    // Get a notification builder that's compatible with platform versions >= 4
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

    // Define the notification settings.
    builder.setSmallIcon(R.drawable.ic_launcher)
            // In a real app, you may want to use a library like Volley
            // to decode the Bitmap.
            .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                    R.drawable.ic_launcher))
            .setColor(Color.RED)
            .setContentTitle(notificationDetails)
            .setContentText(getString(R.string.geofence_transition_notification_text))
            .setContentIntent(notificationPendingIntent);

    // Dismiss notification once the user touches it.
    builder.setAutoCancel(true);

    // Get an instance of the Notification manager
    NotificationManager mNotificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    // Issue the notification
    mNotificationManager.notify(0, builder.build());
}

/**
 * Maps geofence transition types to their human-readable equivalents.
 *
 * @param transitionType    A transition type constant defined in Geofence
 * @return                  A String indicating the type of transition
 */
private String getTransitionString(int transitionType) {
    switch (transitionType) {
        case Geofence.GEOFENCE_TRANSITION_ENTER:
            return getString(R.string.geofence_transition_entered);
        case Geofence.GEOFENCE_TRANSITION_EXIT:
            return getString(R.string.geofence_transition_exited);
        default:
            return getString(R.string.unknown_geofence_transition);
    }
}
}
  

я хочу отправлять сообщение на сервер при каждом входе / выходе из геозоны с использованием tcp

я делаю что-то вроде этого, запускаю соединение внутри при создании

 @Override
public void onCreate() {
    super.onCreate();
    if (mTcpClient == null) {
        new ConnectTask().execute("");
    }
}
  

поток для запуска отправки сообщения на сервер

 Thread thread=new Thread(){
        @Override
        public void run() {
            try {
                mTcpClient.sendMessage("halo server");


            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    };
    thread.start();
  

это работает для первого входа в геозону и успешной отправки сообщения
но при следующем входе / выходе будет выдана ошибка
java.lang.Исключение NullPointerException в методе sendmessage

я думаю, проблема в том, что я неправильно запускаю и закрываю соединение, кто-нибудь может мне помочь?

Редактировать

Класс AsyncTask

  public class ConnectTask extends AsyncTask<String, String, TCPClient> {

    @Override
    protected TCPClient doInBackground(String... message) {

        // create a TCPClient object
        mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
            @Override
            //here the messageReceived method is implemented
            public void messageReceived(String message) {
                //this method calls the onProgressUpdate
                publishProgress(message);
            }
        });
        mTcpClient.run();

        return null;
    }

    @Override
    protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);


    }
}
  

Класс TcpClient

 public class TCPClient {


// message to send to the server
private String mServerMessage;
// sends message received notifications
private OnMessageReceived mMessageListener = null;
// while this is true, the server will continue running
private boolean mRun = false;
// used to send messages
private PrintWriter mBufferOut;
// used to read messages from the server
private BufferedReader mBufferIn;

/**
 * Constructor of the class.
 */
public TCPClient(OnMessageReceived listener) {
    mMessageListener = listener;
}

/**
 * Sends the message entered by client to the server
 *
 * @param message text entered by client
 */
public void sendMessage(String message) {
    if (mBufferOut != null amp;amp; !mBufferOut.checkError()) {
        mBufferOut.println(message);
        mBufferOut.flush();
    }
}

/**
 * Close the connection and release the members
 */
public void stopClient() {

    sendMessage("close" "bye");
    mRun = false;

    if (mBufferOut != null) {
        mBufferOut.flush();
        mBufferOut.close();
    }

    mMessageListener = null;
    mBufferIn = null;
    mBufferOut = null;
    mServerMessage = null;
}

public void run() {

    mRun = true;

    try {

        InetAddress serverAddr = InetAddress.getByName(Constants.SERVER_IP);

        Log.e("TCP Client", "C: Connecting...");

        //create a socket to make the connection with the server
        Socket socket = new Socket(serverAddr, Constants.SERVER_PORT);

        try {

            //sends the message to the server
            mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

            //receives the message which the server sends back
            mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            sendMessage("name" "hi");

            while (mRun) {

                mServerMessage = mBufferIn.readLine();

                if (mServerMessage != null amp;amp; mMessageListener != null) {
                    //call the method messageReceived from MyActivity class
                    mMessageListener.messageReceived(mServerMessage);
                }

            }

            Log.e("RESPONSE FROM SERVER", "S: Received Message: '"   mServerMessage   "'");

        } catch (Exception e) {

            Log.e("TCP", "S: Error", e);

        } finally {

            socket.close();
        }

    } catch (Exception e) {

        Log.e("TCP", "C: Error", e);

    }

}

//Declare the interface. 
public interface OnMessageReceived {
    public void messageReceived(String message);
}
}
  

Комментарии:

1. Я предполагаю, что ConnectTask — это ваш класс, в котором вы открываете соединение с сервером.. Опубликуйте это здесь, чтобы мы могли изучить его..

2. да, я редактирую код, чтобы увидеть это

3. Какой указатель равен нулю, когда у вас есть это исключение NullPointerException? Какой оператор вызывает это? Logcat расскажет вам. Тогда расскажите нам.

4. mTcpClient.SendMessage («halo server»); при первой отправке сообщения второй раз вызовет исключение с нулевым указателем, и если я закрою свой сервер и повторно запущу, он отправит сообщение один раз и обратно до исключения, я должен постоянно закрывать свой сервер и повторно запускать каждое повторное сообщение

Ответ №1:

ну, решение было простым, я просто преобразовал свой tcp-объект в статический

Старый

 private TCPClient mTcpClient;
  

новое

 private static TCPClient mTcpClient;
  

и действительно, я не понимаю, почему это работает как статическое, я буду благодарен, если кто-нибудь сможет это объяснить

Комментарии:

1. потому что IntentService не имеет состояния. Все переменные удаляются после того, как intent был обработан внутри этого IntentService.