마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Fragment Thread 실행 질문

0 추천

private Thread thread;

Fragment의 onViewCreated() 안에서

thread = new Thread(() -> {
    while (threadFlag) {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String secStr = "", minStr = "";
                sec = MainActivity.m_ActiveCallItem.m_nCallTimeoutCount % 60;
                min = MainActivity.m_ActiveCallItem.m_nCallTimeoutCount / 60;

                if (sec < 10)
                    secStr = "0" + sec;
                else
                    secStr = String.valueOf(sec);


                if (min < 10)
                    minStr = "0" + min;
                else
                    minStr = String.valueOf(min);

                String callTime = minStr + ":" + secStr;
                binding.txtCallingTime.setText(callTime);
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

thread를 생성후

Fragment의

public void updateCallInformation(int status, CallItem item) {} 란 함수를 MainAcitivity에서 호출해서

switch (status) {
    case NameSpace.LEG_STATE_INVITING:
    binding.txtCallStateContext.setText(getString(R.string.CALL_INVITING));
        break;

    case NameSpace.LEG_STATE_OFFERING:
        callingFlag = true;
        binding.txtCallStateContext.setText(getString(R.string.CALL_INVITING));
        binding.btnGuardCall.setText(getString(R.string.CALL));
        break;

    case NameSpace.CALL_LEG_STATE_CONNECTED:
        callingFlag = true;
        binding.txtCallStateContext.setText(mContext.getString(R.string.CALL_CONNECTED));
        thread.start();   //시간 표시 thread start
        break;

    case NameSpace.CALL_LEG_STATE_DISCONNECTED:
        if (callingFlag) {
            threadFlag = false;
            thread.interrupt();

            callingFlag = false;

            m_sipCallState = NameSpace.SIP_CALL_LEG_STATE_UNDEFINED;
            navController.popBackStack();
        }
        break;
}

이렇게 따로 분기처리후 thread를 실행해주려하는데 thread가 실행되지 않습니다.

제가 간과하고 있는 부분이 있는지?

 

Otuka (170 포인트) 님이 2021년 11월 4일 질문

1개의 답변

0 추천
 
채택된 답변

액티비티에서 프레그먼트를 접근하는 방법은 아랫처럼, FragmentManager.findFragmentBy를 통해

val fragment: ExampleFragment =
        supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment

해당 프레그먼트가 존재하는 걸 확인한 다음에 프레그먼트에 있는 멤버에 접근할 수 있습니다.

님의 경우에는 이 방법이 권장해도 되는 방법인지 아닌지 모르겠네요. 이유는 프레그먼트에서 실행하는 쓰레드 때문인데요, 쓰레드를 사용할 때는 라이프사이클 등을 주의해서 코드를 짜셔야 합니다. 현재 쓰레드가 무한루프를 도는데, 만약에 앱이 백그라운드로 가게 되더라도 쓰레드는 쓸데없이 계속 돌게 될 것으로 보이구요, 해당 프레그먼트가 종료되었을 경우에도 쓰레드가 종료되지 않고 남아 있을 것 같은 생각이 드네요. 이렇게 되면 결국 메모리 누수가 나게 되고 문제가 생깁니다. 
그리고 쓰레드 안에서 Thread.sleep를 사용하셨는데, 이 메소드는 blocking이라 UI를 얼게 만들기 때문에 사용하지 말라고 하는 메소드 중의 하나입니다. 쓰레드 대신에 Timer 같은 클래스를 고려해 보시는 것도 좋을 것 같습니다.

그리고 가능하다면, UI와 관련된 로직은 UI로부터 분리해서 작성하는게 일반적인 원칙입니다. 그리고 observer 패턴을 사용하면 이벤트가 발생할 때 알 수 있으므로 굳이 액티비티가 프레그먼트의 메소드를 직접 액세스할 필요성이 없어지거나 확 줄어듭니다. 

 

public class CallTimer {
   public interface Listener {
        void onStarted();
        void onStopped();
        void onProgress(String callTime);
   }

 private final Set<Listener> listeners = new HashSet<>();
   
  public void addListener(Listener listener) {
     listeners.add(listener);
  }

  public void removeListener(Listener listener) {
     listeners.remove(listener);
  }

  public void start() {
     // 쓰레드 로직. 쓰레드 안에서 lisetner.onProgress() 호출
     
     for (Listener listener : listeners) {
          lisetner.onStarted();
     }
  }

  public void stop() {
        for (Listener listener : listeners) {
          lisetner.onStopped();
     }
  }

  // Singleton이 필요할 경우 생성자를 private로 만들고 getInstance() 메소드를 사용.
  public static CallTimer getInstance() {
     return Impl.INSTANCE;
  }

  private static class Impl {
       private CallTimer INSTANCE.= new CallTimer();
  }
}

// Activity
public void onStart(...)  {
    super.onStart(..)
   CallTimer.getInstance().start();
}

public void onStop(...)  {
    super.onStop(..)
   CallTimer.getInstance().stop();
}


// Fragment
public ExampleFramment implements CallTimer.Lsitener {
    public void onStart(...) {
        super.onStart(...);
        CallTimer.getInstance().addListener(this);
    }

    public void onStop(...) {
        super.onStop(...);
        CallTimer.getInstance().removeListener(this);
    }

    // CallTimer.Lsitener 메소드 구현 생략
}

 

위처럼 Call timer를 처리하는 클래스를 하나 만들고 observer를 등록해서 시간변경에 대한 이벤트를 받을 수 있을 것 같고, 액티비티와 프레그먼트 간에 동일한 인스턴스를 공유하면 액티비티가 프레그먼트에 직접 접근하지 않아도 될 것 같습니다.

 위의 코드가 기본적인 아이디어이니 참고하셔서 개선된 구조를 가져가시기 바랍니다.

spark (224,800 포인트) 님이 2021년 11월 4일 답변
Otuka님이 2021년 11월 5일 채택됨
...