我在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。只有创建视图层次结构的原始线程才能触及它的视图。
然而,我在这里所做的事情基本上与我在SeekBar中所做的事情相同——在onCreate中创建视图,然后在run()中触摸它——它没有给我这个抱怨。
RunOnUIThread似乎对我不起作用,但下面的程序最终解决了我的问题。
_ = 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);
}
});
在启动过程中。Init方法有ReactiveUI路由,这需要在主线程上调用。这个Invoke方法也比RunOnUIThread更好地接受async/await。
所以我需要在主线程上调用方法的任何地方都使用这个。
请评论这一点,如果有人知道一些我不知道的,可以帮助我提高我的申请。
如果你在一个片段中,那么你还需要获取活动对象,因为runOnUIThread是活动上的一个方法。
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))
})
这是上述异常的堆栈跟踪
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线程上创建它,然后尝试在工作线程中修改。
我们可以通过下面的代码片段验证它
Thread.currentThread().getName()
当我们膨胀布局之后,你会得到例外。
“这是我的代码”
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
KeyValuePair<String, String> keyValuePair = getCalculateDifferenceBetweenTwoTimes();
CharSequence charSequence = getPresenterUtil().contact(
getPresenterUtil().messageStyle("Check-in", Color.GRAY, Typeface.NORMAL, 0.9f),
UtilPresenter.LINE,
getPresenterUtil().messageStyle(keyValuePair.getKey(), Color.BLACK, Typeface.NORMAL, 1.3f),
UtilPresenter.LINE,
UtilPresenter.LINE,
getPresenterUtil().messageStyle("Tempo de execução", Color.GRAY, Typeface.NORMAL, 0.9f),
UtilPresenter.LINE,
getPresenterUtil().messageStyle(keyValuePair.getValue(), Color.BLACK, Typeface.NORMAL, 1.3f)
);
if (materialDialog.getContentView() != null) {
materialDialog.getContentView().setText(charSequence);
}
}
});
}
}, 0, 1000);