用于位图的Android Share Intent - 是否有可能在共享之前不保存它?

Pio*_*otr 63 android android-intent

我尝试使用共享意图从我的应用程序导出位图,而不保存文件的临时位置.我发现的所有例子都是两步1)保存到SD卡并为该文件创建Uri 2)用这个Uri启动意图

是否可以在不需要WRITE_EXTERNAL_STORAGE权限的情况下进行保存,保存文件[并在之后将其删除]?如何在没有ExternalStorage的情况下寻址设备?

Sur*_*gch 217

我有同样的问题.我不想要求读写外部存储权限.此外,有时手机没有SD卡或卡未安装时会出现问题.

以下方法使用名为FileProviderContentProvider.从技术上讲,您仍然在共享之前保存位图(在内部存储中),但您不需要请求任何权限.此外,每次共享位图时,图像文件都会被覆盖.由于它位于内部缓存中,因此当用户卸载应用程序时,它将被删除.所以在我看来,它与保存图像一样好.此方法比将其保存到外部存储更安全.

文档非常好(参见下面的进一步阅读),但有些部分有点棘手.这是一个对我有用的摘要.

在Manifest中设置FileProvider

<manifest>
    ...
    <application>
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>
        ...
    </application>
</manifest>
Run Code Online (Sandbox Code Playgroud)

替换com.example.myapp为您的应用包名称.

创建res/xml/filepaths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="shared_images" path="images/"/>
</paths>
Run Code Online (Sandbox Code Playgroud)

这告诉FileProvider在哪里获取要共享的文件(在这种情况下使用缓存目录).

将图像保存到内部存储

// save bitmap to cache directory
try {

    File cachePath = new File(context.getCacheDir(), "images");
    cachePath.mkdirs(); // don't forget to make the directory
    FileOutputStream stream = new FileOutputStream(cachePath + "/image.png"); // overwrites this image every time
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
    stream.close();

} catch (IOException e) {
    e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)

分享图片

File imagePath = new File(context.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = FileProvider.getUriForFile(context, "com.example.myapp.fileprovider", newFile);

if (contentUri != null) {
    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
    shareIntent.setDataAndType(contentUri, getContentResolver().getType(contentUri));
    shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
    startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
Run Code Online (Sandbox Code Playgroud)

进一步阅读

  • @Jamrelian,因为目的只是为了共享图像而不是保存它,我选择了缓存目录.缓存目录中的内容不长.但我想,使用另一个目录可以正常工作. (3认同)
  • 很棒的答案!*对于未来的读者* - 大多数应用程序相对于图像文件名保存缩略图缓存.如果文件名保持不变,则不会更新缓存.我通过删除目录的内容并将文件名设置为当前日期(以毫秒为单位)来解决此问题. (3认同)

Com*_*are 9

我尝试使用共享意图从我的应用程序导出位图,而不保存文件的临时位置.

从理论上讲,这是可能的.实际上,这可能是不可能的.

理论上,您需要共享的是Uri将解析为位图的所有内容.最简单的方法是,如果该文件可由其他应用程序直接访问,例如在外部存储上.

根本不要将它写入闪存,你需要实现自己的ContentProvider,找出如何实现openFile()返回你的内存中的位图,然后传递一个Uri代表该位图的ACTION_SEND Intent.由于openFile()需要返回a ParcelFileDescriptor,我不知道如果没有磁盘表示你会怎么做,但我没有花太多时间搜索.

是否可以在不需要WRITE_EXTERNAL_STORAGE权限的情况下进行保存,保存文件[并在之后将其删除]?

如果您只是不希望它在外部存储上,您可以ContentProvider使用内部存储上的文件进行路由.此示例项目演示了如何ContentProvider通过设备ACTION_VIEW上的PDF查看器提供PDF文件; 可以使用相同的方法ACTION_SEND.


Faj*_*han 7

如果有人仍在寻找没有任何存储许可的简单和简短的解决方案(也支持牛轧糖 7.0)。这里是。

在清单中添加这个

<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>
Run Code Online (Sandbox Code Playgroud)

现在创建 provider_paths.xml

<paths>
    <external-path name="external_files" path="."/>
</paths>
Run Code Online (Sandbox Code Playgroud)

最后将此方法添加到您的活动/片段(rootView 是您要共享的视图)

 private void ShareIt(View rootView){
        if (rootView != null && context != null && !context.isFinishing()) {
            rootView.setDrawingCacheEnabled(true);
            Bitmap bitmap = Bitmap.createBitmap(rootView.getDrawingCache());
            if (bitmap != null ) {
                 //Save the image inside the APPLICTION folder
                File mediaStorageDir = new File(AppContext.getInstance().getExternalCacheDir() + "Image.png");

                try {
                    FileOutputStream outputStream = new FileOutputStream(String.valueOf(mediaStorageDir));
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                    outputStream.close();

                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if (ObjectUtils.isNotNull(mediaStorageDir)) {

                    Uri imageUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", mediaStorageDir);

                    if (ObjectUtils.isNotNull(imageUri)) {
                        Intent waIntent = new Intent(Intent.ACTION_SEND);
                        waIntent.setType("image/*");
                        waIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
                        startActivity(Intent.createChooser(waIntent, "Share with"));
                    }
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

更新:

正如@Kathir 在评论中提到的,

DrawingCache从 API 28+ 中弃用。使用下面的代码来Canvas代替。

 Bitmap bitmap = Bitmap.createBitmap(rootView.getWidth(), rootView.getHeight(), quality);
    Canvas canvas = new Canvas(bitmap);

    Drawable backgroundDrawable = view.getBackground();
    if (backgroundDrawable != null) {
        backgroundDrawable.draw(canvas);
    } else {
        canvas.drawColor(Color.WHITE);
    }
    view.draw(canvas);

    return bitmap;
Run Code Online (Sandbox Code Playgroud)

  • API 28+ 中已弃用“DrawingCache”。使用 [Canvas](https://developer.android.com/guide/topics/graphics/drawables) 或 [PixelCopy](https://developer.android.com/reference/android/view/PixelCopy) 来完成此操作。[Canvas 使用示例](/sf/answers/2125227611/)。并且,不要忘记设置背景颜色(无论是“视图”还是“位图”),否则您将得到黑色背景 (2认同)