如何在 Android 7.1.1 上将图像文件共享到 WhatsApp 和 Viber?
How to share an image file to WhatsApp and Viber on Android 7.1.1?
背景
类似于我最近遇到的 sharing/opening APK 文件 (, and ) 问题,我现在有发送图像文件的问题(在资产中,res/raw 甚至从 URL) 到特定应用程序:WhatsApp 和 Viber。
我需要能够将图像文件共享到所有应用程序,尤其是流行的应用程序,例如 WhatsApp 和 Viber。
问题
当我尝试在 Andorid 7.1.1 上共享图像文件时,WhatsApp 和 Viber 都出现问题。在其他应用程序和 Android 的早期版本中,它运行良好。
在我尝试过的所有测试中,它们要么显示黑屏(无图像),要么自行关闭。
我的尝试和发现
1.I 开始使用名为“cwac-provider”的库共享应用资产文件夹中的文件。它适用于所有应用程序,WhatsApp 和 Viber 除外。
在 WhatsApp 上,我得到了这个日志(这与我为 Viber 得到的非常相似):
02-06 17:05:04.379 24590-24590/com.whatsapp W/Bundle: Key
android.intent.extra.STREAM expected ArrayList but value was a
android.net.Uri$HierarchicalUri. The default value was returned. 02-06
17:05:04.382 24590-24590/com.whatsapp W/Bundle: Attempt to cast
generated internal exception: java.lang.ClassCastException:
android.net.Uri$HierarchicalUri cannot be cast to java.util.ArrayList
at android.os.Bundle.getParcelableArrayList(Bundle.java:916) at
android.content.Intent.getParcelableArrayListExtra(Intent.java:6357)
at com.whatsapp.ContactPicker.k(ContactPicker.java:618) at
com.whatsapp.ContactPicker.onCreate(ContactPicker.java:360) at
android.app.Activity.performCreate(Activity.java:6688) at
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2633)
at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741)
at android.app.ActivityThread.-wrap12(ActivityThread.java) at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488)
at android.os.Handler.dispatchMessage(Handler.java:102) at
android.os.Looper.loop(Looper.java:154) at
android.app.ActivityThread.main(ActivityThread.java:6169) at
java.lang.reflect.Method.invoke(Native Method) at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)
2.I 被告知 (here) 通过将 ArrayList<Uri>
放入 EXTRA_STREAM
:
来尝试分享
ArrayList<Uri> uriArrayList=new ArrayList<>();
uriArrayList.add(getUri());
share.putExtra(Intent.EXTRA_STREAM, uriArrayList);
没有成功,WhatsApp的日志显示:
Caused by: java.lang.SecurityException: Permission Denial: opening provider
com.commonsware.cwac.provider.StreamProvider from
ProcessRecord{9405e93 12914:com.whatsapp/u0a210} (pid=12914,
uid=10210) that is not exported from uid 10123
at android.os.Parcel.readException(Parcel.java:1684)
at android.os.Parcel.readException(Parcel.java:1637)
at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:4213)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:5526)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2239)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1517)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1131)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
at com.whatsapp.util.ah.b(MediaFileUtils.java:1290)
at com.whatsapp.util.ah.a(MediaFileUtils.java:1498)
at com.whatsapp.util.ah.a(MediaFileUtils.java:1543)
at com.whatsapp.gallerypicker.ImagePreview$b.a(ImagePreview.java:901)
at com.whatsapp.gallerypicker.ImagePreview$b.doInBackground(ImagePreview.java:896)
at android.os.AsyncTask.call(AsyncTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
3.I也试过把intent的action改成ACTION_SEND_MULTIPLE
:
Intent share = new Intent(Intent.ACTION_SEND_MULTIPLE);
...
ArrayList<Uri> uriArrayList=new ArrayList<>();
uriArrayList.add(getUri());
share.putExtra(Intent.EXTRA_STREAM, uriArrayList);
但它也没有帮助,显示了 Viber 的日志(看不到 WhatsApp 的任何特殊内容):
02-07 09:54:07.084 926-10718/system_process W/ActivityManager:
Permission Denial: opening provider
com.commonsware.cwac.provider.StreamProvider from
ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565,
uid=10175) that is not exported from uid 10123 02-07 09:54:07.087
926-10717/system_process W/ActivityManager: Permission Denial: opening
provider com.commonsware.cwac.provider.StreamProvider from
ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565,
uid=10175) that is not exported from uid 10123 02-07 09:54:07.091
926-946/system_process W/ActivityManager: Permission Denial: opening
provider com.commonsware.cwac.provider.StreamProvider from
ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565,
uid=10175) that is not exported from uid 10123
4.The 奇怪的是,对于 WhatsApp,在上述所有尝试中,它都要求存储许可,即使它不应该(因为应用程序本身提供内容)。
5.Another 我发现的一件奇怪的事是 Google Photos app 可以很好地与这些应用程序共享图像,即使图像来自服务器。它将文件下载到某处并共享。不过,我看不到它在哪里下载文件。我以为它会在应用程序的外部存储路径 ("/.../Android/data/com.google.android.apps.photos/...") 上,但它不在那里。
6.I 尝试使用支持库的 FileProvider 制作一个从外部存储共享文件的 POC(因为我知道如何使用共享 APK 文件):
清单
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
代码:
final File bitmapFile = new File(getExternalFilesDir(null), "test.jpg");
if (!bitmapFile.exists()) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), android.R.drawable.sym_def_app_icon);
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(bitmapFile));
}
Intent intent = new Intent(Intent.ACTION_SEND);
Uri fileUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", bitmapFile);
intent.setType(MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg"));
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
但它只适用于 WhatsApp,而不适用于显示日志的 Viber
02-07 10:21:19.285 24043-24043/com.viber.voip W/Bundle: Key
android.intent.extra.STREAM expected ArrayList but value was a
android.net.Uri$HierarchicalUri. The default value was
returned. 02-07 10:21:19.285 24043-24043/com.viber.voip W/Bundle:
Attempt to cast generated internal exception:
java.lang.ClassCastException: android.net.Uri$HierarchicalUri cannot
be cast to java.util.ArrayList
at android.os.Bundle.getParcelableArrayList(Bundle.java:916)
at android.content.Intent.getParcelableArrayListExtra(Intent.java:6357)
at com.viber.voip.util.af.f(SourceFile:156)
at com.viber.voip.util.af.a(SourceFile:106)
at com.viber.voip.HomeActivity.i(SourceFile:487)
at com.viber.voip.HomeActivity.onCreate(SourceFile:317)
at android.app.Activity.performCreate(Activity.java:6688)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2633)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6169)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)
我尝试执行我在 #2 和 #3 上所做的操作,但它仍然无法在 Viber 上运行。
7.I 尝试使用旧方法从外部存储共享文件,该方法应该被弃用并由 FileProvider 替换:
startActivityForResult(Intent.createChooser(prepareIntentToSharePhoto(bitmapFile.getAbsolutePath(), "title",
"body"), "choose"), 1);
public static Intent prepareIntentToSharePhoto(String imagePath, String title, String body) {
Intent sharingIntent = new Intent(Intent.ACTION_SEND).setType("image/*")
.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imagePath)).putExtra(android.content.Intent.EXTRA_SUBJECT, title)
.putExtra(android.content.Intent.EXTRA_TEXT, body);
return sharingIntent;
}
它适用于这两个应用程序,但只有在它们都被授予存储权限时才有效。对于Viber,如果没有存储权限,则显示黑色图像,对于WhatsApp,则要求用户授予。
问题
为什么上述任何一个都不起作用?
我应该如何正确地将图像文件共享给那些应用程序?即使通过 FileProvider 共享有什么问题?为什么 Google 照片应用运行良好?
有解决办法吗?
这是应用本身的问题,还是 Android 的问题?
现在我将使用解决方案 #7,但它并不完美,因为它需要 Viber 和 WhatsApp 应用程序在能够访问文件之前授予存储权限(给它们自己)。
遗憾的是,我认为选择器上的所有应用都需要此权限。
如果 Viber 尚未授予此权限,则会显示黑屏。
背景
类似于我最近遇到的 sharing/opening APK 文件 (
我需要能够将图像文件共享到所有应用程序,尤其是流行的应用程序,例如 WhatsApp 和 Viber。
问题
当我尝试在 Andorid 7.1.1 上共享图像文件时,WhatsApp 和 Viber 都出现问题。在其他应用程序和 Android 的早期版本中,它运行良好。
在我尝试过的所有测试中,它们要么显示黑屏(无图像),要么自行关闭。
我的尝试和发现
1.I 开始使用名为“cwac-provider”的库共享应用资产文件夹中的文件。它适用于所有应用程序,WhatsApp 和 Viber 除外。
在 WhatsApp 上,我得到了这个日志(这与我为 Viber 得到的非常相似):
02-06 17:05:04.379 24590-24590/com.whatsapp W/Bundle: Key android.intent.extra.STREAM expected ArrayList but value was a android.net.Uri$HierarchicalUri. The default value was returned. 02-06 17:05:04.382 24590-24590/com.whatsapp W/Bundle: Attempt to cast generated internal exception: java.lang.ClassCastException: android.net.Uri$HierarchicalUri cannot be cast to java.util.ArrayList at android.os.Bundle.getParcelableArrayList(Bundle.java:916) at android.content.Intent.getParcelableArrayListExtra(Intent.java:6357) at com.whatsapp.ContactPicker.k(ContactPicker.java:618) at com.whatsapp.ContactPicker.onCreate(ContactPicker.java:360) at android.app.Activity.performCreate(Activity.java:6688) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2633) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6169) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)
2.I 被告知 (here) 通过将 ArrayList<Uri>
放入 EXTRA_STREAM
:
ArrayList<Uri> uriArrayList=new ArrayList<>();
uriArrayList.add(getUri());
share.putExtra(Intent.EXTRA_STREAM, uriArrayList);
没有成功,WhatsApp的日志显示:
Caused by: java.lang.SecurityException: Permission Denial: opening provider
com.commonsware.cwac.provider.StreamProvider from ProcessRecord{9405e93 12914:com.whatsapp/u0a210} (pid=12914, uid=10210) that is not exported from uid 10123 at android.os.Parcel.readException(Parcel.java:1684) at android.os.Parcel.readException(Parcel.java:1637) at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:4213) at android.app.ActivityThread.acquireProvider(ActivityThread.java:5526) at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2239) at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1517) at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1131) at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984) at android.content.ContentResolver.openInputStream(ContentResolver.java:704) at com.whatsapp.util.ah.b(MediaFileUtils.java:1290) at com.whatsapp.util.ah.a(MediaFileUtils.java:1498) at com.whatsapp.util.ah.a(MediaFileUtils.java:1543) at com.whatsapp.gallerypicker.ImagePreview$b.a(ImagePreview.java:901) at com.whatsapp.gallerypicker.ImagePreview$b.doInBackground(ImagePreview.java:896) at android.os.AsyncTask.call(AsyncTask.java:305) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:243) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761)
3.I也试过把intent的action改成ACTION_SEND_MULTIPLE
:
Intent share = new Intent(Intent.ACTION_SEND_MULTIPLE);
...
ArrayList<Uri> uriArrayList=new ArrayList<>();
uriArrayList.add(getUri());
share.putExtra(Intent.EXTRA_STREAM, uriArrayList);
但它也没有帮助,显示了 Viber 的日志(看不到 WhatsApp 的任何特殊内容):
02-07 09:54:07.084 926-10718/system_process W/ActivityManager: Permission Denial: opening provider com.commonsware.cwac.provider.StreamProvider from ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565, uid=10175) that is not exported from uid 10123 02-07 09:54:07.087 926-10717/system_process W/ActivityManager: Permission Denial: opening provider com.commonsware.cwac.provider.StreamProvider from ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565, uid=10175) that is not exported from uid 10123 02-07 09:54:07.091 926-946/system_process W/ActivityManager: Permission Denial: opening provider com.commonsware.cwac.provider.StreamProvider from ProcessRecord{adbb1ed 5565:com.viber.voip/u0a175} (pid=5565, uid=10175) that is not exported from uid 10123
4.The 奇怪的是,对于 WhatsApp,在上述所有尝试中,它都要求存储许可,即使它不应该(因为应用程序本身提供内容)。
5.Another 我发现的一件奇怪的事是 Google Photos app 可以很好地与这些应用程序共享图像,即使图像来自服务器。它将文件下载到某处并共享。不过,我看不到它在哪里下载文件。我以为它会在应用程序的外部存储路径 ("/.../Android/data/com.google.android.apps.photos/...") 上,但它不在那里。
6.I 尝试使用支持库的 FileProvider 制作一个从外部存储共享文件的 POC(因为我知道如何使用共享 APK 文件):
清单
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
代码:
final File bitmapFile = new File(getExternalFilesDir(null), "test.jpg");
if (!bitmapFile.exists()) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), android.R.drawable.sym_def_app_icon);
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(bitmapFile));
}
Intent intent = new Intent(Intent.ACTION_SEND);
Uri fileUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", bitmapFile);
intent.setType(MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg"));
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
但它只适用于 WhatsApp,而不适用于显示日志的 Viber
02-07 10:21:19.285 24043-24043/com.viber.voip W/Bundle: Key android.intent.extra.STREAM expected ArrayList but value was a android.net.Uri$HierarchicalUri. The default value was returned. 02-07 10:21:19.285 24043-24043/com.viber.voip W/Bundle: Attempt to cast generated internal exception: java.lang.ClassCastException: android.net.Uri$HierarchicalUri cannot be cast to java.util.ArrayList at android.os.Bundle.getParcelableArrayList(Bundle.java:916) at android.content.Intent.getParcelableArrayListExtra(Intent.java:6357) at com.viber.voip.util.af.f(SourceFile:156) at com.viber.voip.util.af.a(SourceFile:106) at com.viber.voip.HomeActivity.i(SourceFile:487) at com.viber.voip.HomeActivity.onCreate(SourceFile:317) at android.app.Activity.performCreate(Activity.java:6688) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2633) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6169) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)
我尝试执行我在 #2 和 #3 上所做的操作,但它仍然无法在 Viber 上运行。
7.I 尝试使用旧方法从外部存储共享文件,该方法应该被弃用并由 FileProvider 替换:
startActivityForResult(Intent.createChooser(prepareIntentToSharePhoto(bitmapFile.getAbsolutePath(), "title",
"body"), "choose"), 1);
public static Intent prepareIntentToSharePhoto(String imagePath, String title, String body) {
Intent sharingIntent = new Intent(Intent.ACTION_SEND).setType("image/*")
.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imagePath)).putExtra(android.content.Intent.EXTRA_SUBJECT, title)
.putExtra(android.content.Intent.EXTRA_TEXT, body);
return sharingIntent;
}
它适用于这两个应用程序,但只有在它们都被授予存储权限时才有效。对于Viber,如果没有存储权限,则显示黑色图像,对于WhatsApp,则要求用户授予。
问题
为什么上述任何一个都不起作用?
我应该如何正确地将图像文件共享给那些应用程序?即使通过 FileProvider 共享有什么问题?为什么 Google 照片应用运行良好?
有解决办法吗?
这是应用本身的问题,还是 Android 的问题?
现在我将使用解决方案 #7,但它并不完美,因为它需要 Viber 和 WhatsApp 应用程序在能够访问文件之前授予存储权限(给它们自己)。
遗憾的是,我认为选择器上的所有应用都需要此权限。
如果 Viber 尚未授予此权限,则会显示黑屏。