调用系统相机拍照有两种方式:

ONE:  使用Intent传递Bitmap

    button.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");startActivityForResult(intent,1);}

为一个button 设置点击事件,隐式intent的action为"android.media.action.IMAGE_CAPTURE"

     同样应该为其设置回调方法。。

   protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode,resultCode,data);if(resultCode==RESULT_OK){if(requestCode==1){Bundle  bundler=data.getExtras();Bitmap bitmap=(Bitmap) bundler.get("data");    iamgeview.setImageBitmap(bitmap);   //自定义一个ImageView接受Bitmap}}}

相机的Activity会将Bitmap对象序列化后存放Bundler里 。大概代码:

               Bundle bundle=new Bundle();bundle.putParcelable("data", object);  //object即是序列化后的Bitmap对象intent.putExtras(bundle);

这里不需要权限,因为我们只是通过intent打开相机的activity而打开相机的activity并没有权限。      这种方法得到的Bitmap只是缩略图,画质非常的差,android做了相关处理防止内存泄露。  所以一般我们用的是第二种。

TWO:   使用Intent传递URI:

二者不同的是前者直接在两个Activity之间传递Bitmap,而后者是发送一个URI给相机的Acitivity后让其把Bitmap数据直接保存到URI对应的文件里,

然后我们的app再直接访问这个文件,这样就不存在内存泄露了。

      button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {File file=new File(getExternalCacheDir().getPath(),"Camera");if (file.exists()){file.delete();}try {file.createNewFile();} catch (IOException e) {e.printStackTrace();}Uri image_uri=Uri.fromFile(file);Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");intent.putExtra(MediaStore.EXTRA_OUTPUT,image_uri);startActivityForResult(intent,1);}
});     
getExternalCacheDir() // 得到系统给app分配的外置存储空间文件夹:0/Android/data/包名/cache
 
   我们在这里创建了一个名为Camera的文件。然后通过Uri.fromFile(file)得到文件的uri,然后将其存放在一个"key"为MediaStore.EXTRA_OUTPUT这个必须是固定格式不然相机Activity得不到正确的数据。    这个key的本身的值为"output"。继续修改回调方法。
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode,resultCode,data); if(resultCode==RESULT_OK){ if(requestCode==1){ File file=new File(getExternalCacheDir(),"Camera"); if(file.exists()){ try { imageview.setImageBitmap(BitmapFactory.decodeStream(new FileInputStream(file))); } catch (FileNotFoundException e) { } } } } }
         将这个file文件读取后放到imageview中,然后就ok了。  
  为什么这里任然要执行onActivityResult 应为我们发起的intent目标Activity本身就是一个最终会执行setResult(),然后finish()。
 不同上述的是 前者无intent.putExtra(MediaStore.EXTRA_OUTPUT,image_uri);所以猜想源码肯定是会读取这个值,如果读不到则把Bitmap数据包装成intent
发回来,能读到则把Bitmap数据存放到uri对应的File文件,而intent的返回值为null。
 
  注意: intent传输uri方法在sdk版本低于24也就是android7.0时使用,而对于7.0及以上我们则需要用到内容提供器(FileProvider),
 详细阅读这里:http://blog.csdn.net/djy1992/article/details/72533310   
   为什么出现这种问题,其实就是Uri.fromFile(file)方法不让用了,我们不能从File转换Uri了,所以需要FileProvider。
从继承关系看   
public class FileProvider extends ContentProvider 

FileProvider是继承ContentProvider所以也需要在Mainfest中定义

  <providerandroid:name="android.support.v4.content.FileProvider"    android:authorities="com.android.shuai"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file"></meta-data>
</provider>

  name:为定值"android.support.v4.content.FileProvider"。相当自定义的ContentProvider,FileProvider为重写的类。

     authorities:自定义值, 
android:exported="false": 不可更改否则报错  android:grantUriPermissions="true"> :不可更改否则报错

     <meta-data>标签内的name:为定值, 

 resource:新建XML目录新建XML文件 

   <?xml version="1.0" encoding="utf-8"?><paths xmlns:android="http://schemas.android.com/apk/res/android"><external-path name="LinLangTianXia" path="" /></paths>
    <external-path> : 首地址,通俗讲就是地址从这里开始。 
    path="" :  首地址后的详细地址:
 臂如标签就是上述的<external-path> ,path=""; 那么我们的内容提供器可以提供的转换File地址为:
 
"/Storage/"。只要要转换的File文件被这个目录包括就可以。

如果path="A" 那就是"/Storage/A/"。

   同理<external-path>标签还有很多;像<files-path><cache-path>这种,如果要转换的File文件不在上述内就会报错。
  name则是上述地址的映射,比如上述"/Storage/A/" name="LinLangTianXia" 最后为/LinLangTianXia/, 也就是说将我们的真实地址隐藏了。
 
 举个例子    :7.0以下得到的Uri为:file:///storage/emulated/0/Android/data/listen.myapplication/cache/Camera: 
          7.0以上得到的Uri为: content://com.android.shuai/LinLangTianXia/Android/data/listen.myapplication/cache/Camera:
                     格式为:content://authorities/name/+未映射的地址。 authorities告诉程序是那个内容提供者, name映射真实地址。 
当然这个转换过程不用我们来操作看代码:
if(Build.VERSION.SDK_INT>=24){ image_uri=FileProvider.getUriForFile(Main.this,"com.android.shuai",file); } 
 调用一下FileProvider.getUriForFile(Context,authorities,file)就ok了, 这个方法会自动帮我们封装Uri,这个file就是上述我们说的file
必须在其内部,否则报错。     其他不变同7.0以下下,   通俗讲FileProvider只是帮我们修改了Uri使其更安全而已。   当然阅读到这会有个疑问它们具体的过程到底是怎样的,我们看源码:
public static Uri getUriForFile(Context context, String authority, File file) { final PathStrategy strategy = getPathStrategy(context, authority); return strategy.getUriForFile(file); //看到strategy对象可以帮我们转换File为Uri } 
 进入PathStrategy发现是个接口实现它的是类SimplePathStrategy,继续进:
 
 
static class SimplePathStrategy implements PathStrategy {private final String mAuthority;private final HashMap<String, File> mRoots = new HashMap<String, File>();public SimplePathStrategy(String authority) {mAuthority = authority;    //传入的authority。}@Overridepublic Uri getUriForFile(File file) {String path;try {path = file.getCanonicalPath();   //相当file.getPath();} catch (IOException e) {throw new IllegalArgumentException("Failed to resolve canonical path for " + file);}// Find the most-specific root pathMap.Entry<String, File> mostSpecific = null;for (Map.Entry<String, File> root : mRoots.entrySet()) {final String rootPath = root.getValue().getPath();if (path.startsWith(rootPath) && (mostSpecific == null|| rootPath.length() > mostSpecific.getValue().getPath().length())) {mostSpecific = root;}}if (mostSpecific == null) {throw new IllegalArgumentException("Failed to find configured root that contains " + path);}// Start at first char of path under rootfinal String rootPath = mostSpecific.getValue().getPath();if (rootPath.endsWith("/")) {path = path.substring(rootPath.length());} else {path = path.substring(rootPath.length() + 1);}// Encode the tag and path separatelypath = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/");return new Uri.Builder().scheme("content").authority(mAuthority).encodedPath(path).build();}
}                                         //截取部分
 
 
看这行: return new Uri.Builder().scheme("content")   .authority(mAuthority).encodedPath(path).build(); 是不是就是我们上述的结果。
 看到相信你肯定会有疑惑,既然Uri被这种方式修改那在接收端会不会存在另一种方式给它修改回去,没错,接着看剩下源码:
 

class SimplePathStrategy {

private final String mAuthority;

private final HashMap<String, File> mRoots = new HashMap<String, File>();

public SimplePathStrategy(String authority) {

mAuthority = authority;

}

/**

* Add a mapping from a name to a filesystem root. The provider only offers

* access to files that live under configured roots.

*/

public void addRoot(String name, File root) {

if (TextUtils.isEmpty(name)) {

throw new IllegalArgumentException("Name must not be empty");

}

try {

// Resolve to canonical path to keep path checking fast

root = root.getCanonicalFile();

} catch (IOException e) {

throw new IllegalArgumentException(

"Failed to resolve canonical path for " + root, e);

}

mRoots.put(name, root);

}

public File getFileForUri(Uri uri) {

String path = uri.getEncodedPath();

final int splitIndex = path.indexOf('/', 1);

final String tag = Uri.decode(path.substring(1, splitIndex));

path = Uri.decode(path.substring(splitIndex + 1));

final File root = mRoots.get(tag);

if (root == null) {

throw new IllegalArgumentException("Unable to find configured root for " + uri);

}

File file = new File(root, path);

try {

file = file.getCanonicalFile();

} catch (IOException e) {

throw new IllegalArgumentException("Failed to resolve canonical path for " + file);

}

if (!file.getPath().startsWith(root.getPath())) {

throw new SecurityException("Resolved path jumped beyond configured root");

}

return file}

}

笔者将其提取然后直接调用getFileForUri报错提示root为空,所以上述的addRoot要先调用,

那这个addroot方法又在那里调用呢?

public SimplePathStrategy parsePathStrategy(Context context, String authority)throws IOException, XmlPullParserException {final SimplePathStrategy strat = new SimplePathStrategy(authority);    //这里创建了SimplePathStrategy对象final ProviderInfo info = context.getPackageManager().resolveContentProvider(authority, PackageManager.GET_META_DATA);final XmlResourceParser in = info.loadXmlMetaData(context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);if (in == null) {throw new IllegalArgumentException("Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");}int type;while ((type = in.next()) != END_DOCUMENT) {if (type == START_TAG) {final String tag = in.getName();final String name = in.getAttributeValue(null, ATTR_NAME);String path = in.getAttributeValue(null, ATTR_PATH);File target = null;if (TAG_ROOT_PATH.equals(tag)) {target = DEVICE_ROOT;} else if (TAG_FILES_PATH.equals(tag)) {target = context.getFilesDir();} else if (TAG_CACHE_PATH.equals(tag)) {target = context.getCacheDir();} else if (TAG_EXTERNAL.equals(tag)) {target = Environment.getExternalStorageDirectory();} else if (TAG_EXTERNAL_FILES.equals(tag)) {File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);if (externalFilesDirs.length > 0) {target = externalFilesDirs[0];}} else if (TAG_EXTERNAL_CACHE.equals(tag)) {File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context);if (externalCacheDirs.length > 0) {target = externalCacheDirs[0];}}if (target != null) {strat.addRoot(name, buildPath(target, path));       //这里调用了addRoot方法();}}}return strat;
}
        ok  我们的逻辑也算梳理顺了,  在相机源码中肯定先调用了parsePathStrategy()方法得到了SimplePathStrategy
对象,然后直接调用了它的getFileForUri()方法于是就将Uri给改了回来。    

     

     

 
 

android 使用相机拍照以及FileProvider源码浅析相关推荐

  1. Android 热修复 Tinker接入及源码浅析

    本文已在我的公众号hongyangAndroid首发. 转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/54882693 本文出自 ...

  2. Android 热修复 Tinker接入及源码浅析最精彩没有之一

    接入Tinker 接入tinker目前给了两种方式,一种是基于命令行的方式,类似于AndFix的接入方式:一种就是gradle的方式. 考虑早期使用Andfix的app应该挺多的,以及很多人对grad ...

  3. Android驱动代码dump,Android 重学系列 ion驱动源码浅析

    前言 上一篇文章,在解析初始化GraphicBuffer中,遇到一个ion驱动,对图元进行管理.首先看看ion是怎么使用的: 1.打开驱动: mIonFd = open(ION_DEVICE, O_R ...

  4. Android自定义相机拍照、图片裁剪的实现

    原文:Android自定义相机拍照.图片裁剪的实现 最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用ocr识别出来,再调用题库搜索接口搜索出来展示给用户,类 ...

  5. 十分钟实现 Android Camera2 相机拍照

    1. 前言 因为工作中要使用Android Camera2 API,但因为Camera2比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在CSDN上记录了下,希望能帮助到更 ...

  6. android 点击事件消费,Android View事件分发和消费源码简单理解

    Android View事件分发和消费源码简单理解 前言: 开发过程中觉得View事件这块是特别烧脑的,看了好久,才自认为看明白.中间上网查了下singwhatiwanna粉丝的读书笔记,有种茅塞顿开 ...

  7. Android Jetpack组件之Navigation使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  8. Android Jetpack组件之 Room使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  9. Android Jetpack组件之 Paging使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

最新文章

  1. 现代农业谋定县域经济-农业大健康·万祥军:载体幸福美丽
  2. 使用netsh.exe命令配置TCP/IP
  3. mqtt 一对多_MQTT协议简要分析
  4. ●BZOJ 4556 [Tjoi2016Heoi2016]字符串
  5. Generate Java objects for FpML using JAXB and Maven: The Easy Way!
  6. 字节流读数据(一次读一个字节数组数据)
  7. 【数据库系统】DBMS、DBS、DBA、DB的区别
  8. 亚马逊创始人下月将乘自家火箭进入太空 亲弟弟同行
  9. python断点调试出现问题_Python错误、调试
  10. PDO NOsuch file 将DNHOST修改
  11. Jmeter之app性能测试(ios,android)
  12. matlab画圆的命令_matlab 如何画圆
  13. 技巧:Eclipse阿里代码规范插件
  14. visio一分二连接线_Visio-换线流程(初稿)
  15. Gauss 求积公式及代码
  16. 三个点在同一个半圆的概率_求解四只鸭子在同一半圆池塘的概率
  17. NaN是什么? isNaN()/Number.NaN
  18. 2017qs世界计算机排名,2017qs世界大学学科排名完整版
  19. 微信小程序(四) 节点查询 | wx.createSelectorQuery
  20. (十一)openstack------块存储服务cinder,磁盘(卷)扩容,使用 NFS---后端存储

热门文章

  1. 跳棋的C语言,跳棋游戏C语言程序设计(数据结构课程设计).doc
  2. 《树莓派项目实战》第二节 制作LED呼吸灯
  3. 2022年美国大学生数学建模竞赛规律总结
  4. android opencv
  5. 再谈微积分下放中学的现实意义
  6. The Suspects浅析
  7. 5.5.4. Removing a Constraint
  8. Python随机数判断
  9. SQL每日一练——第1天: 基础查询
  10. Java分布式事务实现Atomikos