반응형

최근 블루 스크린이 나서

 

fatal: not a git repository (or any of the parent directories): .git

이런 오류가 깃에서 나기 시작했다

 

별생각없다가 커밋하려고 보니 발생했는데

 

해결책은 간단했다.

 

발생하는 해당 프로젝트 window로 이동 ->

 

git bash 키기 ->

 

rm -f .git/index

git reset

 

차례대로 기입

 

끝!

반응형

'AOS' 카테고리의 다른 글

(안드로이드) 키보드 관련 정리  (0) 2021.06.25
(안드로이드) timer  (0) 2021.06.16
(안드로이드) constraintLayout animation  (0) 2021.06.01
(Android) 트위치 App 3 - retrofit2  (0) 2021.04.19
(Android) 트위치 app 2 - ANR WatchDog  (0) 2021.04.16
반응형

목표 화면

 

 

 

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

    <data>
        <variable
            name="ttsVm"
            type="com.example.layoutpj.ui.tts.vm.TTSViewModel" />
        <variable
            name="frg"
            type="com.example.layoutpj.ui.tts.TTSFragment" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main_constraint"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:id="@+id/testbtn"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@id/top_cover"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toTopOf="@id/btn_container"
            android:background="@null"/>

        <View
            android:id="@+id/top_cover"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_constraintBottom_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:background="@color/sunflower_green_300"/>




        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/btn_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="parent"
            android:focusable="true"
            android:paddingTop="20dp">

            <ImageView
                android:id="@+id/tts_btn_1"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:background="@color/sunflower_yellow_300"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintBottom_toTopOf="@id/tv_positon_count"
                app:layout_constraintHorizontal_chainStyle="spread"
                app:layout_constraintEnd_toStartOf="@id/tts_btn_2"
                android:layout_marginBottom="20dp"/>
            <ImageView
                android:id="@+id/tts_btn_2"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:background="@color/sunflower_white"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toEndOf="@id/tts_btn_1"
                app:layout_constraintBottom_toBottomOf="@id/tts_btn_1"
                app:layout_constraintEnd_toStartOf="@id/tts_btn_3"/>
            <ImageView
                android:id="@+id/tts_btn_3"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:background="@color/sunflower_green_500"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toEndOf="@id/tts_btn_2"
                app:layout_constraintBottom_toBottomOf="@id/tts_btn_1"
                app:layout_constraintEnd_toStartOf="@id/tts_btn_4"/>
            <ImageView
                android:id="@+id/tts_btn_4"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:background="@color/sunflower_black"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toEndOf="@id/tts_btn_3"
                app:layout_constraintBottom_toBottomOf="@id/tts_btn_1"
                app:layout_constraintEnd_toStartOf="@id/tts_btn_5"/>
            <ImageView
                android:id="@+id/tts_btn_5"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:background="@color/sunflower_yellow_300"
                app:layout_constraintStart_toEndOf="@id/tts_btn_4"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="@id/tts_btn_1"
                app:layout_constraintEnd_toEndOf="parent" />
            <TextView
                android:id="@+id/tv_positon_count"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
                android:textColor="@color/black"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:text="@{String.valueOf(ttsVm.currentPosition)}"
                android:background="@color/white"/>
        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

- top_cover와 btn_container를 위아래로 숨기고 보이고 할것이다.

 

 

    val constraintSet1 = ConstraintSet()
    val constraintSet2 = ConstraintSet()
    private fun initAnim(){
        //바뀔 layout
        constraintSet2.clone(binding.mainConstraint)
        constraintSet2.clear(R.id.btn_container,ConstraintSet.TOP)
        constraintSet2.clear(R.id.btn_container,ConstraintSet.BOTTOM)
        constraintSet2.connect(R.id.btn_container,ConstraintSet.TOP,ConstraintSet.PARENT_ID,ConstraintSet.BOTTOM)
        constraintSet2.clear(R.id.top_cover,ConstraintSet.BOTTOM)
        constraintSet2.clear(R.id.top_cover,ConstraintSet.TOP)
        constraintSet2.connect(R.id.top_cover,ConstraintSet.BOTTOM,ConstraintSet.PARENT_ID,ConstraintSet.TOP)


        //원본 layout
        constraintSet1.clone(binding.mainConstraint)
        constraintSet1.clear(R.id.btn_container,ConstraintSet.BOTTOM)
        constraintSet1.clear(R.id.btn_container,ConstraintSet.TOP)
        constraintSet1.connect(R.id.btn_container,ConstraintSet.BOTTOM,ConstraintSet.PARENT_ID,ConstraintSet.BOTTOM)
        constraintSet1.clear(R.id.top_cover,ConstraintSet.TOP)
        constraintSet1.clear(R.id.top_cover,ConstraintSet.BOTTOM)
        constraintSet1.connect(R.id.top_cover,ConstraintSet.TOP,ConstraintSet.PARENT_ID,ConstraintSet.TOP)
    }

- clone 대상은 parent view의 constraint

- 이전 제약조건을 제거한뒤 ( clear ) 새 제약조건을 준다 ( connect )

 

    var isChange = true
    private fun toggleAnim(){
        val at = ChangeBounds()
        at.addListener(object :Transition.TransitionListener{
            override fun onTransitionStart(transition: Transition) {

            }
            override fun onTransitionEnd(transition: Transition) {
              Log.e("asdf","go")
                (requireActivity() as TTSMainActivity).toggle()
            }
            override fun onTransitionCancel(transition: Transition) {}
            override fun onTransitionPause(transition: Transition) {}
            override fun onTransitionResume(transition: Transition) {}
        })
        TransitionManager.beginDelayedTransition(binding.mainConstraint,at)
        val ctr =  if (isChange) constraintSet1 else constraintSet2
        ctr.applyTo(binding.mainConstraint)
        isChange = !isChange
    }

 

반응형

'AOS' 카테고리의 다른 글

(안드로이드) timer  (0) 2021.06.16
(안드로이드) git 꼬엿을때.  (0) 2021.06.02
(Android) 트위치 App 3 - retrofit2  (0) 2021.04.19
(Android) 트위치 app 2 - ANR WatchDog  (0) 2021.04.16
(안드로이드)채팅 구현하기  (0) 2021.01.14
반응형

개요

 

비동기 network 환경에 최적화되어있는 라이브러리.

 

live data, room ,viewModel 패턴과 함께 사용.

 

GOOGLE에서 정식으로 채택한 NETWORK LIBRARY

 

 

공식 사이트

square.github.io/retrofit/

 

Retrofit

A type-safe HTTP client for Android and Java

square.github.io

 

구성

 

gradle

    //[network] Retrofit, Gson, RxAdapter
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
    implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"

    //[network] okhttp
    implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
    implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"

- version 경우 def로 버전 string값 정의 하면됨.

 

Result.kt

sealed class Result<out T: Any>{
    data class Success<out T : Any>(val data: T) : Result<T>()
    data class Error(val exception: String) : Result<Nothing>()
}

- network response object

 

MusicApiService.kt

public interface MusicApiService{
    @GET("/2020-flo/song.json")
    suspend fun fetchSong():Response<Song>
}

- 자기 통신에 맞는 uri interface 정의

 

NetworkContainer.kt

public class NetworkContainer {
    private val interceptor = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    }
    private val client:OkHttpClient by lazy {
         OkHttpClient.Builder()
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            .addNetworkInterceptor(interceptor)
//                .addInterceptor(new AddCookiesInterceptor(getApplicationContext()))
//                .addInterceptor(new ReceivedCookiesInterceptor(getApplicationContext()))
            .build()
    }
    private val retrofit:Retrofit by lazy{
        Retrofit.Builder()
            .baseUrl(NetworkContainer.MEDIA_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    fun getApiService():MusicApiService{
        return retrofit.create(MusicApiService::class.java)
    }
    suspend fun <T : Any> safeApiCall(call: suspend () -> Response<T>): Result<T> {
        return try {
            val myResp = call.invoke()
            if (myResp.isSuccessful) {
                Result.Success(myResp.body()!!)
            } else {
                Result.Error(myResp.errorBody()?.string() ?: "Something goes wrong")
            }
        } catch (e: Exception) {
            Result.Error(e.message ?: "Internet error runs")
        }
    }
    public suspend fun fetchSong():Result<Song>{
        return this.safeApiCall(call = { getApiService().fetchSong() } )
    }

    companion object{
        val CONNECT_TIMEOUT = 5000L
        val WRITE_TIMEOUT = 5000L
        val READ_TIMEOUT = 5000L
        val MEDIA_URL = "custom url"
    }
}

- getApitService()로 미리 정의 해둔 api interface를 전달.

 

- 컴파일시 정확한 내용 전달을 위해 safeApiCall()로 한번 감싸줌.

 

- fetchSong 부분이 실제적인 호출 부분.

 

SongViewModel.kt

class SongViewModel(val networkContainer:NetworkContainer) :ViewModel(){
    private val song: MutableLiveData<Song> by lazy {
        MutableLiveData<Song>()
    }
    fun getSong(): LiveData<Song> {
        return song
    }
    val errorMessage: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

    fun fetchSong() {
        viewModelScope.launch {
            val result = networkContainer.fetchSong()
            when(result){
                is Result.Success ->{
                    song.postValue(result.data)
                }
                is Result.Error -> {
                    errorMessage.postValue(result.exception)
                }
            }
        }
    }
}

- ViewModel을 상속받은 클래스로 위에서 정의했던 NetworkContainer가저옴.

- fetchSong()으로 결과값 가저오기.

- when .. is 조합으로 타입 검사후 결과값이 Success인지 Error인지 구분함

- postValue를 통해 observe하고있는 class쪽에 알림

 

 


추가옵션

viewModel사용시 provider를 생성하지않고 viewModels를 사용해 객체를 생성할때 parameter를 넘기려면 아래같이 작업해 줘야한다.

 

SongViewModelFactory.kt

class SongViewModelFactory(val networkContainer: NetworkContainer) : ViewModelProvider.Factory{
    override fun <T : ViewModel?> create(modelClass: Class<T>): T =  modelClass.getConstructor(networkContainer::class.java).newInstance(networkContainer)
}

 

Activity

 /**
     * network viewmodel
     */
    val songVM by viewModels<SongViewModel>{
        SongViewModelFactory(networkContainer)
    }



 //observe network viewModel
 	songVM.getSong().observe(this, Observer {

	})

 

반응형

'AOS' 카테고리의 다른 글

(안드로이드) git 꼬엿을때.  (0) 2021.06.02
(안드로이드) constraintLayout animation  (0) 2021.06.01
(Android) 트위치 app 2 - ANR WatchDog  (0) 2021.04.16
(안드로이드)채팅 구현하기  (0) 2021.01.14
(안드로이드) jobService  (0) 2020.10.15
반응형

어디에 쓰이는가?

  • android의 ANR(애플리케이서 응답 없음)에러를 핸들링하는 라이브러리로 추측.

 

GIT

github.com/SalomonBrys/ANR-WatchDog

 

SalomonBrys/ANR-WatchDog

A simple watchdog that detects Android ANR (Application Not Responding) error and throws a meaningful exception - SalomonBrys/ANR-WatchDog

github.com

gradle

implementation 'com.github.anrwatchdog:anrwatchdog:1.4.0'
반응형
반응형

결과

실물

html


객체 

    function MyDataa(mkey,mtitle,mvalue){
        this.key = mkey
        this.title = mtitle
        this.value = mvalue
    }

 

더미데이터

    var mmList = [
        new MyDataa('key1','제목1','values1'),
        new MyDataa('key2','제목2','valueqs'),
        new MyDataa('key3','제목w 3','valuest'),
        new MyDataa('key4','제목4','valu123 es'),
        new MyDataa('key5','제목5','valuaa es'),
        new MyDataa('key6','제목6','valu des'),
        new MyDataa('key7','제목7',['m1','m2','m3']),
        new MyDataa('key8','제목8','valuasdes')
    ]

 

테이블만들기

  //@param stringSelector: #example , listData: [MyDataa,MyDataa,.. ]
    function makeTable(stringSelector ,listData){
        var d = document.querySelector(stringSelector)
        while (d.firstChild) {
            d.removeChild(d.firstChild);
        }

        var colnum = 4;
        var tbl = document.createElement('table');
        tbl.setAttribute('text-align', 'center')
        tbl.style.width = '100%'
        tbl.border = 1
        var colgroup = document.createElement('colgroup')
        var tbody = document.createElement('tbody')
        for (var i = 0; i<colnum; i++){
            var col = document.createElement('col')
            col.attributes.width = '25%'
            colgroup.appendChild(col)
        }
        var tr = document.createElement('tr')
        listData.forEach(function(e,i){
            var tTD = document.createElement('td')
                tTD.appendChild(document.createTextNode(e.title))
                var vTD = document.createElement('td')
                if (Array.isArray(e.value)){
                    var ultag = document.createElement('ul')
                    e.value.forEach((arrayValue)=>{
                        var litag = document.createElement('li')
                        litag.appendChild(document.createTextNode(arrayValue))
                        ultag.appendChild(litag)
                    })
                    vTD.appendChild(ultag)
                } else {
                    vTD.appendChild(document.createTextNode(e.value))
                }
                
                tr.appendChild(tTD)
                tr.appendChild(vTD)
            if (e.key === 'key7'||e.key === 'key8'){
                vTD.setAttribute('colSpan', '3')
                tbody.appendChild(tr)
                tr = document.createElement('tr')
            } else {
                if(i%2 != 0){
                    tbody.appendChild(tr)
                    tr = document.createElement('tr')
                }
            }
        })
        tbl.appendChild(colgroup)
        tbl.appendChild(tbody)
        d.appendChild(tbl)
    }

- value 값의 object type에 따라 list 타입이면 ul ,li 수직으로 표현함.

 

사용

<body>
	<div id="showme"></div>
</body>
makeTable('#showme',mmList);
반응형
반응형
파일 업로드

controller

//(작성) 제출 api
@RequestMapping(value = "/submitVerification.do", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Object> submitVerification(VerifyReportSubmitDTO formData) {
  try {
    int fileCount = veriReportService.saveSubmitReportData(formData);
    if (fileCount > 0) {
    	return ResponseEntity.ok()
     	.body(VerifyResContainer.Success.body("200", "ok"));
  	}
  } catch(Exception e) {
    return ResponseEntity.status(HttpStatus.BAD_REQUEST)
     .body(VerifyResContainer.Error.errorMsg("저장중 오류가 발생했습니다."));
  }
    return ResponseEntity.status(HttpStatus.BAD_REQUEST)
     .body(VerifyResContainer.Error.errorMsg("데이터를 찾지 못했습니다."));
}

request vo

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class VerifyReportSubmitDTO implements Serializable{
    private static final long                                 serialVersionUID = 1772260604963268207L;
	private String project_id;
	private int verify_sn;
	private int report_degree;
	private List<MultipartFile>  excel_data;
	private List<MultipartFile> raw_data_list;
	private String json_oss_data_list;
}

- List multipart 형태와 string 형태가 통신하는 모습.

 

 

 

파일 다운로드
// file_id 기준 존재하는 파일 다운로드
@RequestMapping(value = "/fileDownload.do", method = RequestMethod.GET)
public ResponseEntity<Resource> fileDownload(@RequestParam String file_id) throws IOException {
    	//파일에 실제 경로를 가저오는 작업
		VerifyFileVO vo = veriReportService.getFileVerifyFile(file_id);
		String str = VerifyFileVO.getMyFileFullPath(vo);
        
        //파일 생성
	    File f = new File(str);
	    InputStream is = FileUtils.openInputStream(f);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        headers.add(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=\"" + f.getName() + "\"");
		return ResponseEntity.ok()
			    .headers(headers)
	            .contentType(MediaType.APPLICATION_OCTET_STREAM)
	            .body(new InputStreamResource(is));
}

 

 

 

 

 

 

반응형
반응형
  • 필요한 기술

1. 리사이클 뷰.

2. google fcm push서버에 http통신.

3. firebase realtimeDB 쿼리문 가저오기. 

4. background 상태 확인.

 

DB구성

  • ChatList: 사용자간에 채팅 기록을 저장.

 

  • receiver와 sender로 사용자를 구분함.

 

 

  • fcmToken: 해당유저의 현재 fcm토큰을 저장.

 

 1. 리사이클 뷰.

 


2. google fcm push서버에 http통신.

 

  1.기존

firebase.google.com/docs/cloud-messaging/http-server-ref?hl=ko

 

Firebase 클라우드 메시징 HTTP 프로토콜

firebase.ml.naturallanguage.translate

firebase.google.com

 

Request

Header

{
	"Content-Type:application/json",
	"Authorization:key=YOUR_FCM_SERVER_KEY"
}

Body

{
    "to":"your-send-firebasekey"
    ,"data":{
     "title":"1234",
     "body":"asd",
     "key_1":"1234",
     "key_2":"1234"   
    }
}
  • data object 안쪽 데이터 구성은 원하는 데이터로 구성하면 된다.

Response

{
    "multicast_id": 123456781234,
    "success": 1,
    "failure": 0,
    "canonical_ids": 0,
    "results": [
        {
            "message_id": "0:129831yh2-eubsd9fh23093j234"
        }
    ]
}

 

 

 

  2. 새버전

firebase.google.com/docs/cloud-messaging/migrate-v1?hl=ko

 

이전 HTTP에서 HTTP v1로 이전  |  Firebase

FCM의 이전 HTTP API를 사용하는 앱은 이 가이드의 안내에 따라 HTTP v1 API로 마이그레이션해야 합니다. HTTP v1 API는 이전 API에 비해 다음과 같은 장점이 있습니다. 액세스 토큰을 통한 보안 향상: HTTP v

firebase.google.com

 

firebase.google.com/docs/cloud-messaging/concept-options?hl=ko#customizing_a_message_across_platforms

 

FCM 메시지 정보  |  Firebase

Firebase 클라우드 메시징(FCM)은 다양한 메시징 옵션과 기능을 제공합니다. 이 페이지의 정보는 다양한 유형의 FCM 메시지에 관한 이해를 돕고 FCM으로 구현할 수 있는 기능을 소개하기 위한 내용입

firebase.google.com

  • 새버전은 google api 라이브러리를 받아서 써야한다.
		<dependency>
	    	 <groupId>com.google.api-client</groupId>
	    	 <artifactId>google-api-client</artifactId>
	    	 <version>1.30.10</version>
	   </dependency>

		<dependency>
		    <groupId>com.google.code.gson</groupId>
		    <artifactId>gson</artifactId>
		    <version>2.8.6</version>
		</dependency>

 

 

  • fcm 서버로 부터 인증토큰을 받아온다.
  private static String getAccessToken() throws IOException {
    GoogleCredential googleCredential = GoogleCredential
        .fromStream(new FileInputStream("YOUR_JSON_FILE_LOCATION"))
        .createScoped(Arrays.asList(SCOPES));
    googleCredential.refreshToken();
    return googleCredential.getAccessToken();
  }

 

 

  • 받은 토큰을 header key 중 Authorization의 값으로 사용한다.
  private static HttpURLConnection getConnection() throws IOException {
    // [START use_access_token]
    URL url = new URL(BASE_URL + FCM_SEND_ENDPOINT);
    HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
    httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken());
    httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
    return httpURLConnection;
    // [END use_access_token]
  }

 

 

  • 위에서 setting한 데이터로 통신을 보낸다.
  /**
   * Send request to FCM message using HTTP.
   *
   * @param fcmMessage Body of the HTTP request.
   * @throws IOException
   */
  private static void sendMessage(JsonObject fcmMessage) throws IOException {
    HttpURLConnection connection = getConnection();
    connection.setDoOutput(true);
    DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
    outputStream.writeBytes(fcmMessage.toString());
    outputStream.flush();
    outputStream.close();

    int responseCode = connection.getResponseCode();
    if (responseCode == 200) {
      String response = inputstreamToString(connection.getInputStream());
      System.out.println("Message sent to Firebase for delivery, response:");
      System.out.println("responseCode: "+responseCode);
      System.out.println(response);
    } else {
      System.out.println("Unable to send message to Firebase:");
      String response = inputstreamToString(connection.getErrorStream());
      System.out.println("responseCode: "+responseCode);
      System.out.println(response);
    }
  }

 

Request

{
  "message": {
    "notification": {
      "title": "FCM Notification",
      "body": "Notification from FCM"
    },
    "token": "YOUR_DEVICE_FCM_TOKEN"
  }
}
  • notification 값은 별도의 FirebaseMessagingService.class 구현없이도 알림이 가능하다.

Response

{ 
	"name": "projects/YOUR_PROJECT_ID/messages/0:1610703319299158%f3fd72f7f3fd72f7"
}

 


3. firebase realtimeDB 쿼리문 가저오기. 

 

firebase.google.com/docs/database/android/start?hl=ko

 

Android에서 설치 및 설정  |  Firebase 실시간 데이터베이스

Firebase에 앱 연결 아직 추가하지 않았다면 Android 프로젝트에 Firebase를 추가합니다. 데이터베이스 만들기 Firebase 프로젝트를 아직 만들지 않았다면 Firebase Console에서 프로젝트 추가를 클릭한 후 화

firebase.google.com

 


4. background 상태 확인.

 

구현

public class CustomApplication extends Application implements Application.ActivityLifecycleCallbacks {

    public static boolean isBackground = true;
    private static int numStarted = 0;

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

    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) { }
    @Override
    public void onActivityStarted(@NonNull Activity activity) {
        if (numStarted == 0) {
            // app went to foreground
            isBackground = false;
        }
        numStarted++;
    }
    @Override
    public void onActivityResumed(@NonNull Activity activity) { }
    @Override
    public void onActivityPaused(@NonNull Activity activity) { }
    @Override
    public void onActivityStopped(@NonNull Activity activity) {
        numStarted--;
        if (numStarted == 0) {
            // app went to background
            isBackground = true;
        }
    }
    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) { }
    @Override
    public void onActivityDestroyed(@NonNull Activity activity) { }
}
  • - isBackground로 구분 가능.
반응형

'AOS' 카테고리의 다른 글

(Android) 트위치 App 3 - retrofit2  (0) 2021.04.19
(Android) 트위치 app 2 - ANR WatchDog  (0) 2021.04.16
(안드로이드) jobService  (0) 2020.10.15
(안드로이드) 인앱결제(billingClient v3)  (0) 2020.10.08
(android)pinch zoom imageview  (0) 2020.09.03
반응형

- 반복작업에 좋음(작업의 텀이 긴경우)

- 설정한 시간에 따라 정확히 동작하진 않는다.

 

manifest

        <service android:name=".job.CouponJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="true"/>

- android.permission.BIND_JOB_SERVICE 는 필수 퍼미션이다.

 

run jobService

        val COUPON_JOB_SERVICE_ID = 1253

        fun makeAJob(context: Context ,jobID:Int ,  timeInterval:Long = TimeUnit.HOURS.toMillis(2)) {
            val job =  context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler?
            job?.cancelAll()//설정한 이전작업들을 취소함.
            val componentName = ComponentName(context, CouponJobService::class.java)
            job!!.schedule(
                JobInfo.Builder(jobID, componentName)
                    .setRequiresDeviceIdle(false)// idle 상태일때만 작동
                    .setRequiresCharging(false)//충전중일때만 작동
                    .setPeriodic(timeInterval)  //15분이상으로 설정해야된다.
                    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//네트워크 타입을 설정해 작동시킬수 있다.
                    .build()
            )
        }

 

make Job

class MyJobService :JobService(){
    override fun onStopJob(params: JobParameters?): Boolean {
    
        //처음 설정했던 job설정에 맞다고 판단하면 true리턴해 작업을 재시작하면된다.
        //아니라면 false
        return true
    }

    override fun onStartJob(params: JobParameters?): Boolean {
   
   		.....
        // long term work (network....)
        ....
        
        //긴작업 이후엔 잡이 끝낫음을 알린다.
        jobFinished(params,false)
        
   		//true인 경우 해당작업을 반복한다.
        return true
    }


}
반응형

+ Recent posts