Android UI 调用服务时冻结
Android UI freeze when calling service
我开发了在线广播应用程序。我通过服务呼叫了 shoutcast 广播服务器。当我调用服务时,所有 UI 控件都被冻结,因此我使用了 ProgressDialog 但仍然冻结并且没有显示进度对话框。我使用了 AsyncTask 但没有用。
HomeFragment.java
在按钮 onClick 事件中,执行 CallingStreamServer AsyncTask。
public class CallingStreamServer extends AsyncTask<Void, Void, Void>{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
progressDialog = new ProgressDialog(getActivity());
progressDialog.setTitle("Please Wait");
progressDialog.setMessage("Connecting Radio Station ...");
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
progressDialog.show();
//progressDialog = ProgressDialog.show(getActivity(), "Please Wait", "Connecting Radio Station ...");
}
@Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
getActivity().startService(new Intent(getActivity(), StreamService.class));
return null;
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
progressDialog.dismiss();
super.onPostExecute(result);
}
}
StreamService.java
StreamService 创建媒体播放器并连接 shoutcast 广播服务器。
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.d(TAG, "onCreate");
try {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
sharedPreEditor = prefs.edit();
notiManager = (NotificationManager)getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = "Connecting ... ";
Intent mainActivity = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, mainActivity, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
notiManager.notify(notiID, noti);
String url = prefs.getString("streamURL", "");
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "onCreate Method : " + e.getMessage());
}
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d(TAG, "onStart");
try {
mediaPlayer.start();
sharedPreEditor.putBoolean("isPlaying", true);
sharedPreEditor.commit();
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = prefs.getString("displayStation", "") + " radio is now playing.";
Intent _intent = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, _intent, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
noti.flags = Notification.FLAG_NO_CLEAR;
notiManager.notify(notiID, noti);
} catch (Exception e) {
Log.e(TAG, "onStart Method : " + e.getMessage());
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.d(TAG, "onDestroy");
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
sharedPreEditor.putBoolean("isPlaying", false);
sharedPreEditor.commit();
notiManager.cancel(notiID);
stopSelf();
}
一个Android服务onCreate总是运行在主线程上,所以它会阻塞主线程上的其他东西,比如UI和动画。默认情况下,服务 class 不提供任何线程,因此如果您需要,您必须自己提供。
因此,您需要一种策略,将服务 onCreate 中的所有阻塞工作从主线程移至其他线程。特别是,mediaPlayer.prepare() 将阻塞一段未知的时间。
我为我的问题做了一些技巧。在服务 onCreate 事件中,mediaPlayer.prepare() 被冻结为 UI,这一点来自@Doug Stevenson。因此,我为 mediaPlayer.prepare() 和 mediaPlayer.start() 制作了 1 个 thread。我使用 BroadcastReceiver 进行服务调用和结果。 :)
HomeFragment.java
注册 BroadcastReceiver 并将按钮 onClick 事件发送到 BroadcastReceiver 以进行服务调用
@Override
public void onClick(View v) {
if (v.getId() == butBoxOfMusic.getId()) {
Intent intent = new Intent();
intent.setAction(ACTION_STREAM_RECEIVER);
intent.putExtra("result", "start");
getActivity().sendBroadcast(intent);
}
}
public class StreamServiceReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getStringExtra("result").equals("start")) {
/// Calling service(call from onClick event)
progressDialog = ProgressDialog.show(getActivity(), "Please Wait", "Connecting Radio Station ...");
getActivity().startService(new Intent(getActivity(), StreamService.class));
}else{
/// Streaming playing(result from service)
progressDialog.dismiss();
}
}
}
StreamService.java
StreamService 创建媒体播放器并连接 shoutcast 广播服务器。
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.d(TAG, "onCreate");
try {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
sharedPreEditor = prefs.edit();
notiManager = (NotificationManager)getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = "Connecting ... ";
Intent mainActivity = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, mainActivity, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
notiManager.notify(notiID, noti);
String url = prefs.getString("streamURL", "");
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
// mediaPlayer.prepare();
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "onCreate Method : " + e.getMessage());
}
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d(TAG, "onStart");
try {
Thread thread = new Thread("MediaPlayer" + String.valueOf(startId)){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
mediaPlayer.prepare();
mediaPlayer.start();
sharedPreEditor.putBoolean("isPlaying", true);
sharedPreEditor.commit();
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = prefs.getString("displayStation", "") + " radio is now playing.";
Intent _intent = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, _intent, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
noti.flags = Notification.FLAG_NO_CLEAR;
notiManager.notify(notiID, noti);
/// Send reslt to BroadcastReceiver ///
Intent intent = new Intent();
intent.setAction(HomeFragment.ACTION_STREAM_RECEIVER);
intent.putExtra("result", "Playing");
sendBroadcast(intent);
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
thread.start();
} catch (Exception e) {
Log.e(TAG, "onStart Method : " + e.getMessage());
}
}
MediaPlayer.prepare()
可能不会立即 return,因此这会阻塞您的 UI 线程。尝试使用 MediaPlayer.prepareAsync()
并实现 OnPreparedListerner
接口,以便在 MediaPlayer
准备好后接收回调。
http://developer.android.com/reference/android/media/MediaPlayer.OnPreparedListener.html
我开发了在线广播应用程序。我通过服务呼叫了 shoutcast 广播服务器。当我调用服务时,所有 UI 控件都被冻结,因此我使用了 ProgressDialog 但仍然冻结并且没有显示进度对话框。我使用了 AsyncTask 但没有用。
HomeFragment.java
在按钮 onClick 事件中,执行 CallingStreamServer AsyncTask。
public class CallingStreamServer extends AsyncTask<Void, Void, Void>{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
progressDialog = new ProgressDialog(getActivity());
progressDialog.setTitle("Please Wait");
progressDialog.setMessage("Connecting Radio Station ...");
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
progressDialog.show();
//progressDialog = ProgressDialog.show(getActivity(), "Please Wait", "Connecting Radio Station ...");
}
@Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
getActivity().startService(new Intent(getActivity(), StreamService.class));
return null;
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
progressDialog.dismiss();
super.onPostExecute(result);
}
}
StreamService.java
StreamService 创建媒体播放器并连接 shoutcast 广播服务器。
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.d(TAG, "onCreate");
try {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
sharedPreEditor = prefs.edit();
notiManager = (NotificationManager)getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = "Connecting ... ";
Intent mainActivity = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, mainActivity, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
notiManager.notify(notiID, noti);
String url = prefs.getString("streamURL", "");
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "onCreate Method : " + e.getMessage());
}
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d(TAG, "onStart");
try {
mediaPlayer.start();
sharedPreEditor.putBoolean("isPlaying", true);
sharedPreEditor.commit();
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = prefs.getString("displayStation", "") + " radio is now playing.";
Intent _intent = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, _intent, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
noti.flags = Notification.FLAG_NO_CLEAR;
notiManager.notify(notiID, noti);
} catch (Exception e) {
Log.e(TAG, "onStart Method : " + e.getMessage());
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.d(TAG, "onDestroy");
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
sharedPreEditor.putBoolean("isPlaying", false);
sharedPreEditor.commit();
notiManager.cancel(notiID);
stopSelf();
}
一个Android服务onCreate总是运行在主线程上,所以它会阻塞主线程上的其他东西,比如UI和动画。默认情况下,服务 class 不提供任何线程,因此如果您需要,您必须自己提供。
因此,您需要一种策略,将服务 onCreate 中的所有阻塞工作从主线程移至其他线程。特别是,mediaPlayer.prepare() 将阻塞一段未知的时间。
我为我的问题做了一些技巧。在服务 onCreate 事件中,mediaPlayer.prepare() 被冻结为 UI,这一点来自@Doug Stevenson。因此,我为 mediaPlayer.prepare() 和 mediaPlayer.start() 制作了 1 个 thread。我使用 BroadcastReceiver 进行服务调用和结果。 :)
HomeFragment.java
注册 BroadcastReceiver 并将按钮 onClick 事件发送到 BroadcastReceiver 以进行服务调用
@Override
public void onClick(View v) {
if (v.getId() == butBoxOfMusic.getId()) {
Intent intent = new Intent();
intent.setAction(ACTION_STREAM_RECEIVER);
intent.putExtra("result", "start");
getActivity().sendBroadcast(intent);
}
}
public class StreamServiceReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getStringExtra("result").equals("start")) {
/// Calling service(call from onClick event)
progressDialog = ProgressDialog.show(getActivity(), "Please Wait", "Connecting Radio Station ...");
getActivity().startService(new Intent(getActivity(), StreamService.class));
}else{
/// Streaming playing(result from service)
progressDialog.dismiss();
}
}
}
StreamService.java
StreamService 创建媒体播放器并连接 shoutcast 广播服务器。
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.d(TAG, "onCreate");
try {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
sharedPreEditor = prefs.edit();
notiManager = (NotificationManager)getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = "Connecting ... ";
Intent mainActivity = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, mainActivity, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
notiManager.notify(notiID, noti);
String url = prefs.getString("streamURL", "");
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
// mediaPlayer.prepare();
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "onCreate Method : " + e.getMessage());
}
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d(TAG, "onStart");
try {
Thread thread = new Thread("MediaPlayer" + String.valueOf(startId)){
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
mediaPlayer.prepare();
mediaPlayer.start();
sharedPreEditor.putBoolean("isPlaying", true);
sharedPreEditor.commit();
Context context = getApplicationContext();
String notiTitle = context.getResources().getString(R.string.app_name);
String notiMsg = prefs.getString("displayStation", "") + " radio is now playing.";
Intent _intent = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, _intent, 0);
noti = new Notification.Builder(context)
.setContentTitle(notiTitle)
.setContentText(notiMsg)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.build();
noti.flags = Notification.FLAG_NO_CLEAR;
notiManager.notify(notiID, noti);
/// Send reslt to BroadcastReceiver ///
Intent intent = new Intent();
intent.setAction(HomeFragment.ACTION_STREAM_RECEIVER);
intent.putExtra("result", "Playing");
sendBroadcast(intent);
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
thread.start();
} catch (Exception e) {
Log.e(TAG, "onStart Method : " + e.getMessage());
}
}
MediaPlayer.prepare()
可能不会立即 return,因此这会阻塞您的 UI 线程。尝试使用 MediaPlayer.prepareAsync()
并实现 OnPreparedListerner
接口,以便在 MediaPlayer
准备好后接收回调。
http://developer.android.com/reference/android/media/MediaPlayer.OnPreparedListener.html