前言

网上关于 Context 的文章也已经有不少了,比如值得参考的有:

Android Context完全解析,你所不知道的Context的各种细节

Android Context 到底是什么?

但看了一下,发现还有值得讨论的地方,比如这个等式:

Context个数 = Service 个数 + Activity 个数 + 1

老实说,我不明白这个等式有什么意义,而且还是错的。首先多进程情况下,Application 对象就不止一个;其次,Activity、Service、Application 继承自 ContextWrapper,它们自己就是一个 Context,里面又有一个 Base Context;最后,还有各种 outer context、display context 什么的,这部分没深入研究过,但 Context 的数量绝对大于上述等式的两倍了。

上面这部分算一个讨论,下面正式进入正题。

Context 家族

Context 本身是一个抽象类,主要实现类为 ContextImpl,另外有子类 ContextWrapper 和 ContextThemeWrapper,这两个子类都是 Context 的代理类,主要区别是 ContextThemeWrapper 有自己的主题资源。它们继承关系如下:

Context 有什么用?

如果要弄清楚 “某个类有什么用” 这样的问题,其实很简单,看一下它提供了什么接口就知道了,下面列举一些主要的:

  1. /**
  2. * Interface to global information about an application environment. This is
  3. * an abstract class whose implementation is provided by
  4. * the Android system. It
  5. * allows access to application-specific resources and classes, as well as
  6. * up-calls for application-level operations such as launching activities,
  7. * broadcasting and receiving intents, etc.
  8. */
  9. public abstract class Context {
  10. // 四大组件相关
  11. public abstract void startActivity(@RequiresPermission Intent intent);
  12. public abstract void sendBroadcast(@RequiresPermission Intent intent);
  13. public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
  14. IntentFilter filter);
  15. public abstract void unregisterReceiver(BroadcastReceiver receiver);
  16. public abstract ComponentName startService(Intent service);
  17. public abstract boolean stopService(Intent service);
  18. public abstract boolean bindService(@RequiresPermission Intent service,
  19. @NonNull ServiceConnection conn, @BindServiceFlags int flags);
  20. public abstract void unbindService(@NonNull ServiceConnection conn);
  21. public abstract ContentResolver getContentResolver();
  22. // 获取系统/应用资源
  23. public abstract AssetManager getAssets();
  24. public abstract Resources getResources();
  25. public abstract PackageManager getPackageManager();
  26. public abstract Context getApplicationContext();
  27. public abstract ClassLoader getClassLoader();
  28. public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) { ... }
  29. public final String getString(@StringRes int resId) { ... }
  30. public final int getColor(@ColorRes int id) { ... }
  31. public final Drawable getDrawable(@DrawableRes int id) { ... }
  32. public abstract Resources.Theme getTheme();
  33. public abstract void setTheme(@StyleRes int resid);
  34. public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { ... }
  35. // 获取应用相关信息
  36. public abstract ApplicationInfo getApplicationInfo();
  37. public abstract String getPackageName();
  38. public abstract Looper getMainLooper();
  39. public abstract int checkPermission(@NonNull String permission, int pid, int uid);
  40. // 文件相关
  41. public abstract File getSharedPreferencesPath(String name);
  42. public abstract File getDataDir();
  43. public abstract boolean deleteFile(String name);
  44. public abstract File getExternalFilesDir(@Nullable String type);
  45. public abstract File getCacheDir();
  46. ...
  47. public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
  48. public abstract boolean deleteSharedPreferences(String name);
  49. // 数据库相关
  50. public abstract SQLiteDatabase openOrCreateDatabase(...);
  51. public abstract boolean deleteDatabase(String name);
  52. public abstract File getDatabasePath(String name);
  53. ...
  54. // 其它
  55. public void registerComponentCallbacks(ComponentCallbacks callback) { ... }
  56. public void unregisterComponentCallbacks(ComponentCallbacks callback) { ... }
  57. ...
  58. }
  59. public interface ComponentCallbacks {
  60. void onConfigurationChanged(Configuration newConfig);
  61. void onLowMemory();
  62. }

结合注释,可以发现,Context 就相当于 Application 的大管家,主要负责:

  1. 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
  2. 获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等
  3. 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
  4. 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
  5. 其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

ContextImpl 、ContextWrapper、ContextThemeWrapper 有什么区别?

ContextWrapper

先看 ContextWrapper:

  1. /**
  2. * Proxying implementation of Context that simply delegates all of its calls to
  3. * another Context. Can be subclassed to modify behavior without changing
  4. * the original Context.
  5. */
  6. public class ContextWrapper extends Context {
  7. // 注意这个成员
  8. Context mBase;
  9. public ContextWrapper(Context base) {
  10. mBase = base;
  11. }
  12. protected void attachBaseContext(Context base) {
  13. if (mBase != null) {
  14. throw new IllegalStateException("Base context already set");
  15. }
  16. mBase = base;
  17. }
  18. // 这就是经常让人产生疑惑的 Base Context 了
  19. public Context getBaseContext() {
  20. return mBase;
  21. }
  22. // 下面这些方法全都直接通过 mBase 完成
  23. @Override
  24. public AssetManager getAssets() {
  25. return mBase.getAssets();
  26. }
  27. @Override
  28. public Resources getResources() {
  29. return mBase.getResources();
  30. }
  31. @Override
  32. public PackageManager getPackageManager() {
  33. return mBase.getPackageManager();
  34. }
  35. ...
  36. }

可以看到,ContextWrapper 实际上就是 Context 的代理类而已,所有的操作都是通过内部成员 mBase 完成的,另外,Activity、Service 的 getBaseContext 返回的就是这个 mBase。

ContextThemeWrapper

接着看 ContextThemeWrapper,这个类的代码并不多,主要看 Resource 和 Theme 相关的:

  1. /**
  2. * A context wrapper that allows you to modify or replace the theme of the
  3. * wrapped context.
  4. */
  5. public class ContextThemeWrapper extends ContextWrapper {
  6. private int mThemeResource;
  7. private Resources.Theme mTheme;
  8. private LayoutInflater mInflater;
  9. private Configuration mOverrideConfiguration;
  10. private Resources mResources;
  11. public ContextThemeWrapper() {
  12. super(null);
  13. }
  14. public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
  15. super(base);
  16. mThemeResource = themeResId;
  17. }
  18. public ContextThemeWrapper(Context base, Resources.Theme theme) {
  19. super(base);
  20. mTheme = theme;
  21. }
  22. @Override
  23. protected void attachBaseContext(Context newBase) {
  24. super.attachBaseContext(newBase);
  25. }
  26. // 在 Recource 初始化之前,传入配置信息
  27. public void applyOverrideConfiguration(Configuration overrideConfiguration) {
  28. if (mResources != null) {
  29. throw new IllegalStateException(...);
  30. }
  31. if (mOverrideConfiguration != null) {
  32. throw new IllegalStateException(...);
  33. }
  34. mOverrideConfiguration = new Configuration(overrideConfiguration);
  35. }
  36. public Configuration getOverrideConfiguration() {
  37. return mOverrideConfiguration;
  38. }
  39. // 没有重写 setResource,即 setResource 行为和父类一样
  40. @Override
  41. public Resources getResources() {
  42. return getResourcesInternal();
  43. }
  44. private Resources getResourcesInternal() {
  45. if (mResources == null) {
  46. if (mOverrideConfiguration == null) {
  47. mResources = super.getResources();
  48. } else {
  49. // 根据配置信息初始化 Resource
  50. // 注意,这里创建了另一个和 Base Context 不同的 Resource
  51. final Context resContext = createConfigurationContext(mOverrideConfiguration);
  52. mResources = resContext.getResources();
  53. }
  54. }
  55. return mResources;
  56. }
  57. @Override
  58. public void setTheme(int resid) {
  59. if (mThemeResource != resid) {
  60. mThemeResource = resid;
  61. initializeTheme();
  62. }
  63. }
  64. private void initializeTheme() {
  65. final boolean first = mTheme == null;
  66. if (first) {
  67. // 根据 Resource 获取 Theme
  68. mTheme = getResources().newTheme();
  69. // 复制内容
  70. final Resources.Theme theme = getBaseContext().getTheme();
  71. if (theme != null) {
  72. mTheme.setTo(theme);
  73. }
  74. }
  75. onApplyThemeResource(mTheme, mThemeResource, first);
  76. }
  77. protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
  78. theme.applyStyle(resId, true);
  79. }
  80. @Override
  81. public Resources.Theme getTheme() {
  82. // 只会初始化一次
  83. if (mTheme != null) {
  84. return mTheme;
  85. }
  86. mThemeResource = Resources.selectDefaultTheme(mThemeResource,
  87. getApplicationInfo().targetSdkVersion);
  88. initializeTheme();
  89. return mTheme;
  90. }
  91. ...
  92. }

结合注释及源码,可以发现,相比 ContextWrapper,ContextThemeWrapper 有自己的另外 Resource 以及 Theme 成员,并且可以传入配置信息以初始化自己的 Resource 及 Theme。即 Resource 以及 Theme 相关的行为不再是直接调用 mBase 的方法了,也就说,ContextThemeWrapper 和它的 mBase 成员在 Resource 以及 Theme 相关的行为上是不同的。

ContextImpl

下面看一下 ContextImpl 有关 Theme 以及 Resource 的部分,以分析它和 ContextThemeWrapper 的区别:

  1. /**
  2. * Common implementation of Context API, which provides the base
  3. * context object for Activity and other application components.
  4. */
  5. class ContextImpl extends Context {
  6. private int mThemeResource = 0;
  7. private Resources.Theme mTheme = null;
  8. private @NonNull Resources mResources;
  9. // 用于创建 Activity Context
  10. static ContextImpl createActivityContext(...) {
  11. ContextImpl context = new ContextImpl(...);
  12. context.setResources(resourcesManager.createBaseActivityResources(...));
  13. return context;
  14. }
  15. // 用于创建 Application Context、Service Context
  16. static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
  17. ContextImpl context = new ContextImpl(...);
  18. context.setResources(packageInfo.getResources());
  19. return context;
  20. }
  21. private static Resources createResources(...) {
  22. return ResourcesManager.getInstance().getResources(...);
  23. }
  24. // ContextThemeWrapper 没有重写父类的 setResources
  25. // 因此会调用 mBase 的 setResources,即和 ContextImpl 的行为一样
  26. void setResources(Resources r) {
  27. if (r instanceof CompatResources) {
  28. ((CompatResources) r).setContext(this);
  29. }
  30. mResources = r;
  31. }
  32. @Override
  33. public Resources getResources() {
  34. return mResources;
  35. }
  36. /* ---------- 主题相关 ------------ */
  37. @Override
  38. public void setTheme(int resId) {
  39. synchronized (mSync) {
  40. if (mThemeResource != resId) {
  41. mThemeResource = resId;
  42. initializeTheme();
  43. }
  44. }
  45. }
  46. // 直接创建一个 Themem 对象,相比 ContextThemeWrapper,少了一部分内容
  47. private void initializeTheme() {
  48. if (mTheme == null) {
  49. mTheme = mResources.newTheme();
  50. }
  51. mTheme.applyStyle(mThemeResource, true);
  52. }
  53. @Override
  54. public Resources.Theme getTheme() {
  55. synchronized (mSync) {
  56. // 和 ContextThemeWrapper 基本一样
  57. if (mTheme != null) {
  58. return mTheme;
  59. }
  60. mThemeResource = Resources.selectDefaultTheme(mThemeResource,
  61. getOuterContext().getApplicationInfo().targetSdkVersion);
  62. initializeTheme();
  63. return mTheme;
  64. }
  65. }
  66. }

从代码中可以看出,ContextImpl 和 ContextThemeWrapper 最大的区别就是没有一个 Configuration 而已,其它的行为大致是一样的。另外,ContextImpl 可以用于创建 Activity、Service 以及 Application 的 mBase 成员,这个 Base Context 时除了参数不同,它们的 Resource 也不同。需要注意的是,createActivityContext 等方法中 setResource 是 mBase 自己调用的,Activity、Service 以及 Application 本身并没有执行 setResource。

小结

  1. ContextWrapper、ContextThemeWrapper 都是 Context 的代理类,二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化

  2. ContextImpl 是 Context 的主要实现类,Activity、Service 和 Application 的 Base Context 都是由它创建的,即 ContextWrapper 代理的就是 ContextImpl 对象本身

  3. ContextImpl 和 ContextThemeWrapper 的主要区别是, ContextThemeWrapper 有 Configuration 对象,Resource 可以根据这个对象来初始化

  4. Service 和 Application 使用同一个 Recource,和 Activity 使用的 Resource 不同

Activity Context、Service Context、Application Context、Base Context 有什么区别?

Activity Context

先看 Activity,Activity 在启动时,最终会执行 ActivityThread 的 performLaunchActivitiy:

  1. public final class ActivityThread {
  2. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  3. ...
  4. // 这个 Context 将会作为 Activity 的 Base Context
  5. ContextImpl appContext = createBaseContextForActivity(r);
  6. Activity activity = null;
  7. try {
  8. ClassLoader cl = appContext.getClassLoader();
  9. // 创建 Activity
  10. activity = mInstrumentation.newActivity(
  11. cl, component.getClassName(), r.intent);
  12. StrictMode.incrementExpectedActivityCount(activity.getClass());
  13. } catch (Exception e) {
  14. ...
  15. }
  16. try {
  17. // 创建 Application
  18. Application app = r.packageInfo.makeApplication(false, mInstrumentation);
  19. if (activity != null) {
  20. // 初始化 Activity,注意参数 appContext
  21. activity.attach(appContext, ...);
  22. ...
  23. }
  24. } catch (...) {
  25. ...
  26. }
  27. return activity;
  28. }
  29. private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
  30. ContextImpl appContext = ContextImpl.createActivityContext(...);
  31. ...
  32. }
  33. }

可以看到,Activity 的 Base Context 就是上面分析过的 ContextImpl 的 createActivityContext 创建的。

同时,Service 的 Base Context 的创建过程和 Application 一样,调用的都是 ContextImpl 的 createAppContext,即 Service Context 和 Application Context 的 Resource 是相同的。因此这里跳过 Service,下面看一下 Application Context。

Application Context

在上面 ActivityThread 的 performLaunchActivity 方法中,可以看到一个 makeApplication 的调用,它是 LoaedApk 的方法:

  1. public final class LoadedApk {
  2. private Application mApplication;
  3. public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
  4. if (mApplication != null) {
  5. return mApplication;
  6. }
  7. Application app = null;
  8. try {
  9. // 创建 Base Context
  10. ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
  11. // 创建 Application 并设置 Base Context
  12. app = mActivityThread.mInstrumentation.newApplication(
  13. cl, appClass, appContext);
  14. appContext.setOuterContext(app);
  15. } catch (Exception e) {
  16. ...
  17. }
  18. // Application 创建成功,赋值给 mApplication
  19. mApplication = app;
  20. ...
  21. return app;
  22. }
  23. // 获取 mApplication
  24. Application getApplication() {
  25. return mApplication;
  26. }
  27. }
  1. public class Application extends ContextWrapper implements ComponentCallbacks2 {
  2. /* package */ final void attach(Context context) {
  3. // 调用父类的 attachBaseContext 以设置 mBase
  4. attachBaseContext(context);
  5. }
  6. }

可以看到,Instrumentation 是使用反射的方法创建 Application 对象,创建完毕后,会执行 Application 的 attach 方法设置 mBase 成员。

Application 及其 Base Context 的创建过程我们了解了,接下来看一下 getApplicationContext 的实现:

  1. class ContextImpl extends Context {
  2. ActivityThread mMainThread;
  3. LoadedApk mPackageInfo;
  4. @Override
  5. public Context getApplicationContext() {
  6. return (mPackageInfo != null) ?
  7. mPackageInfo.getApplication() : mMainThread.getApplication();
  8. }
  9. }

从代码中可以看出,getApplicationContext 的返回值可能有两个:第一个是 LoadedApk 的 getApplication 方法,这个方法的返回值就是刚刚创建的 Application 对象;第二个是 ActivityThread 的 getApplication 方法:

  1. public final class ActivityThread {
  2. Application mInitialApplication;
  3. public Application getApplication() {
  4. return mInitialApplication;
  5. }
  6. public static ActivityThread systemMain() {
  7. // 创建 ActivityThread
  8. ActivityThread thread = new ActivityThread();
  9. thread.attach(true);
  10. return thread;
  11. }
  12. private void attach(boolean system) {
  13. mSystemThread = system;
  14. if (!system) {
  15. ...
  16. } else {
  17. try {
  18. mInstrumentation = new Instrumentation();
  19. // 注意参数 getSystemContext().mPackageInfo
  20. ContextImpl context = ContextImpl.createAppContext(
  21. this, getSystemContext().mPackageInfo);
  22. // 创建 Application
  23. mInitialApplication = context.mPackageInfo.makeApplication(true, null);
  24. mInitialApplication.onCreate();
  25. } catch (Exception e) {
  26. ...
  27. }
  28. }
  29. ...
  30. }
  31. }

ActivityThread 中的 mInitialApplication 是在 systemMain 方法执行时创建的,而这个方法又是 SystemServer 启动时调用的,结合参数 getSystemContext().mPackageInfo,因此个人推测 mInitialApplication 对应的是系统的某个 apk,即系统级别的 Application,但具体是不是这样,目前还没有深入研究过,有兴趣的可以自己研究。

为什么不推荐使用 Base Context?

一般情况下,使用代理而不直接使用某个对象,目的可能有两个:

  1. 定制自己的行为
  2. 不影响原对象

其中 Servcie 和 Application 的父类 ContextWrapper 完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper 则自定义了 Resource 以及 Theme 的相关行为,因此,个人理解:

  1. 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
  2. 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象

对于 Activity 的 getResource 问题,我写了一份代码来验证:

  1. public class MainActivity extends AppCompatActivity {
  2. private Configuration mOverrideConfiguration;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. Log.i(TAG, "getResources: " + getResources() + ", getBaseContext().getResources():"
  8. + getBaseContext().getResources());
  9. }
  10. // 因为 Android 会在 onCreate 之前自动调用 getResource
  11. // 因此需要在这里执行 applyOverrideConfiguration
  12. @Override
  13. public Resources getResources() {
  14. if (mOverrideConfiguration == null) {
  15. mOverrideConfiguration = new Configuration();
  16. applyOverrideConfiguration(mOverrideConfiguration);
  17. }
  18. return super.getResources();
  19. }
  20. }

输出(我用的是小米手机):

  1. getResources: android.content.res.MiuiResources@3c660a7,
  2. getBaseContext().getResources():android.content.res.MiuiResources@5143954

可以看到,就像源码显示的那样,应用了 Configuration 之后,Activity 的 getResource 方法返回的和 getBaseContext().getResources() 方法返回的不是同一个对象

小结

  1. Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类
  2. getApplicationContext 返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application
  3. 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
  4. 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象

总结

Context 的继承关系如下:

Context 相当于 Application 的大管家,主要负责:

  1. 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
  2. 获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等
  3. 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
  4. 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
  5. 其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

ContextWrapper、ContextThemeWrapper、ContextImpl 的区别:

  1. ContextWrapper、ContextThemeWrapper 都是 Context 的代理类,二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化
  2. ContextImpl 是 Context 的主要实现类,Activity、Service 和 Application 的 Base Context 都是由它创建的,即 ContextWrapper 代理的就是 ContextImpl 对象本身
  3. ContextImpl 和 ContextThemeWrapper 的主要区别是, ContextThemeWrapper 有 Configuration 对象,Resource 可以根据这个对象来初始化

Activity Context、Service Context、Application Context、Base Context 的区别:

  1. Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类
  2. Service 和 Application 使用同一个 Recource,和 Activity 使用的 Resource 不同
  3. getApplicationContext 返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application

深入理解 Android 中的各种 Context的更多相关文章

  1. 【转】Android菜单详解——理解android中的Menu--不错

    原文网址:http://www.cnblogs.com/qingblog/archive/2012/06/08/2541709.html 前言 今天看了pro android 3中menu这一章,对A ...

  2. 深入理解Android中View

    文章目录   [隐藏] 一.View是什么? 二.View创建的一个概述: 三.View的标志(Flag)系统 四.MeasureSpec 五.几个重要方法简介 5.1 onFinishInflate ...

  3. 绝对让你理解Android中的Context

    这个问题是StackOverFlow上面一个热门的问题What is Context in Android? 整理这篇文章的目的是Context确实是一个非常抽象的东西.我们在项目中随手都会用到它,但 ...

  4. 深入理解Android中ViewGroup

    文章目录   [隐藏] 一.ViewGroup是什么? 二.ViewGroup这个容器 2.1 添加View的算法 2.1.1 我们先来分析addViewInner方法: 2.1.2 addInArr ...

  5. 理解android中ListFragment和Loader

    一直以来不知Android中Loader怎么用,今天晚上特意花了时间来研究,算是基本上搞明白了,现在把相关的注释和代码发出来,以便笔记和给网友一个参考,错误之处还望大家给我留言,共同进步,这个例子采用 ...

  6. Android菜单详解(一)——理解android中的Menu

    前言 今天看了pro android 3中menu这一章,对Android的整个menu体系有了进一步的了解,故整理下笔记与大家分享. PS:强烈推荐<Pro Android 3>,是我至 ...

  7. 彻底理解 Android 中的阴影

    如果我们想创造更好的 Android App,我相信我们需要遵循 Material Design 的设计规范.一般而言,Material Design 是一个包含光线,材质和投影的三维环境.如果我们想 ...

  8. 一个demo让你彻底理解Android中触摸事件的分发

    注:本文涉及的demo的地址:https://github.com/absfree/TouchDispatch 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOW ...

  9. 【转】深入理解Android中的SharedPreferences

    SharedPreferences作为Android中数据存储方式的一种,我们经常会用到,它适合用来保存那些少量的数据,特别是键值对数据,比如配置信息,登录信息等.不过要想做到正确使用SharedPr ...

随机推荐

  1. [bzoj2120] [洛谷P1903] 数颜色

    Description 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令: 1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜 ...

  2. 【java面试】Web篇

    1.AJAX创建步骤 step1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象:  step2. 创建一个新的HTTP请求,并指定改HTTP请求的方法.URL以及验证信息:  s ...

  3. vue-cli3 axios解决跨域问题

    这种错误就是跨域问题: 我百度了各种方法,最终下面这种方法解决了,直接上代码:  解决: 如果没安装axios: npm install axios -save //安装axios main.js / ...

  4. python从excel中读取数据传给其他函数使用

    首先安装xlrd库 pip install xlrd 方法1: 表格内容如下: 场景描述,读取该表格A列数据,然后打印出数据 代码何解析如下: import xlrd #引入xlrd库 def exc ...

  5. String字符串性能优化的几种方案

    String字符串是系统里最常用的类型之一,在系统中占据了很大的内存,因此,高效地使用字符串,对系统的性能有较好的提升. 针对字符串的优化,我在工作与学习过程总结了以下三种方案作分享: 一.优化构建的 ...

  6. postman的简单介绍及运用

    postman下载地址 https://www.getpostman.com/downloads/ postman的工作原理:发送请求给服务器,服务器处理postman发送的数据然后返回给postma ...

  7. SpringCloud与微服务Ⅲ --- SpringCloud入门概述

    一. 什么是SpringCloud SpringCloud基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetF ...

  8. 实验12: OSPF

    实验9-1:单区域点到点链路的OSPF 实验目的通过本实验可以掌握:(1)在路由器上启动OSPF 路由进程(2)启用参与路由协议的接口,并且通告网络及所在的区域(3)度量值cost 的计算(4)点到点 ...

  9. linux中rz、rs命令无法执行的情况

    执行如下安装命令: yum install -y lrzsz

  10. 遍历CSDN博客

    --http://blog.csdn.net/leixiaohua1020/article/list/14?viewmode=contents function saveData(data) loca ...