Android应用在方向更改期间丢失数据

Rox*_*era 6 android orientation

我有一个应用程序,我从一个捕获图像的教程中复制了MediaStore.ACTION_IMAGE_CAPTURE.当我在手机上运行应用程序时,我有一些奇怪的感觉.

即使我没有移动手机,相机应用程序本身也会在操作过程中翻转其方向几次.在返回教程应用程序之前,它会简要地进入横向模式.因此,教程应用程序在控制返回到它后会回到纵向模式,并且图像丢失.我尝试将相机活动的方向设置为横向,图像不会丢失.

但该应用程序的布局适用于纵向模式.或者,如果我在拍摄照片的同时横向握住相机,我可以在应用程序恢复焦点后转动手机,而不会丢失图像.

我在网上做了一些讨论.Stackoverflow上有人提到方向的改变引起了额外的调用onCreate." onCreate()被调用的原因是因为当您在纵向方向上调用相机活动时,它将改变方向并破坏您之前的活动." 我在调试模式下运行应用程序,在onCreate和onActivityResult方法中设置了断点.onCreate当我以肖像模式拍摄照片时,确实会被调用.调用的顺序是onCreate,onActivityResult,onCreate.如果我以横向模式拍摄照片(这是我的相机应用程序以任何方式结束的地方),onCreate不会被调用.现在我已经知道发生了什么,我该如何避免这个问题?这是应用程序现在的样子:

package com.example.testapp;

import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.app.WallpaperManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;

public class CameraActivity extends Activity implements View.OnClickListener {

    ImageButton ib;
    Button b;
    ImageView iv;
    Intent i;
    final static int cameraData = 0;
    Bitmap bmp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.photo_activity);
        initialize();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        // TODO Auto-generated method stub
        super.onConfigurationChanged(newConfig);
        setContentView(R.layout.photo_activity);
        initialize();
    }

    private void initialize() {
        iv = (ImageView)findViewById(R.id.imageViewReturnedPicture);
        ib = (ImageButton)findViewById(R.id.imageButtonTakePicture);
        b = (Button)findViewById(R.id.buttonSetWallpaper);
        b.setOnClickListener(this);
        ib.setOnClickListener(this);
    }

    @Override
    public void onClick(View arg0) {
        switch (arg0.getId()) {

        case R.id.buttonSetWallpaper:
            try {
                WallpaperManager wm = WallpaperManager.getInstance(getApplicationContext());
                wm.setBitmap(bmp);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;

        case R.id.imageButtonTakePicture:
            i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(i, cameraData);
            break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            bmp = (Bitmap)extras.get("data");
            iv.setImageBitmap(bmp);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是我在此活动清单中的内容:

                android:name="com.example.testapp.CameraActivity"
                android:label="Camera Activity"
                android:configChanges="orientation"
                android:screenOrientation="portrait" 

我已经做了相当多的搜索,但我发现的大部分内容都缺乏具体的例子.我需要知道代码的样子,而不仅仅是要使用的功能.

我的手机是LG Motion.有没有其他人遇到这个问题?怎么修好?

Cab*_*zas 11

在清单中,在每个活动中我使用configChanges:

 <activity
        android:name=".MainActivity"
        android:configChanges="screenLayout|orientation|screenSize">
Run Code Online (Sandbox Code Playgroud)

我希望这对你有所帮助.


Bud*_*ius 7

您必须覆盖onRetainNonConfigurationInstance并使用getLastNonConfigurationInstance来保存/恢复位图.

像这样:

// during onCreate(Bundle), after initialise()
bmp = getLastNonConfigurationInstance();
if(bmp!=null){ iv.setImageBitmap(bmp); }
else { /* the image was not taken yet */ }
Run Code Online (Sandbox Code Playgroud)

那么你的活动你覆盖:

@Override
public Object onRetainNonConfigurationInstance (){
     return bmp;
}
Run Code Online (Sandbox Code Playgroud)

这将在旋转期间"保存"位图.

编辑:

使用建议使用的onSaveInstanceState的示例但是不可取,因为它会占用大量内存而且速度很慢,但很快就会出现其他情况:

public class SomethingSomething extends Activity{

    String name="";
    int page=0;

    // This is called when the activity is been created
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

            // if you saved something on outState you can recover them here
            if(savedInstanceState!=null){
                name = savedInstanceState.getString("name");
                page = savedInstanceState.getInt("page");
            }
    }

    // This is called before the activity is destroyed
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

            outState.putString("name", name);
            outState.putInt("page", page);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,这个解决方案对你的情况不利的原因是因为用于此的Android Bundle是Android特殊类型的序列化,它可以处理实现Parcelable的原语,字符串和类(这些类实际上只包含它们的原语) .即使你的Bitmaps实现Parcelable,也会花费大量时间将Bitmap上的每个字节复制到bundle中,并且会使Bitmap已经很大的内存消耗倍增.

现在让我们看一下使用的解决方案setRetainInstance(从您可以找到的示例中略微复制一下)\sdk\extras\android\support\samples\Support4Demos\src\com\example\android\supportv4\app\FragmentRetainInstanceSupport.Java.

一定要检查示例,因为它显示了一些其他花哨的技巧.

// This fragment will be managed by the framework but we won't built a UI for it.
public class FragRetained extends Fragment{
   public static final String TAG = "TAG.FragRetained";
   private Bitmap bitmap;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Tell the framework to try to keep this fragment around
        // during a configuration change.
        setRetainInstance(true);
    }
    public Bitmap getBitmap() { return bitmap; }
    public void setBitmap(Bitmap bmp) { bitmap = bmp; }
}

public class MyActivity extends Activity{

    private FragRetained myFragRetained;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
            // set the content view
            img = (ImageView)findViewById(R.id.byImgV);


            myFragRetained = getFragmentManger.findFragmentByTag(FragRetained.TAG);
            if(myFragRetained == null){
                myFragRetained = new FragRetained ();
            }else{
               Bitmap b = myFragRetained.getBitmap();
               if(b==null){
                  // the user still did not choose the photo
               }else{
                  img.setImageBitmap(b);
               }
            }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            bmp = (Bitmap)extras.get("data");
            iv.setImageBitmap(bmp);
            myFragRetained.setBitmap(bmp);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

并确保android:configChanges="orientation"从你的清单中删除该行,因为它可能会造成更大的危害,然后是一个小的引用:

注意:应避免使用此属性,并仅将其用作最后的手段.有关如何通过配置更改正确处理重新启动的详细信息,请阅读处理运行时更改.


Cha*_*ger 1

您应该处理方向更改的方法是保存实例状态。如果您填写onSaveInstanceState方法,则可以在 onCreate 期间取回保存到该包中的数据。这是为您完成的视图,但其他数据您必须自己保存。任何原始的、可分割的或可序列化的对象都可以通过这种方式保存,包括位图。

您应该这样做不仅是为了在配置更改后幸存下来,而且还可以将您的状态保持在内存不足的情况下。