您的位置:首页 > 其它

Context

2016-01-21 14:25 176 查看
回想这么久以来我们所学的内容,你会发现有很多地方都需要用到 Context,弹出Toast 的时候需要、启动活动的时候需要、发送广播的时候需要、操作数据库的时候需要、
使用通知的时候需要等等等等。或许目前你还没有为得不到 Context 而发愁过,因为我们很多的操作都是在活动中进行的,而活动本身就是一个 Context 对象。但是,当应用程序的架构逐渐开始复杂起来的时候,很多的逻辑代码都将脱离 Activity 类,但此时你又恰恰需要使用 Context,也许这个时候你就会感到有些伤脑筋了。举个例子来说吧,我们编写了一个 HttpUtil 类,在这里将一些通用的网络操作封装了起来

public class HttpUtil {
public static void sendHttpRequest(final String address, final HttpCallbackListener
listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);

connection.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new
InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
if (listener != null) {
listener.onFinish(response.toString());
}
} catch (Exception e) {
if (listener != null) {
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}


这里使用 sendHttpRequest()方法来发送 HTTP 请求显然是没有问题的,并且我们还可以在回调方法中处理服务器返回的数据。但现在我们想对 sendHttpRequest()方法进行

一些优化,当检测到网络不存在的时候就给用户一个 Toast 提示,并且不再执行后面的代码。看似一个挺简单的功能,可是却存在一个让人头疼的问题,弹出 Toast 提示需要一个Context 参数,而我们在 HttpUtil 类中显然是获取不到 Context 对象的,这该怎么办呢?其实要想快速解决这个问题也很简单,大不了在 sendHttpRequest()方法中添加一个Context 参数就行了嘛,于是可以将 HttpUtil 中的代码进行如下修改

public class HttpUtil {
public static void sendHttpRequest(final Context context,
final String address, final HttpCallbackListener listener) {

if (!isNetworkAvailable()) {
Toast.makeText(context, "network is unavailable",
Toast.LENGTH_SHORT).show();
return;
}
new Thread(new Runnable() {
@Override
public void run() {
⋯⋯
}
}).start();
}
private static boolean isNetworkAvailable() {
⋯⋯
}
}


可 以 看 到 , 这 里 在 方 法 中 添 加 了 一 个 Context 参 数 , 并 且 假 设 有 一 个isNetworkAvailable()方法用于判断当前网络是否可用,如果网络不可用的话就弹出 Toast

提示,并将方法 return 掉。虽说这也确实是一种解决方案, 但是却有点推卸责任的嫌疑, 因为我们将获取 Context的任务转移给了 sendHttpRequest()方法的调用方,至于调用方能不能得到 Context 对象,那就不是我们需要考虑的问题了。由此可以看出,在某些情况下,获取 Context 并非是那么容易的一件事,有时候还是挺伤脑筋的。不过别担心,下面我们就来学习一种技巧,让你在项目的任何地方都能够轻松获取到 Context。Android 提供了一个 Application 类,每当应用程序启动的时候,系统就会自动将这个类进行初始化。而我们可以定制一个自己的
Application 类,以便于管理程序内一些全局的状态信息,比如说全局 Context。定制一个自己 Application 其实并不复杂,首先我们需要创建一个 MyApplication 类继承自 Application

public class MyApplication extends Application {
private static Context context;
@Override

public void onCreate() {
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
}


可以看到,MyApplication 中的代码非常简单。这里我们重写了父类的 onCreate()方法,并通过调用 getApplicationContext()方法得到了一个应用程序级别的 Context,然

后又提供了一个静态的 getContext()方法,在这里将刚才获取到的 Context 进行返回。接下来我们需要告知系统,当程序启动的时候应该初始化 MyApplication 类,而不是默认的 Application 类。 这一步也很简单, 在 AndroidManifest.xml 文件的<application>标签下进行指定就可以了

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.networktest"
android:versionCode="1"
android:versionName="1.0" >
⋯⋯
<application
android:name="com.example.networktest.MyApplication"
⋯⋯ >
⋯⋯
</application>
</manifest>


注意这里在指定 MyApplication 的时候一定要加上完整的包名,不然系统将无法找到这个类。这样我们就已经实现了一种全局获取 Context 的机制,之后不管你想在项目的任何地方使用 Context,只需要调用一下 MyApplication.getContext()就可以了。那么接下来我们再对 sendHttpRequest()方法进行优化

public static void sendHttpRequest(final String address, final HttpCallbackListener
listener) {
if (!isNetworkAvailable()) {
Toast.makeText(MyApplication.getContext(), "network is unavailable",
Toast.LENGTH_SHORT).show();
return;

}
⋯⋯
}


使用 Intent 传递对象

Intent 的用法相信你已经比较熟悉了,我们可以借助它来启动活动、发送广播、启动服务等。在进行上述操作的时候,我们还可以在 Intent 中添加一些附加数据,以达到传值的效果,比如在 FirstActivity 中添加如下代码

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("string_data", "hello");
intent.putExtra("int_data", 100);
startActivity(intent);

这里调用了 Intent 的 putExtra()方法来添加要传递的数据,之后在 SecondActivity中就可以得到这些值了,代码如下所示
getIntent().getStringExtra("string_data");
getIntent().getIntExtra("int_data", 0);
但是不知道你有没有发现,putExtra()方法中所支持的数据类型是有限的,虽然常用的一些数据类型它都会支持,但是当你想去传递一些自定义对象的时候就会发现无从下手。不用担心,下面我们就学习一下使用 Intent 来传递对象的技巧

使用 Intent 来传递对象通常有两种实现方式,Serializable 和 Parcelable

Serializable 是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。至于序列化的方法也很简单,只需要让一个类去实现 Serializable 这个接口就可以了。比如说有一个 Person 类,其中包含了 name 和 age 这两个字段,想要将它序列化就可以这样写

public class Person implements Serializable{

private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}


其中 get、set 方法都是用于赋值和读取字段数据的,最重要的部分是在第一行。这里让 Person 类去实现了 Serializable 接口,这样所有的 Person 对象就都是可序列化的了。

接下来在 FirstActivity 中的写法非常简单
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("person_data", person);
startActivity(intent);
可以看到,这里我们创建了一个 Person 的实例,然后就直接将它传入到 putExtra()方法中了。由于 Person 类实现了 Serializable 接口,所以才可以这样写。接下来在 SecondActivity 中获取这个对象也很简单

Person person = (Person) getIntent().getSerializableExtra("person_data");


这里调用了 getSerializableExtra()方法来获取通过参数传递过来的序列化对象,接着再将它向下转型成 Person 对象,这样我们就成功实现了使用 Intent 来传递对象的功能了

除了 Serializable 之外,使用 Parcelable 也可以实现相同的效果,不过不同于将对象进行序列化,Parcelable 方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是 Intent 所支持的数据类型,这样也就实现传递对象的功能了。下面我们来看一下 Parcelable 的实现方式,修改 Person 中的代码

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 Parcelable.Creator<Person> CREATOR = new Parcelable.
Creator<Person>() {
@Override
public Person createFromParcel(Pa
afb0
rcel source) {
Person person = new Person();
person.name = source.readString(); // 读取name
person.age = source.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 作为数组大小就可以了。

接下来在 FirstActivity 中我们仍然可以使用相同的代码来传递 Person 对象,只不过在 SecondActivity 中获取对象的时候需要稍加改动

Person person = (Person) getIntent().getParcelableExtra("person_data");


定制自己的日志工具

控制日志的打印,当程序处于开发阶段就让日志打印出来,当程序上线了之后就把日志屏蔽掉

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 这六个整型常量,并且它们对应的值都是递增的。然后又定义了一个

LEVEL 常量,可以将它的值指定为上面六个常量中的任意一个。接下来我们提供了 v()、d()、i()、w()、e()这五个自定义的日志方法,在其内部分别调用了 Log.v()、Log.d()、Log.i()、Log.w()、Log.e()这五个方法来打印日志,只不过在这些自定义的方法中我们都加入了一个 if 判断,只有当 LEVEL 常量的值小于或等于对应日志级别值的时候,才会将日志打印出来。这样就把一个自定义的日志工具创建好了, 之后在项目里我们可以像使用普通的日志工具一样使用 LogUtil,比如打印一行
DEBUG 级别的日志就可以这样写:LogUtil.d("TAG", "debug log");打印一行 WARN 级别的日志就可以这样写:LogUtil.w("TAG", "warn log");然后我们只需要修改 LEVEL 常量的值,就可以自由地控制日志的打印行为了。比如让LEVEL 等于 VERBOSE 就可以把所有的日志都打印出来,让 LEVEL 等于 WARN 就可以只打印警告以上级别的日志,让 LEVEL 等于 NOTHING 就可以把所有日志都屏蔽掉。使用了这种方法之后,刚才所说的那个问题就不复存在了,你只需要在开发阶段将LEVEL
指定成 VERBOSE, 当项目正式上线的时候将 LEVEL 指定成 NOTHING 就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Context Log