CameraRollManager
提供了JS與設(shè)備相冊進行交互的相關(guān)功能,它包括兩個API:
- saveToCameraRoll 將本地圖片存儲到媒體庫
- getPhotos 獲取媒體庫中的圖片信息
具體實現(xiàn)分析如下。
saveToCameraRoll
-
input
:String
uri 待存儲圖片的uri("file:///sdcard/img.png"
),在Android端必須是本地圖片的uri。 -
output
: Promise 回調(diào) 返回存儲成功后的uri
saveToCameraRoll方法啟動了一個后臺異步任務(wù)來完成圖片的存儲工作:
protected void doInBackgroundGuarded(Void... params) {
File source = new File(mUri.getPath()); // 待存儲圖片文件
FileChannel input = null, output = null;
try {
// SD卡目錄
File exportDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
exportDir.mkdirs();
if (!exportDir.isDirectory()) { //沒權(quán)限
mPromise.reject(ERROR_UNABLE_TO_LOAD, "External media storage directory not available");
return;
}
File dest = new File(exportDir, source.getName());
int n = 0;
// 避免重名覆蓋
String fullSourceName = source.getName();
String sourceName, sourceExt;
if (fullSourceName.indexOf('.') >= 0) {
sourceName = fullSourceName.substring(0, fullSourceName.lastIndexOf('.'));
sourceExt = fullSourceName.substring(fullSourceName.lastIndexOf('.'));
} else {
sourceName = fullSourceName;
sourceExt = "";
}
while (!dest.createNewFile()) {
dest = new File(exportDir, sourceName + "_" + (n++) + sourceExt);
}
// 開始文件復(fù)制操作
input = new FileInputStream(source).getChannel();
output = new FileOutputStream(dest).getChannel();
output.transferFrom(input, 0, input.size());
input.close();
output.close();
// 發(fā)起掃描
MediaScannerConnection.scanFile(
mContext,
new String[]{dest.getAbsolutePath()},
null,
new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
if (uri != null) {
mPromise.resolve(uri.toString());
} else {
mPromise.reject(ERROR_UNABLE_TO_SAVE, "Could not add image to gallery");
}
}
});
} catch (IOException e) {
mPromise.reject(e);
} finally {
if (input != null && input.isOpen()) {
try {
input.close();
} catch (IOException e) {
FLog.e(ReactConstants.TAG, "Could not close input channel", e);
}
}
if (output != null && output.isOpen()) {
try {
output.close();
} catch (IOException e) {
FLog.e(ReactConstants.TAG, "Could not close output channel", e);
}
}
}
}
public void getPhotos(final ReadableMap params, final Promise promise)
-
input
:
{
"first": int,返回的圖片個數(shù)
"after": String 上一次調(diào)用所返回的cursor
"groupName":String 相冊名稱
"mimeType":Array //指定要返回的圖片類型
}
-
output
:示例
{
"page_info": { //本頁搜索信息
"end_cursor": "1496373800000",
"has_next_page": true
},
"edges": [// 本次搜索所獲取的圖片信息, 每個node表示一張圖片
{
"node": {
"timestamp": 1496384866,
"group_name": "splash", // 相冊名稱
"type": "image/jpeg",
"image": {
"height": 1920,
"width": 1080,
"uri": "content://media/external/images/media/7217"
}
}
},
{
"node": {
"timestamp": 1496373800,
"group_name": "a",
"type": "image/png",
"image": {
"height": 64,
"width": 96,
"uri": "content://media/external/images/media/7218"
}
}
}
]
}
getPhotos 在啟動了一個后臺異步任務(wù)使用系統(tǒng)提供的ContentResolver
從SD卡中獲取指定的圖片。
protected void doInBackgroundGuarded(Void... params) {
StringBuilder selection = new StringBuilder("1");
List<String> selectionArgs = new ArrayList<>();
if (!TextUtils.isEmpty(mAfter)) {
selection.append(" AND " + SELECTION_DATE_TAKEN);
selectionArgs.add(mAfter);
}
if (!TextUtils.isEmpty(mGroupName)) {
selection.append(" AND " + SELECTION_BUCKET);
selectionArgs.add(mGroupName);
}
if (mMimeTypes != null && mMimeTypes.size() > 0) {
selection.append(" AND " + Images.Media.MIME_TYPE + " IN (");
for (int i = 0; i < mMimeTypes.size(); i++) {
selection.append("?,");
selectionArgs.add(mMimeTypes.getString(i));
}
selection.replace(selection.length() - 1, selection.length(), ")");
}
WritableMap response = new WritableNativeMap();
ContentResolver resolver = mContext.getContentResolver();
try {
Cursor photos = resolver.query(
Images.Media.EXTERNAL_CONTENT_URI,
PROJECTION,
selection.toString(),
selectionArgs.toArray(new String[selectionArgs.size()]),
Images.Media.DATE_TAKEN + " DESC, " + Images.Media.DATE_MODIFIED + " DESC LIMIT " +
(mFirst + 1)); // set LIMIT to first + 1 so that we know how to populate page_info
if (photos == null) {
mPromise.reject(ERROR_UNABLE_TO_LOAD, "Could not get photos");
} else {
try {
putEdges(resolver, photos, response, mFirst);
putPageInfo(photos, response, mFirst);
} finally {
photos.close();
mPromise.resolve(response);
}
}
} catch (SecurityException e) {
mPromise.reject(
ERROR_UNABLE_TO_LOAD_PERMISSION,
"Could not get photos: need READ_EXTERNAL_STORAGE permission",
e);
}
}
在JS端,ReactNative將CameraRollManager封裝為CameraRoll.js,加入了一些入?yún)⒑突貐⒌臋z查。