筆記,RemoteView 無法讀取 ExternalCacheDir 下圖片的解決方法

最近因為某 AD Banner 廣告商要我試試它們家最新 SDK,於是就將過去的一個小 App 拿來改了,這篇文章不是要講怎麼串 AD Banner ,而是我發現在 Nexus 2013 平板下, AppWidget (桌面小程式) 無法讀取圖片,也就是 RemoteView 無法讀取 ExternalCacheDir 下的圖片,後來查了一下原因,原來 Android 4.2 之後似乎對於安全性有多了一些限制。

RemoteView 讀取圖片是以 Launcher Manager 的用戶在做讀取的,而我自己的 App 又是另一個用戶,兩個用戶不同就無法讀取,解決的方法是比須提供一個 ContentProvider 讓其他用戶有權限來讀取。

其實中間也試了 Google 提供的 support library v4 內的 FileProvider ,其實沒用的,所以到最後還是自己寫了個 ContentProvider 專門提供 RemoteView 來取檔案內容。

其實很簡單,我貼個範例

public class CacheProvider extends ContentProvider {
	public CacheProvider() {
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// Implement this to handle requests to delete one or more rows.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public String getType(Uri uri) {
		// TODO: Implement this to handle requests for the MIME type of the data
		// at the given URI.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// TODO: Implement this to handle requests to insert a new row.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public boolean onCreate() {
		// TODO: Implement this to initialize your content provider on startup.
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		// TODO: Implement this to handle query requests from clients.
		// throw new UnsupportedOperationException("Not yet implemented");
		return null;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		// TODO: Implement this to handle requests to update one or more rows.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public ParcelFileDescriptor openFile(Uri uri, String mode)
			throws FileNotFoundException {

		File file = new File(uri.toString());
		File cacheFile = new File(getContext().getExternalCacheDir().getAbsolutePath()  , file.getName());

		try {
			ParcelFileDescriptor fd = ParcelFileDescriptor.open(cacheFile,ParcelFileDescriptor.MODE_READ_ONLY);
			return fd;
		} catch( FileNotFoundException e ) {
			throw e;
		}
		// return null;
	}

}

這個 CacheProvider 主要就是提供給外部 App 來讀取 External Cache Dir 的一個管道,寫完後,AndroidMenifest.xml 加上宣告就可以給外部使用了。

		<provider
		    android:name=".CacheProvider"
		    android:authorities="com.yourname.yourapp"
		    android:exported="true"
		    >

		</provider>

以前要丟給 RemoteView 的 setImageUri 可能是丟 file:///.... ,現在要丟給 RemoteView 的 setImageUri ,就丟 content://com.yourname.yourapp/xxxx.png 。

這樣 Android 就會將內容 pass 給 ContentProvider 的 openFile(),接著就會回傳檔案指標給 RemoteView 了,就可以正常開啟圖片並顯示了。

 

發佈留言