我正在尝试允许用户将图像共享到设备上的其他应用。该图像位于我应用程序内部存储区域的files /子目录中。它适用于Gmail,但Facebook和Twitter在响应我的意图时都崩溃了。
编辑:Google+也可以正常使用。
以下是相关的代码部分。
在Application.xml中
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.iforce2d.myapp.MyActivity"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
XML / filepaths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="shared" path="shared"/>
</paths>
以下是我的活动中的分享代码:
File imagePath = new File(getContext().getFilesDir(), "shared");
File newFile = new File(imagePath, "snapshot.jpg");
Uri contentUri = FileProvider.getUriForFile(getContext(),
"org.iforce2d.myapp.MyActivity", newFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
List<ResolveInfo> resInfos =
getPackageManager().queryIntentActivities(shareIntent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : resInfos) {
getContext().grantUriPermission(info.activityInfo.packageName,
contentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivity(Intent.createChooser(shareIntent, "Share image..."));
记录时contentUri
的值为:
content://org.iforce2d.myapp.MyActivity/shared/snapshot.jpg
我只使用Gmail,Facebook和Twitter作为接收应用程序进行了检查,但结果在各种操作系统版本(从2.2.1到4.4.3)中非常一致,7种设备包含Kindle。
Gmail效果很好。图像缩略图显示在邮件撰写中,并在发送时成功附加到邮件。
Twitter和Facebook都崩溃了,如下所示。
以下是来自logcat的堆栈跟踪,显示了这两个应用程序所遇到的问题,对于它们来说似乎是同样的问题(这取自4.4.3,但错误几乎完全相同)到2.2.1,尽管错误信息的措辞略有不同):
Caused by:
java.lang.IllegalStateException: Couldn't read row 0, col 0 from CursorWindow.
Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:434)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
at android.database.CursorWrapper.getString(CursorWrapper.java:114)
at com.twitter.library.media.util.f.a(Twttr:95)
...
Caused by:
java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.
Make sure the Cursor is initialized correctly before accessing data from it.
at android.database.CursorWindow.nativeGetString(Native Method)
at android.database.CursorWindow.getString(CursorWindow.java:434)
at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
at android.database.CursorWrapper.getString(CursorWrapper.java:114)
at com.facebook.photos.base.media.MediaItemFactory.b(MediaItemFactory.java:233)
...
鉴于在Facebook和Twitter上共享图像是数百万人整天都在做的事情,我很震惊它很难实现:/
有人能发现我在这里做错的事吗?
答案 0 :(得分:32)
Twitter(错误地)假设将有一个MediaStore.MediaColumns.DATA列。从KitKat开始,MediaStore返回null,幸运的是,Twitter优雅地处理空值,并做正确的事情。
public class FileProvider extends android.support.v4.content.FileProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor source = super.query(uri, projection, selection, selectionArgs, sortOrder);
String[] columnNames = source.getColumnNames();
String[] newColumnNames = columnNamesWithData(columnNames);
MatrixCursor cursor = new MatrixCursor(newColumnNames, source.getCount());
source.moveToPosition(-1);
while (source.moveToNext()) {
MatrixCursor.RowBuilder row = cursor.newRow();
for (int i = 0; i < columnNames.length; i++) {
row.add(source.getString(i));
}
}
return cursor;
}
private String[] columnNamesWithData(String[] columnNames) {
for (String columnName : columnNames)
if (MediaStore.MediaColumns.DATA.equals(columnName))
return columnNames;
String[] newColumnNames = Arrays.copyOf(columnNames, columnNames.length + 1);
newColumnNames[columnNames.length] = MediaStore.MediaColumns.DATA;
return newColumnNames;
}
}
答案 1 :(得分:3)
编辑 - 此答案中描述的解决方法有效,但the solution provided by Stefan更优雅。它应该是公认的答案。
此外,截至2015年1月,Facebook应用程序不再存在此错误(现在可以使用标准FileProvider
),但Twitter仍然可以。希望它将来会完全消失,这些都不是必需的。
虽然FileProvider
对象在理论上看起来很精彩,但在与许多第三方应用程序共享时似乎并不起作用(而谷歌那些应用程序,如G +,Gmail和&#c完美运行)。要么它没有公开足够的信息,要么就是假设你正在共享文件,从而崩溃。
真的很令人沮丧! :/
我发现共享图像文件的唯一可靠方法是将它们复制到外部存储目录(您可以创建一个.nomedia
子目录以避免它们出现在Gallery应用程序中)。
例如:
Bitmap bitmapToShare = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
File pictureStorage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File noMedia = new File(pictureStorage, ".nomedia");
if (!noMedia.exists())
noMedia.mkdirs();
File file = new File(noMedia, "shared_image.png");
saveBitmapAsFile(bitmapToShare, file);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
shareIntent.setType("image/png");
startActivity(shareIntent);
附录:当然,在尝试在那里写入/复制文件之前,您还应该检查getExternalStorageState()
。