Ik heb een eenvoudige muziekspeler in Android gebouwd. De weergave voor elk nummer bevat een zoekbalk, zoals dit geïmplementeerd:
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);
}
}
Dit werkt prima. Nu wil ik een timer die de seconden/minuten van de voortgang van het nummer telt. Dus plaatste ik een TextView
in de lay-out, haal het op met findViewById()
in onCreate()
, en plaats dit in run()
na 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);
Maar die laatste regel geeft me de uitzondering:
android.view.ViewRoot$CalledFromWrongThreadException: alleen de originele thread die een weergavehiërarchie heeft gemaakt, kan de weergaven ervan raken.
Toch doe ik hier in wezen hetzelfde als met de SeekBar
– de weergave maken in onCreate
en deze vervolgens aanraken in run()
– en ik krijg deze klacht niet.
Antwoord 1, autoriteit 100%
U moet het gedeelte van de achtergrondtaak dat de gebruikersinterface bijwerkt, verplaatsen naar de hoofdthread. Hier is een eenvoudig stukje code voor:
runOnUiThread(new Runnable() {
@Override
public void run() {
// Stuff that updates the UI
}
});
Documentatie voor Activity.runOnUiThread
.
Nest dit in de methode die op de achtergrond draait, en kopieer en plak de code die updates implementeert in het midden van het blok. Voeg alleen de kleinst mogelijke hoeveelheid code toe, anders mis je het doel van de achtergrondthread.
Antwoord 2, autoriteit 7%
Ik heb dit opgelost door runOnUiThread( new Runnable(){ ..
in run()
te plaatsen:
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();
Antwoord 3, autoriteit 4%
Mijn oplossing hiervoor:
private void setText(final TextView text,final String value){
runOnUiThread(new Runnable() {
@Override
public void run() {
text.setText(value);
}
});
}
Noem deze methode op een achtergrondthread.
Antwoord 4
Meestal moet elke actie met betrekking tot de gebruikersinterface worden uitgevoerd in de hoofd- of ui-draad, dat is degene waarin onCreate()
en evenementafhandeling wordt uitgevoerd. Een manier om zeker te zijn van dat is het gebruik van runonuithread () , een ander gebruikt handlers.
ProgressBar.setProgress()
heeft een mechanisme waarvoor het altijd op de hoofddraad zal uitvoeren, dus daarom werkte het.
Zie Pijnloze draad .
Antwoord 5
Kotlin Coroutines kan uw code meer beknopt maken en leesbaar zijn, zoals deze:
MainScope().launch {
withContext(Dispatchers.Default) {
//TODO("Background processing...")
}
TODO("Update UI here!")
}
of vice versa:
GlobalScope.launch {
//TODO("Background processing...")
withContext(Dispatchers.Main) {
// TODO("Update UI here!")
}
TODO("Continue background processing...")
}
Antwoord 6
Ik heb in deze situatie geweest, maar ik heb een oplossing gevonden met het handlerobject.
In mijn geval wil ik een Progressdialog bijwerken met de observer patroon .
Mijn weergave implementeert Observer en negeert de updatemethode.
Dus, mijn hoofddraad creëert de weergave en een andere thread, bel de updatemethode die de ProgressDialop bijwerken en …:
Alleen de originele draad die een bekijk hiërarchie heeft gemaakt, kan het aanraken
Bekeken.
Het is mogelijk om het probleem op te lossen met het Handler Object.
Hieronder, verschillende delen van mijn code:
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);
}
}
Deze uitleg is te vinden op deze pagina, en jij moet de “Voorbeeld voortgangsdialoogvenster met een tweede draad” lezen.
Antwoord 7
U kunt Handler gebruiken om weergave te verwijderen zonder de hoofd-UI-thread te verstoren.
Hier is een voorbeeldcode
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
Antwoord 8
Ik zie dat je het antwoord van @providence hebt geaccepteerd. Voor het geval dat je ook de handler kunt gebruiken! Voer eerst de int-velden in.
private static final int SHOW_LOG = 1;
private static final int HIDE_LOG = 0;
Maak vervolgens een instantie van een handler als een veld.
//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;
}
}
}
};
Maak een methode.
//TODO __________[ Callbacks ]__________
@Override
public void showHandler(boolean show)
{
handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG);
}
Voeg dit ten slotte toe aan de methode onCreate()
.
showHandler(true);
Antwoord 9
Gebruik deze code, en het is niet nodig om de functie runOnUiThread
te gebruiken:
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();
}
Antwoord 10
Ik had een soortgelijk probleem en mijn oplossing is lelijk, maar het werkt:
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
}
Antwoord 11
Ik gebruik Handler
met Looper.getMainLooper()
. Het werkte prima voor mij.
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// Any UI task, example
textView.setText("your text");
}
};
handler.sendEmptyMessage(1);
Antwoord 12
Dit geeft expliciet een fout. Het zegt welke draad een weergave heeft gemaakt, alleen dat zijn standpunten kan aanraken. Het is omdat het gemaakte uitzicht zich in de ruimte van die draad bevindt. De weergave-creatie (GUI) gebeurt in de UI (hoofd) thread. Dus gebruik je altijd de UI-draad om toegang te krijgen tot die methoden.
In het bovenstaande beeld bevindt de voortgangsvariabele zich in de ruimte van de UI-draad. Dus alleen de UI-draad heeft toegang tot deze variabele. Hier heb je toegang tot vooruitgang via nieuwe thread (), en daarom heb je een fout opgetreden.
Antwoord 13
Ik kreeg een soortgelijk probleem en geen van de hierboven genoemde methoden werkte voor mij. Uiteindelijk heeft dit de truc voor mij gedaan:
Device.BeginInvokeOnMainThread(() =>
{
myMethod();
});
Ik vond dit edelsteen hier .
Antwoord 14
Dit is gebeurd met mijn toen ik een UI-verandering van een doInBackground
van Asynctask
in plaats van met onPostExecute
.
Omgaan met de UI in onPostExecute
Los mijn probleem op.
Antwoord 15
Kotlin Antwoord
We moeten met echte manier ui-thread gebruiken voor de taak. We kunnen UI-draad gebruiken in Kotlin:
runOnUiThread(Runnable {
//TODO: Your job is here..!
})
Antwoord 16
Ik werkte met een klasse die geen verwijzing naar de context bevatte. Het was dus niet mogelijk voor mij om runOnUIThread();
te gebruiken Ik gebruikte view.post();
en het was opgelost.
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);
Antwoord 17
Bij gebruik van AsyncTaskWerk de gebruikersinterface bij volgens de onPostExecute-methode
@Override
protected void onPostExecute(String s) {
// Update UI here
}
Antwoord 18
Als u de runOnUiThread
API niet wilt gebruiken, kunt u in feite AsynTask
implementeren voor de bewerkingen die enkele seconden in beslag nemen. Maar in dat geval, ook na het verwerken van uw werk in doinBackground()
, moet u de voltooide weergave retourneren in onPostExecute()
. De Android-implementatie staat alleen de hoofd-UI-thread toe om te communiceren met weergaven.
Antwoord 19
Dit is de stacktracering van de genoemde uitzondering
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)
Dus als u gaat en digeert, kent u het weten
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Waar mthread initialiseert in constructeur zoals hieronder
mThread = Thread.currentThread();
Alles wat ik wil zeggen dat wanneer we specifieke weergave hebben gemaakt die we het hebben gemaakt op de UI-draad en later proberen te modificeren in een werkdraad.
We kunnen het verifiëren via onderstaande code fragment
Thread.currentThread().getName()
Wanneer we lay-out opblazen en later waar u uitzondering krijgt.
Antwoord 20
Als u eenvoudig wilt beleggen (Oproep Repaint / Redraw-functie) uit uw niet-UI-draad, gebruikt u Postinvalidate ()
myView.postInvalidate();
Hiermee wordt een ongeldigverzoek op de UI-draad geplaatst.
Voor meer informatie: what-does-postinvalidate-do
Antwoord 21
Nou, je kunt het zo doen.
https://developer.android. com/reference/android/view/View#post(java.lang.Runnable)
Een eenvoudige aanpak
currentTime.post(new Runnable(){
@Override
public void run() {
currentTime.setText(time);
}
}
het zorgt ook voor vertraging
https://developer .android.com/reference/android/view/View#postDelayed(java.lang.Runnable,%20long)
Antwoord 22
Voor mij was het probleem dat ik onProgressUpdate()
expliciet vanuit mijn code aanriep. Dit zou niet moeten gebeuren. Ik belde in plaats daarvan publishProgress()
en dat loste de fout op.
Antwoord 23
In mijn geval,
Ik heb EditText
in Adapter, en het staat al in de UI-thread. Wanneer deze activiteit echter wordt geladen, crasht deze met deze fout.
Mijn oplossing is dat ik <requestFocus />
uit EditText in XML moet verwijderen.
Antwoord 24
Voor de mensen die het moeilijk hebben in Kotlin, werkt het als volgt:
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)
}
Antwoord 25
Als u zich in een fragment bevindt, moet u ook het activity object ophalen, aangezien runOnUIThread een methode is voor de activiteit.
Een voorbeeld in Kotlin met wat omringende context om het duidelijker te maken – dit voorbeeld is navigeren van een camerafragment naar een galerijfragment:
// 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))
})
Antwoord 26
Opgelost: plaats deze methode gewoon in doInBackround Class… en geef het bericht door
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);
}
Antwoord 27
In mijn geval krijgt de beller die te vaak in korte tijd belt deze foutmelding. negeer als functie minder dan 0,5 seconde wordt aangeroepen:
private long mLastClickTime = 0;
public boolean foo() {
if ( (SystemClock.elapsedRealtime() - mLastClickTime) < 500) {
return false;
}
mLastClickTime = SystemClock.elapsedRealtime();
//... do ui update
}
Antwoord 28
Als je geen UIThread kunt vinden, kun je deze manier gebruiken.
uwhuidigecontextbetekent dat u de huidige context moet ontleden
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();
Antwoord 29
In Kotlinplaats je gewoon je code in de runOnUiThread-activiteitsmethode
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)
}
Antwoord 30
RunOnUIThread leek niet te werken voor mij, maar het volgende loste mijn problemen uiteindelijk op.
_ = MainThread.InvokeOnMainThreadAsync(async () =>
{
this.LoadApplication(Startup.Init(this.ConfigureServices));
var authenticationService = App.ServiceProvider.GetService<AuthenticationService>();
if (authenticationService.AuthenticationResult == null)
{
await authenticationService.AuthenticateAsync(AuthenticationUserFlow.SignUpSignIn, CrossCurrentActivity.Current.Activity).ConfigureAwait(false);
}
});
Binnen de Startup.Init-methode is er ReactiveUI-routering en deze moet worden aangeroepen op de hoofdthread. Deze Invoke-methode accepteert async/wait ook beter dan RunOnUIThread.
Dus overal waar ik methoden op de mainthread moet aanroepen, gebruik ik dit.
Reageer hier alsjeblieft op als iemand iets weet wat ik niet weet en me kan helpen mijn applicatie te verbeteren.