我正在开发一个 Android 应用程序,该应用程序允许用户使用手机的摄像头扫描他们的合同。当我希望用户添加新合同时,我会启动一个活动,让他们拍摄合同不同页面的许多照片(第二个活动依次使用MediaStore.ACTION_IMAGE_CAPTURE
意图来完成)。这似乎工作正常,我尝试通过以下方式返回完成的合同对象setResult()
并在 MainActivity 中检索它onActivityResult()
:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
{
if ((requestCode == REQUEST_CONTRACT) && (resultCode == RESULT_OK))
{
this.contract = (Contract) intent.getSerializableExtra(ContractActivity.EXTRA_CONTRACT);
}
}
但是,该Contract
对象未正确反序列化,这导致应用程序在Contract
返回到时因 ClassCastException 崩溃(在我将其转换为的行中)MainActivity
:
02-06 23:10:53.891: W/dalvikvm(15398): threadid=1: thread exiting with uncaught exception (group=0x41910700)
02-06 23:10:53.907: E/AndroidRuntime(15398): FATAL EXCEPTION: main
02-06 23:10:53.907: E/AndroidRuntime(15398): java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {myapp/myapp.EndkundenMainActivity}: java.lang.ClassCastException: java.util.ArrayList cannot be cast to mylib.model.Contract
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.app.ActivityThread.deliverResults(ActivityThread.java:3367)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.app.ActivityThread.handleSendResult(ActivityThread.java:3410)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.app.ActivityThread.access$1100(ActivityThread.java:141)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1304)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.os.Handler.dispatchMessage(Handler.java:99)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.os.Looper.loop(Looper.java:137)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.app.ActivityThread.main(ActivityThread.java:5103)
02-06 23:10:53.907: E/AndroidRuntime(15398): at java.lang.reflect.Method.invokeNative(Native Method)
02-06 23:10:53.907: E/AndroidRuntime(15398): at java.lang.reflect.Method.invoke(Method.java:525)
02-06 23:10:53.907: E/AndroidRuntime(15398): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
02-06 23:10:53.907: E/AndroidRuntime(15398): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
02-06 23:10:53.907: E/AndroidRuntime(15398): at dalvik.system.NativeStart.main(Native Method)
02-06 23:10:53.907: E/AndroidRuntime(15398): Caused by: java.lang.ClassCastException: java.util.ArrayList cannot be cast to mylib.model.Contract
02-06 23:10:53.907: E/AndroidRuntime(15398): at de.kreditpruefen.lib.MainActivity.onActivityResult(MainActivity.java:232)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.app.Activity.dispatchActivityResult(Activity.java:5322)
02-06 23:10:53.907: E/AndroidRuntime(15398): at android.app.ActivityThread.deliverResults(ActivityThread.java:3363)
02-06 23:10:53.907: E/AndroidRuntime(15398): ... 11 more
我的Contract
对象只是ContractPage
s 的集合,所以我声明它List<ContractPage>
像这样实现:
public class Contract implements Serializable, List<ContractPage>
因此,我认为序列化出了点问题,Android 将我的Contract
对象错误地ArrayList
序列化为 an 然后再反序列化为 an ArrayList
,这显然不能转换为Contract
.
我很困惑为什么会这样。如果我扩展了 ArrayList,我会认为这可能是由于超类弄乱了序列化,但这里唯一的超类是Object
并且实现接口不应该影响序列化,不是吗?
另外,我像这样保存Contract
对象onSaveInstanceState()
:
@Override
protected void onSaveInstanceState(Bundle outState)
{
outState.putSerializable(EXTRA_CONTRACT, this.contract);
super.onSaveInstanceState(outState);
}
并像这样恢复它:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
this.contract = (Contract) savedInstanceState.get(EXTRA_CONTRACT);
}
}
这不应该导致它也被序列化和反序列化吗?为什么它在这种情况下有效,但在我在活动之间传输对象时无效?
完成部分相关代码如下:
MainActivity.java
public class MainActivity extends Activity
{
private static final int REQUEST_CONTRACT = 1;
private Contract contract;
public void addContract(View view)
{
Intent intent = new Intent(this, ContractActivity.class);
intent.putExtra(ContractActivity.EXTRA_CONTRACT, this.contract);
startActivityForResult(intent, MainActivity.REQUEST_CONTRACT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
{
if ((requestCode == REQUEST_CONTRACT) && (resultCode == RESULT_OK))
{
this.contract = (Contract) intent.getSerializableExtra(ContractActivity.EXTRA_CONTRACT);
}
super.onActivityResult(requestCode, resultCode, intent);
}
}
ContractActivity.java
public class ContractActivity extends Activity
{
public static final String EXTRA_CONTRACT = "ContractKey";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
this.contract = (Contract) savedInstanceState.get(EXTRA_CONTRACT);
}
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
outState.putSerializable(EXTRA_CONTRACT, this.contract);
super.onSaveInstanceState(outState);
}
/**
* Set up the {@link android.app.ActionBar}.
*/
private void setupActionBar()
{
getActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.contract, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home:
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
Intent result = new Intent();
result.putExtra(EXTRA_CONTRACT, this.contract);
setResult(Activity.RESULT_OK, result);
// use finish instead of navigateUp which ensures the same instance
// of the calling activity is restarted which should cause the form
// in the MainActivity to retain entered text etc.
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
// [...]
// other methods that add pages to this.contract
}
Contract.java
public class Contract implements Serializable, List<ContractPage>
{
private List<ContractPage> pages;
private static final long serialVersionUID = -708359261524732081L;
public Contract()
{
this.pages = new ArrayList<ContractPage>();
}
// [...]
// implementations of all the other methods required by the List interface by delegating the method calls to this.pages
}
ContractPage.java
public class ContractPage implements Serializable
{
private static final long serialVersionUID = 2721152546839021601L;
private transient Bitmap image;
private File imagePath;
private transient Bitmap thumbnail;
private int thumbnailWidthAndHeight;
public ContractPage(File imagePath, int thumbnailWidthAndHeight)
{
this.imagePath = imagePath;
this.thumbnailWidthAndHeight = thumbnailWidthAndHeight;
}
// [...]
// getters and setters for the instance variables. image and thumbnail are lazy loaded from the imagePath.
}
更新:
反过来,问题也存在,当我将一个非空合约对象放在启动ContractActivity
. 在这种情况下,当尝试从额外数据中检索联系人对象并将其转换为合同时,它会以同样的方式失败,因为该对象实际上是一个ArrayList
. 所以至少在那种情况下它是一致的。