在测试cordova开发的安卓APP过程中,使用$cordovaImagePicker.getPictures(options)获取相册照片时,华为机型总是会闪退。
config.xml已经添加了权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" />
如果先调用相机$cordovaCamera.getPicture(options)弹出权限申请,赋予权限后再调用$cordovaImagePicker.getPictures(options)就不会闪退。
查看了一下调用相机插件cordova-plugin-camera源码org.apache.cordova.camera.CameraLauncher中的方法callTakePicture,可知其做了权限的检测,源码如下:
/** * Take a picture with the camera. * When an image is captured or the camera view is cancelled, the result is returned * in CordovaActivity.onActivityResult, which forwards the result to this.onActivityResult. * * The image can either be returned as a base64 string or a URI that points to the file. * To display base64 string in an img tag, set the source to: * img.src="data:image/jpeg;base64,"+result; * or to display URI in an img tag * img.src=result; * * @param returnType Set the type of image to return. * @param encodingType Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) */ public void callTakePicture(int returnType, int encodingType) { boolean saveAlbumPermission = PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); boolean takePicturePermission = PermissionHelper.hasPermission(this, Manifest.permission.CAMERA); // CB-10120: The CAMERA permission does not need to be requested unless it is declared // in AndroidManifest.xml. This plugin does not declare it, but others may and so we must // check the package info to determine if the permission is present. if (!takePicturePermission) { takePicturePermission = true; try { PackageManager packageManager = this.cordova.getActivity().getPackageManager(); String[] permissionsInPackage = packageManager.getPackageInfo(this.cordova.getActivity().getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions; if (permissionsInPackage != null) { for (String permission : permissionsInPackage) { if (permission.equals(Manifest.permission.CAMERA)) { takePicturePermission = false; break; } } } } catch (NameNotFoundException e) { // We are requesting the info for our package, so this should // never be caught } } if (takePicturePermission && saveAlbumPermission) { takePicture(returnType, encodingType); } else if (saveAlbumPermission && !takePicturePermission) { PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.CAMERA); } else if (!saveAlbumPermission && takePicturePermission) { PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.READ_EXTERNAL_STORAGE); } else { PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, permissions); } }
然后又查看了一下cordova-plugin-image-picker插件com.synconset.ImagePicker.java的源码发现方法execute没有做权限检测直接执行this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0)方法,而这个方法最终调用的是android.app.Activity.java中的startActivityForResult(intent, requestCode)方法。
android.app.Activity.java中startActivityForResult方法的源码:
/** * Same as calling {@link #startActivityForResult(Intent, int, Bundle)} * with no options. * * @param intent The intent to start. * @param requestCode If >= 0, this code will be returned in * onActivityResult() when the activity exits. * * @throws android.content.ActivityNotFoundException * * @see #startActivity */ public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) { startActivityForResult(intent, requestCode, null); }
可以看到这个方法加了@RequiresPermission,所以在没有权限的情况下直接调用可能会被拒绝。
所以需要在调用前申请权限。
解决:
这是com.synconset.ImagePicker.java插件源码:
/** * An Image Picker Plugin for Cordova/PhoneGap. */ package com.synconset; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.PermissionHelper; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import android.Manifest; import android.app.Activity; import android.content.Intent; import android.util.Log; public class ImagePicker extends CordovaPlugin { public static String TAG = "ImagePicker"; private CallbackContext callbackContext; private JSONObject params; public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { this.callbackContext = callbackContext; this.params = args.getJSONObject(0); if (action.equals("getPictures")) { Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class); int max = 20; int desiredWidth = 0; int desiredHeight = 0; int quality = 100; if (this.params.has("maximumImagesCount")) { max = this.params.getInt("maximumImagesCount"); } if (this.params.has("width")) { desiredWidth = this.params.getInt("width"); } if (this.params.has("height")) { desiredHeight = this.params.getInt("height"); } if (this.params.has("quality")) { quality = this.params.getInt("quality"); } intent.putExtra("MAX_IMAGES", max); intent.putExtra("WIDTH", desiredWidth); intent.putExtra("HEIGHT", desiredHeight); intent.putExtra("QUALITY", quality);
if (this.cordova != null) { this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); } } return true; } public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK && data != null) { ArrayList<String> fileNames = data.getStringArrayListExtra("MULTIPLEFILENAMES"); JSONArray res = new JSONArray(fileNames); this.callbackContext.success(res); } else if (resultCode == Activity.RESULT_CANCELED && data != null) { String error = data.getStringExtra("ERRORMESSAGE"); this.callbackContext.error(error); } else if (resultCode == Activity.RESULT_CANCELED) { JSONArray res = new JSONArray(); this.callbackContext.success(res); } else { this.callbackContext.error("No images selected"); } } }
这是我修改后添加了权限申请的代码:
/** * An Image Picker Plugin for Cordova/PhoneGap. */ package com.synconset; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.PermissionHelper; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import android.Manifest; import android.app.Activity; import android.content.Intent; import android.util.Log; public class ImagePicker extends CordovaPlugin { public static String TAG = "ImagePicker"; private CallbackContext callbackContext; private JSONObject params; public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { this.callbackContext = callbackContext; this.params = args.getJSONObject(0); if (action.equals("getPictures")) { Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class); int max = 20; int desiredWidth = 0; int desiredHeight = 0; int quality = 100; if (this.params.has("maximumImagesCount")) { max = this.params.getInt("maximumImagesCount"); } if (this.params.has("width")) { desiredWidth = this.params.getInt("width"); } if (this.params.has("height")) { desiredHeight = this.params.getInt("height"); } if (this.params.has("quality")) { quality = this.params.getInt("quality"); } intent.putExtra("MAX_IMAGES", max); intent.putExtra("WIDTH", desiredWidth); intent.putExtra("HEIGHT", desiredHeight); intent.putExtra("QUALITY", quality); //添加权限申请 boolean saveAlbumPermission = PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE); if(!saveAlbumPermission){ PermissionHelper.requestPermission(this, 0, Manifest.permission.READ_EXTERNAL_STORAGE); }else{ if (this.cordova != null) { this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); } } } return true; } public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK && data != null) { ArrayList<String> fileNames = data.getStringArrayListExtra("MULTIPLEFILENAMES"); JSONArray res = new JSONArray(fileNames); this.callbackContext.success(res); } else if (resultCode == Activity.RESULT_CANCELED && data != null) { String error = data.getStringExtra("ERRORMESSAGE"); this.callbackContext.error(error); } else if (resultCode == Activity.RESULT_CANCELED) { JSONArray res = new JSONArray(); this.callbackContext.success(res); } else { this.callbackContext.error("No images selected"); } } }
这样问题就解决了。