android系统相机的使用、及解决拍照闪退的问题

相机已成为android手机不可或缺的一部分,我们在开发可能也会涉及到关于相机的使用,以及在使用中会遇到一些问题(重点),下面把我个人的一点经验贴出来,仅供大家参考学习。

最简单的方式直接调用系统相机进行拍照,虽然能满足拍照的要求,但拍照的功能和界面却局限系统相机的样子,于是我们这时根据Camera API实现自己的拍照和摄像程序,当然这并不是我想说重点,今天的重点在于开发中不同型号手机调用系统相机拍完照出现闪退的问题

一、调用安卓系统相机拍照


首先应该添加权限:
1
2
3
<uses-permission android:name= "android.permission.CAMERA"  />  
<uses-feature android:name= "android.hardware.camera"  />   
<uses-feature android:name= "android.hardware.camera.autofocus"  />

通过Intent 打开系统相机

1
2
3
4
5
6
7
8
9
10
openbButton.setOnClickListener( new  OnClickListener() {
             @Override
             public  void  onClick(View v) {
                 // 使用意图直接调用安装在手机上的照相机
                 Intent intent =  new  Intent(
                         android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                 // 打开照相机,设置请求码
                 startActivityForResult(intent, OPEN_RESULT);
             }
         });

在onActivityResult中,通过相机返回意图intent.get("data") 取得拍照的图片

1
2
3
4
5
6
7
8
9
@Override
protected  void  onActivityResult( int  requestCode,  int  resultCode, Intent data) {
     if  (requestCode == OPEN_RESULT) {
         if  (resultCode == RESULT_OK) {
             Bundle bundle = data.getExtras();
             Bitmap bitmap = (Bitmap) bundle.get( "data" );
             imageView.setImageBitmap(bitmap);
         }
     }
二.系统相机拍照方式---解决系统相机拍照闪退
上面的代码,实现了调用系统相机,但是经过我在测试数10台手机之后发现,红米和三星出现拍完照后返回时,黑屏闪退。测试结果说明,该方法只试用于部分手机。
观察错误信息, 提示 onActivityResult 中空指针异常,相机拍照返回的 Intent data这个参数为空无法取出刚刚拍照的Bitmap,研究了一下,应该是不同手机厂商在系统定制时,隐藏了拍照返回意图。厂商系统定制我们不能修改,只能采取另外的方式来保存照片。针对返回意图为空的问题,解决办法如下:
通过把照片保存在指定路径,完成后通过路径取出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
takePhoto2.setOnClickListener( new  OnClickListener() {
     @Override
     public  void  onClick(View v) {
         // 调用系统相机
         Intent intent =  new  Intent(MediaStore.ACTION_IMAGE_CAPTURE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         // 取当前时间为照片名
         mPictureFile = DateFormat.format( "yyyyMMdd_hhmmss" ,
                 Calendar.getInstance(Locale.CHINA))
                 ".jpg" ;
         Log.d( "onactivity" "mPictureFile:"  + mPictureFile);
         filePath = getPhotoPath() + mPictureFile;
         // 通过文件创建一个uri中
         Uri imageUri = Uri.fromFile( new  File(filePath));
         // 保存uri对应的照片于指定路径
         intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
         startActivityForResult(intent, OPEN_RESULT2);
     }
});

拍照完成返回时,在onActivityResult中,通过指定文件的绝对路径,获得拍照的图片

1
2
3
4
5
6
7
8
if  (requestCode == OPEN_RESULT2) {
     if  (resultCode == RESULT_OK) {
         Log.e( "takePhoto" , filePath);
         Bitmap bitmap = BitmapFactory.decodeFile(filePath);
         // imageView.setImageURI(Uri.fromFile(new File(filePath)));
         imageView.setImageBitmap(bitmap);
     }
}

三、使用自定义路径拍照闪退问题
如果使用方法二拍照依然部分手机出现闪退的情况,观察打印的错误信息,依然NullPointerException,这个问题相对来说也比较简单,但第一次遇到也把我困住了一会,异常信息我就不贴出来了,错误的原因是这样的:
当我们 通过 startActivityForResult(intent, OPEN_RESULT);   打开系统相机时,部门手机由于系统优化的原因,会把当前页面的成员变量回收,当拍完照片返回时,在onActivityResult方法中是要通过照片路径来获得Bitmap,而这种情况下保存照片路径的mPictureFile指向的字符串已经被回收,mPictureFile值就为空,导致产生空指针异常而闪退。解决办法是在Activity中加上onSaveInstanceState生命周期方法,保证对象在被系统回收之前能够先以hashMap的形式保存下来,当返回时再从onRestoreInstanceState方法中恢复出来
下面把整个应用程序代码写出来,结合Activity的生命周期方法,可以更好的观察理解这个问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package  cn.keno.takephoto;
 
import  java.io.File;
import  java.util.Calendar;
import  java.util.Locale;
 
import  android.app.Activity;
import  android.content.Intent;
import  android.graphics.Bitmap;
import  android.graphics.BitmapFactory;
import  android.net.Uri;
import  android.os.Bundle;
import  android.os.Environment;
import  android.provider.MediaStore;
import  android.text.TextUtils;
import  android.text.format.DateFormat;
import  android.util.Log;
import  android.view.View;
import  android.view.View.OnClickListener;
import  android.widget.Button;
import  android.widget.ImageView;
 
public  class  MainActivity  extends  Activity {
     private  final  String TAG =  "TakePhoto" ;
     private  Button openbButton;
     private  Button takePhoto2;  // 拍照2
     private  Button pickButton;
     private  ImageView imageView;
     private  String mPictureFile;
     private  String filePath;
 
     private  final  int  OPEN_RESULT =  1 // 打开相机
     private  final  int  PICK_RESULT =  2 // 查看相册
     private  final  int  OPEN_RESULT2 =  3 // 打开相机2
 
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         Log.i(TAG,  "onCreate" );
         openbButton = (Button) findViewById(R.id.btnTakePhoto);
         takePhoto2 = (Button) findViewById(R.id.btnTakePhoto2);
         pickButton = (Button) findViewById(R.id.btnPick);
         imageView = (ImageView) findViewById(R.id.imgPotho);
         openbButton.setOnClickListener( new  OnClickListener() {
             @Override
             public  void  onClick(View v) {
                 // 使用意图直接调用安装在手机上的照相机
                 Intent intent =  new  Intent(
                         android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
                 // 打开照相机,设置请求码
                 startActivityForResult(intent, OPEN_RESULT);
             }
         });
 
         takePhoto2.setOnClickListener( new  OnClickListener() {
             @Override
             public  void  onClick(View v) {
                 // 调用系统相机
                 Intent intent =  new  Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                 intent.addCategory(Intent.CATEGORY_DEFAULT);
                 // 取当前时间为照片名
                 mPictureFile = DateFormat.format( "yyyyMMdd_hhmmss" ,
                         Calendar.getInstance(Locale.CHINA))
                         ".jpg" ;
                 Log.d( "onactivity" "mPictureFile:"  + mPictureFile);
                 filePath = getPhotoPath() + mPictureFile;
                 // 通过文件创建一个uri中
                 Uri imageUri = Uri.fromFile( new  File(filePath));
                 // 保存uri对应的照片于指定路径
                 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                 startActivityForResult(intent, OPEN_RESULT2);
             }
         });
 
         pickButton.setOnClickListener( new  OnClickListener() {
             @Override
             public  void  onClick(View v) {
                 // 使用意图直接调用手机相册
                 Intent intent =  new  Intent(
                         Intent.ACTION_PICK,
                         android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                 // 打开手机相册,设置请求码
                 startActivityForResult(intent, PICK_RESULT);
             }
         });
     }
 
     @Override
     protected  void  onSaveInstanceState(Bundle outState) {
         super .onSaveInstanceState(outState);
         outState.putString( "filePath" , filePath);
         Log.d(TAG,  "onSaveInstanceState" );
     }
 
     @Override
     protected  void  onRestoreInstanceState(Bundle savedInstanceState) {
         super .onRestoreInstanceState(savedInstanceState);
         if  (TextUtils.isEmpty(filePath)) {
             filePath = savedInstanceState.getString( "filePath" );
         }
         Log.d(TAG,  "onRestoreInstanceState" );
     }
 
     @Override
     protected  void  onStart() {
         super .onStart();
         Log.i(TAG,  "onStart" );
     }
 
     @Override
     protected  void  onRestart() {
         super .onRestart();
         Log.i(TAG,  "onRestart" );
     }
 
     @Override
     protected  void  onResume() {
         super .onResume();
         Log.i(TAG,  "onResume" );
     }
 
     @Override
     protected  void  onPause() {
         super .onPause();
         Log.i(TAG,  "onPause" );
     }
 
     @Override
     protected  void  onStop() {
         super .onStop();
         Log.i(TAG,  "onStop" );
     }
 
     @Override
     protected  void  onDestroy() {
         super .onDestroy();
         Log.i(TAG,  "onDestroy" );
     }
 
     @Override
     protected  void  onActivityResult( int  requestCode,  int  resultCode, Intent data) {
         if  (requestCode == OPEN_RESULT) {
             if  (resultCode == RESULT_OK) {
                 Bundle bundle = data.getExtras();
                 Bitmap bitmap = (Bitmap) bundle.get( "data" );
                 imageView.setImageBitmap(bitmap);
             }
         else  if  (requestCode == PICK_RESULT) {
             // 表示选择图片库的图片结果
             if  (resultCode == RESULT_OK) {
                 Uri uri = data.getData();
                 imageView.setImageURI(uri);
             }
         else  if  (requestCode == OPEN_RESULT2) {
             if  (resultCode == RESULT_OK) {
                 Log.e( "takePhoto" , filePath);
                 Bitmap bitmap = BitmapFactory.decodeFile(filePath);
                 // imageView.setImageURI(Uri.fromFile(new File(filePath)));
                 imageView.setImageBitmap(bitmap);
             }
         }
     }
 
     /**
      * 获得照片路径
     
      * @return
      */
     private  String getPhotoPath() {
         return  Environment.getExternalStorageDirectory() +  "/DCIM/" ;
     }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值