2013-02-23 33 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

相關問題