我是 Android 开发新手,正在尝试调试我们的应用程序崩溃的原因。当我们尝试向 Android 设备发送推送通知时,我们遇到了崩溃。这是我需要解决的入职问题。我不知道是什么导致了这个问题,该问题可以在 Android N、O 和 P 中重现。下面给出了 Fabric 的堆栈跟踪
Caused by java.lang.SecurityException: UID 10243 does not have permission to content://media/external/audio/media/5927 [user 0]
at android.os.Parcel.createException + 1966(Parcel.java:1966)
at android.os.Parcel.readException + 1934(Parcel.java:1934)
at android.os.Parcel.readException + 1884(Parcel.java:1884)
at android.app.INotificationManager$Stub$Proxy.enqueueNotificationWithTag + 1653(INotificationManager.java:1653)
at android.app.NotificationManager.notifyAsUser + 429(NotificationManager.java:429)
at android.app.NotificationManager.notify + 379(NotificationManager.java:379)
at android.app.NotificationManager.notify + 355(NotificationManager.java:355)
at com.myproject.mobile.notifications.NotificationHelper.process + 66(NotificationHelper.java:66)
at com.myproject.mobile.notifications.NotificationService.constructNotification + 709(NotificationService.java:709)
at com.myproject.mobile.notifications.NotificationService.processNotification + 173(NotificationService.java:173)
at com.myproject.mobile.notifications.PushJobIntentService.onHandleWork + 24(PushJobIntentService.java:24)
at androidx.core.app.JobIntentService$CommandProcessor.doInBackground + 392(JobIntentService.java:392)
at androidx.core.app.JobIntentService$CommandProcessor.doInBackground + 383(JobIntentService.java:383)
at android.os.AsyncTask$2.call + 333(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run + 266(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker + 1167(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run + 641(ThreadPoolExecutor.java:641)
at java.lang.Thread.run + 764(Thread.java:764)
调用NotificationManager的代码块
public void process()
{
NotificationPayload payload = getPayload(); # Helper function to create a payload object. The object is a Java Bean.
final NotificationHelper helper = new NotificationHelper(context, payloadObject);
if (helper != null)
{
// Notification channels set up.
setupChannels(context); // IntentService context.
Notification notification = helper.buildNotification(payload); // to set up notifications.
if (notification != null)
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(payload.getNotificationId(), notification);
}
}
文件提供者 XML 文件file_provider_paths.xml
.
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path
name="photos"
path="images/" />
</paths>
Android Manifest 中的 File Provider 定义
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
来自 Fabric 的更多堆栈跟踪
Caused by android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActivityManagerService.checkGrantUriPermissionLocked(ActivityManagerService.java:12738)
at com.android.server.am.ActivityManagerService.checkGrantUriPermission(ActivityManagerService.java:12755)
at com.android.server.notification.NotificationRecord.visitGrantableUri(NotificationRecord.java:1147)
at com.android.server.notification.NotificationRecord.calculateGrantableUris(NotificationRecord.java:1123)
at com.android.server.notification.NotificationRecord.<init>(NotificationRecord.java:208)
我提议的 PR 会将以下条目添加到file_provider_paths.xml
,但是我的 PR 被关闭了,因为审稿人的评论是
声音文件是包资源,不保存在外部存储中
<external-path
name="external_files"
path="." />
UPDATE
用于构建通知的构建器函数
public Notification buildNotification(NotificationPayload payload)
{
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "channelId");
final RemoteViews collapsedView = new RemoteViews(context.getPackageName(), R.layout.expanded_notification)
if (payload.getTitle() != null)
builder.setContentTitle(payload.getTitle());
if (payload.getBody() != null)
builder.setContentText(payload.getBody());
builder.setContentIntent(payload.getClickIntent(context));
builder.setDeleteIntent(payload.getDeleteIntent(context));
builder.setCustomContentView(collapsedView);
builder.setAutoCancel(true);
builder.setSound(Uri.parse("android.resource://" + BuildConfig.APPLICATION_ID + "/" + R.raw.notif_general));
return builder.build();
}
更新#2*
这就是为通知通道设置声音的方式。
@RequiresApi(Build.VERSION_CODES.O)
@VisibleForTesting()
void setSound(Context context, NotificationChannel channel, String soundUri)
{
AudioAttributes audioAttribute = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
channel.setSound(Uri.parse(soundUri), audioAttribute);
}
通知通道的创建
@TargetApi(26)
void registerChannel(Context context, NotificationManager notificationManager, String id, String name, String soundUri)
{
if (notificationManager.getNotificationChannel(id) != null)
return;
NotificationChannel channel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true);
channel.setLightColor(Color.BLUE);
channel.enableVibration(false);
if (soundUri != null)
{
setSound(context, channel, soundUri);
}
notificationManager.createNotificationChannel(channel);
}
我的问题是如何解决此崩溃。