这一章学到的:
- 全局获取Context的技巧
- 使用Intent传递对象
- Serialiable方式
- Parcelable方式
- 定制自己的日志工具
- 调试Android程序
13.1 全局获取Context的技巧
当应用程序的架构逐渐开始复杂起来的时候,很多的逻辑代码都将脱离Activity类,但此时你又恰恰需要使用Context,也许这个时候你就会感到有些伤脑经了。
在某些情况下,获取Context并非是那么一件事,下面我们就来学习一种技巧,让你在项目的任何地方都能够轻松获取到Context。
Android提供了一个Application类,每当应用程序启动的时候,系统就会自动将这个类进行初始化。而我们可以定制一个自己的Aoolication类,以便于管理程序内一些全局的状态信息,比如说全局Context。
定制一个自己的Application其实并不困难,首先我们需要创建一个MyApplication类继承自Application。
代码如下:
public class MyApplication extends Application
{
private static Context mContext;
@Override
public void onCreate()
{
mContext = getApplicationContext();
}
public static Context getContext()
{
return mContext;
}
}
这里我们重写了父类的onCreate()方法,并通过调用getApplicationContext()方法得到了一个应用程序级别的Context,然后又提供了一个静态的getContext()方法,在这里将刚才获取到的Context进行返回。
注意:getContext()返回的也要是一个static的变量,所以mContext要加static。
接下来我们需要告知系统,当程序启动的时候应该初始化MyApplication类,而不是默认的Application类。这一步也很简单,在AndroidManifest.xml文件的<application>标签下进行指定就可以了。
代码如下所示:
<application
········
android:name="com.example.wumen.getcontext.MyApplication">
·········
</application>
注意这里在指定MyApplication的时候,一定要加上完整的包名,不然系统将无法找到这个类。
这样我们就已经实现了一种全局获取Context的机制,之后不管你想在项目的任何地方使用Context,只需要调用一下MyApplication.getContext()就可以了。
任何一个项目都只能配置一个Application。
13.2 使用Intent传递对象
我们可以借助Intent来启动活动,发送广播,启动服务等。在进行上述操作的时候,我们还可以在Intent中添加一些附加数据,已达到传值的效果。
putExtra()方法中所支持的数据类型是有限的,虽然常用的一些数据类型它都会支持,但是当你想去传递一些自定义对象的时候,就会发现无从下手。
13.2.1 Serializable方式(谁瑞来日报)
Serializable是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。至于序列化的方法也很简单,只需要让一个类去实现Serializable这个借口就可以了。
比如说Person类:
public class Person implements Serializable
{
private String name;
private int age;
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
让Person类去实现了Serializable接口,这样所有的Person对象就都是可序列化的了。
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("person_data",person);
startActivity(intent);
这里我们创建了一个Person的实例,然后就直接将它传入到putExtra()方法中了。由于Person类实现了Serializable接口,所以才可以这样写。
Intent intent = getIntent();
Person person = (Person) intent.getSerializableExtra("person_data");
这里调用了getSerializableExtra()方法来获取通过参数传递过来的序列化对象,接着再将它向下转型成Person对象,这样我们就成功实现了使用Intent来传递对象的功能了。
13.2.2 Parcelable方式(扑瑞赛了包)
不同于将对象进行序列化,Parcelable
方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent
所支持的数据类型,这样也就实现传递对象的功能了。
public class Person implements Parcelable
{
private String name;
private int age;
············
@Override
public int describeContents()
{
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeString(name);//写出name
dest.writeInt(age);//写出age
}
public static final Creator<Person> CREATOR = new Creator<Person>()
{
@Override
public Person createFromParcel(Parcel in)
{
Person person = new Person();
person.name = in.readString();//读取name
person.age = in.readInt();//读取age
return person;
}
@Override
public Person[] newArray(int size)
{
return new Person[size];
}
};
}
Parcelable
的实现方式要复杂一些。首先我们让Person
类去实现了Parcelable
接口,这样就必须重写describeContents()
和writeToParcel()
这两个方法。其中describeContents()
方法直接返回0就可以了,而writeToParcel()
方法中我们需要调用Parcel
的writeXxx()
方法,将Person
类中的字段一一写出。注意,字符串型数据就调用writeString()
方法,整型数据就调用writeInt()
方法,依此类推。
除此之外,我们还必须在Person
类中提供一个名为CREATOR
的常量,这里创建了Parcelable.Creator
接口的一个实现,并将泛型指定为Person
。接着需要重写createFromParcel()
和newArray()
这两个方法,在createFromParcel()
方法中我们要去读取刚才写出的name
和age
字段,并创建一个Person
对象进行返回,其中name
和age
都是调用Parcel
的readXxx()
方法读取到的,注意这里读取的顺序一定要和刚才写出的顺序完全一致。而newArray()
方法中的实现就简单多了,只需要new
出一个Person
数组,并使用方法中传入的size
作为数组大小就可以了。
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("person_data",person);
startActivity(intent);
我们仍然可以使用相同的代码来传递Person
对象,只不过在获取对象的时候需要稍加改动。
Intent intent = getIntent();
Person person = (Person) intent.getParcelableExtra("person_data");
对比一下,Serializable
的方式较为简单,但由于会把整个对象进行序列化,因此效率会比 Parcelable
方式低一些,所以在通常情况下还是更加推荐使用 Parcelable
的方式来实现Intent
传递对象的功能。
13.3 定制自己的日志工具
最理想的情况是能够自由地控制日志的打印,当程序处于开发阶段时就让日志打印出来,当程序上线了之后就把日志屏蔽掉。
看起来好像是挺高级的一个功能,其实并不复杂,我们只需要定制一个自己的日志工具就可以轻松完成了。比如新建一个LogUtil类,代码如下所示:
public class LogUtil
{
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int NOTHING = 6;
public static final int level = VERBOSE;
public static void v(String tag,String msg)
{
if (level <= VERBOSE)
{
Log.v(tag,msg);
}
}
public static void d(String tag,String msg)
{
if (level <= DEBUG)
{
Log.d(tag,msg);
}
}
public static void i(String tag,String msg)
{
if (level <= INFO)
{
Log.i(tag,msg);
}
}
public static void w(String tag,String msg)
{
if (level <= WARN)
{
Log.w(tag,msg);
}
}
public static void e(String tag,String msg)
{
if (level <= ERROR)
{
Log.e(tag,msg);
}
}
}
我们在LogUtil中先是定义了VERBOSE,DEBUG,INFO,WARN,ERROR,NOTHING这6个整形常量,并且它们对应的值都是递增的。然后又定义了一个静态变量level,可以将它的值指定为上面6个常量中的任意一个。
接下里我们提供了v(),d(),i(),w(),e()这5个自定义的日志方法,在其内部分别调用了Log.v(),Log.d(),Log.i(),Log.w(),Log.e()这5个方法来打印日志,只不过在这些自定义的方法中我们都加入了一个if判断,只有当level的值小于或等于对应日志级别值的时候,才会将日志打印出来。
这样就把一个自定义的日志工具创建好了,之后在项目里我们可以像使用普通的日志工具一样使用LogUtil,比如打印一行DEBUG级别的日志就可以这样写:
LogUtil.d("Tag","debug log");
然后我们只需要修改level变量的值,就可以自由地控制日志的打印行为了。比如让level等于VERBOSE就可以把所有的日志都打印出来,让level等于WARN就可以只打印警告以上级别的日志,让level等于NOTHING就可以把所有日志都屏蔽掉。
13.4 调试Android程序
当开发过程中遇到一些奇怪的bug,但又迟迟定位不出来原因是什么的时候。最好的解决办法就是调试了。调试允许我们逐行地执行代码,并可以实时观察内存中的数据,从而能够比较轻易地查出问题的原因。
调试工作的第一步肯定是添加断点,这里由于我们要调试登陆部分的问题,所以断点可以加在登录按钮的点击时间里。添加断点的方法也很简单,只需要在相应代码行的左边点击一下就可以了。
如果想要取消这个断点,对着它再次点击就可以了。
添加好了断点,接下来就可以对程序进行调试了,点击Android Studio顶部工具栏中的Debug按钮(最右边的按钮),就会使用调试模式来启动程序。
在输入框中输入账号和密码,并点击Login按钮,这时Android Studio就会自动打开Debug窗口。
接下来每按一次F8键,代码就会向下执行一行,并且通过Variables视图还可以看到内存中的数据。
调试完之后点击Debug窗口中的Stop按钮(最下边的按钮)来结束调试即可。
这种调试方式虽然完全可以正常工作,但在调试模式下,程序的运行效率将会大大地降低,如果你的断点加在一个比较靠后的位置,需要执行很多的操作才能运行到这个断点,那么前面这些操作就都会有一些卡顿的感觉。
Android还提供了另外一种调试的方式,可以让程序随时进入到调试模式。
这次不需要选择调试模式来启动程序了,就使用正常的方式来启动程序。把账号和密码输入好,然后点击Android Studio顶部工具栏的Attach debugger to Android process按钮(最左边的按钮)
此时会让弹出一个进程选择提示框
这里目前只列出了一个进程,也就是我们当前程序的进程,选中这个进程,然后点击Ok按钮,就会让这个进程进入到调试模式了。
接下来在程序中点击Login按钮,Android Studio同样也会自动打开Debug窗口,之后的流程就都是相同的了。