her*_*erp 879 multithreading android
我在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);
}
}
Run Code Online (Sandbox Code Playgroud)
这很好用.现在我想要一个计时器来计算歌曲进度的秒/分钟.所以,我把一个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);
Run Code Online (Sandbox Code Playgroud)
但是最后一行给了我一个例外:
android.view.ViewRoot $ CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触及其视图.
然而,我在这里做的基本上和我做的一样SeekBar- 创建视图onCreate,然后触摸它run()- 它并没有给我这个抱怨.
pro*_*nce 1804
您必须将更新UI的后台任务部分移动到主线程上.有一个简单的代码:
runOnUiThread(new Runnable() {
@Override
public void run() {
// Stuff that updates the UI
}
});
Run Code Online (Sandbox Code Playgroud)
只需将其嵌套在后台运行的方法中,然后复制粘贴在块中间实现任何更新的代码.只包括可能的最小代码量,否则你开始打败后台线程的目的.
Gün*_*kin 136
我解决了这个通过把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();
Run Code Online (Sandbox Code Playgroud)
小智 63
我对此的解决方案:
private void setText(final TextView text,final String value){
runOnUiThread(new Runnable() {
@Override
public void run() {
text.setText(value);
}
});
}
Run Code Online (Sandbox Code Playgroud)
在后台线程上调用此方法.
big*_*nes 27
通常,涉及用户界面的任何操作都必须在主线程或UI线程中完成,即onCreate()执行和执行事件处理的线程.确保这一点的一种方法是使用runOnUiThread(),另一种方法是使用Handler.
ProgressBar.setProgress() 它有一个机制,它总是在主线程上执行,这就是它工作的原因.
请参阅无痛线程.
Ken*_*chi 27
Kotlin 协程可以使您的代码更加简洁和可读,如下所示:
MainScope().launch {
withContext(Dispatchers.Default) {
//TODO("Background processing...")
}
TODO("Update UI here!")
}
Run Code Online (Sandbox Code Playgroud)
或相反亦然:
GlobalScope.launch {
//TODO("Background processing...")
withContext(Dispatchers.Main) {
// TODO("Update UI here!")
}
TODO("Continue background processing...")
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*han 20
我一直处于这种情况,但我找到了Handler Object的解决方案.
在我的例子中,我想用观察者模式更新ProgressDialog .我的视图实现了观察者并覆盖了更新方法.
所以,我的主线程创建视图,另一个线程调用更新ProgressDialop和....的更新方法:
只有创建视图层次结构的原始线程才能触及其视图.
使用Handler对象可以解决问题.
下面是我的代码的不同部分:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
可以在此页面上找到此说明,您必须阅读"使用第二个线程的示例ProgressDialog".
使用此代码,无需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();
}
Run Code Online (Sandbox Code Playgroud)
我看到你接受了@ providence的回答.以防万一,您也可以使用处理程序!首先,做int字段.
private static final int SHOW_LOG = 1;
private static final int HIDE_LOG = 0;
Run Code Online (Sandbox Code Playgroud)
接下来,将处理程序实例作为字段.
//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;
}
}
}
};
Run Code Online (Sandbox Code Playgroud)
制定方法.
//TODO __________[ Callbacks ]__________
@Override
public void showHandler(boolean show)
{
handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG);
}
Run Code Online (Sandbox Code Playgroud)
最后,把它放在onCreate()方法上.
showHandler(true);
Run Code Online (Sandbox Code Playgroud)
我有一个类似的问题,我的解决方案很难看,但它有效:
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
}
Run Code Online (Sandbox Code Playgroud)
您可以使用Handler删除视图而不会干扰主UI线程.这是示例代码
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
Run Code Online (Sandbox Code Playgroud)
我遇到了类似的问题,上面提到的方法都不适合我。最后,这对我有用:
Device.BeginInvokeOnMainThread(() =>
{
myMethod();
});
Run Code Online (Sandbox Code Playgroud)
我在这里找到了这颗宝石。
对于该方法的单行版本runOnUiThread(),您可以使用 lambda 函数,即:
runOnUiThread(() -> doStuff(Object, myValue));
Run Code Online (Sandbox Code Playgroud)
其中doStuff()can 表示用于修改某些 UI 对象的值的某些方法(设置文本、更改颜色等)。
我发现当尝试更新多个 UI 对象时,这要简洁得多,而不需要像最受支持的答案中提到的那样每个都需要 6 行 Runnable 定义,这绝不是不正确的,它只是占用了更多空间,我发现可读性较差。
所以这:
runOnUiThread(new Runnable() {
@Override
public void run() {
doStuff(myTextView, "myNewText");
}
});
Run Code Online (Sandbox Code Playgroud)
可以变成这样:
runOnUiThread(() -> doStuff(myTextView, "myNewText"));
Run Code Online (Sandbox Code Playgroud)
doStuff 的定义位于其他地方。
或者,如果您不需要如此通用,只需设置 TextView 对象的文本:
runOnUiThread(() -> myTextView.setText("myNewText"));
Run Code Online (Sandbox Code Playgroud)
我用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);
Run Code Online (Sandbox Code Playgroud)
这显然会引发错误。它说,无论哪个线程创建了一个视图,只有该线程才能触摸其视图。这是因为创建的视图在该线程的空间内。视图创建(GUI)在UI(主)线程中进行。因此,您始终使用UI线程来访问这些方法。
在上图中,progress变量位于UI线程的空间内。因此,只有UI线程才能访问此变量。在这里,您正在通过新的Thread()访问进度,这就是为什么出现错误的原因。
我正在使用一个不包含对上下文的引用的类。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);
Run Code Online (Sandbox Code Playgroud)
科特林答案
我们必须以真正的方式使用 UI 线程来完成这项工作。我们可以在 Kotlin 中使用 UI 线程:
runOnUiThread(Runnable {
//TODO: Your job is here..!
})
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
526605 次 |
| 最近记录: |