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

时间:2012-01-28 22:20:29

标签: android android-intent

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

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

5 个答案:

答案 0 :(得分:201)

我有同样的问题。我不想要求读写外部存储权限。此外,有时当手机没有SD卡或卡被卸下时会出现问题。

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

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

在清单

中设置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>

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>

这告诉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();
}

分享图片

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"));
}

进一步阅读

答案 1 :(得分:9)

  

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

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

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

要完全不将它写入闪存,您需要实现自己的ContentProvider,弄清楚如何实现openFile()以返回内存中的位图,然后传递{{1在Uri ACTION_SEND中表示该位图。由于Intent需要返回openFile(),我不知道如果没有磁盘表示,你会怎么做,但我没有花太多时间搜索。

  

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

如果您只是不希望它在外部存储上,您可以使用内部存储上的文件转到ParcelFileDescriptor路由。 This sample project演示ContentProvider通过ContentProvider向设备上的PDF查看器提供PDF文件;同样的方法可以用于ACTION_VIEW

答案 2 :(得分:2)

这用于将CardView共享为图像,然后将其保存在应用程序内部存储区域的缓存子目录中。 希望它会有所帮助。

        @Override
        public void onClick(View view) {

            CardView.setDrawingCacheEnabled(true);
            CardView.buildDrawingCache();
            Bitmap bitmap = CardView.getDrawingCache();

            try{
                File file = new File(getContext().getCacheDir()+"/Image.png");
                bitmap.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(file));
                Uri uri = FileProvider.getUriForFile(getContext(),"com.mydomain.app", file);

                Intent shareIntent = new Intent();
                shareIntent.setAction(Intent.ACTION_SEND);
                shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
                shareIntent.setType("image/jpeg");
                getContext().startActivity(Intent.createChooser(shareIntent, "Share"));

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

        }
    });

答案 3 :(得分:2)

如果仍然有人在寻求简单且简短的解决方案,没有任何存储权限(也支持牛轧糖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>

现在创建provider_paths.xml

<paths>
    <external-path name="external_files" path="."/>
</paths>

最后将此方法添加到您的活动/片段中(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"));
                    }
                }
            }
        }
    }

答案 4 :(得分:1)

这是通过任何消息或电子邮件客户端制作自己应用的屏幕截图分享作为图像的工作方法。

要解决 位图未更新 问题,我使用 Gurupad Mamadapur Suragch 的答案>评论并添加了自己的修改。

以下是 Kotlin 语言中的代码:

private lateinit var myRootView:View // root view of activity
@SuppressLint("SimpleDateFormat")
private fun shareScreenshot() {
    // We need date and time to be added to image name to make it unique every time, otherwise bitmap will not update
    val sdf = SimpleDateFormat("yyyyMMdd_HHmmss")
    val currentDateandTime = sdf.format(Date())
    val imageName = "/image_$currentDateandTime.jpg"       

    // CREATE
    myRootView = window.decorView.rootView
    myRootView.isDrawingCacheEnabled = true
    myRootView.buildDrawingCache(true) // maybe You dont need this
    val bitmap = Bitmap.createBitmap(myRootView.drawingCache)
    myRootView.isDrawingCacheEnabled = false

    // SAVE
    try {
        File(this.cacheDir, "images").deleteRecursively() // delete old images
        val cachePath = File(this.cacheDir, "images")
        cachePath.mkdirs() // don't forget to make the directory
        val stream = FileOutputStream("$cachePath$imageName")
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream) // can be png and any quality level
        stream.close()
    } catch (ex: Exception) {
        Toast.makeText(this, ex.javaClass.canonicalName, Toast.LENGTH_LONG).show() // You can replace this with Log.e(...)
    }

    // SHARE
    val imagePath = File(this.cacheDir, "images")
    val newFile = File(imagePath, imageName)
    val contentUri = FileProvider.getUriForFile(this, "com.example.myapp.fileprovider", newFile)
    if (contentUri != null) {
        val shareIntent = Intent()
        shareIntent.action = Intent.ACTION_SEND
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // temp permission for receiving app to read this file
        shareIntent.type = "image/jpeg" // just assign type. we don't need to set data, otherwise intent will not work properly
        shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
        startActivity(Intent.createChooser(shareIntent, "Choose app"))
    } 
}