事例场景: 在公司开发的产品中 有开启相机的功能 开启系统相机后 把拍下的照片保存 并且预览 然后拿到这个照片地址 返回 发布前已经测试 这个模块没有问题 后来在一款三星的机型上面 测试出来启动相机 后 拍照点击保存 不会再出现 我自己实现的 预览的界面 后来分析 是个别机型 ROM 太少 把拍照调用系统相机后 系统相机在前台 后台进程在资源不足的情况下 被系统回收了 导致这个BUG的出现 先贴一下 我没有解决BUG之前的代码 和 布局文件 如下:
public class TakingPicturesActivity extends FragmentActivity implements View.OnClickListener {
private static final int START_CAMERA = 0x1;
private final static int REQUEST_CAMERA = 0x2;
private static final String ACTION_CAMERA = "android.media.action.IMAGE_CAPTURE";
private Button mButtonCancel;
private Button mButtonSend;
private Uri mSavedPicUri;
private FragmentManager fm;
private FragmentTransaction ft;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case START_CAMERA:
//开启系统相机
startCamera();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.rc_ac_camera);
mButtonCancel = (Button) findViewById(R.id.rc_back);
mButtonSend = (Button) findViewById(R.id.rc_send);
Message msg = Message.obtain();
msg.what = START_CAMERA;
mHandler.sendMessage(msg);
mButtonCancel.setOnClickListener(this);
mButtonSend.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.rc_send) {
if (mSavedPicUri != null) {
Intent data = new Intent();
data.setData(mSavedPicUri);
setResult(RESULT_OK, data);
}
finish();
} else if (v.getId() == R.id.rc_back) {
finish();
}
}
//调用系统相机拍照并且保存
private void startCamera() {
Intent intent = new Intent(ACTION_CAMERA);
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String name = System.currentTimeMillis() + ".jpg";
File file = new File(path, name);
mSavedPicUri = Uri.fromFile(file);
RLog.d(this, "startCamera", "output pic uri =" + mSavedPicUri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mSavedPicUri);
startActivityForResult(intent, REQUEST_CAMERA);
}
@Override
public void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CAMERA:
//用户对拍照完以后执行的操作
if (resultCode == RESULT_CANCELED) {//用户选择取消保存
finish();
Log.e("TakingPicturesActivity","RESULT_CANCELED");
}
if (mSavedPicUri != null && resultCode == Activity.RESULT_OK) {//用户选择发送
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
showImage(mSavedPicUri);
Log.e("TakingPicturesActivity","RESULT_OK");
}
}, 10);
}
break;
}
}
private final void showImage(Uri uri) {
if (uri != null) {
//拿到uri 用桢布局展示 图片
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
//这里的 fragment 就是用来展示 bitmap的
PictureFragment mPhotoFragment = new PictureFragment(this, uri);
ft.replace(R.id.rc_frame, mPhotoFragment);
ft.commit();
}else{
finish();
}
}
}
布局文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@id/rc_frame"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:background="#000"
android:orientation="vertical" >
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:background="#c000">
<Button
android:id="@id/rc_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:background="@null"
android:gravity="center"
android:text="取消"
android:textColor="#fff"
android:textSize="14sp" />
<Button
android:id="@id/rc_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginLeft="50dp"
android:background="@null"
android:gravity="center"
android:text="发送"
android:textColor="#fff"
android:textSize="14sp"
android:layout_gravity="right|center_vertical" />
</FrameLayout>
</LinearLayout>
图片展示一下,有直观的感觉
问题的原因已经分析清楚 但是怎么解决这个问题呢 ? XXX主程帮忙分析 让我用onSaveInstanceState与onRestoreInstanceState来解决这个问题 这两个生命周期的方法比较冷门 虽然知道 但是根本没有正真的在实际应用过 我随即去网上看了几篇相关的博文 但是感觉将概念性的居多 看了半天还是没有特别明白 这边感谢Jenny姐 帮我对 onSaveInstanceState与onRestoreInstanceState的调试和分析 从中学到很多 下面说下具体解决步骤
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
mSavedPicUri = Uri.parse(savedInstanceState.getString("photo_uri"));
super.onRestoreInstanceState(savedInstanceState);
//还原
Log.e("TakingPicturesActivity","onRestoreInstanceState");
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("photo_uri",mSavedPicUri.toString());
super.onSaveInstanceState(outState);
//保存
Log.e("TakingPicturesActivity","onSaveInstanceState");
}
打断点log 崩溃 Carsh 发现是 mSavedPicUri 为null了 我们要保存系统 回收activity 的这个值 所以我们在 onCreate 里做 判断
if(savedInstanceState == null) {
Message msg = Message.obtain();
msg.what = START_CAMERA;
mHandler.sendMessage(msg);
}
我们再来浏览整体的一个 代码 看看跟之前的不同之处 :
public class TakingPicturesActivity extends FragmentActivity implements View.OnClickListener {
private static final int START_CAMERA = 0x1;
private final static int REQUEST_CAMERA = 0x2;
private static final String ACTION_CAMERA = "android.media.action.IMAGE_CAPTURE";
private Button mButtonCancel;
private Button mButtonSend;
private Uri mSavedPicUri;
private FragmentManager fm;
private FragmentTransaction ft;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case START_CAMERA:
startCamera();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.rc_ac_camera);
mButtonCancel = (Button) findViewById(R.id.rc_back);
mButtonSend = (Button) findViewById(R.id.rc_send);
if(savedInstanceState == null) {
Message msg = Message.obtain();
msg.what = START_CAMERA;
mHandler.sendMessage(msg);
}
mButtonCancel.setOnClickListener(this);
mButtonSend.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.rc_send) {
if (mSavedPicUri != null) {
Intent data = new Intent();
data.setData(mSavedPicUri);
setResult(RESULT_OK, data);
}
finish();
} else if (v.getId() == R.id.rc_back) {
finish();
}
}
private void startCamera() {
Intent intent = new Intent(ACTION_CAMERA);
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String name = System.currentTimeMillis() + ".jpg";
File file = new File(path, name);
mSavedPicUri = Uri.fromFile(file);
RLog.d(this, "startCamera", "output pic uri =" + mSavedPicUri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mSavedPicUri);
startActivityForResult(intent, REQUEST_CAMERA);
}
@Override
public void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CAMERA:
if (resultCode == RESULT_CANCELED) {
finish();
Log.e("TakingPicturesActivity","RESULT_CANCELED");
}
if (mSavedPicUri != null && resultCode == Activity.RESULT_OK) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
showImage(mSavedPicUri);
Log.e("TakingPicturesActivity","RESULT_OK");
}
}, 10);
}
break;
}
}
private final void showImage(Uri uri) {
if (uri != null) {
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
PictureFragment mPhotoFragment = new PictureFragment(this, uri);
ft.replace(R.id.rc_frame, mPhotoFragment);
ft.commit();
}else{
finish();
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
mSavedPicUri = Uri.parse(savedInstanceState.getString("photo_uri"));
super.onRestoreInstanceState(savedInstanceState);
//还原
Log.e("TakingPicturesActivity","onRestoreInstanceState");
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("photo_uri",mSavedPicUri.toString());
super.onSaveInstanceState(outState);
//保存
Log.e("TakingPicturesActivity","onSaveInstanceState");
}
}
如此保存状态以后 在资源不足 被系统回收 顶层的任务栈退出后 恢复之前被回收的activity, 我们只需要保存 carsh 当时为null的值