3

到目前为止,我一直在将位图加载到我RemoteViews直接使用remoteViews.setImageViewBitmap()的 . 它工作正常。

但是有几个用户遇到了问题,我认为是在加载非常大的位图时。无论如何,我已经将位图缓存到本地存储,所以我的想法是改用setImageViewUri()其他地方的建议。

但我无法让它工作......我只是得到一个空白小部件。以下片段说明了我在做什么(省略了一些上下文,但希望留下足够的内容)......

// Bitmap bmp is fetched from elsewhere... and then...

FileOutputStream fos = context.openFileOutput("img.png", Context.MODE_PRIVATE);
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();

// ... later ...

// this works...
remoteViews.setImageViewBitmap(R.id.imageView, bmp);

// but this doesn't...
remoteViews.setImageViewUri(R.id.imageView, Uri.fromFile(new File(context.getFilesDir().getPath(), "img.png")));

编辑

尝试FileProvider按照以下 CommonsWare 的建议使用...

显现:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.xyz.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

xml/file_paths.xml:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="cache" path="."/>
</paths>

代码:

File imagePath = new File(context.getFilesDir(), ".");
File newFile = new File(imagePath, "img.png"); // img.png created as above, in top level folder
remoteViews.setImageViewUri(R.id.imageView, FileProvider.getUriForFile(context, "com.xyz.fileprovider", newFile));

但是我仍然像以前一样留下一个空白小部件,并且在 logcat 中没有错误。

4

4 回答 4

5

您的应用小部件正在由主屏幕呈现。主屏幕无法访问您应用的内部存储。

相反,请尝试使用FileProvider在您的呼叫中Uri提供由内部存储提供的文件。FileProvidersetImageViewUri()

更新:我忘记了FileProvider的权限限制。我不知道Uri通过RemoteViews. 理想情况下,您只允许导出提供程序,但FileProvider不支持。

那么,您的选择是:

  1. 编写你自己的流ContentProvider,类似于这个,提供者被导出的地方,所以任何人都可以使用这些Uri值。

  2. 将文件放在外部存储上,而不是内部存储上,然后希望主屏幕具有READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE权限。

  3. 确保您的图像始终相当小,因此您的 IPC 事务保持在 1MB 限制以下,并采用您的原始setImageViewBitmap()方法。

我很抱歉忘记了FileProvider不允许导出提供程序。

于 2016-02-01T19:45:55.403 回答
2

这个答案可能会来(相当)有点晚。

可以通过使用FileProvider允许导出提供程序的稍微修改来使其工作。这是您需要做的:

  1. FileProvider这里下载 的源代码。
  2. 将文件复制到您的项目,例如:com.xyz.fileprovider.FileProvider.java
  3. 如果您将提供程序声明为已导出,请修改attachInfo(Context context, ProviderInfo info)以使其不会抛出:SecurityException

    @Override
    public void attachInfo(Context context, ProviderInfo info) {
        super.attachInfo(context, info);
    
        // Sanity check our security
    
        // Do not throw an exception if the provider is exported
        /*
        if (info.exported) {
            throw new SecurityException("Provider must not be exported");
        }
        */
        if (!info.grantUriPermissions) {
            throw new SecurityException("Provider must grant uri permissions");
        }
    
        mStrategy = getPathStrategy(context, info.authority);
    }
    
  4. 更新您的清单文件,使其引用您的修改FileProvider并设置android:exportedtrue

    <provider>
        android:name="com.xyz.fileprovider.FileProvider"
        android:authorities="com.xyz.fileprovider"
        android:exported="true"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
    
  5. 更新您的代码,以便您使用com.xyz.fileprovider.FileProvider而不是android.support.v4.content.FileProvider

    // Bitmap bmp is fetched from elsewhere... and then...
    
    FileOutputStream fos = context.openFileOutput("img.png", Context.MODE_PRIVATE);
    bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
    fos.flush();
    fos.close();
    
    // ... later ...
    
    // this should work...
    File imagePath = new File(context.getFilesDir(), ".");
    File newFile = new File(imagePath, "img.png"); // img.png created as above, in top level folder
    
    remoteViews.setImageViewUri(R.id.imageView, Uri.parse("")); // this is necessary, without it the launcher is going to cache the Uri we set on the next line and effectively update the widget's image view just once!!!
    remoteViews.setImageViewUri(R.id.imageView, com.xyz.fileprovider.FileProvider.getUriForFile(context, "com.xyz.fileprovider", newFile));
    
于 2017-03-17T16:09:17.560 回答
0

今天遇到了这个问题,但认为授予对任何启动器应用程序的访问权限比导出整个提供程序的安全漏洞要小:)

因此,在将 uri 设置为远程视图之前,我现在授予对每个已安装主屏幕的 URI 的访问权限:

protected void bindTeaserImage(Context context, final RemoteViews remoteViews, final TeaserInfo teaserInfo) {
    final Uri teaserImageUri = getTeaserImageUri(teaserInfo); // generates a content: URI for my FileProvider
    grantUriAccessToWidget(context, teaserImageUri);
    remoteViews.setImageViewUri(R.id.teaser_image, teaserImageUri);
}

protected void grantUriAccessToWidget(Context context, Uri uri) {
    Intent intent= new Intent(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_HOME);
    List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    for (ResolveInfo resolveInfo : resInfoList) {
        String packageName = resolveInfo.activityInfo.packageName;
        context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
}
于 2019-11-29T16:19:13.120 回答
0

该方法remoteViews.setImageViewUri还具有位图大小的限制ImageView(大约 1-2 Mb)。所以你会得到错误或小部件将是透明的。

remoteViews.setImageViewBitmap()很好的做法也是如此,但是您也必须使用创建过程并BitmapFactory.Options inSampleSize尝试将位图限制为屏幕大小并捕获OutOfMemoryError

于 2021-10-29T10:16:46.623 回答