在项目中突然碰到一个crash,而且还经常打不出log,logcat 提示Unable to find app for caller android.app.ApplicationThreadProxy,而且在Android 5.0以下的机器常见,在网上搜了搜,stackoverFlow上给了一些解释,但是都没说到点子上,在这里贴出一个测试成功了的解决方案。
触发这个异常是在开启一个新activity的时候,经过分析应该是intent.putExtra(String,Serilizable)中,序列化对象太大了,当时app直接传输了400多个对象,而且每个对象里面又有很多的字段,过大的数据量直接导致activity在发送序列化对象的时候崩溃。
鉴于这个现象我想到了一个解决办法,就是安卓官方推荐使用的Parcelable类,这个类在安卓的定义中就是专门为activity传值而生的,可以自定义那些字段需要序列化,哪些不需要,极大的减少了数据量,然后我就试了试,居然成功了,并且性能也很不错。
那么在这里简单介绍一下Parcelable吧
Intent除了可以用putExtraString,putExtraInt等方法发送基本数据类型以外,还可以发送自己写的类的对象,但是需要进行序列化;把一个类序列化只需要实现Parcelable即可,再在这个类里面声明一个Parcelable.Creator 内部类对象;代码如下
package com.example.parcel_test;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable{
private String name;
private int age;
private boolean gendar;
public Person(String name, int age, boolean gendar) {
super();
this.name = name;
this.age = age;
this.gendar = gendar;
}
public Person(){}
public Person(Parcel source) {//构造方法,用于传入一个parcel解析成类
// TODO Auto-generated constructor stub
//在这里面可以限制哪些成员可以被序列化,哪些不会被序列化
name = source.readString();
age = source.readInt();
}
@Override
public int describeContents() {//没搞懂有什么用,反正直接返回0也可以
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {//该方法将类的数据写入外部提供的Parcel中
// TODO Auto-generated method stub
//在这里面可以限制哪些成员可以被序列化,哪些不会被序列化
dest.writeString(name);
dest.writeInt(age);
}
//声明一个内部类的对象
public static final Parcelable.Creator<Person> CREATOR //必须全为大写CREATOR
= new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {//实现从source中创建出类的实例的功能
// TODO Auto-generated method stub
return new Person(source);
}
@Override
public Person[] newArray(int size) {//创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。估计本方法是供外部类反序列化本类数组使用。
// TODO Auto-generated method stub
return new Person[size];
}
};
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", gendar=" + gendar
+ "]";
}
}
如果一个Parcelable对象中包含了其他自定义类,那么这个里面的自定义类也要实现Parcelable接口,并且他的写法也稍有不同,并且布尔值的写法也要注意一下,下面是项目里实际应用的一段代码
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.id);
dest.writeString(this.imageUrl);
dest.writeString(this.title);
dest.writeString(this.description);
dest.writeString(this.createTimeStr);
dest.writeString(this.createTime);
dest.writeByte(this.isLike ? (byte) 1 : (byte) 0);//boolean的写法
dest.writeInt(this.likeCount);
dest.writeInt(this.commentCount);
dest.writeString(this.creator);
dest.writeInt(this.type);
dest.writeParcelable(this.photoCredit, flags);//内置的自定义类,也要实现Parcelable接口
dest.writeParcelable(this.photoCreditImpl, flags);//内置的自定义类,也要实现Parcelable接口
}
protected SVRGLCreditPhotoDishListReturnEntity(Parcel in) {
this.id = in.readString();
this.imageUrl = in.readString();
this.title = in.readString();
this.description = in.readString();
this.createTimeStr = in.readString();
this.createTime = in.readString();
this.isLike = in.readByte() != 0;//布尔值的写法
this.likeCount = in.readInt();
this.commentCount = in.readInt();
this.creator = in.readString();
this.type = in.readInt();
this.photoCredit = in.readParcelable(SVRPhotoCreditReturnEntity.class.getClassLoader());//内置的Parcelable类
this.photoCreditImpl = in.readParcelable(SVRPhotoCreditReturnEntity.class.getClassLoader());//内置的Parcelable类
}
一个可以序列化的类生成好了,下面可以发送了
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//用intent向activity发送对象
Person p = new Person("小王",20,true);
Intent intent = new Intent(this,SecondActivity.class);
intent.putExtra("parcel", p);//这样就可以直接把对象发过去了
startActivity(intent);
}
换一个activity测试一下接收结果
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//接受intent发送的对象
Intent intent = getIntent();
Person p2 = intent.getParcelableExtra("parcel");//接收parcel对象
System.out.println(p2);// [name=小王, age=20, gendar=false],因为规定了成员gendar属性不参加序列化,所以打出默认值false
}
安卓给的Parcelable要比普通的serializable接口执行速度更快,资源占用更少,而且可以规定哪些成员参与序列化,哪些不参与,减少了序列化的数据量,极为推荐使用
PS:如果觉得手写这么多字段很费事而且容易写错的话,推荐一个Android Studio插件Android Parcelable code generator,在需要变成Parcelable的类按Alt+Insert呼出菜单,选择Parcelable就可以用了,我在写这一块的时候就这么用的。