ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 마스크 구매 알림이 개발기 - 1
    안드로이드 2020. 3. 11. 10:48

    요즘 좀 시간이 남아돌아 온라인 마스크 구매 알림이를 개발해보기로 마음먹었다.

     

    오프라인은 있으니 온라인으로 타겟을 잡았다.

     

    일단 마스크를 어디서 주로 판매하는지 알아보니 네이버 스마트 스토어에서 많이 판매하고 있었다.

    오호.. 구매하기 버튼이 활성화 되있는지만 주기적으로 검사해주면 될 것 같았다.

     

    여기까지 내가 해야 할 일을 정리하자면 아래와 같았다.

     - 구매하기 버튼 활성화 여부 검사 방법

     - 안드로이드 웹 파싱 소스코드 작성

     - 안드로이드 웹 파싱 소스코드 작성

     - 안드로이드 웹 파싱 소스코드 작성

     - ...

     

    사실 안드로이드에서 파싱만 할 수 있다면 다 끝날 것 같았다.

    하지만 인간의 욕심은 끝이 없으니.. 개발해갈수록 더 많은 부기능을 요구하게 됐다.

     

    어쨌든, 일단 구매하기 버튼 활성화 여부를 어떻게 판단해야할지 먼저 알아봤다.

     

    뭔가 내 감으로는 disable이라던가 이런 내용이 들어간 클래스로 활성화 여부를 구분하지 않을까 싶었다.

    마스크를 판매하는 페이지에 들어가 개발자 도구로 바로 확인해 보았다.

     

    이게 비활성화된 구매 버튼
    이게 활성화된 구매 버튼

    그럼 그렇지 _buy_button이라는 class가 포함되있으면 활성화된 구매 버튼으로 보면 될 것 같다.

     

    그렇게 나온 코드이다. parsePage 배열에는 파싱할 마스크 판매 페이지들이 여러개 들어있다.

    그리고 인터넷 관련 작업은 별도 쓰레드 내부에서 try catch문과 함께 수행시켜주어야 한다.

    Document doc = Jsoup.connect(parsePage[i]).get();
    Element buybtn = doc.select("a[title=구매하기]").first();
    
    if(buybtn != null) {
    
    	String buyEnable = buybtn.attr("class");
    	isMaskEnable = buyEnable.contains("_buy_button _click");
    
    	if (isMaskEnable) {
    		// 마스크 구매 가능!
    	}
    }

    Jsoup 이라는 라이브러리를 사용해 파싱하였다. 아래와 같이 gradle에서 추가할 수 있다.

    implementation 'org.jsoup:jsoup:1.10.3'

    2번째 줄에서 Jsoup.connect 메소드를 이용해 파싱할 페이지에 접근 후, 해당 페이지에서 가져온 html 코드 중 a 태그의 title 속성이 "구매하기" 인 객체를 가져온다.

     

    못 가져올 경우를 대비해 null이 아닌지 체크를 해주고 null이 아니면 가져온 객체의 class 속성을 String 형식으로 가져온다.

    그리고 class 중에 _buy_button _click 이라는 문장이 포함된다면 ---> 마스크가 구매 가능한 상태다!

     

    여기까지 오는길은 아주 순조로웠다. 이제 UI 작업에 들어가보자.

     

    일단 UI는 최대한 간단하게 작성해보았다.

     

    아이 이쁘다

    초기 또는 마스크를 구매하지 못하는 상태일 경우엔 빨간 배경

    -> 구매가 가능하면 초록색으로 바뀌면서 "No Masks" 글자가 "Mask Available" 이라는 글자로 바뀜

     

    이렇게 만들고 싶었다.

     

    AsyncTask 내부에선 UI 변경 작업을 할 수 없으므로 따로 함수로 분리해 해당 함수를 AsyncTask 내부에서 호출하는 방식으로 UI에 변화를 주었다.

    위에 있던 구매 가능여부 판단 코드에서 isMaskEnable 변수를 넘겨주면서 호출하면 된다.

    private void changeBackgroundColor(boolean enable){
    	if(enable) {
    		mainBackground.setBackgroundColor(Color.parseColor("#4caf50")); //초록으로
    	}
    	else{
    		mainBackground.setBackgroundColor(Color.parseColor("#ff4c4c")); //빨강으로
    	}
    	return;
    }
    private void changeMaskText(boolean enable){
    	if(enable) {
    		maskAvail.setText("Mask Available"); // 마스크 가능(?)
    	}
    	else{
    		maskAvail.setText("No Masks"); // 마스크 없음
    	}
    	return;
    }

     

    AsyncTask는 하나의 객체로 여러번 실행이 안된다 해서 가운데에 있는 refresh 버튼을 누를 때마다 AsyncTask 객체를 새로 만들어 여러번 동작도 가능하게 만들었다. 그래도 뭔가 찝찝했다.

     

    '이미 하나의 AsyncTask 객체가 수행중인데 새로운게 중간에 또 수행되면 메모리에도 부하가 가고 사용자 입장에서 불편할 수 있지 않을까?'

     

    그래서 코드를 아래와 같이 수정해 주었다.

    ///// AsyncTask 수행 시작할때
    @Override
    protected void onPreExecute() {
    	isParsingFinished = false;
    	super.onPreExecute();
    }
    
    ///// AsyncTask 수행 끝날때
    @Override
    protected void onPostExecute(Void result){
    	isParsingFinished = true;
    }
    
    ///// 가운데 refresh 버튼 클릭 이벤트
    View.OnClickListener onClickListener = new View.OnClickListener() {
    	@Override
    	public void onClick(View view) {
    		switch(view.getId()){
    			case R.id.refresh:
    				if(isParsingFinished) { ///// AsyncTask 하던게 끝나면
    					final JsoupAsyncTask jsoupAsyncTaskchild = new JsoupAsyncTask();
    					jsoupAsyncTaskchild.execute();
    				}
    		}
    	}
    };

    boolean 변수 하나를 이용해 AsyncTask가 시작할때와 끝날때를 체크하고, 앞의 동작이 끝났을 때 새로운 객체를 만들어 시작하게 하였다.

    댓글

Designed by Tistory.