GGUF 파일을 Llama.cpp 도구로 실행하는 방법

AI|2025. 4. 14. 16:25
반응형

Ubuntu 24.04 기준으로 작성하였습니다.

모델 다운로드 디렉토리(/data/Llama4/Q4_K_M/)는 임의로 정하였습니다.

 

 

1. 모델 다운로드

 

모델은 기본적으로 파일 사이즈가 크므로, 용량이 넉넉한 디렉토리를 선택해야 합니다.

 

# mkdir -p /data/Llama4/Q4_K_M/

# cd /data/Llama4/Q4_K_M

# wget https://huggingface.co/unsloth/Llama-4-Scout-17B-16E-Instruct-GGUF/resolve/main/Q4_K_M/Llama-4-Scout-17B-16E-Instruct-Q4_K_M-0000{1..2}-of-00002.gguf

 

 

2. Llama.cpp 다운로드

 

Llama.cpp 는 모델을 간단히 가동, 병합, 양자화 등을 할 수 있는 도구 입니다.

 

# apt -y install cmake libopenblas-dev g++
# git clone https://github.com/ggerganov/llama.cpp.git

# cd llama.cpp
# mkdir build
# cd build
# cmake ..

# cmake --build . --config Release

 

 

3. 분할된 파일 합치기

 

# cd bin

# ./llama-gguf-split --merge /data/Llama4/Q4_K_M/Llama-4-Scout-17B-16E-Instruct-Q4_K_M.gguf

 

※ 양자화 (양자화는 이미 되어있으므로, 원본을 양자화하는 방법이 필요한 분만 참고하세요)

# ./llama-quantize  /data/Llama4/BF16/Llama-4-Scout-17B-16E-Instruct-BF16-00001-of-00005.gguf /data/Llama4/Q4_K_M/Llama-4-Scout-17B-16E-Instruct-Q4_K_M.gguf Q4_K_M

 

 

4. LLM 실행

 

1) 로컬 질의

로컬에서 간단히 실행하고 질의합니다.

# ./llama-cli -m /data/Llama4/Q4_K_M/Llama-4-Scout-17B-16E-Instruct-Q4_K_M.gguf -p "오늘은 며칠이야?"

 

2) API 사용

API를 사용해 외부 네트워크에서도 접근 허용합니다.

# ./llama-server -m /data/Llama4/Q4_K_M/Llama-4-Scout-17B-16E-Instruct-Q4_K_M.gguf --host 0.0.0.0 --port 8080

 

다음과 같이 다른 터미널이나 외부 네트워크의 시스템에서 질의해 봅니다.

# apt -y install jq

# curl http://{서버IP}:8080/completion -H "Content-Type: application/json" -d '{
  "prompt": "오늘은 며칠이야?",
  "n_predict": 100
}' | jq

 

반응형

댓글()

Ubuntu 24.04 에서 RTX5080 드라이버 설치하기

리눅스/OS 일반|2025. 4. 11. 12:42
반응형

Ubuntu 24.04 에서 여러 AMD, NVIDIA 제품을 설치해 보았지만, RTX5080 은 설치 방식이 조금 달라서 내용을 기록해 둡니다.

 

 

1. nouveau 드라이버 차단

 

nouveau 드라이버를 블랙리스트에 추가합니다.

# cat <<EOF | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
blacklist nouveau
options nouveau modeset=0
EOF

 

initramfs 이미지를 업데이트하여 블랙리스트 설정을 반영합니다.

# update-initramfs -u

 

시스템을 재시작 합니다.

# reboot

 

 

2. NVIDIA Driver 설치

 

리포지토리를 추가합니다.
# add-apt-repository ppa:graphics-drivers/ppa

# apt update
 
설치 가능한 드라이버 버전을 확인합니다.
# ubuntu-drivers devices
udevadm hwdb is deprecated. Use systemd-hwdb instead.
udevadm hwdb is deprecated. Use systemd-hwdb instead.
== /sys/devices/pci0000:97/0000:97:01.0/0000:98:00.0 ==
modalias : pci:v000010DEd00002C02sv00001458sd00004196bc03sc00i00
vendor   : NVIDIA Corporation
driver   : nvidia-driver-570-open - third-party non-free recommended
driver   : nvidia-driver-570 - third-party non-free
driver   : xserver-xorg-video-nouveau - distro free builtin

여기에서 위와 같이 recommended(추천) 라고 되어진 버전을 설치하면 됩니다.

# apt -y install nvidia-driver-570-open

 

설치 후 시스템을 재부팅 하면 관련 명령 사용이 가능해 집니다.

# reboot

 

아래 명령으로 드라이버가 잘 설치된 것을 확인할 수 있습니다.

# nvidia-smi

 

 

3. CUDA 설치

 

설치 방법을 따라 설치를 하면 됩니다. (Install Type 을 runfile 로 선택하면 설치가 간단해 집니다)

# wget https://developer.download.nvidia.com/compute/cuda/12.8.1/local_installers/cuda_12.8.1_570.124.06_linux.run

# sh cuda_12.8.1_570.124.06_linux.run

 

처음 설치 화면이 뜨기까지 시간이 어느정도 소요됩니다.

설치 과정은 Continue 선택 > accept 입력 > (미리 설치했으므로) 'Driver' 제외 및 나머지 기본값으로 Install 입니다.

파일 사이즈가 크기 때문에 여기에서도 시간이 다소 소요됩니다.

 

CUDA 설치가 완료되면, ~/.bashrc 파일에 경로를 추가하여 CUDA 바이너리에 접근할 수 있도록 합니다.

# echo 'export PATH=/usr/local/cuda-12.8/bin:$PATH' >> ~/.bashrc

# echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.8/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc

# source ~/.bashrc

 

설치된 CUDA 버전을 확인합니다.

# nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2025 NVIDIA Corporation
Built on Fri_Feb_21_20:23:50_PST_2025
Cuda compilation tools, release 12.8, V12.8.93
Build cuda_12.8.r12.8/compiler.35583870_0

 

반응형

댓글()

RockyLinux 9 에서 root 원격접속 허용하기

리눅스/OS 일반|2025. 3. 14. 09:29
반응형

일반계정이 아닌 root 로 바로 접근을 허용하려는 경우 아래와 같은 SSH 설정 옵션으로 셋팅 후 데몬을 재시작 해 줍니다.


# vi /etc/ssh/sshd_config

...
PermitRootLogin yes
PubkeyAuthentication yes
PasswordAuthentication yes
...

 

# systemctl restart sshd

 

반응형

댓글()

Ubuntu 24.04 APT 저장소 및 키 초기화 하기

리눅스/OS 일반|2025. 2. 19. 10:31
반응형

모든 추가 저장소 삭제
# rm -f /etc/apt/sources.list.d/*.list

모든 GPG 키 삭제
# rm -f /etc/apt/keyrings/*.gpg

기본 APT 저장소 복구 (Ubuntu 24.04 Noble 기준)
# tee /etc/apt/sources.list <<EOF
deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu noble-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu noble-security main restricted universe multiverse
EOF

APT 캐시 및 설정 초기화
# apt clean
# apt autoclean
# apt -y autoremove --purge
r# m -rf /var/lib/apt/lists/*

저장소 업데이트 및 확인
# apt -y update && apt -y upgrade

 

반응형

댓글()

RockyLinux 9 기본 방화벽 변경하기 (firewalld -> iptables)

리눅스/Security|2025. 1. 10. 14:35
반응형

Rocky Linux 9는 기본적으로 firewalld를 사용하지만, iptables를 설치하고 사용할 수도 있습니다.

아래는 iptables 설치 및 활성화 방법입니다.

 

1. firewalld 비활성화

우선 사용하지 않을 firewalld 를 비활성화 합니다.

# systemctl disable --now firewalld

 

2. iptables 설치

iptables 패키지를 설치합니다.

# dnf -y install iptables iptables-services

 

3. iptables 룰 작성

방화벽을 가동하기 전에 룰셋을 정합니다.

# vi /etc/sysconfig/iptables

*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type any -j ACCEPT

-A INPUT -s 192.168.10.2/32 -p tcp --dport 22 -j ACCEPT
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

COMMIT

 

 

4. iptables 방화벽 가동 

# systemctl enable --now iptables

 

5. iptables 상태 확인

# systemctl status iptables

# iptables -nL

 

반응형

댓글()

Nginx 에서 Proxy 구성시 Arguments 사용하기

리눅스/APACHE|2024. 12. 12. 12:35
반응형

Nginx 를 프록시 서버로 만들건데, Backend 서버를 내가 원하는 IP 로 접속하는 예제 입니다.

Client 에서는 Backend 서버에 직접 접속이 안되기 때문에, 프록시 서버를 이용하는 것입니다.

 

- 접속예 : http://www.sysdocu.kr/?url=192.168.10.2

- 192.168.10.2 사설 IP 를 가진 서버에 www.sysdocu.kr  이라는 프록시 서버를 이용해 접근합니다. (통신, 트래픽 흐름)

 

nginx.conf 파일에서 서버 구성내용을 아래와 같이 수정합니다.

아래 예시에서는 upstream 없이 구성하였습니다.

url 값은 arg_url 로 받아야 합니다.

 

# vi /etcc/nginx/nginx.conf

...

    ###################################
    server {
        listen 80;
        server_name www.sysdocu.kr;

        # 404 에러 페이지 (선택, 404 출력 파일의경로와 파일명 입력)

        location = /custom_404.html {

            root /etc/nginx;

            internal;

        }

 

        location / {
            set $backend "http://$arg_url";
            proxy_pass $backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Connection "";
        }
    }
    ###################################

...

 

# systemctl restart nginx

 

반응형

댓글()

안드로이드 java 에서 웹페이지 출력 내용을 String 에 넣기

프로그래밍/Android (Java)|2024. 11. 30. 22:39
반응형

(onCreate 안에서)

 

        // 웹페이지의 내용을 가져오기
        new Thread(() -> {
            // AtomicReference 사용
            AtomicReference<String> webContent = new AtomicReference<>("");

            // fetchWebPage 결과를 AtomicReference에 저장
            webContent.set(fetchWebPage("https://sysdocu.tistory.com/list.html"));

            runOnUiThread(() -> {
                // UI 스레드에서 Toast 실행
                Toast.makeText(MainActivity.this, webContent.get(), Toast.LENGTH_SHORT).show();
            });
        }).start();

 

 

(onCreate 밖에 같은 레벨에서)


    // 웹페이지 json 코드 가져오기
    private String fetchWebPage(String urlString) {
        String result = "";
        OkHttpClient client = new OkHttpClient();
        // 요청 객체 생성
        Request request = new Request.Builder()
                .url(urlString)
                .build();
        try {
            // 요청 실행 및 응답 받기
            Response response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                result = response.body().string(); // 응답 내용을 String으로 변환
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

반응형

댓글()

[Arduino-IDE] Seeed Studio XIAO ESP32S3 Sense 기본 예제 및 에러 조치

프로그래밍/C, C++|2024. 11. 1. 18:48
반응형

Seeed Studio XIAO ESP32S3 Sense는 카메라(OV2640)와 디지털 마이크가 내장된 ESP32-S3 기반 개발 보드로, Wi-Fi를 통해 영상 스트리밍이 가능합니다.

 

 

1. Arduino IDE 기본 세팅

 

Arduino IDE 실행 > 파일 > 기본 설정 > (중간) Language 에 한국어 선택 후 확인.

 

Arduino IDE 실행 > 파일 > 기본 설정 > (맨아래) 추가 보드 관리자 URL 에 아래 내용입력 후 확인.

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

 

Arduino IDE 실행 > 도구 > 보드 > 보드매니저 실행해서 아래 두개 설치

- Arduino ESP32 Boards

- esp32

 

Arduino IDE 실행 > 도구 > 보드 > esp32 > ESP32S3 Dev Module Octal (WROOM2) 선택

 

USB 에 기기 연결 후, 상단 셀렉트 옵션에서 기기 선택해 놓습니다.

 

 

2. 시리얼 포트 접근 권한 추가

 

리눅스 시스템에서 시리얼 포트에 대한 접근 권한을 주어야 합니다.

다음과 같이 사용자를 dialout 그룹에 추가합니다.

명령을 실행한 후 시스템에서 로그아웃했다가 다시 로그인해야 변경사항이 적용됩니다. (생략 가능)

$ sudo usermod -a -G dialout $USER

 

시리얼 포트에 직접 권한 부여하는 방법도 있습니다.

$ sudo chmod 666 /dev/ttyACM0

하지만 이 방법은 임시적인 해결책으로, 재부팅 후에는 다시 권한을 설정해야 합니다.

 

 

3. 예제 입력 및 컴파일

 

Arduino IDE에는 여러가지 예제가 준비되어 있습니다.

ESP32S3 카메라를 올바르게 사용하기 위해 기본 제공되는 예제를 사용해 봅니다.

 

Arduino IDE 실행 > 파일 > 예제 > ESP32 > Camera > CameraWebServer 선택

 

열려진 예제 코드에서 WiFi 설정만 수정합니다.
const char* ssid = "CDH";                   // Wi-Fi SSID
const char* password = "12345678";  // Wi-Fi 비밀번호

보드 선택이 "ESP32S3 Dev Module" 또는 "ESP32S3 Sense"로 설정되어 있는지 확인합니다.
업로드를 실행 합니다.

 

아래와 같은 결과가 출력되면 성공한 것입니다.

 

[출력]

스케치는 프로그램 저장 공간 1000446 바이트(76%)를 사용. 최대 1310720 바이트.
전역 변수는 동적 메모리 62908바이트(19%)를 사용, 264772바이트의 지역변수가 남음.  최대는 327680 바이트.
esptool.py v4.8.1
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 (QFN56) (revision v0.2)
Features: WiFi, BLE, Embedded PSRAM 8MB (AP_3v3)
Crystal is 40MHz
MAC: d8:3b:da:45:d2:7c
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Flash will be erased from 0x00000000 to 0x00004fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00010000 to 0x00104fff...
Compressed 18976 bytes to 12282...
Writing at 0x00000000... (100 %)
Wrote 18976 bytes (12282 compressed) at 0x00000000 in 0.3 seconds (effective 499.2 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 134...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (134 compressed) at 0x00008000 in 0.0 seconds (effective 568.3 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 47...
Writing at 0x0000e000... (100 %)
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 756.9 kbit/s)...
Hash of data verified.
Compressed 1000592 bytes to 653189...
Writing at 0x00010000... (2 %)
Writing at 0x0001c4b8... (5 %)
Writing at 0x000253f6... (7 %)
Writing at 0x0002926d... (10 %)
Writing at 0x00030be0... (12 %)
Writing at 0x0003b4c8... (15 %)
Writing at 0x000449a9... (17 %)
Writing at 0x0004a26a... (20 %)
Writing at 0x0004f8be... (22 %)
Writing at 0x0005531c... (25 %)
Writing at 0x0005aabf... (27 %)
Writing at 0x0005fd0b... (30 %)
Writing at 0x00065104... (32 %)
Writing at 0x0006a55c... (35 %)
Writing at 0x0006fc7e... (37 %)
Writing at 0x00075228... (40 %)
Writing at 0x0007a3d1... (42 %)
Writing at 0x0007f636... (45 %)
Writing at 0x00084d7b... (47 %)
Writing at 0x0008aafd... (50 %)
Writing at 0x0009111e... (52 %)
Writing at 0x0009695f... (55 %)
Writing at 0x0009be10... (57 %)
Writing at 0x000a13d3... (60 %)
Writing at 0x000a6810... (62 %)
Writing at 0x000ab6c9... (65 %)
Writing at 0x000b0853... (67 %)
Writing at 0x000b5c6b... (70 %)
Writing at 0x000baf83... (72 %)
Writing at 0x000bffbe... (75 %)
Writing at 0x000c5188... (77 %)
Writing at 0x000cac39... (80 %)
Writing at 0x000d07d9... (82 %)
Writing at 0x000d5cc7... (85 %)
Writing at 0x000e0b33... (87 %)
Writing at 0x000e64ab... (90 %)
Writing at 0x000eb3f0... (92 %)
Writing at 0x000f35f3... (95 %)
Writing at 0x000f92d7... (97 %)
Writing at 0x000fee95... (100 %)
Wrote 1000592 bytes (653189 compressed) at 0x00010000 in 6.9 seconds (effective 1154.1 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting with RTC WDT...

 

[ 에러 ]

/dev/ttyACM0 가 보였다 안보였다 하는 증상은 Arduino IDE 에서 포트가 보였다 안보였다 하는 증상과 같습니다.

이는 업로드를 이미 했거나, 하는중 오류가 발생하였거나 기타 등등의 문제로 인해 생기는데,

해결책은 아래와 같습니다.

 

[ 해결 ]

데이터 케이블과 ESP32S3 Sense (기기) 와 분리합니다.

C-Type 머리가 보이게, 그리고 위로 올린 방향으로 두면 C-Type 오른쪽으로 보이는 버튼이 BOOT 버튼입니다.

BOOT 버튼을 누른채 데이터 케이블을 연결하면 불이 깜빡이며, 멈추었을때 손을 떼면 초기화 됩니다.

초기화 된 상태에서 다시 케이블을 분리하면 원래대로 돌아갑니다.

그러므로 연결된 상태 그대로 소스코드를 수정하고 업로드 해야 합니다.

 

※ BootLoader 모드란?
- 공장 초기화가 아닌, 새로운 펌웨어를 강제로 업로드할 수 있도록 하는 모드 

- 기존의 프로그램이 손상되었거나 업로드가 실패할 때, 이 모드를 통해 새로운 펌웨어를 강제로 설치 가능 

- 일반적인 운영 중에는 사용되지 않으며, 포트 인식 오류나 업로드 문제 해결 시 사용

 

[ 에러 ]

업로드시 아래와 같은 에러메세지가 출력된다면, 현재 사용자에게 장치 접근 권한을 추가해야 합니다.

 

스케치는 프로그램 저장 공간 1000446 바이트(76%)를 사용. 최대 1310720 바이트. 전역 변수는 동적 메모리 62908바이트(19%)를 사용, 264772바이트의 지역변수가 남음. 최대는 327680 바이트. esptool.py v4.8.1 Serial port /dev/ttyACM0 A fatal error occurred: Could not open /dev/ttyACM0, the port is busy or doesn't exist. ([Errno 13] could not open port /dev/ttyACM0: [Errno 13] Permission denied: '/dev/ttyACM0') Hint: Try to add user into dialout or uucp group. Failed uploading: uploading error: exit status 2

 

[ 해결 ]

# sudo usermod -a -G dialout $USER

그런 다음 로그아웃, 로그인없이 적용하기 위해서 현재 터미널에서 새 그룹 권한을 바로 적용합니다.

# newgrp dialout

 

이제 android-ide 를 실행하면  /dev/ttyACM0 장치에 대한 접근권한이 생깁니다.

 

반응형

댓글()

Ubuntu 24.04 에서 Arduino IDE 2.3.5 설치하기

리눅스/OS 일반|2024. 11. 1. 18:48
반응형

다운로드 및 실행은 일반 계정으로 해야 합니다.


공식 사이트 또는 깃허브에서 압축파일을 다운로드 합니다.

- https://github.com/arduino/arduino-ide/releases

 

$ wget https://downloads.arduino.cc/arduino-ide/arduino-ide_2.3.5_Linux_64bit.zip
$ unzip arduino-ide_2.3.5_Linux_64bit.zip 

$ cd arduino-ide_2.3.5_Linux_64bit

$ ./arduino-ide

 

반응형

댓글()

Notification 알림으로 인한 Activity 중복 호출 방지

반응형

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Activity 중복 생성 방지
        if (!isTaskRoot) {
            finish()
            return
        }

}

 

반응형

댓글()

[Android Kotlin] 포그라운드 서비스 (Foreground Service)

반응형

[출처] https://iamjm29.tistory.com/13

 

자세한 설명은 출처를 확인하도록 합니다.

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
http://schemas.android.com/apk/res/android"</linearlayout xmlns: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"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".MainActivity">

    <Button
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:text="Start Service"
        android:id="@+id/btn_start"/>

    <Button
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:text="Stop Service"
        android:id="@+id/btn_stop"/>

</LinearLayout>

 

 

AndroidManifest.xml

아래 전체를 복사하지 말고, 색칠한 곳만 참고하도록 합니다.

생성된 패키지명이 다르면 테마 이름도 다르기 때문입니다.

<?xml version="1.0" encoding="utf-8"?>
http://schemas.android.com/apk/res/android"</manifest xmlns:android="
    xmlns:tools="
http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Test"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true" />

    </application>

</manifest>

 

 

MainActivity.kt

package kr.sysdocu.test

import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    //Button
    var btn_start: Button? = null
    var btn_stop: Button? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn_start = findViewById(R.id.btn_start)
        btn_stop = findViewById(R.id.btn_stop)

        btn_start!!.setOnClickListener(View.OnClickListener {
            val serviceIntent = Intent(this@MainActivity, MyService::class.java)
            startService(serviceIntent)
            Toast.makeText(this@MainActivity, "Service start", Toast.LENGTH_SHORT).show()
        })

        btn_stop!!.setOnClickListener(View.OnClickListener {
            val serviceIntent = Intent(this@MainActivity, MyService::class.java)
            stopService(serviceIntent)
            Toast.makeText(this@MainActivity, "Service stop", Toast.LENGTH_SHORT).show()
        })
    }
}

 


MyService.kt

package kr.sysdocu.test

import android.app.*
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat

class MyService : Service() {
    private var mThread: Thread? = null

    companion object {
        private const val TAG = "MyService"
        private const val NOTI_ID = 1
    }

    private fun createNotification() {
        val builder = NotificationCompat.Builder(this, "default")
        builder.setSmallIcon(R.mipmap.ic_launcher)
        builder.setContentTitle("Foreground Service")
        builder.setContentText("포그라운드 서비스")
        builder.color = Color.RED

        val notificationIntent = Intent(this, MainActivity::class.java)
        notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
        val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
        builder.setContentIntent(pendingIntent) // 알림 클릭 시 이동

        val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationManager.createNotificationChannel(
                NotificationChannel(
                    "default",
                    "기본 채널",
                    NotificationManager.IMPORTANCE_DEFAULT
                )
            )
        }
        val notification = builder.build()
        startForeground(NOTI_ID, notification)
    }

    override fun onCreate() {
        super.onCreate()
        createNotification()
        mThread = object : Thread("My Thread") {
            override fun run() {
                super.run()
                for (i in 0..99) {
                    Log.d(TAG, "count : $i")
                    try {
                        sleep(1000)
                    } catch (e: InterruptedException) {
                        currentThread().interrupt()
                        break
                    }
                }
            }
        }
        mThread!!.start()
        Log.d(TAG, "onCreate")
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand")
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        if (mThread != null) {
            mThread!!.interrupt()
            mThread = null
        }
        Log.d(TAG, "onDestroy")
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

 

 

 

 

반응형

댓글()