Android 自定义拍照,解决图片旋转,拍照参数设置兼容问题

  今天客户提了一个需求: 需要结合摄像头将拍照的图片和自己的产品(图片)整合到一张图片上面,如下图:  
 
20150908143828051.jpg
 
针对这个功能需要做自定义相机,根据Camera相机类和SurfaceView类来实现自定义图形预览拍照功能。
但在实现过程中出现几个难点:
1.如何将自己产品图片(上图的台灯)和摄像头预览的图片结合成一张图片。
2.拍照的图片在有些手机上面出现旋转了90度的情况(Android兼容性问题)。
3.某些手机会出现在camera.setParameters(parameters)的调用时候出现设置失败的异常java.lang.RuntimeException: setParameters failed
 
针对第一个问题:
    开始的考虑是在布局中SurfaceView上加一层ImageView来显示产品图片,然后通过截取View的方法截取SurfaceView和ImageView的父布局来实现,但截取view的时候发现只能截取静态的方法,对于像surfaceview之类的动态的view是不能够截取的(具体的截取方法请参考下面的源码),后来才有的是绘图的方式将预览图片的bitmap和产品图片的bitmap绘制到一张图片实现图片的合并(具体方法请参考下面源码)。
 
针对第二个问题:
    这个显然是android手机的兼容性问题,网上也有很大类似的网友提出此类问题,但给出的解决方法大都是先通过ExifInterface类来读取bitmap的旋转角度,然后根据旋转角度旋转bitmap实现,我也用这个方法检验过,但实际上此方法并不靠谱,原因是读取bitmap的角度始终为0,即使我图片在拍照后出现的旋转了90度,也是读取不了的。后来找一篇文章:http://www.xuebuyuan.com/1223069.html 该文章介绍了ExifInterface类在某些机型的有兼容性问题,并指出了问题产生的原因,我采用,拍照完成后,强制调用setPictureDegreeZero方法(具体实现参考下面源码)来重新将图片的旋转角度设置为0,终于以次方解决了问题。终于松了口气,啊哈哈。
 
 
针对第三个问题: 
  某些机型出现了设置拍照参数失败,是因为某些参数设置对应该机型不支持,这里我需要做到的是需要确认哪些参数有兼容性问题的,经测试发现,
// parameters.setPreviewFrameRate(3);// 每秒3帧 每秒从摄像头里面获得3个画面, 此参数在小米2上面支持,在红米note2上面不支持,
// parameters.setPreviewSize(PreviewWidth,PreviewHeight);// 获得摄像区域的大小
// parameters.setPictureSize(PreviewWidth,PreviewHeight);// 获得保存图片的大小 ----这个两个参数也会有兼容性问题。
 
    目前发现这里3个参数有兼容性问题,其它若有发现在补充,同时如果网友们发现了其它参数存在兼容性问题也烦请指出,谢谢。
针对不兼容性问题,尽量避免去设置它,
对应setPreviewSize和setPictureSize可以通过以下方式取得合适的预览尺寸进行设置,具体方法请参考下面源代码。
// 选择合适的预览尺寸List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();
 
 
具体实现代码:
  1. package com.example.eclipsetest;
    
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Iterator;
    import java.util.List;
    
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.Config;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.PixelFormat;
    import android.graphics.drawable.Drawable;
    import android.hardware.Camera;
    import android.hardware.Camera.AutoFocusCallback;
    import android.hardware.Camera.Parameters;
    import android.hardware.Camera.PictureCallback;
    import android.hardware.Camera.ShutterCallback;
    import android.hardware.Camera.Size;
    import android.media.ExifInterface;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Environment;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.Display;
    import android.view.Gravity;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.view.View.MeasureSpec;
    import android.view.View.OnClickListener;
    import android.view.WindowManager;
    import android.widget.Button;
    import android.widget.FrameLayout;
    import android.widget.ImageView;
    import android.widget.ImageView.ScaleType;
    import android.widget.Toast;
    
    /**
     * 自定义拍照,将特定图片添加到预览图片中保存起来
     * 
     * @ClassName: MyCameraDemo
     * @Description:
     * @author xiaoxiao
     * @date modify by 2015-9-8 下午2:09:16
     * 
     */
    public class MyCameraDemo extends Activity {
    	private SurfaceView surface = null;
    	private Button but = null;
    	private SurfaceHolder holder = null;
    	private Camera cam = null;
    	private boolean previewRunning = true;
    	private Button but2;
    	private ImageView iv_img;
    	private FrameLayout flay_view;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		super.setContentView(R.layout.main);
    		this.but = (Button) super.findViewById(R.id.but);
    		this.but2 = (Button) super.findViewById(R.id.but2);
    		this.surface = (SurfaceView) super.findViewById(R.id.surface);
    		iv_img = (ImageView) findViewById(R.id.iv_img);
    		flay_view = (FrameLayout) findViewById(R.id.flay_view);
    		this.holder = this.surface.getHolder();
    		this.holder.addCallback(new MySurfaceViewCallback());
    		this.holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    		this.holder.setFixedSize(500, 350);
    		this.but.setOnClickListener(new OnClickListenerImpl());
    		but2.setOnClickListener(new OnClickListener() {
    
    			@Override
    			public void onClick(View v) {
    				iv_img.setVisibility(View.VISIBLE);
    				iv_img.setImageResource(R.drawable.taideng);
    			}
    		});
    	}
    
    	private class OnClickListenerImpl implements OnClickListener {
    
    		@Override
    		public void onClick(View v) {
    			if (cam != null) {
    				cam.autoFocus(new AutoFocusCallbackImpl());
    			}
    		}
    
    	}
    
    	private class MySurfaceViewCallback implements SurfaceHolder.Callback {
    
    		@Override
    		public void surfaceChanged(SurfaceHolder holder, int format, int width,
    				int height) {
    
    		}
    
    		@Override
    		public void surfaceCreated(SurfaceHolder holder) {
    			if (cam != null) {
    				cam.stopPreview();// 停掉原来摄像头的预览
    				cam.release();// 释放资源
    				cam = null;// 取消原来摄像头
    			}
    			try {
    				cam = Camera.open(0); // 取得第一个摄像头
    			} catch (Exception e) {
    				// TODO: handle exception
    				Toast.makeText(MyCameraDemo.this, "摄像头打开失败", 0).show();
    				return;
    			}
    			cam = deal2(cam);
    		}
    
    		@Override
    		public void surfaceDestroyed(SurfaceHolder holder) {
    			if (cam != null) {
    				if (MyCameraDemo.this.previewRunning) {
    					cam.stopPreview(); // 停止预览
    					MyCameraDemo.this.previewRunning = false;
    				}
    				cam.stopPreview();
    				cam.release();
    				cam = null;
    			}
    		}
    
    	}
    
    	private class AutoFocusCallbackImpl implements AutoFocusCallback {
    
    		@Override
    		public void onAutoFocus(boolean success, Camera camera) {
    			if (success) { // 成功
    				cam.takePicture(sc, pc, jpgcall);
    			}
    		}
    
    	}
    
    	private PictureCallback jpgcall = new PictureCallback() {
    
    		@Override
    		public void onPictureTaken(byte[] data, Camera camera) { // 保存图片的操作
    			Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
    			// Bitmap bmp2 = new BitmapDrawable(iv_img).getDrawable();
    			// Bitmap bmp2 = BitmapFactory.decodeResource(getResources(),
    			// R.drawable.taideng);
    			// Bitmap bmp2 = drawableToBitamp(iv_img.getDrawable());
    			String fileName = "test_" + System.currentTimeMillis() + ".jpg";
    			String filePath = Environment.getExternalStorageDirectory()
    					.toString()
    					+ File.separator
    					+ "xiao"
    					+ File.separator
    					+ fileName;
    			bmp = rotateBitmapByDegree(bmp, 90);
    
    			save(bmp, filePath, fileName);
    			bmp = loadBitmap(filePath, true);
    			setPictureDegreeZero(filePath);
    
    			Bitmap bmp2 = getSmallBitmap(MyCameraDemo.this,
    					R.drawable.taideng, dip2px(MyCameraDemo.this, 200),
    					dip2px(MyCameraDemo.this, 200));
    			Bitmap bmp3 = combineBitmap(bmp, bmp2);
    			save(bmp3, filePath, fileName);
    			cam.stopPreview();
    			cam.startPreview();
    		}
    
    	};
    
    	private ShutterCallback sc = new ShutterCallback() {
    		@Override
    		public void onShutter() {
    			// 按下快门之后进行的操作
    		}
    	};
    	private PictureCallback pc = new PictureCallback() {
    
    		@Override
    		public void onPictureTaken(byte[] data, Camera camera) {
    
    		}
    
    	};
    
    	/**
    	 * 开始考虑用剪切的方法,但是截取只适合静态界面,这里surfaceView是动态的(在不断重绘)不能剪切,后来考虑用绘图的方式将两个bitmap合在一起。
    	 * 
    	 * @param view
    	 * @return
    	 */
    	private Bitmap cropView(View view) {
    		view.measure(MeasureSpec.makeMeasureSpec(100, MeasureSpec.UNSPECIFIED),
    				MeasureSpec.makeMeasureSpec(100, MeasureSpec.UNSPECIFIED));
    		view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    		view.buildDrawingCache();
    		Bitmap bitmap = view.getDrawingCache();
    		return bitmap;
    	}
    
    	
    	/**
    	 * 合并两张bitmap为一张
    	 * 
    	 * @param background
    	 * @param foreground
    	 * @return Bitmap
    	 */
    	public static Bitmap combineBitmap(Bitmap background, Bitmap foreground) {
    		if (background == null) {
    			return null;
    		}
    		int bgWidth = background.getWidth();
    		int bgHeight = background.getHeight();
    		int fgWidth = foreground.getWidth();
    		int fgHeight = foreground.getHeight();
    		Bitmap newmap = Bitmap
    				.createBitmap(bgWidth, bgHeight, Config.ARGB_8888);
    		Canvas canvas = new Canvas(newmap);
    		canvas.drawBitmap(background, 0, 0, null);
    		canvas.drawBitmap(foreground, (bgWidth - fgWidth) / 2,
    				(bgHeight - fgHeight) / 2, null);
    		canvas.save(Canvas.ALL_SAVE_FLAG);
    		canvas.restore();
    		return newmap;
    	}
    
    	private void save(Bitmap bitmap, String filePath, String fileName) {
    		File file = new File(filePath);
    		if (!file.getParentFile().exists()) {
    			file.getParentFile().mkdirs(); // 创建文件夹
    		}
    		try {
    			BufferedOutputStream bos = new BufferedOutputStream(
    					new FileOutputStream(file));
    			bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos); // 向缓冲区之中压缩图片
    			bos.flush();
    			bos.close();
    			Toast.makeText(MyCameraDemo.this,
    					"拍照成功,照片已保存在" + fileName + "文件之中!", Toast.LENGTH_SHORT)
    					.show();
    		} catch (Exception e) {
    			Toast.makeText(MyCameraDemo.this, "拍照失败!", Toast.LENGTH_SHORT)
    					.show();
    		}
    	}
    
    	private Bitmap bitmap;
    
    	private Bitmap drawableToBitamp(Drawable drawable) {
    		int w = drawable.getIntrinsicWidth();
    		int h = drawable.getIntrinsicHeight();
    		System.out.println("Drawable转Bitmap");
    		Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
    				: Bitmap.Config.RGB_565;
    		bitmap = Bitmap.createBitmap(w, h, config);
    		// 注意,下面三行代码要用到,否在在View或者surfaceview里的canvas.drawBitmap会看不到图
    		Canvas canvas = new Canvas(bitmap);
    		drawable.setBounds(0, 0, w, h);
    		drawable.draw(canvas);
    		return bitmap;
    	}
    
    	/**
    	 * 读取图片的旋转的角度, 某些机型此方法无效
    	 * 
    	 * @param path
    	 *            图片绝对路径
    	 * @return 图片的旋转角度
    	 */
    	private int getBitmapDegree(String path) {
    		int degree = 0;
    		try {
    			// 从指定路径下读取图片,并获取其EXIF信息
    			ExifInterface exifInterface = new ExifInterface(path);
    			// 获取图片的旋转信息
    			int orientation = exifInterface.getAttributeInt(
    					ExifInterface.TAG_ORIENTATION,
    					ExifInterface.ORIENTATION_NORMAL);
    			switch (orientation) {
    			case ExifInterface.ORIENTATION_ROTATE_90:
    				degree = 90;
    				break;
    			case ExifInterface.ORIENTATION_ROTATE_180:
    				degree = 180;
    				break;
    			case ExifInterface.ORIENTATION_ROTATE_270:
    				degree = 270;
    				break;
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return degree;
    	}
    
    	/**
    	 * 将图片按照某个角度进行旋转
    	 * 
    	 * @param bm
    	 *            需要旋转的图片
    	 * @param degree
    	 *            旋转角度
    	 * @return 旋转后的图片
    	 */
    	public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
    		Bitmap returnBm = null;
    
    		// 根据旋转角度,生成旋转矩阵
    		Matrix matrix = new Matrix();
    		matrix.postRotate(degree);
    		try {
    			// 将原始图片按照旋转矩阵进行旋转,并得到新的图片
    			returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(),
    					bm.getHeight(), matrix, true);
    		} catch (OutOfMemoryError e) {
    		}
    		if (returnBm == null) {
    			returnBm = bm;
    		}
    		if (bm != returnBm) {
    			bm.recycle();
    		}
    		return returnBm;
    	}
    
    	// 控制图像的正确显示方向
    	private void setDispaly(Camera.Parameters parameters, Camera camera) {
    		if (Integer.parseInt(Build.VERSION.SDK) >= 8) {
    			setDisplayOrientation(camera, 90);
    		} else {
    			parameters.setRotation(90);
    		}
    
    	}
    
    	// 实现的图像的正确显示
    	private void setDisplayOrientation(Camera camera, int i) {
    		Method downPolymorphic;
    		try {
    			downPolymorphic = camera.getClass().getMethod(
    					"setDisplayOrientation", new Class[] { int.class });
    			if (downPolymorphic != null) {
    				downPolymorphic.invoke(camera, new Object[] { i });
    			}
    		} catch (Exception e) {
    			Log.e("Came_e", "图像出错");
    		}
    	}
    
    	private Camera deal2(Camera mCamera) {
    		// 设置camera预览的角度,因为默认图片是倾斜90度的
    		// mCamera.setDisplayOrientation(90);
    
    		int PreviewWidth = 0;
    		int PreviewHeight = 0;
    		WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);// 获取窗口的管理器
    		Display display = wm.getDefaultDisplay();// 获得窗口里面的屏幕
    		Camera.Parameters parameters = mCamera.getParameters();
    		// parameters.setFlashMode(Parameters.FLASH_MODE_TORCH); //开启闪光灯,支持
    		setDispaly(parameters, mCamera);
    		// parameters.setRotation(90);
    		// parameters.setPreviewFrameRate(3);// 每秒3帧 每秒从摄像头里面获得3个画面,
    		// 某些机型(红米note2)不支持
    		parameters.setPictureFormat(PixelFormat.JPEG);// 设置照片输出的格式
    		parameters.set("jpeg-quality", 100);// 设置照片质量
    		try {
    			// 选择合适的预览尺寸
    			List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();
    			// 如果sizeList只有一个我们也没有必要做什么了,因为就他一个别无选择
    			if (sizeList.size() > 1) {
    				Iterator<Camera.Size> itor = sizeList.iterator();
    				while (itor.hasNext()) {
    					Camera.Size cur = itor.next();
    					if (cur.width >= PreviewWidth
    							&& cur.height >= PreviewHeight) {
    						PreviewWidth = cur.width;
    						PreviewHeight = cur.height;
    						break;
    					}
    				}
    			}
    			parameters.setPreviewSize(PreviewWidth, PreviewHeight); // 获得摄像区域的大小
    			parameters.setPictureSize(PreviewWidth, PreviewHeight); // 获得保存图片的大小
    			// parameters.setPreviewSize(display.getWidth(),
    			// display.getWidth()); // 获得摄像区域的大小
    			// parameters.setPictureSize(display.getWidth(),
    			// display.getWidth());// 设置拍出来的屏幕大小
    
    		} catch (Exception e) {
    			Log.e("MyCameraDemo", e.toString());
    		}
    		try {
    			cam.setPreviewDisplay(MyCameraDemo.this.holder);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		mCamera.setParameters(parameters);// 把上面的设置 赋给摄像头
    		mCamera.startPreview();// 开始预览
    		mCamera.cancelAutoFocus();// 2如果要实现连续的自动对焦,这一句必须加上
    		previewRunning = true;
    		return mCamera;
    	}
    
    	/** 从给定路径加载图片 */
    	public Bitmap loadBitmap(String imgpath) {
    		return BitmapFactory.decodeFile(imgpath);
    	}
    
    	/** 从给定的路径加载图片,并指定是否自动旋转方向 */
    	public Bitmap loadBitmap(String imgpath, boolean adjustOritation) {
    		if (!adjustOritation) {
    			return loadBitmap(imgpath);
    		} else {
    			Bitmap bm = loadBitmap(imgpath);
    			int digree = 0;
    			ExifInterface exif = null;
    			try {
    				exif = new ExifInterface(imgpath);
    			} catch (IOException e) {
    				e.printStackTrace();
    				exif = null;
    			}
    			if (exif != null) {
    				// 读取图片中相机方向信息
    				// int ori = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
    				// ExifInterface.ORIENTATION_NORMAL);
    				int ori = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
    						ExifInterface.ORIENTATION_FLIP_VERTICAL);
    				// 计算旋转角度
    				switch (ori) {
    				case ExifInterface.ORIENTATION_ROTATE_90:
    					digree = 90;
    					break;
    				case ExifInterface.ORIENTATION_ROTATE_180:
    					digree = 180;
    					break;
    				case ExifInterface.ORIENTATION_ROTATE_270:
    					digree = 270;
    					break;
    				default:
    					digree = 0;
    					break;
    				}
    			}
    			if (digree != 0) {
    				// 旋转图片
    				Matrix m = new Matrix();
    				m.postRotate(digree);
    				bm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(),
    						bm.getHeight(), m, true);
    			}
    			return bm;
    		}
    	}
    
    	/**
    	 * 将图片的旋转角度置为0  ,此方法可以解决某些机型拍照后图像,出现了旋转情况
    	 * 
    	 * @Title: setPictureDegreeZero
    	 * @param path
    	 * @return void
    	 * @date 2012-12-10 上午10:54:46
    	 */
    	private void setPictureDegreeZero(String path) {
    		try {
    			ExifInterface exifInterface = new ExifInterface(path);
    			// 修正图片的旋转角度,设置其不旋转。这里也可以设置其旋转的角度,可以传值过去,
    			// 例如旋转90度,传值ExifInterface.ORIENTATION_ROTATE_90,需要将这个值转换为String类型的
    			exifInterface.setAttribute(ExifInterface.TAG_ORIENTATION, "no");
    			exifInterface.saveAttributes();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    
    	}
    
    	/**
    	 * 根据路径获取图片并压缩返回bitmap用于显示
    	 * 
    	 * @param context
    	 * @param id
    	 * @return
    	 */
    
    	private  Bitmap getSmallBitmap(Context context, int id,int width,int height) {
    		final BitmapFactory.Options options = new BitmapFactory.Options();
    		options.inJustDecodeBounds = true;
    		BitmapFactory.decodeResource(context.getResources(), id, options);
    		// 计算 缩略图大小为原始图片大小的几分之一 inSampleSize:缩略图大小为原始图片大小的几分之一
    		options.inSampleSize = calculateInSampleSize(options, width, height);
    		options.inJustDecodeBounds = false;
    
    		return BitmapFactory.decodeResource(context.getResources(), id, options);
    	}
    	
    	/**
    	 * 计算图片的缩放值
    	 * 
    	 * @param options
    	 * @param reqWidth
    	 * @param reqHeight
    	 * @return
    	 */
    	private  int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    		// Raw height and width of image
    		final int height = options.outHeight;
    		final int width = options.outWidth;
    		int inSampleSize = 1;
    
    		if (height > reqHeight || width > reqWidth) {
    			final int heightRatio = Math.round((float) height / (float) reqHeight);
    			final int widthRatio = Math.round((float) width / (float) reqWidth);
    			inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    
    		}
    		return inSampleSize;
    
    	}
    	/**
    	 * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
    	 */
    	public static int dip2px(Context context, float dpValue) {
    		final float scale = context.getResources().getDisplayMetrics().density;
    		return (int) (dpValue * scale + 0.5f);
    	}
    
    	/**
    	 * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
    	 */
    	public static int px2dip(Context context, float pxValue) {
    		final float scale = context.getResources().getDisplayMetrics().density;
    		return (int) (pxValue / scale + 0.5f);
    	}
    }
    

      


 
布局文件:
  1. <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
        <FrameLayout
            android:id="@+id/flay_view"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="1000" >
            <SurfaceView
                android:id="@+id/surface"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_weight="1000" />
            <ImageView 
                android:id="@+id/iv_img"
                android:layout_width="200dp"
                android:layout_height="200dp"
                android:layout_gravity="center"
                android:visibility="gone"
                android:src="@drawable/taideng"
                android:scaleType="fitCenter"/>
        </FrameLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="horizontal" >
            <Button
                android:id="@+id/but"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="照相" />
            <Button
                android:id="@+id/but2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="添加图片" />
        </LinearLayout>
    </LinearLayout>  
    

      

 
最后不要忘了权限:
    
  1. <uses-feature android:name="android.hardware.camera" />
        <uses-feature android:name="android.hardware.camera.autofocus" />
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
 
 



原文地址:https://www.cnblogs.com/xiaoxiao-study/p/867d2ad9206c8600186c90690f1e7965.html