2013-02-23 85 views
5

我有一个应用程序,我从一个教程中复制而来,该教程使用MediaStore.ACTION_IMAGE_CAPTURE捕获图像。当我在手机上运行应用程序时,出现了一些奇怪的现象。Android应用程序在方向更改期间丢失数据

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

但该应用程序的布局旨在用于肖像模式。或者,如果我在捕捉照片时以横向方向握住相机,则在应用程序返回焦点后可以打开手机,而不会丢失图像。

我在网上做了一些动作。有人在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); 
     } 
    } 
} 

这里就是我在清单这项活动:

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

我已经做了相当多的搜索,但大部分是在我发现缺少具体的例子。我需要知道代码的外观,而不仅仅是使用什么功能。

我的手机是LG Motion。有其他人遇到这个问题吗?它如何被修复?

回答

1

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

您应该这样做不仅仅是为了在配置更改中幸存下来,还要让您的状态保持在低内存条件下。

+0

onSaveInstanceState无法保存位图 – Budius 2013-02-23 17:36:16

+0

@Budius是的它可以 - http://it-ride.blogspot.com/2010/08/save-image-in-instance-state-android.html – 2013-02-27 02:43:31

+1

虽然你的链接doesn没有工作,在我发表评论后,我发现我错了,我为此道歉,我会很乐意让你失望,但除非你编辑你的答案,否则我不会。但我坚持认为,尽管Bitmaps是可以parcelable的,但在SavedInstanceState期间不宜使用它,因为它需要时间和内存来完成此类事务,并且可能会使轮换非常滞后。 – Budius 2013-02-27 09:54:05

0

您是否尝试过将android:launchMode =“singleTop”添加到android menifest中的活动中,为我解决问题。问题是android在方向更改和数据丢失时重新启动活动,如果您的活动已在运行,则不会创建新实例。

 <activity 
      android:name=".FacebookActivity" 
      android:label="@string/facebook_app_name" 
      android:launchMode="singleTop" 
      android:configChanges="keyboardHidden|orientation" 
      > 
     </activity> 
+0

谢谢。只是为了咧嘴笑,我尝试过,但它不会改变我的应用程序的行为。 – 2013-02-23 21:29:02

0

要么使用onSaveInstanceState,然后你通过的onCreate得到论证,或在其上的情况下,你希望自己(来处理,你会处理由使用configChanges和设置方位的变化清单设置,例如方向| screenSize | screenLayout)。

+0

onSaveInstanceState无法保存位图 – Budius 2013-02-23 17:31:05

+0

好的,我对这个问题感到困惑。你能解释一些吗?另外,你有没有尝试设置configChanges? – 2013-02-23 17:38:14

+0

我不是那个问这个问题的人。我只是向你解释你建议他使用onSavedInstanceState,但Bitmaps不是Parcelable,因此不能用onSavedInstanceState保存。 – Budius 2013-02-23 17:43:43

7

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

像这样:

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

那么你的活动信息ü覆盖:旋转过程中

@Override 
public Object onRetainNonConfigurationInstance(){ 
    return bmp; 
} 

这将 '保存' 的位图。

编辑:建议使用的onSaveInstanceState

例如,将工作but is not advisable because it will use a lot of memory and be very slow但你很快就会需要其他情况:

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); 
    } 
} 

,你可以看到,这个解决方案的原因是不好的你的情况是因为Android Bundle用于此目的是Android特殊类型的序列化,它可以处理实现Parcelable的基本类,字符串和类(这些类实际上仅包含它们的基元)。即使你的Bitmaps确实实现了Parcelable,它也需要花费很多时间来拷贝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); 
     } 
    } 

} 

,并确保从您的manifest because it's probably doing more harm then good删除行android:configChanges="orientation",小帖吧:

注意:使用这个属性应该被避免,并且仅用作 最后的手段。请阅读处理运行时更改以获取更多信息 关于如何正确处理由于配置更改而导致的重新启动。

+1

显然,onRetainNonConfigurationInstance已被弃用。该文档建议使用Fragment API setRetainInstance(boolean)。有谁知道如何使用它? – 2013-02-23 21:23:02

+1

这确实是,但现在还没有什么能阻止你继续学习。要使用片段版本,您可以在onCreate()期间使用setRetainInstance(true)来请求保留其实例的无UI UI片段,并且您可以在其上创建setBitmap()getBitmap()方法。所以你在获得图像后,可以初始化片段和setBitmap()。在循环之后(在activity的onCreate())过程中,你可以用getFragmentManager()。findFragmentByTag(String)得到片段,并调用getBitmap()。但我认为这对刚刚学习平台的人来说太过分了。 – Budius 2013-02-24 00:26:26

+1

显然(http://stackoverflow.com/a/7677326/906362)在使用SDK,FragmentRetainInstanceSupport或类似的东西下载的例子中,有一个使用UI-less Fragment + setRetainInstance()的例子。 – Budius 2013-02-24 00:36:23

3

我星期一去了一个移动开发小组会议,得到了一个提示。为大公司的各种平台编写应用程序的程序员建议将位图附加到应用程序对象。当我看着我怎么可能做到这一点,我终于想出了工作基础上建议在以下博客文章(在后结束新问题)的解决方案: http://android-er.blogspot.com/2011/10/share-bitmap-between-activities-as.html

基本上,这些行动包含设置位图作为应用程序中的任何活动可以访问它的位置的常见资源。我创建了一个新的类,如下所示:

package com.example.testapp; 
  
import android.graphics.Bitmap; 
  
public class CommonResources { 
        public static Bitmap cameraBmp = null; 
} 

然后我改变了所有引用的CameraActivity.java为bmp到CommonResources.cameraBmp。

在初始化期间,如果CommonResources.cameraBmp不为空,那么我使用它来设置ImageView中的位图。

的onCreate现在初始化这个样子:

@Override 
protected void onCreate(Bundle savedInstanceState) { 
        // TODO Auto-generated method stub 
              super.onCreate(savedInstanceState); 
        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); 
        if (CommonResources.cameraBmp != null) { 
                  iv.setImageBitmap(CommonResources.cameraBmp); 
          } 
        b.setOnClickListener(this); 
        ib.setOnClickListener(this); 
} 

,然后做一些清理工作,我说的onPause如下:现在

@Override 
protected void onPause() { 
        // TODO Auto-generated method stub 
              super.onPause(); 
        if (isFinishing()) { 
                  // Save the bitmap. 
                        CommonResources.cameraBmp = null; 
          } 
} 

我最大的问题是:有我正确处理内存或者我创建了垃圾回收问题?

1

经过一段时间寻找一个简单的解决方案后,我查看了一些代码,并提出了这个问题,希望它有助于任何人在将来观看此主题!

我在我的Activity类中有一个全局变量public Uri imageURI = null;,它用于设置我正在使用的ImaveView的图像URI;在onCreate()下面的代码和onSaveInstanceState()

@Override 
protected void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_thisactivity); 

    //restore the bitmap for the image 
    if (savedInstanceState!=null) 
    { 
     imageURI=savedInstanceState.getParcelable("imageURI"); 
     ImageView photo=(ImageView)findViewById(R.id.image); 
     photo.setImageURI(imageURI); 
    } 
} 

@Override 
public void onSaveInstanceState(Bundle savedInstanceState) 
{ 
    super.onSaveInstanceState(savedInstanceState); 
    savedInstanceState.putParcelable("imageURI", imageURI); 
} 

我希望有人认为这有用的 - 只是问,如果你想了解更多详细信息。

8

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

<activity 
     android:name=".MainActivity" 
     android:configChanges="screenLayout|orientation|screenSize"> 

我希望这可以帮助您。

+1

这是最好的回答工作像魅力 – 2016-06-02 06:26:49

+0

请注意'方向'标志是最重要的答案和保存我的一天... – JamesC 2017-11-07 11:20:27

相关问题