728x90
반응형

그래프 같은 UI를 만들기 전에 MQTT broker에서 받은 데이터에 따라 알림을 보내는 기능을 먼저 구현해보려고 한다

 

먼저 알림부터 구현해보자

 

https://twinw.tistory.com/50

https://www.youtube.com/watch?v=4BuRMScaaI4

 

위 블로그와 영상을 참고로 제작

일단 블로그의 코드를 복붙한 다음에 영상을 따라서 알림을 구현했다

 

//notificationService.java
package com.example.mqtt_test;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.widget.Toast;

import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

        public class notificationService extends Service {
    NotificationManager Notifi_M;
    ServiceThread thread;
    Notification Notifi;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Notifi_M = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        myServiceHandler handler = new myServiceHandler();
        thread = new ServiceThread(handler);
        thread.start();
        return START_STICKY;
    }

    //서비스가 종료될 때 할 작업

    public void onDestroy() {
        thread.stopForever();
        thread = null;//쓰레기 값을 만들어서 빠르게 회수하라고 null을 넣어줌.
    }

    class myServiceHandler extends Handler {
        @Override
        public void handleMessage(android.os.Message msg) {
            Intent intent = new Intent(notificationService.this, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(notificationService.this, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            {
                NotificationChannel channel = new NotificationChannel("notify test", "notiName", NotificationManager.IMPORTANCE_DEFAULT);
                NotificationManager manager = getSystemService(NotificationManager.class);
                manager.createNotificationChannel(channel);
            }

            NotificationCompat.Builder builder = new NotificationCompat.Builder(notificationService.this, "notify test");
            builder.setContentTitle("알림 테스트")
                    .setContentText("이것은 알림 테스트입니다.")
                    .setSmallIcon(R.drawable.ic_alarm_icon)
                    .setDefaults(Notification.DEFAULT_VIBRATE)
                    .setContentIntent(pendingIntent)
                    .setAutoCancel(true);

            NotificationManagerCompat managerCompat = NotificationManagerCompat.from(notificationService.this);
            managerCompat.notify(1,builder.build());

            //Toast.makeText(notificationService.this, "알림 생성", Toast.LENGTH_SHORT).show();
        }
    };
}
//ServiceThread
package com.example.mqtt_test;

import android.os.Handler;
import android.os.Message;

public class ServiceThread extends Thread{
    Handler handler;
    boolean isRun = true;

    public ServiceThread(Handler handler){
        this.handler = handler;
    }

    public void stopForever(){
        synchronized (this) {
            this.isRun = false;
        }
    }

    public void run(){
        //반복적으로 수행할 작업을 한다.
        while(isRun){
            handler.sendEmptyMessage(0);//쓰레드에 있는 핸들러에게 메세지를 보냄
            try{
                Thread.sleep(10000); //10초씩 쉰다.
            }catch (Exception e) {}
        }
    }
}

 

 

그리고 conn와 disconn 함수에 Service 시작과 중지를 해놨다

//MainActivity.java
public void conn(View v) //try connecting to MQTT
    {
        try {
            setHost();

            Intent intent = new Intent(MainActivity.this, notificationService.class);
            startService(intent);

            //Toast.makeText(MainActivity.this, "Service start", Toast.LENGTH_SHORT).show();

            IMqttToken token = client.connect();
            //IMqttToken token = client.connect(options);
            token.setActionCallback(new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    // We are connected
                    //Log.d(TAG, "onSuccess");
                    Toast.makeText(MainActivity.this, "connected", Toast.LENGTH_SHORT).show();
                    setSubscription();
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    // Something went wrong e.g. connection timeout or firewall problems
                    //Log.d(TAG, "onFailure");
                    Toast.makeText(MainActivity.this, "failed to connect", Toast.LENGTH_SHORT).show();

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

    public void disconn(View v) //disconnect MQTT
    {
        try {

            Intent intent = new Intent(MainActivity.this, notificationService.class);
            stopService(intent);

            //Toast.makeText(MainActivity.this, "Service stop", Toast.LENGTH_SHORT).show();

            IMqttToken token = client.disconnect();
            token.setActionCallback(new IMqttActionListener() {
                @Override
                public void onSuccess(IMqttToken asyncActionToken) {
                    // We are connected
                    //Log.d(TAG, "onSuccess");
                    Toast.makeText(MainActivity.this, "disconnected", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    // Something went wrong e.g. connection timeout or firewall problems
                    //Log.d(TAG, "onFailure");
                    Toast.makeText(MainActivity.this, "could not disconnect..", Toast.LENGTH_SHORT).show();

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

 

 

이제 코드를 좀 수정해서 특정값을 받으면 알람이 가도록 해보자

일단 스레드는 나중에...

 

client.callback에 무지성으로 알림 생성 코드를 떄려박았다

 client.setCallback(new MqttCallback() { //setCallback for message arrive
            @Override
            public void connectionLost(Throwable cause) {

            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                subText.setText(new String(message.getPayload())); //show text on subText

                String text = new String(message.toString());
                Log.d("valueOf", String.valueOf(message.getPayload()));
                Log.d("text", text);

                if(text.equals("hello"))
                {
                    Intent intent = new Intent(MainActivity.this, MainActivity.class);
                    PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    {
                        NotificationChannel channel = new NotificationChannel("notify test", "notiName", NotificationManager.IMPORTANCE_DEFAULT);
                        NotificationManager manager = getSystemService(NotificationManager.class);
                        manager.createNotificationChannel(channel);
                    }

                    NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, "notify test");
                    builder.setContentTitle("알림 테스트")
                            .setContentText("이것은 알림 테스트입니다.")
                            .setSmallIcon(R.drawable.ic_alarm_icon)
                            .setDefaults(Notification.DEFAULT_VIBRATE)
                            .setContentIntent(pendingIntent)
                            .setAutoCancel(true);

                    NotificationManagerCompat managerCompat = NotificationManagerCompat.from(MainActivity.this);
                    managerCompat.notify(1,builder.build());

                    Toast.makeText(MainActivity.this, "알림 생성", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {

            }
        });

hello라는 문자열을 받으면 알림 생성

 

이제 이걸 Service에서 백그라운드로 동작하게 만들면 된다

 

Service로 옮기기

원래 코드에서 수정하다가는 대참사가 일어나서 일어날 것 같아서 새로운 프로젝트를 만들어서 진행

mqtt_service라는 이름의 프로젝트 생성

 

build.gradle은 mqtt_test에서 그대로 복사한 뒤 이름만 바꿔주었다

plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.mqtt_service"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

repositories {
    maven {
        url "https://repo.eclipse.org/content/repositories/paho-releases/"
    }
}

dependencies {
    implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
    implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

    implementation 'androidx.core:core:1.0.0'

    implementation group: 'androidx.legacy', name: 'legacy-support-v4', version: '1.0.0'

    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

 

그리고 UI는 간단하게 IP와 subTopic만 정할 수 있도록 변경

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/disconnectButton"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="disconnect"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.938"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.571" />

    <Button
        android:id="@+id/connectButton"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:text="connect"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.938"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.483" />

    <EditText
        android:id="@+id/subTopic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="subTopic"
        android:inputType="textPersonName"
        android:text="test"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.104"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.567" />

    <EditText
        android:id="@+id/IP_address"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="IP address"
        android:inputType="textPersonName"
        android:text="192.168.1.104"
        app:layout_constraintBottom_toTopOf="@+id/subTopic"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.104"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.956" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

MainActivity.java는 입력받은 HOST와 TOPIC을 서비스로 넘겨주고

서비스는 MQTT broker와 연결하여 백그라운드에서 직접 통신을 한다

 

MainActivity에서 필요한 코드는 입력받은 HOST와 TOPIC을 intent를 통해 서비스로 넘겨주는 것

package com.example.mqtt_service;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText hostNameText;
    EditText subTopicText;

    Button connButton;
    Button disconnButton;

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

        hostNameText = (EditText)findViewById(R.id.IP_address);
        subTopicText = (EditText)findViewById(R.id.subTopic);

        connButton = (Button)findViewById(R.id.connectButton);
        disconnButton = (Button)findViewById(R.id.disconnectButton);

        connButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                connect();
            }
        });
        disconnButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                disconnect();
            }
        });
    }

    String getHost()
    {
        String _host = hostNameText.getText().toString();
        String MQTTHOST = "tcp://"+_host+":1883";

        return MQTTHOST;
    }
    String getSubTopic()
    {
        String _topic = subTopicText.getText().toString();

        return _topic;
    }

    private void connect()
    {
        Intent intent = new Intent(MainActivity.this, mqttService.class);
        intent.putExtra("HOST", getHost());
        intent.putExtra("TOPIC", getSubTopic());

        startService(intent);
        //Toast.makeText(MainActivity.this, "service Start", Toast.LENGTH_SHORT).show();
    }
    private void disconnect()
    {
        Intent intent = new Intent(MainActivity.this, mqttService.class);
        stopService(intent);
    }
}

 

서비스는 mqttService라는 이름으로 새로 class를 만들었다

그리고 extends Service를 추가

 

전체 코드는 아래와 같다

package com.example.mqtt_service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttSecurityException;

public class mqttService extends Service {
    String MQTTHOST = "";
    String subTopic = "";

    MqttAndroidClient client;

    //MqttConnectOptions options;
    //String USERNAME = "";
    //String PASSWORD = "";

    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return null;
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("host", intent.getStringExtra("HOST"));
        Log.d("topic", intent.getStringExtra("TOPIC"));

        MQTTHOST = intent.getStringExtra("HOST");
        subTopic = intent.getStringExtra("TOPIC");

        setHost();

        connect();

        client.setCallback(new MqttCallback() { //setCallback for message arrive
            @Override
            public void connectionLost(Throwable cause) {

            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {

                String text = new String(message.toString());
                Log.d("valueOf", String.valueOf(message.getPayload()));
                Log.d("text", text);

                if(text.equals("hello"))
                {
                    Intent intent = new Intent(mqttService.this, MainActivity.class);
                    PendingIntent pendingIntent = PendingIntent.getActivity(mqttService.this, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    {
                        NotificationChannel channel = new NotificationChannel("notify test", "notiName", NotificationManager.IMPORTANCE_DEFAULT);
                        NotificationManager manager = getSystemService(NotificationManager.class);
                        manager.createNotificationChannel(channel);
                    }

                    NotificationCompat.Builder builder = new NotificationCompat.Builder(mqttService.this, "notify test");
                    builder.setContentTitle("알림 테스트")
                            .setContentText("이것은 알림 테스트입니다.")
                            .setSmallIcon(R.drawable.ic_alarm_icon)
                            .setDefaults(Notification.DEFAULT_VIBRATE)
                            .setContentIntent(pendingIntent)
                            .setAutoCancel(true);

                    NotificationManagerCompat managerCompat = NotificationManagerCompat.from(mqttService.this);
                    managerCompat.notify(0,builder.build());

                    Toast.makeText(mqttService.this, "알림 생성", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {

            }
        });

        return START_REDELIVER_INTENT;
    }

    @Override
    public void onDestroy() {
        disconnect();
        super.onDestroy();
    }

    private void setHost()
    {
        String clientId = MqttClient.generateClientId();
        client = new MqttAndroidClient(this.getApplicationContext(), MQTTHOST, clientId);

        //options = new MqttConnectOptions();
        //options.setUserName(USERNAME);
        //options.setPassword(PASSWORD.toCharArray());
    }

    private void setSubscription()
    {
        try
        {
            client.subscribe(subTopic, 0);
            Toast.makeText(mqttService.this, "subscribe "+ subTopic, Toast.LENGTH_SHORT).show();
        }
        catch(MqttException e)
        {
            e.printStackTrace();
        }
    }

    private void connect()
    {
        Log.d("log", "try connection to "+MQTTHOST);
        try {
            IMqttToken token = client.connect();
            token.setActionCallback(new IMqttActionListener()
            {
                @Override
                public void onSuccess(IMqttToken asyncActionToken)
                {
                    Toast.makeText(mqttService.this, "connected", Toast.LENGTH_SHORT).show();
                    setSubscription();
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Toast.makeText(mqttService.this, "failed to connect", Toast.LENGTH_SHORT).show();
                    Log.d("log", "failed to connect");
                }
            });
        }
        catch (MqttException e)
        {
            e.printStackTrace();
        }
    }
    private void disconnect()
    {
        try
        {
            IMqttToken token = client.disconnect();
            token.setActionCallback(new IMqttActionListener()
            {
                @Override
                public void onSuccess(IMqttToken asyncActionToken)
                {
                    Toast.makeText(mqttService.this, "disconnected", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception)
                {
                    Toast.makeText(mqttService.this, "could not disconnect..", Toast.LENGTH_SHORT).show();
                }
            });
        }
        catch (MqttException e)
        {
            e.printStackTrace();
        }
    }
}

 

hello를 받으면 알림이 생성된다

그런데 가끔씩 강제종료되었다고 뜬다

 

이는 Oreo버전부터 background service를 시스템이 종료하는 것 때문인 것 같은데

이왕 이렇게 된거 실행중일 때는 알림이 떠있도록 해보자

 

 

Foreground Service

Foreground Service로 백그라운드에서 실행중일 때는 알림이 떠있게 하는 방법이 있다

 

수정된 코드

package com.example.mqtt_service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttSecurityException;

public class mqttService extends Service {

    public static final String CHANNEL_ID = "ForegrounndServiceChannel";

    String MQTTHOST = "";
    String subTopic = "";

    MqttAndroidClient client;

    //MqttConnectOptions options;
    //String USERNAME = "";
    //String PASSWORD = "";

    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return null;
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("host", intent.getStringExtra("HOST"));
        Log.d("topic", intent.getStringExtra("TOPIC"));

        MQTTHOST = intent.getStringExtra("HOST");
        subTopic = intent.getStringExtra("TOPIC");

        setHost();

        connect();

        client.setCallback(new MqttCallback() { //setCallback for message arrive
            @Override
            public void connectionLost(Throwable cause) {

            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {

                String text = new String(message.toString());
                Log.d("valueOf", String.valueOf(message.getPayload()));
                Log.d("text", text);

                if(text.equals("hello"))
                {
                    Intent intent = new Intent(mqttService.this, MainActivity.class);
                    PendingIntent pendingIntent = PendingIntent.getActivity(mqttService.this, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

                    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    {
                        NotificationChannel channel = new NotificationChannel("notify test", "notiName", NotificationManager.IMPORTANCE_DEFAULT);
                        NotificationManager manager = getSystemService(NotificationManager.class);
                        manager.createNotificationChannel(channel);
                    }

                    NotificationCompat.Builder builder = new NotificationCompat.Builder(mqttService.this, "notify test");
                    builder.setContentTitle("알림 테스트")
                            .setContentText("이것은 알림 테스트입니다.")
                            .setSmallIcon(R.drawable.ic_alarm_icon)
                            .setDefaults(Notification.DEFAULT_VIBRATE)
                            .setContentIntent(pendingIntent)
                            .setAutoCancel(true);

                    NotificationManagerCompat managerCompat = NotificationManagerCompat.from(mqttService.this);
                    managerCompat.notify(0,builder.build());

                    Toast.makeText(mqttService.this, "알림 생성", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {

            }
        });

        return START_REDELIVER_INTENT;
    }

    @Override
    public void onDestroy() {
        disconnect();
        super.onDestroy();
    }

    private void setHost()
    {
        String clientId = MqttClient.generateClientId();
        client = new MqttAndroidClient(this.getApplicationContext(), MQTTHOST, clientId);

        //options = new MqttConnectOptions();
        //options.setUserName(USERNAME);
        //options.setPassword(PASSWORD.toCharArray());
    }

    private void setSubscription()
    {
        try
        {
            client.subscribe(subTopic, 0);
            Toast.makeText(mqttService.this, "subscribe "+ subTopic, Toast.LENGTH_SHORT).show();
        }
        catch(MqttException e)
        {
            e.printStackTrace();
        }
    }

    private void connect()
    {
        Log.d("log", "try connection to "+MQTTHOST);
        try {
            IMqttToken token = client.connect();
            token.setActionCallback(new IMqttActionListener()
            {
                @Override
                public void onSuccess(IMqttToken asyncActionToken)
                {
                    Toast.makeText(mqttService.this, "connected", Toast.LENGTH_SHORT).show();
                    setSubscription();
                    createNotificationChannel();
                    Intent notificationIntent = new Intent(mqttService.this, MainActivity.class);
                    PendingIntent pendingIntent = PendingIntent.getActivity(mqttService.this, 0, notificationIntent, 0);
                    Notification notification = new NotificationCompat.Builder(mqttService.this, CHANNEL_ID)
                            .setContentTitle("mqtt_service")
                            .setContentText("mqtt service is running on background")
                            .setSmallIcon(R.mipmap.ic_launcher)
                            .setContentIntent(pendingIntent)
                            .build();
                    startForeground(1, notification);
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
                    Toast.makeText(mqttService.this, "failed to connect", Toast.LENGTH_SHORT).show();
                    Log.d("log", "failed to connect");
                }
            });
        }
        catch (MqttException e)
        {
            e.printStackTrace();
        }
    }
    private void disconnect()
    {
        try
        {
            IMqttToken token = client.disconnect();
            token.setActionCallback(new IMqttActionListener()
            {
                @Override
                public void onSuccess(IMqttToken asyncActionToken)
                {
                    Toast.makeText(mqttService.this, "disconnected", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(IMqttToken asyncActionToken, Throwable exception)
                {
                    Toast.makeText(mqttService.this, "could not disconnect..", Toast.LENGTH_SHORT).show();
                }
            });
        }
        catch (MqttException e)
        {
            e.printStackTrace();
        }
    }

    private void createNotificationChannel()
    {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            NotificationChannel servcieChannel = new NotificationChannel(CHANNEL_ID, "Foreground Service Channel", NotificationManager.IMPORTANCE_NONE);

            NotificationManager manager = getSystemService(NotificationManager.class);
            assert manager != null;
            manager.createNotificationChannel(servcieChannel);
        }
    }
}

 

이제 백그라운드에서 실행중임을 알리는 알림과 함께 hello를 받았을 때 뜨는 알림 두개 모두 나온다

 

마지막으로 알림에서 서비스를 중지할 수 있도록 해보자

 

중지 버튼 추가

connect함수에 있는 onSuccess에서 foreground service를 생성하는 곳을 수정했다

 @Override
                public void onSuccess(IMqttToken asyncActionToken)
                {
                    Toast.makeText(mqttService.this, "connected", Toast.LENGTH_SHORT).show();
                    setSubscription();
                    createNotificationChannel();

                    Intent notificationIntent = new Intent(mqttService.this, MainActivity.class);
                    PendingIntent pendingIntent = PendingIntent.getActivity(mqttService.this, 0, notificationIntent, 0);

                    Intent stopSelf = new Intent(mqttService.this, mqttService.class);
                    stopSelf.setAction("STOP");

                    PendingIntent pStopSelf = PendingIntent.getService(mqttService.this, 0, stopSelf, PendingIntent.FLAG_CANCEL_CURRENT);

                    Notification notification = new NotificationCompat.Builder(mqttService.this, CHANNEL_ID)
                            .setContentTitle("mqtt_service")
                            .setContentText("mqtt service is running on background")
                            .setSmallIcon(R.mipmap.ic_launcher)
                            .setContentIntent(pendingIntent)
                            .addAction(R.drawable.ic_close, getString(R.string.close), pStopSelf)
                            .build();

                    startForeground(1, notification);
                }

stopSelf라는 intent와 pStopSelf라는 pendingIntent를 각각 생성하고

stopSelf에 "STOP" Action를 설정한다

pStopSelf에 stopSelf를 넣어주고

notification에 addAction를 통해 pStopSelf를 사용하게 한다

 

 

흠 근데 close 버튼을 누르니까 앱이 강제종료된다

오류를 확인해보니 getStringExtra가 null이기 때문인듯 하다

이부분을 null이 아닐때만 실행하도록 했다

 

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if(intent.getStringExtra("HOST") != null && intent.getStringExtra("TOPIC") != null)
        {
            Log.d("host", intent.getStringExtra("HOST"));
            Log.d("topic", intent.getStringExtra("TOPIC"));

            MQTTHOST = intent.getStringExtra("HOST");
            subTopic = intent.getStringExtra("TOPIC");
        }