Context

本文介绍了如何在Android应用中灵活使用Context对象,并演示了通过Intent传递复杂对象的两种方法:Serializable和Parcelable。此外,还分享了一种自定义日志工具的实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

回想这么久以来我们所学的内容,你会发现有很多地方都需要用到 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(Parcel 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 in Programming or IT Systems In programming and IT systems, context refers to the environment and conditions under which certain operations are performed. This concept encompasses several aspects depending on the specific area within computing: #### Execution Context Execution context pertains specifically to how code executes at runtime. Each function call creates its own execution context that includes variables, scope chain, and this binding[^1]. For instance, when discussing JavaScript, each time a script starts running or enters a new function, an execution context is created. #### Thread Context Thread context involves all information associated with a particular thread's state including registers, stack pointers, program counter, etc.[^3]. When switching between threads, saving and restoring these contexts ensures continuity of operation without data loss or corruption. #### Security Context Security context defines what actions can be taken by processes based on their permissions level. It plays a critical role in determining access control policies applied to resources like files, network connections, hardware devices, among others. #### Application Context Application context represents settings relevant to applications such as configuration parameters, user preferences, session states, etc., ensuring consistent behavior across different parts of software while maintaining isolation from other programs sharing similar environments. ```python def example_function(): # An execution context is formed here containing local variable definitions, # parameter bindings, parent scopes references (scope chain), along with 'this' value. pass ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值