RingtoneManager获取铃声uri返回空值的问题
在android中使用RingtoneManager管理系统铃声,我们可以利用RingtoneManager类提供的方法获取铃声。
但在使用中,往往会出现返回值为空,或者返回一个对象,但对象中的uri为空的问题。
以getRingtone(int position)这个方法为例,可以传入的参数只有int值,SDK上描述,这个position指ringtone在cursor中的位置。起初遇到这个问题的时候,无论我传入什么值,返回的ringtone的属性(包括title和url)都为空。我尝试在网上查找各种资料,无奈都没有此问题的描述。于是无奈之下,只好查阅源码,这才发现其中的玄机。
源码中getRingtone()这个方法有多个重载函数,我们调用的是
public Ringtone getRingtone(int position) {
if (mStopPreviousRingtone && mPreviousRingtone != null) {
mPreviousRingtone.stop();
}
mPreviousRingtone = getRingtone(mContext, getRingtoneUri(position), inferStreamType());
return mPreviousRingtone;
}
可以看到它实际上调用的又是getRingtone(mContext, getRingtoneUri(position), inferStreamType()) 其中,inferStreamType()返回的是铃声的类型,getRingtoneUri(position)是根据传入的position获取对应的uri,先看getRingtone,源码如下:
private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
Log.d(TAG, "getRingtone() ringtoneUri = " + ringtoneUri
+ ", streamType = " + streamType);
try {
final Ringtone r = new Ringtone(context, true);
if (streamType >= 0) {
r.setStreamType(streamType);
}
r.setUri(ringtoneUri);
return r;
} catch (Exception ex) {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
}
return null;
}
可以看到,该方法实际上重新new了一个Ringtone,然后传入的了Uri,然后将新的Ringtone返回。既然我们获得的Ringtone的title和uri是空值,那就说明传入的这个ringtoneUri一定是空值,也就是说,getRingtoneUri(int position)返回的是一个空值,那我们再看一下这个方法是怎么写的
public Uri getRingtoneUri(int position) {
// use cursor directly instead of requerying it, which could easily
// cause position to shuffle.
if (mCursor == null) {
Log.v(TAG, "mCursor is null");
return null;
}
try {
if (mCursor.isClosed() || !mCursor.moveToPosition(position)) {
Log.v(TAG, "mCursor position is wrong");
return null;
}
} catch (IllegalStateException e) {
Log.v(TAG, "mCursor exception");
return null;
}
return getUriFromCursor(mCursor);
}
可见,有三种情况都会返回null,那到底会是哪一种呢?好在源码中用三各不同的log进行了区分,抓一下log,可见
03-12 13:33:01.531 19356 19356 V RingtoneManager: mCursor is null
可见,是mCursor值为空,到这里,已经柳暗花明了。这个mCursor是RingtoneManage的一个私有属性,当我们在new 一个RingtoneManager的时候,这个mCursor并没有被赋值,所以我们获取的uri为空值。
问题找到了,该如何解决呢?源码中还有这样一个方法
public Cursor getCursor() {
if (mCursor != null && mCursor.requery()) {
Log.v(TAG, "getCursor with old cursor = " + mCursor);
return mCursor;
}
final Cursor internalCursor = getInternalRingtones();
final Cursor drmCursor = mIncludeDrm ? getDrmRingtones() : null;
final Cursor mediaCursor = getMediaRingtones();
mCursor = new SortCursor(new Cursor[] { internalCursor, drmCursor, mediaCursor },
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
Log.v(TAG, "mCursor.hashCode " + mCursor.hashCode());
Log.v(TAG, "getCursor with new cursor = " + mCursor);
return mCursor;
}
此方法对mCursor进行赋值,然后返回。于是,我们可以定义一个Cursor,接受getCursor返回的值,尽管我们不使用这个Cursor,但此时,已经对RingManager的mCursor赋值了,这样,我们获取uri的时候就不会再返回空值了