我目前正在开发我的第三个 Android 项目以进行大规模发布。与我目前的应用程序相比,我的前两个相对基本,这是迄今为止我最复杂的工作。因此,我不得不与比我习惯的更多的听众打交道。
具体来说,我的应用程序中处理在 MediaPlayer 中播放网络流的部分已经使用了三个侦听器。
微调器的侦听器:
stationSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
String newStreamUrl;
@Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
switch (position) {
// Based on the user's selection, change the URL to match the appropriate station and stream quality.
case 0:
// 128kb 89.7 stream
// Default case, always executed on activity creation.
newStreamUrl = "defaultStreamUrl";
changeStream(newStreamUrl);
break;
case 1:
// 320kb stream
newStreamUrl = "URL1";
changeStream(newStreamUrl);
break;
case 2:
// 128kb Stream 2
newStreamUrl = "URL2";
changeStream(newStreamUrl);
break;
case 3:
// 320kb Steam 2
newStreamUrl = "URL3";
changeStream(newStreamUrl);
}
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
}
});
progressDialog 的监听器:
pd = ProgressDialog.show(this, "Loading...", "Buffering Stream", true, true, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
pd.dismiss();
mp.reset();
}
});
以及在缓冲完成时触发播放的侦听器:
mp.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// When the stream is buffered, kill prompt and start playing automatically.
pd.dismiss();
mp.start();
Log.i(TAG, "Stream playback started.");
}
});
...而且我什至还没有完成一半的实现侦听器来处理所有可能的情况。
根据我目前的知识,必须以匿名方式定义侦听器(如我上面所做的那样),或者将其编写为实现侦听器的单独类,然后在所需的类中实例化。也许只有我一个人,但我认为匿名定义这些会使我的代码变得混乱并模糊活动背后的逻辑。但是,由于我实际上只在此类中使用这些侦听器,因此将它们移动到自己的单独文件中似乎是一种浪费,因为它会占用我包的名称空间。
我想知道这种情况的最佳做法是什么。是否有关于侦听器的明确定义的规则,还是仅取决于开发人员的偏好?我正在尝试使这个项目尽可能接近最佳编码实践,因为我是编写 Android 应用程序的新手,所以希望我能从中学到一些东西。有什么想法吗?
如有必要,我的整个上下文代码:
public class StreamActivity extends Activity {
static private MediaPlayer mp;
static ProgressDialog pd;
static String streamUrl = "defaultStreamURL"; // Default value is 128kb/s stream.
private static final String TAG = StreamActivity.class.getName(); // Tag constant for logging purposes
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.stream, menu);
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stream_layout);
// Build selection spinner
Spinner stationSpinner = (Spinner)findViewById(R.id.station_spinner);
ArrayAdapter<CharSequence> stationAdapter = ArrayAdapter.createFromResource(this, R.array.station_string_array, android.R.layout.simple_spinner_item);
stationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
stationSpinner.setAdapter(stationAdapter);
// Set spinner decision logic
stationSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
String newStreamUrl;
@Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
switch (position) {
// Based on the user's selection, change the URL to match the appropriate station and stream quality.
case 0:
// 128kb 89.7 stream
// Default case, always executed on activity creation.
newStreamUrl = "defaultStreamUrl";
changeStream(newStreamUrl);
break;
case 1:
// 320kb stream
newStreamUrl = "URL1";
changeStream(newStreamUrl);
break;
case 2:
// 128kb Stream 2
newStreamUrl = "URL2";
changeStream(newStreamUrl);
break;
case 3:
// 320kb Steam 2
newStreamUrl = "URL3";
changeStream(newStreamUrl);
}
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
}
});
// Build audio player using default settings.
mp = buildAudioPlayer();
}
/**
* Builds and returns a configured, unprepared MediaPlayer.
*/
public MediaPlayer buildAudioPlayer() {
// Build MediaPlayer
mp = new MediaPlayer();
try {
mp.reset();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setDataSource(streamUrl);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Caught IllegalArgumentException: ");
e.printStackTrace();
} catch (IllegalStateException e) {
Log.e(TAG, "Caught IllegalStateException: ");
e.printStackTrace();
} catch (SecurityException e) {
Log.e(TAG, "Caught SecurityException: ");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "Caught IOException: ");
e.printStackTrace();
}
pd = new ProgressDialog(this);
return mp;
}
protected void changeStream(String newStreamUrl) {
streamUrl = newStreamUrl;
// Stop stream if it is currently playing to prevent state exceptions
if (mp.isPlaying()) {
Log.i(TAG, "Stream source changed by user. Rebuilding stream.");
Log.i(TAG, "Stream playback stopped.");
mp.stop();
}
// Rebuild player with new stream URL.
mp.reset();
mp = buildAudioPlayer();
}
/**
* Stops audio, drops connection to stream, and returns Media Player to an unprepared state. Called by a button onClick event.
* @param v Button pressed by user.
*/
public void stopAudio(View v) {
mp.stop();
Log.i(TAG, "Stream playback stopped.");
}
/**
* Pauses audio with no change to connection or Media Player. Called by a button onClick event.
* @param v Button pressed by user.
*/
public void pauseAudio(View v) {
mp.pause();
Log.i(TAG, "Stream playback paused.");
}
/**
* Prepares Media Player asynchronously. Displays prompt while buffering and automatically starts when finished.
* @param v Button pressed by user.
*/
public void playAudio(View v) {
// If we are paused, resume playback without rebuffering.
if (mp.isPlaying()) {
mp.start();
} else {
// If audio is NOT playing, we need to prepare and buffer.
try {
mp.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// When the stream is buffered, kill prompt and start playing automatically.
pd.dismiss();
mp.start();
Log.i(TAG, "Stream playback started.");
}
});
// Prepares stream without blocking UI Thread
mp.prepareAsync();
} catch (IllegalStateException e) {
Log.e(TAG, "Caught IllegalStateException when preparing: ");
e.printStackTrace();
}
// Stop user input while buffering by displaying ProgressDialog
pd.setCancelable(true);
pd.setCanceledOnTouchOutside(false);
pd = ProgressDialog.show(this, "Loading!", "Buffering...", true, true, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
pd.dismiss();
mp.reset();
}
});
}
}
}