728x90
반응형

안녕하세요.

이번 프로젝트 관련해서 서비스와 클라이언트간의 통신시 서비스가 종료 되면 다시 재 실행이 되고 클라이언트는 서비스와의 연결을 유지 해야 합니다.

 

원치 않게 연결이 끊길 때 클라이언트가 자동으로 서비스와 다시 연결을 시도 해야 합니다.

여러가지 방법이 있을 수 있겠지만 intent로 처리하는 방법을 사용 하였습니다.

728x90

Service

 //서비스가 재 실행 되었을 때 해당 intent를 client에게 전달 한다.
private fun sendRestartService() {
    val intent = Intent("com.example.restartService")
    sendBroadcast(intent)
    Log.i(TAG, "sendRestartService!")
}

//sendRestartService를 호출 하는 부분
class MyService: Service() {
	override fun onCreate() {
    	sendRestartService()
    }
}

물론 위 서비스 말고 Activity에서 하고 싶은 경우는 MainActivity에서 하면 됩니다.

 

 

Client

class MainActivity {
	override fun onCreate() {
    	connectService()	//서비스 연결
        registerReceiver(connectionReceiver, IntentFilter("com.example.restartService"),
            RECEIVER_NOT_EXPORTED
        )
    }
    
    private val connectionReceiver = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Log.i(TAG, "onReceive")

            //서비스가 재 실행이 되면 intent로 해당 action값을 보내서 있는 경우 현재 클라이언트에게 알려주며
            //맞는 action값이 들어오면 서비스에게 다시 연결을 시도 한다.
            if (intent?.action.equals("com.example.restartService")) {
                Log.i(TAG, "서비스가 재시작되었음.")
                connectService() 서비스 연결
            }
        }
    }
}

클라이언트에서의 설정입니다.

기본적으로 서비스가 시작되어 있는 상태에서 클라이언트가 실행 되었을 때 자동으로 서비스 연결을 시도하고

intent값을 등록 합니다.

이후 서비스가 종료 후 다시 시작 되면 서비스에서 com.example.restartService 라는 action값을 보내게 되고 해당 값을 수신 받으면 서비스가 재시작됨을 알 수 있고 클라이언트는 서비스에게 다시 연결을 시도 합니다.

 

로그를 볼까요?

서비스가 종료되어 연결이 끊어진 후 서비스가 재시작된 후에 클라이언트에게 재시작 intent를 보냄.

이후 클라이언트는 해당 메세지를 수신하고 다시 서비스와의 연결을 시도 한다.

반응형

 

위 방법으로 우선 처리를 하였고. 그전에는 타이머로 와치독 방식으로 할까도 생각 했지만 이게 가장 적절한 방법이 아니었나 싶습니다.

 

혹시 또 좋은 방법이 있으면 댓글 환영합니다

 

감사합니다

728x90
728x90
반응형

안드로이드 스튜디오로 개발 중 git 또는 다른 경로를 통해서 새로운 프로젝트를 받았을 때 위와 같은 에러가 발생되는 경우가 있다.
The project is using an incompatible version (AGP 8.6.1) of the Android Gradle plugin. Latest supported version is AGP 8.5.2

 

이 때 굳이 변경 하지말고 나는 내 환경에서 쓰고 싶은데? 이럴때는 일단 build.gradle을 변경하자

 

"사용자 환경에 따라서 다르니 참고만 할 것!"

 

1. build.gradle open

변경 전
plugins {
    id 'com.android.application' version '8.6.1' apply false
    id 'com.android.library' version '8.6.1' apply false
}


변경 후
plugins {
    id 'com.android.application' version '8.5.2' apply false
    id 'com.android.library' version '8.5.2' apply false
}

그 다음 다시 sync를 하면 정상적으로 동작 된다.

728x90
728x90
반응형

전체화면의 사이즈를 상황에 따라서 동적으로 사이즈를 조절 하기 위한 예제.

 

레이아웃 설정 

<?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">

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="Hello World!"
        android:background="#ff0022"
        android:gravity="center"
        android:textColor="@color/cardview_light_background"
        app:layout_constraintBottom_toTopOf="@id/guidelineHorizontal"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@id/guidelineVertical"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/test"
        android:text="클릭"
        app:layout_constraintEnd_toEndOf="parent"
        />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guidelineVertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.2" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guidelineHorizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.7" />



</androidx.constraintlayout.widget.ConstraintLayout>

 

이렇게 레이아웃만 설정 하면 TextView의 전체 사이즈가 가이드라인을 통해서 조절이 된다.

이런식으로 수평/수직의 퍼센트를 설정함으로 해당 가이드라인을 기준으로 TextView을 그리고 있다.

 

그럼 상황에 따라서 TextView의 사이즈가 조절되야 하는 경우가 있다.

실제 실무에서도 필요한 기능이므로 그럴 때는 코드단에서 처리를 해줘야 한다.

 

아래와 같이 해보자.

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

        var btn: Button = findViewById(R.id.test)
        btn.setOnClickListener {
            changeGuideline(0.5, 0.8)
        }
    }

    private fun changeGuideline(ver:Double, hor:Double) {
        val gd1: Guideline = findViewById(R.id.guidelineHorizontal)
        val gd2: Guideline = findViewById(R.id.guidelineVertical)

        gd1.layoutParams = (gd1.layoutParams as ConstraintLayout.LayoutParams).apply {
            guidePercent = ver.toFloat()
        }

        gd2.layoutParams = (gd2.layoutParams as ConstraintLayout.LayoutParams).apply {
            guidePercent = hor.toFloat()
        }
    }
}

 

동적인 상황을 고려하여 클릭 했을 경우 변경 되도록 처리.

위와 같이 사이즈가 조절 되었음.

 

참조 해서 잘 사용해보자..기록기록.

728x90
728x90
반응형

프로젝트시 특정모듈에 따라서 기능을 on/off해야 하는데 메타데이터에서 컨트롤 하는게 더 효율적으로 생각되어 메타데이터에서 설정후 실제 코드에서 읽어오는 방법을 정리 하였다.

 

매니페스트 설정

<manifest>
......
	<meta-data
        android:name="testEnable"
        android:value="true"
    />
</manifest>

메니패스트에 사용할 메타데이터를 등록한다.

나의 목표는 testEnable이 true이면 특정 기능을 사용하고 없으면 사용하지 않는 것이다.

 

private fun getTestEnable(): Boolean {
	return packageManager.getApplicationInfo(
            packageName,
            PackageManager.GET_META_DATA)
            .metaData
            .getBoolean("testEnable")
}

 

만약에 testEnable 메타데이터가 없는 경우는 무조건 false로 리턴한다.

728x90
728x90
반응형

현재 설정 관련 레이아웃을 만들다가 표시해주는 상자를 곡선으로 그리고 싶었다.

아무래도 더 이쁘기도 하고 벤치마킹하는 유튜브나 다른 플레이어들도 코너를 많이 적용 했기 때문이다.

 

먼저. 나는 constraintLayout을 사용하는데 이 contraintLayout에 corner를 적용하고 싶다.

먼저 일반적으로 사용 할때.. 이렇게 사용 하였음.

<androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="@dimen/_180sdp"
        android:layout_height="wrap_content"
        android:background="#212121"
        android:visibility="visible"
        android:layout_marginBottom="@dimen/_10sdp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">
        
        <TextView
           ......
        />
</androidx.constraintlayout.widget.ConstraintLayout>

자 이런식으로 한 후에 실행을 시키면 단순히 네모난 모양으로 출력이 된다.

나는 네모난 모양을 곡선으로 변경하고 싶을때 다음과 같이 사용 하였다.

 

view_card.xml

<?xml version="1.0" encoding="utf-8"?><!-- 다이얼로그 배경 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="10dp" />
    <solid android:color="#383838" />
</shape>

위에 내용은 corner를 그려주는 레이아웃이다. 이 레이아웃을 contraintLayout에 background에 적용하면된다.

여기서 참고 할 것은. view_card.xml은. drawable아래에 생성을 해주어야한다!!!

 

아래와 같이 적용하면 이제 네모가 아닌 코너가 만들어진 상태로 화면에 보여준다.

<androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="@dimen/_180sdp"
        android:layout_height="wrap_content"
        android:background="@drawable/view_card"
        android:visibility="visible"
        android:layout_marginBottom="@dimen/_10sdp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">
        
        <TextView
           ......
        />
</androidx.constraintlayout.widget.ConstraintLayout>

constraintLayout에 background="#212121" -> @drawable/view_card 로 변경하면 끝!

728x90
728x90
반응형

코틀린을 사용하다 보면 random함수가 필요한 경우가 종종 있다.

그래서 랜덤함수의 사용방법을 기록해보기로 한다.

 

1. random()

fun main() {
	val range = (0..45)
    
    println(range.random())
    println(range.random())
}

결과 .

0 부터 45까지의 랜덤한 함수가 출력되었다.

총 6개의 랜덤함수를 만들어 보자.

fun main() {
	val range = (0..45) 
    var cnt = 0
    
    while(cnt < 6) {
        print(range.random()  )    
        print(" ")
        cnt++
    }   
}

 

0 부터 45번까지 총 6개의 데이터를 생성 하여 나열하였다.

 

위 내용은 정해진 패턴으로 랜덤 숫자를 생성한다. 그래서 매번 처음 동작 시킬때마다 같은 숫자대역이 나올확률이 있다.

이러한 정해진 패턴을 무시하는 랜덤함수를 사용하자.

 

2. SecureRandom

fun main() {
	val range = SecureRandom()
    println(range.nextInt(45))  
 
}

nextInt(45)는 0부터 45까지의 랜덤숫자를 출력 하도록 설정 한다.

 

똑같이 1번내용 처럼 살짝 응용을 하면 원하는 개수를 지정 할수 있고, 중복되지 않다던지, 특정 숫자를 제외 한다던지의 내용을 추가하여 구현 할수있다.

 

상황에 따라 다르지만 되도록이면 secureRange를 사용하는게 좋을 것 같ㄷ.

728x90
728x90
반응형

추가 기능을 만들려고 하는데, Youtube나 네이버 동영상 등 여러가지 동영상 플랫폼에서 지원하는 기능이다.

맨처음에 기능의 이름을 몰라서 여러가지로 검색을 해보았는데 해당 기능의 이름은 PIP라고 부른다.

 

간단한 예제를 먼저 소개를 하고 추후에 기능을 추가할 예정이다.

 

구현 순서.

1. 설정 : 매니페스트 수정. ( AndroidManifest.xml )

<activity
  ......
  android:resizeableActivity="true"
  android:supportsPictureInPicture="true"
  ......
/activity>

매니페스트에 위 두개를 추가 한다.

 

2. xml 수정

기본생성된 내용을 수정한다.

<VideoView
        android:id="@+id/main_video_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <Button
            android:id="@+id/main_pip_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Start PIP mode"
            android:backgroundTint="@android:color/holo_orange_light"
            />
    </ScrollView>

 

3. 코드 수정.

class MainActivity : AppCompatActivity() {
    private val mPipBuilder = PictureInPictureParams.Builder()
    private lateinit var binding : ActivityMainBinding
    private val TAG = "PIP"

    override fun onCreate(savedInstanceState: Bundle?) {
        Log.d(TAG, "[power cycle] onCreate")
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.mainVideoView.run {
            //영상이 끝나면 영상을 재시작해서 무한반복 시킴.
            setOnCompletionListener {
                it.start()
            }

            //res폴더에 있는 비디오를 비디오뷰에 set시켜준다.
            setVideoURI(Uri.parse("android.resource://$packageName/${R.raw.sample}"))
            start()
        }

        binding.mainPipBtn.setOnClickListener {
            mPipBuilder.setAspectRatio(Rational(binding.mainVideoView.width, binding.mainVideoView.height))
            enterPictureInPictureMode(mPipBuilder.build())
        }
    }
}

 

4. 결과 화면

START PIP MODE를 클릭하면 오른쪽 사진처럼 화면이 작아짐을 볼수있다.

 

추후에 화면이 작아졌을 경우 재생버튼등의 layout등을 표시하고 그에 따른 기능을 구현하는 예제를 추가로 만들어 볼 예정이다. 

728x90
728x90
반응형

안녕하세요.

이번에 안드로이드 개발을 시작하면서 미들웨어의 함수를 호출해야 하는 일이 생겼습니다.

백그라운드는 전부 C++코드이기 때문에 직접 호출이 필요한 경우가 있습니다.

간단하게 호출하는 방법을 소개 하려고 합니다.

 

1. 코드 이름 및 종류

- exam.kt, mw.cpp

 

2. 간단한 소스 

exam.kt

extern fun testNativeCode()

fun call() {
	testNativeCode
}

여기서 위 내용처럼 하면 Android studio에서 native code를 생성 할 것인지 묻는다.
그때 만들어질 위치를 선택해주면 자동으로 생성 한다.

 

mw.cpp

#include "exam.h"

extern "C"
JNIEXPORT void JNICALL
Java_com_mobipintech_zui_MobileMainDialog_testNativeCode(JNIEnv *env, jobject thiz) {
    // TODO: implement at3NativeTestFunc()
    at3NativeTestFunc();
}

위 내용은 Java_com_mobipintech_zui_MobileMainDialog까지는 경로.
TestNativeCode는 함수 이름이다.

 

exam.cpp

void atNativeTestFunc()
{
	printf("TEST 코드 입니다.\n")
}

 

exam.h

void atNativeTestFunc();

 

이렇게 사용할 내용을 c++에 정의 해놓고 코틀린 내부에서 호출해서 직접 사용 하면 됩니다.

 

728x90

+ Recent posts