Context
2016-01-21 14:25
176 查看
回想这么久以来我们所学的内容,你会发现有很多地方都需要用到 Context,弹出Toast 的时候需要、启动活动的时候需要、发送广播的时候需要、操作数据库的时候需要、
使用通知的时候需要等等等等。或许目前你还没有为得不到 Context 而发愁过,因为我们很多的操作都是在活动中进行的,而活动本身就是一个 Context 对象。但是,当应用程序的架构逐渐开始复杂起来的时候,很多的逻辑代码都将脱离 Activity 类,但此时你又恰恰需要使用 Context,也许这个时候你就会感到有些伤脑筋了。举个例子来说吧,我们编写了一个 HttpUtil 类,在这里将一些通用的网络操作封装了起来
这里使用 sendHttpRequest()方法来发送 HTTP 请求显然是没有问题的,并且我们还可以在回调方法中处理服务器返回的数据。但现在我们想对 sendHttpRequest()方法进行
一些优化,当检测到网络不存在的时候就给用户一个 Toast 提示,并且不再执行后面的代码。看似一个挺简单的功能,可是却存在一个让人头疼的问题,弹出 Toast 提示需要一个Context 参数,而我们在 HttpUtil 类中显然是获取不到 Context 对象的,这该怎么办呢?其实要想快速解决这个问题也很简单,大不了在 sendHttpRequest()方法中添加一个Context 参数就行了嘛,于是可以将 HttpUtil 中的代码进行如下修改
可 以 看 到 , 这 里 在 方 法 中 添 加 了 一 个 Context 参 数 , 并 且 假 设 有 一 个isNetworkAvailable()方法用于判断当前网络是否可用,如果网络不可用的话就弹出 Toast
提示,并将方法 return 掉。虽说这也确实是一种解决方案, 但是却有点推卸责任的嫌疑, 因为我们将获取 Context的任务转移给了 sendHttpRequest()方法的调用方,至于调用方能不能得到 Context 对象,那就不是我们需要考虑的问题了。由此可以看出,在某些情况下,获取 Context 并非是那么容易的一件事,有时候还是挺伤脑筋的。不过别担心,下面我们就来学习一种技巧,让你在项目的任何地方都能够轻松获取到 Context。Android 提供了一个 Application 类,每当应用程序启动的时候,系统就会自动将这个类进行初始化。而我们可以定制一个自己的
Application 类,以便于管理程序内一些全局的状态信息,比如说全局 Context。定制一个自己 Application 其实并不复杂,首先我们需要创建一个 MyApplication 类继承自 Application
可以看到,MyApplication 中的代码非常简单。这里我们重写了父类的 onCreate()方法,并通过调用 getApplicationContext()方法得到了一个应用程序级别的 Context,然
后又提供了一个静态的 getContext()方法,在这里将刚才获取到的 Context 进行返回。接下来我们需要告知系统,当程序启动的时候应该初始化 MyApplication 类,而不是默认的 Application 类。 这一步也很简单, 在 AndroidManifest.xml 文件的<application>标签下进行指定就可以了
注意这里在指定 MyApplication 的时候一定要加上完整的包名,不然系统将无法找到这个类。这样我们就已经实现了一种全局获取 Context 的机制,之后不管你想在项目的任何地方使用 Context,只需要调用一下 MyApplication.getContext()就可以了。那么接下来我们再对 sendHttpRequest()方法进行优化
使用 Intent 传递对象
Intent 的用法相信你已经比较熟悉了,我们可以借助它来启动活动、发送广播、启动服务等。在进行上述操作的时候,我们还可以在 Intent 中添加一些附加数据,以达到传值的效果,比如在 FirstActivity 中添加如下代码
使用 Intent 来传递对象通常有两种实现方式,Serializable 和 Parcelable
Serializable 是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。至于序列化的方法也很简单,只需要让一个类去实现 Serializable 这个接口就可以了。比如说有一个 Person 类,其中包含了 name 和 age 这两个字段,想要将它序列化就可以这样写
其中 get、set 方法都是用于赋值和读取字段数据的,最重要的部分是在第一行。这里让 Person 类去实现了 Serializable 接口,这样所有的 Person 对象就都是可序列化的了。
接下来在 FirstActivity 中的写法非常简单
这里调用了 getSerializableExtra()方法来获取通过参数传递过来的序列化对象,接着再将它向下转型成 Person 对象,这样我们就成功实现了使用 Intent 来传递对象的功能了
除了 Serializable 之外,使用 Parcelable 也可以实现相同的效果,不过不同于将对象进行序列化,Parcelable 方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是 Intent 所支持的数据类型,这样也就实现传递对象的功能了。下面我们来看一下 Parcelable 的实现方式,修改 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 中获取对象的时候需要稍加改动
定制自己的日志工具
控制日志的打印,当程序处于开发阶段就让日志打印出来,当程序上线了之后就把日志屏蔽掉
可以看到,我们在 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 而发愁过,因为我们很多的操作都是在活动中进行的,而活动本身就是一个 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 就可以了。
相关文章推荐
- MySQL Server 日志
- MSSQL 2005 LOG备份webshell的方法
- javascript prototype,executing,context,closure
- 使用MySQL Slow Log来解决MySQL CPU占用高的问题
- 清理SQL Server 2008日志文件Cannot shrink log file 2 的解决方案
- Android系统开发中log的使用方法及简单的原理
- android杂记:C++文件的添加log方法分享
- 提取oralce当天的alert log的shell脚本代码
- mysql正确安全清空在线慢查询日志slow log的流程分享
- PHP error_log()将错误信息写入一个文件(定义和用法)
- Apache启动错误Permission denied: httpd: could not open error log file解决方法
- android基础教程之context使用详解
- Android编程中context及全局变量实例详解
- Android编程获取全局Context的方法
- Android开发笔记之:Log图文详解(Log.v,Log.d,Log.i,Log.w,Log.e)
- Android编程之基于Log演示一个activity生命周期实例详解
- PHP如何将log信息写入服务器中的log文件
- SQL Server中减小Log文件尺寸的方法分享
- Python的Django框架中的Context使用
- 详解Django框架中用context来解析模板的方法