Music应用负责音乐的播放,如果某音乐还有图片,也需要把图片显示出来;
当Music应用播放的音乐发生切换后,需要通过广播消息,将正在播放的音乐的名称、图片等通过广播消息通知给B应用。
如果播放的是歌曲1,应用B可以正常收到歌曲1的名称、图片等;但如果切换到歌曲2,应用B无法收到广播消息。
Music应用中发送广播消息的代码如下:
System.out.println("play " + "send intent, musicTitle="
+ getTrackName() + " musicAlbum" + getAlbumName());
Intent intent = new Intent();
intent.setAction("com.zhao3546.ACTION_MUSIC_DETAIL");
intent.putExtra("musicTitle", getTrackName());
intent.putExtra("musicAlbum", getAlbumName());
Bitmap bmp = MusicUtils.getArtwork(this.getApplicationContext(),
getAudioId(),
getAlbumId());
intent.putExtra("musicImage", bmp);
System.out.println("bmp.getByteCount() = " + bmp.getByteCount());
sendBroadcast(intent);
B应用中动态注册接收广播消息的代码如下:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.zhao3546.ACTION_MUSIC_DETAIL");
registerReceiver(mReceiver, intentFilter);
private class MusicDetailReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
final String title = intent.getStringExtra("musicTitle");
final String album = intent.getStringExtra("musicAlbum");
System.out.println("MusicDetailReceiver.onReceiver" + " title="
+ title + " album=" + album);
final Bitmap bmp = (Bitmap) intent.getParcelableExtra("musicImage");
播放歌曲1,输出的日志如下:
09-11 16:12:16.732: I/System.out(14177): play send intent, musicTitle=最近超级火热的变形金刚简单机械短信息音效 musicAlbumwww.mozhao.net
09-11 16:12:16.776: I/System.out(14177): bmp.getByteCount() = 367236
09-11 16:12:16.802: I/System.out(13265): MusicDetailReceiver.onReceiver title=最近超级火热的变形金刚简单机械短信息音效 album=www.mozhao.net
播放歌曲2,输出的日志如下:
09-11 16:12:25.266: I/System.out(14177): play send intent, musicTitle=Bach-Goldberg Variations 1 2 3 musicAlbumClassical 1
09-11 16:12:25.329: I/System.out(14177): bmp.getByteCount() = 524288
在使用歌曲1测试时,和使用歌曲2测试时,中间我还修改过一些代码,一开始一起以为修改代码引入的问题;
但后来使用歌曲1测试发现最新的代码也是好使的,再仔细分析日志,发现在 bmp.getByteCount() = 524288 这行日志有一个错误日志,见红色字体:
09-11 16:12:25.266: I/System.out(14177): play send intent, musicTitle=Bach-Goldberg Variations 1 2 3 musicAlbumClassical 1
09-11 16:12:25.329: I/System.out(14177): bmp.getByteCount() = 524288
09-11 16:12:25.342: E/JavaBinder(9985): !!! FAILED BINDER TRANSACTION !!!
手头有Android 4.2.2完整的源码,根据“FAILED BINDER TRANSACTION” 搜索了一下代码,发现是在 android_util_Binder.cpp 这个类的signalExceptionForError中输出的:
void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
bool canThrowRemoteException)
{
switch (err) {
...
case FAILED_TRANSACTION:
ALOGE("!!! FAILED BINDER TRANSACTION !!!");
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
// to enable us to distinguish these cases in the future.
jniThrowException(env, canThrowRemoteException
? "android/os/TransactionTooLargeException"
: "java/lang/RuntimeException", NULL);
break;
...
再根据 “FAILED_TRANSACTION” 这个关键字,进一步搜索,在IPCThreadState.cpp的waitForResponse()方法中,有如下代码:
从这个问题出现的所在的函数,我们可以知道,说明这个广播消息已经传输给Binder驱动层了,正在等待Binder驱动层回应答。
为什么?这是一个相当长的话题,如果你不知道,看一下《Android框架揭秘》,或者老罗的Blog:《Android进程间通信(IPC)机制Binder简要介绍和学习计划》。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
<< getReturnString(cmd) << endl;
}
switch (cmd) {
...
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
...
}
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
IPCThreadState::waitForResponse() 将 BR_FAILED_REPLY这个错误码,转成了FAILED_TRANSACTION抛给了上层,
再看看BR_FAILED_REPLY这个错误码是在哪里生成的,进一步搜索了一下,发现这个错误码是在binder.c(Binder的驱动源码中)大量出现:
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
Binder.c (e:\git\android4.2.2源码\driver\binder): return_error = BR_FAILED_REPLY;
binder.c 这个类通过正常方式下载的Android全量源码中是不包含的,如何下载可以参考我的另一个blog:《Android Binder驱动源码下载地址》。
再进一步分析一下之前的日志:
正常传输的图片大小 : 09-11 16:12:16.776: I/System.out(14177): bmp.getByteCount() = 367236
无法正常传输的图片大小 : 09-11 16:12:25.329: I/System.out(14177): bmp.getByteCount() = 524288
异常的那个图片,比正常的图片要大42%左右,再反向去推测,既然是图片过大,那这个问题可能出现在哪里?
出现BR_FAILED_REPLY关键字的地方,我都过了一下,如下地方我个人觉得最像,由于图片大小过大,导致分配buffer出现了问题:
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
...
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
}
当然,仅是推测,没有进一步验证。
大概分析出了问题根因,那怎么解决,其实就比较简单了:
1、将图片保存到sd卡,将路径传给应用B,让应用B自己去从sd卡加载;
2、将图片进行压缩后再传输;
搞Android,有一个好处,就是遇到问题,你可以通过全部的源码去进一步分析找到问题根因;通过解决某个问题,反过来又可以加深对Android的整体的了解。