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);
하지만 그 마지막 줄은 저에게 예외입니다.
안드로이드.뷰잘못된 스레드에서 호출된 루트 $보기 예외:보기 계층 구조를 만든 원래 스레드만 해당 보기를 터치할 수 있습니다.
하지만 저는 여기서 제가 하는 것과 기본적으로 같은 일을 하고 있습니다.SeekBar
에서 보기 onCreate
그러고 나서 그것을 만집니다.run()
그리고 그것은 저에게 이런 불만을 주지 않습니다.
UI를 업데이트하는 백그라운드 작업의 일부를 메인 스레드로 이동해야 합니다.이를 위한 간단한 코드가 있습니다.
runOnUiThread(new Runnable() {
@Override
public void run() {
// Stuff that updates the UI
}
});
에 대한 설명서.
백그라운드에서 실행 중인 메서드 내부에 이를 중첩한 다음 블록 중간에 업데이트를 구현하는 코드를 복사하여 붙여넣기만 하면 됩니다.가능한 한 적은 양의 코드만 포함합니다. 그렇지 않으면 백그라운드 스레드의 목적을 무시하기 시작합니다.
저는 이것을 퍼팅으로.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);
}
});
}
백그라운드 스레드에서 이 메서드를 호출합니다.
코틀린 코루틴은 다음과 같이 코드를 더 간결하고 읽기 쉽게 만들 수 있습니다.
MainScope().launch {
withContext(Dispatchers.Default) {
//TODO("Background processing...")
}
TODO("Update UI here!")
}
또는 그 반대:
GlobalScope.launch {
//TODO("Background processing...")
withContext(Dispatchers.Main) {
// TODO("Update UI here!")
}
TODO("Continue background processing...")
}
일반적으로 사용자 인터페이스와 관련된 모든 작업은 메인 스레드 또는 UI 스레드에서 수행되어야 합니다.onCreate()
이벤트 처리가 실행됩니다.이를 확인하는 한 가지 방법은 runOnUiThread()를 사용하는 것이고 다른 방법은 Handlers를 사용하는 것입니다.
ProgressBar.setProgress()
메인 스레드에서 항상 실행되는 메커니즘이 있기 때문에 작동했습니다.
무통 스레드를 참조하십시오.
기본 UI 스레드를 방해하지 않고 처리기를 사용하여 보기를 삭제할 수 있습니다.다음은 예제 코드입니다.
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
코틀린 앤서
우리는 진정한 방식으로 작업을 위해 UI 스레드를 사용해야 합니다.다음과 같은 UI 스레드를 Kotlin에서 사용할 수 있습니다.
runOnUiThread(Runnable {
//TODO: Your job is here..!
})
이런 상황에 처했지만 핸들러 오브젝트로 해결책을 찾았습니다.
저의 경우 관찰자 패턴으로 진행률 대화 상자를 업데이트하고 싶습니다.내 보기는 관찰자를 구현하고 업데이트 방법을 재정의합니다.
그래서 내 메인 스레드는 보기를 만들고 다른 스레드는 진행률 대화 상자를 업데이트하는 업데이트 메서드를 호출합니다.:
보기 계층 구조를 만든 원래 스레드만 해당 보기를 터치할 수 있습니다.
Handler Object로 문제를 해결할 수 있습니다.
다음은 내 코드의 다른 부분입니다.
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);
}
}
이 설명은 이 페이지에서 찾을 수 있으며, "두 번째 스레드가 있는 진행률 대화 상자 예제"를 읽어야 합니다.
저는 당신이 @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);
이 코드를 사용하면 다음을 수행할 필요가 없습니다.runOnUiThread
함수:
private Handler handler;
private Runnable handlerTask;
void StartTimer(){
handler = new Handler();
handlerTask = new Runnable()
{
@Override
public void run() {
// do something
textView.setText("some text");
handler.postDelayed(handlerTask, 1000);
}
};
handlerTask.run();
}
저도 비슷한 문제가 있었고, 제 해결책은 추악하지만, 효과가 있습니다.
void showCode() {
hideRegisterMessage(); // Hides view
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
showRegisterMessage(); // Shows view
}
}, 3000); // After 3 seconds
}
저는 비슷한 문제에 직면해 있었고 위에서 언급한 어떤 방법도 저에게 효과가 없었습니다.결국, 이것은 저에게 효과가 있었습니다.
Device.BeginInvokeOnMainThread(() =>
{
myMethod();
});
여기서 이 보석을 찾았어요.
사용합니다Handler
와 함께Looper.getMainLooper()
저한테는 잘 됐어요.
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// Any UI task, example
textView.setText("your text");
}
};
handler.sendEmptyMessage(1);
이것은 분명히 오류를 던지고 있습니다.보기를 만든 스레드만 해당 보기를 터치할 수 있다고 말합니다.작성된 보기가 해당 스레드의 공간 안에 있기 때문입니다.보기 생성(GUI)은 UI(메인) 스레드에서 발생합니다.따라서 항상 UI 스레드를 사용하여 이러한 메서드에 액세스합니다.
위 그림에서 진행률 변수는 UI 스레드의 공간 안에 있습니다.따라서 UI 스레드만 이 변수에 액세스할 수 있습니다.여기서, 당신은 새로운 스레드()를 통해 진행 상황에 접근하고 있으며, 그것이 당신이 오류를 얻은 이유입니다.
줄버경우의의 한 .runOnUiThread()
람다 함수를 사용할 수 있습니다. 즉, 다음과 같습니다.
runOnUiThread(() -> doStuff(Object, myValue));
doStuff()
일부 UI 개체의 값을 수정하는 데 사용되는 일부 방법(텍스트 설정, 색상 변경 등)을 나타낼 수 있습니다.
가장 많이 업데이트된 답변에서 언급한 것처럼 각각 6행 실행 가능한 정의를 사용하지 않고 여러 UI 개체를 업데이트하려고 할 때 이것이 훨씬 더 깔끔하다는 것을 알게 되었습니다. 이는 결코 틀린 것이 아니며, 단지 훨씬 더 많은 공간을 차지할 뿐이고 읽기가 덜하다는 것을 알게 되었습니다.
그래서 이것은:
runOnUiThread(new Runnable() {
@Override
public void run() {
doStuff(myTextView, "myNewText");
}
});
다음과 같이 될 수 있습니다.
runOnUiThread(() -> doStuff(myTextView, "myNewText"));
여기서 doStuff의 정의는 다른 곳에 있습니다.
또는 일반화할 필요 없이 TextView 개체의 텍스트만 설정하면 되는 경우:
runOnUiThread(() -> myTextView.setText("myNewText"));
프래그먼트를 사용하는 모든 사용자:
(context as Activity).runOnUiThread {
//TODO
}
에서 UI 변경을 요청했을 때 이 문제가 발생했습니다.doInBackground
Asynctask
사하는대에를 사용하는 onPostExecute
.
『 UI』에서 onPostExecute
내 문제를 해결했습니다.
저는 컨텍스트에 대한 참조가 포함되지 않은 클래스로 작업하고 있었습니다.그래서 제가 사용할 수 없었습니다.runOnUIThread();
저는 용한사를 요.view.post();
그리고 그것은 해결되었습니다.
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
final int currentPosition = mediaPlayer.getCurrentPosition();
audioMessage.seekBar.setProgress(currentPosition / 1000);
audioMessage.tvPlayDuration.post(new Runnable() {
@Override
public void run() {
audioMessage.tvPlayDuration.setText(ChatDateTimeFormatter.getDuration(currentPosition));
}
});
}
}, 0, 1000);
비동기 작업을 사용하는 경우 게시 시 UI 업데이트실행 방법
@Override
protected void onPostExecute(String s) {
// Update UI here
}
언급된 예외의 스택 추적입니다.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.setFlags(View.java:8938)
at android.view.View.setVisibility(View.java:6066)
그래서 만약 당신이 가서 땅을 파면 당신은 알게 됩니다.
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
여기서 mThread는 아래와 같이 생성자에서 초기화됩니다.
mThread = Thread.currentThread();
제 말은 특정 뷰를 만들 때 UI 스레드에서 생성한 후 나중에 Worker Thread에서 수정을 시도했다는 것입니다.
아래 코드 스니펫을 통해 확인할 수 있습니다.
Thread.currentThread().getName()
레이아웃을 부풀릴 때와 나중에 예외가 발생하는 경우.
▁want▁you를 .runOnUiThread
를 구현할 수 .AsynTask
작업을 완료하는 데 몇 초가 걸립니다.하만그런경우는에후에처, 리또당한작을업의신한지▁in▁process▁aftering▁but▁your에후▁work▁also한리처,는▁in,.doinBackground()
는 보기에반합니다야환해서에 .onPostExecute()
Android 구현에서는 기본 UI 스레드만 보기와 상호 작용할 수 있습니다.
UI 스레드가 아닌 스레드에서 단순히 무효화(재도장/재도장 함수 호출)하려면 postInvalidate()를 사용합니다.
myView.postInvalidate();
그러면 UI 스레드에 잘못된 요청이 게시됩니다.
자세한 내용: what-do-postinvalid-do
이렇게 하면 됩니다.
https://developer.android.com/reference/android/view/View#post(java.lang.Runnable)
간단한 접근법
currentTime.post(new Runnable(){
@Override
public void run() {
currentTime.setText(time);
}
}
또한 지연을 제공합니다.
https://developer.android.com/reference/android/view/View#postDelayed(java.lang.Runnable,%20long)
저에게 문제는 제가 전화를 하는 것이었습니다.onProgressUpdate()
내 코드에서 명시적으로.이러면 안 돼요.전화해봤어요.publishProgress()
대신 오류를 해결했습니다.
저 같은 경우에는.EditText
이미 UI 스레드에 있습니다.그러나 이 활동을 로드하면 이 오류와 함께 작업이 중단됩니다.
은 내해책제한것입다니다는야거해은결다것니입▁remove▁i▁my▁to▁is를 제거해야 한다는 것입니다.<requestFocus />
.XML" "EditText" 에서 됩니다.
코틀린에서 어려움을 겪고 있는 사람들을 위해, 그것은 다음과 같이 작동합니다.
lateinit var runnable: Runnable //global variable
runOnUiThread { //Lambda
runnable = Runnable {
//do something here
runDelayedHandler(5000)
}
}
runnable.run()
//you need to keep the handler outside the runnable body to work in kotlin
fun runDelayedHandler(timeToWait: Long) {
//Keep it running
val handler = Handler()
handler.postDelayed(runnable, timeToWait)
}
UI 스레드를 찾을 수 없는 경우 이 방법을 사용할 수 있습니다.
현재 컨텍스트 평균, 현재 컨텍스트를 구문 분석해야 합니다.
new Thread(new Runnable() {
public void run() {
while (true) {
(Activity) yourcurrentcontext).runOnUiThread(new Runnable() {
public void run() {
Log.d("Thread Log","I am from UI Thread");
}
});
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
}
}
}).start();
Kotlin에서 코드를 runOnUiThread 활동 메서드에 넣기만 하면 됩니다.
runOnUiThread{
// write your code here, for example
val task = Runnable {
Handler().postDelayed({
var smzHtcList = mDb?.smzHtcReferralDao()?.getAll()
tv_showSmzHtcList.text = smzHtcList.toString()
}, 10)
}
mDbWorkerThread.postTask(task)
}
조각 내에 있는 경우 활동 개체를 runOn으로 가져와야 합니다.UIT 스레드는 활동에 대한 메서드입니다.
보다 명확하게 하기 위해 주변 상황과 함께 Kotlin의 예 - 이 예는 카메라 조각에서 갤러리 조각으로 탐색하는 것입니다.
// Setup image capture listener which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
Log.d(TAG, "Photo capture succeeded: $savedUri")
//Do whatever work you do when image is saved
//Now ask navigator to move to new tab - as this
//updates UI do on the UI thread
activity?.runOnUiThread( {
Navigation.findNavController(
requireActivity(), R.id.fragment_container
).navigate(CameraFragmentDirections
.actionCameraToGallery(outputDirectory.absolutePath))
})
해결됨: 이 메소드를 DoInBackround Class에 넣기만 하면 됩니다...그리고 메시지를 전달합니다.
public void setProgressText(final String progressText){
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// Any UI task, example
progressDialog.setMessage(progressText);
}
};
handler.sendEmptyMessage(1);
}
언급URL : https://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi
'IT' 카테고리의 다른 글
.git 폴더를 추적할 파일 외부에 저장할 수 있습니까? (0) | 2023.06.27 |
---|---|
MongoDB 대량 입력 대 대량 입력 (0) | 2023.06.27 |
mongodb 및 C# 단위 작업 (0) | 2023.06.27 |
CSS 삼각형은 어떻게 작동합니까? (0) | 2023.06.27 |
명령행에서 직접 iOS 기기에서 반응 네이티브 애플리케이션을 실행하시겠습니까? (0) | 2023.06.27 |