Android에서 간단한 뮤직 플레이어를 만들었습니다. 각 노래의보기에는 다음과 같이 구현 된 SeekBar가 포함됩니다.
public class Song extends Activity implements OnClickListener,Runnable {
private SeekBar progress;
private MediaPlayer mp;
// ...
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer
progress.setVisibility(SeekBar.VISIBLE);
progress.setProgress(0);
mp = appService.getMP();
appService.playSong(title);
progress.setMax(mp.getDuration());
new Thread(Song.this).start();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
// ...
progress = (SeekBar) findViewById(R.id.progress);
// ...
}
public void run() {
int pos = 0;
int total = mp.getDuration();
while (mp != null && pos<total) {
try {
Thread.sleep(1000);
pos = appService.getSongPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
progress.setProgress(pos);
}
}
이것은 잘 작동합니다. 이제 노래 진행의 초 / 분을 계산하는 타이머가 필요합니다. 나는를 넣어 그래서 TextView
레이아웃에, 그것을 얻을 수 findViewById()
있는 onCreate()
, 및이를 넣어 run()
후 progress.setProgress(pos)
:
String time = String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(pos),
TimeUnit.MILLISECONDS.toSeconds(pos),
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(
pos))
);
currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
그러나 마지막 줄은 나에게 예외를줍니다.
android.view.ViewRoot $ CalledFromWrongThreadException :보기 계층 구조를 만든 원래 스레드 만 해당보기를 만질 수 있습니다.
그러나 나는 기본적으로 여기 SeekBar
에서보기를 만들고 onCreate
나서 만지는 것과 똑같은 일을하고 있으며 run()
나 에게이 불만을 제기하지는 않습니다.
답변
UI를 업데이트하는 백그라운드 작업의 일부를 기본 스레드로 이동해야합니다. 이에 대한 간단한 코드가 있습니다.
runOnUiThread(new Runnable() {
@Override
public void run() {
// Stuff that updates the UI
}
});
에 대한 설명서 Activity.runOnUiThread
.
백그라운드에서 실행중인 메소드 안에 이것을 중첩시키고 블록 중간에 업데이트를 구현하는 코드를 복사하여 붙여 넣으십시오. 가능한 한 적은 양의 코드 만 포함하십시오. 그렇지 않으면 백그라운드 스레드의 목적을 무너 뜨리기 시작합니다.
답변
내가 넣어이 문제를 해결 runOnUiThread( new Runnable(){ ..
내부 run()
:
thread = new Thread(){
@Override
public void run() {
try {
synchronized (this) {
wait(5000);
runOnUiThread(new Runnable() {
@Override
public void run() {
dbloadingInfo.setVisibility(View.VISIBLE);
bar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(),MainActivity.class);
startActivity(mainActivity);
};
};
thread.start();
답변
이것에 대한 나의 해결책 :
private void setText(final TextView text,final String value){
runOnUiThread(new Runnable() {
@Override
public void run() {
text.setText(value);
}
});
}
백그라운드 스레드에서이 메소드를 호출하십시오.
답변
일반적으로 사용자 인터페이스와 관련된 모든 작업은 기본 또는 UI 스레드에서 수행해야합니다. 즉 onCreate()
, 이벤트 처리가 실행됩니다. 이를 확인하는 한 가지 방법은 runOnUiThread ()를 사용하는 것입니다. 하고 다른 방법은 핸들러를 사용하는 것입니다.
ProgressBar.setProgress()
메인 스레드에서 항상 실행되는 메커니즘이 있으므로 작동합니다.
무통 스레딩을 참조하십시오 .
답변
나는이 상황에 있었지만 핸들러 객체로 해결책을 찾았습니다.
제 경우에는 관찰자 패턴 으로 ProgressDialog를 업데이트하고 싶습니다 . 내보기는 관찰자를 구현하고 업데이트 방법을 재정의합니다.
따라서 내 주요 스레드는 뷰를 만들고 다른 스레드는 ProgressDialop 및 ….을 업데이트하는 update 메서드를 호출합니다.
뷰 계층 구조를 만든 원래 스레드 만 해당 뷰를 만질 수 있습니다.
핸들러 오브젝트의 문제점을 해결할 수 있습니다.
아래 코드의 다른 부분 :
public class ViewExecution extends Activity implements Observer{
static final int PROGRESS_DIALOG = 0;
ProgressDialog progressDialog;
int currentNumber;
public void onCreate(Bundle savedInstanceState) {
currentNumber = 0;
final Button launchPolicyButton = ((Button) this.findViewById(R.id.launchButton));
launchPolicyButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDialog(PROGRESS_DIALOG);
}
});
}
@Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading");
progressDialog.setCancelable(true);
return progressDialog;
default:
return null;
}
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog.setProgress(0);
}
}
// Define the Handler that receives messages from the thread and update the progress
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int current = msg.arg1;
progressDialog.setProgress(current);
if (current >= 100){
removeDialog (PROGRESS_DIALOG);
}
}
};
// The method called by the observer (the second thread)
@Override
public void update(Observable obs, Object arg1) {
Message msg = handler.obtainMessage();
msg.arg1 = ++currentPluginNumber;
handler.sendMessage(msg);
}
}
이 설명은 이 페이지 에서 찾을 수 있으며 “두 번째 스레드가있는 예제 ProgressDialog”를 읽어야합니다.
답변
기본 UI 스레드를 방해하지 않고 핸들러를 사용하여보기를 삭제할 수 있습니다. 예제 코드는 다음과 같습니다
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
답변
@providence의 답변을 수락하셨습니다. 만일의 경우에도 핸들러를 사용할 수도 있습니다! 먼저 int 필드를 수행하십시오.
private static final int SHOW_LOG = 1;
private static final int HIDE_LOG = 0;
다음으로 핸들러 인스턴스를 필드로 만듭니다.
//TODO __________[ Handler ]__________
@SuppressLint("HandlerLeak")
protected Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// Put code here...
// Set a switch statement to toggle it on or off.
switch(msg.what)
{
case SHOW_LOG:
{
ads.setVisibility(View.VISIBLE);
break;
}
case HIDE_LOG:
{
ads.setVisibility(View.GONE);
break;
}
}
}
};
방법을 만드십시오.
//TODO __________[ Callbacks ]__________
@Override
public void showHandler(boolean show)
{
handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG);
}
마지막으로 이것을 onCreate()
방법에 넣으십시오 .
showHandler(true);