6、Configuration类:

      Configuration类专门用于描述手机设备上的配置信息,这些配置信息既包括用户特定的配置项,也包括系统的动态设备配置

      程序可调用Activity的如下方法来获取系统的Configuration对象:

Configuration cfg = getResources().getConfiguration();

      此后,可以调用Configuration对象的属性来获取设备状态

7、Android提供Handler 和 Looper 来满足线程间的通信:

    Handler类包含如下方法用于发送、处理消息:

    void handleMessage(Message msg): 处理消息的方法。该方法通常用于被重写。

    final boolean hasMessages(int what): 检查消息队列中是否包含what属性为指定值的消息。

    final boolean hasMessages(int what, Object object): 检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息。

    多个重载的Message obtainMessage(): 获取消息。


 

    sendEmptyMessage(int what): 发送空消息。

    final boolean sendEmptyMessageDelayed(int what, long delayMillis): 指定多少毫秒之后发送空消息。

    final boolean sendMessage(Message msg): 立即发送消息。

    final boolean sendMessageDelayed(Message msg, long delayMillis): 指定多少毫秒之后发送消息。 

    Looper、MessageQueue、Handler各自的作用如下:

    线程:UI Thread通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。

    Looper:Looper类用来管理特定线程内对象之间的消息交换。每个线程只有一个Looper,它负责管理MessageQueue,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。

    MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。

    Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息,先进先出原则。

    在线程中使用Handler的步骤如下:

    调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。

    有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。

    调用Looper的loop()方法启动Looper。

8、异步任务(AsyncTask):

    新线程也可能需要动态更新UI组件,异步任务(AsyncTask)可以解决这一问题,相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现。

    AsyncTask<Params, Progress, Result>是一个抽象类,通常用于被继承,继承AsyncTask时需要指定如下三个泛型参数:

    Params: 启动任务执行的输入参数的类型

    Progress: 后台任务完成的进度值的类型

    Result: 后台执行任务完成后返回结果的类型

    使用AsyncTask只要如下三步即可:

    #创建AsyncTask的子类,并为三个泛型参数指定类型。如果某个泛型参数不需要指定类型,可将它指定为Void。

    #根据需要,实现AsyncTask的一些方法。

    #调用AsyncTask子类的实例的execute(Params...params)开始执行耗时任务。

    使用AsyncTask时必须遵守如下规则:

    必须在UI线程中创建AsyncTask的实例;

    必须在UI线程中调用AsyncTask的execute()方法;

    AsyncTask的一些方法,不应该由程序员代码调用,而是由Android系统负责调用;

    每个AsyncTask只能被执行一次,多次调用将会引发异常。  

9、使用Intent和IntentFilter进行通信:

  #Android使用Intent来封装程序的“调用意图”,很明显使用Intent提供了一致的编程模型。在某些时候,应用程序只是想启动具有某种特征的组件,并不想和某个具体的组件耦合。

    Intent还是应用程序组件之间通信的重要媒介,两个Activity可以把需要交换的数据封装成Bundle对象,然后使用Intent来携带Bundle对象,这样就实现了两个Activity之间的数据交换

    Android的应用程序包含三种重要组件:Activity、Service、BroadcastReceiver,应用程序采用了一致的方式来启动它们——都是依靠Intent来进行启动的。

    Intent还可用于与被启动组件交换信息

                                  使用Intent启动不同组件的方法
Activity:

startActivity(Intent intent)
startActivityForResult(Intent intent, int requestCode)

Service:

ComponentName startService(Intent service)
boolean bindService(Intent service, ServiceConnection conn, int flags)

BroadcastReceiver:

sendBroadcast(Intent intent)
sendBroadcast(Intent intent, String receiverPermission)
sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
sendOrderedBroadcast(Intent intent, String receiverPermission)
sendStickyBroadcast(Intent intent)
sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

 

    Intent对象大致包含Component、Action、Category、Data、Type、ExtraFlag这7种属性,其中Component用于明确指定需要启动的目标组件,而Extra则用于“携带”需要交换的数据。

  #显式Intent:

    Intent的Component属性需要接受一个ComponentName对象,ComponentName对象包含如下几个构造器:

            ComponentName(String pkg, String cls):创建pkg所在包下的cls类所对应的组件。

            ComponentName(Context pkg, String cls):创建pkg所对应的包下的cls类所对应的组件。

            ComponentName(Context pkg, Class<?> cls):创建pkg所对应的包下的cls类所对应的组件。

    上面内容说明创建一个ComponentName需要指定包名和类名——可唯一确定一个组件类,这样应用程序即可根据给定的组件类去启动特定的组件。

    除此之外,Intent还包含了如下三个方法:

            setClass(Context packageContext, Class<?> cls):设置该Intent将要启动的组件对应的类。

            setClassName(Context packageContext, String className):设置该Intent将要启动的组件对应的类名。

            setClassName(String packageName, String className):设置该Intent将要启动的组件对应的类名。

    Android应用的Context代表了访问该应用环境信息的接口,而Android应用的包名则作为应用的唯一标识,因此Android应用的Context对象与该应用的包名有一一对应的关系

   上面三个setClass()方法就是指定了包名(分别通过Context指定或String指定)和组件的实现类(分别通过Class指定或通过String指定)。

   指定了Component属性的Intent已经明确了它将要启动哪个组件。

   当程序通过Intent的Component属性(明确指定了启动哪个组件)启动特定组件时,被启动组件几乎不需要使用<intent-filter.../>元素进行配置。

   隐式Intent:

   没有指定Component属性的Intent被称为隐式Intent——隐式Intent没有明确指定要启动哪个组件,应用将会根据Intent指定的规则去启动符合条件的组件,但具体是哪个组件则不确定。

 #Intent通过指定Action属性(其实就是一个普通字符串),就可以把该Intent与具体的Activity分离,从而提供高层次的解耦

// 创建Intent对象
Intent intent = new Intent();
// 为Intent设置Action属性(属性值就是一个普通字符串)
intent.setAction(ActionAttr.CRAZYIT_ACTION);
intent.addCategory(String str); startActivity(intent);

 

   每个Intent只能指定一个Action“要求”,但可以指定多个Category要求。

   上面的程序中指定了根据Intent来启动Activity——但该Intent并未以“硬编码”的方法指定要启动哪个Activity,启动哪个Activity取决于Activity配置中<intent-filter.../>元素的配置。

   <intent-filter.../>元素也可以是<service.../>、<receiver.../>两个元素的子元素,用于表明它们可以响应的Intent。

   <intent-filter.../>元素里通常可包含如下子元素:

             0~N个<action.../>子元素

             0~N个<category.../>子元素

             0~1个<data.../>子元素

   当<activity.../>元素里的<intent-filter.../>子元素里包含多个<action.../>子元素(相当于指定了多个字符串)时,就表明该Activity能响应Action属性值为其中任意一个字符串的Intent。

   每个组件可以声明自己满足多个Action要求、可满足多个Category要求,只要某个组件能满足的要求大于、等于Intent所制定的要求,那么该Intent就能启动该组件。

//  获取该Activity对应的Intent的Action属性
String action = getIntent().getAction();
//  显示Action属性
show.setText("Action为:" + action);
//  获取该Activity对应的Intent的Category属性
Set<String> cates = getIntent().getCategories();
//  显示Action属性
cate.setText("Category属性为:" + cates);

 

 #实际上Intent对象不仅可以启动本应用内程序组件,也可启动Android系统的其他应用的程序组件,包括系统自带的程序组件——只要权限允许。  

 #Data、Type属性

 #使用Bundle在Activity之间交换数据:

   Intent提供了多个重载的方法来“携带”额外的数据,如下所示:

   putExtras(Bundle data):向Intent中放入需要“携带”的数据包

   Bundle getExtras():取出Intent所“携带”的数据包

   putExtra(String name, Xxx value):向Intent中按key-value对的形式存入数据

   getXxxExtra(String name):从Intent中按key取出指定类型的数据

   上面两个方法是便捷的方法,是直接存、取Intent所携带的Bundle中的数据:如果该Intent中已经携带了Bundle对象,则该方法支架向Intent所夏代的Bundle存入数据;如果Intent还没有携带Bundle对象,该方法会纤维Intent创建一个Bundle,再向Bundle存入数据。

   上面方法中的Bundle就是一个简单的数据携带包,该Bundle对象包含了多个方法来存入数据:

   putXxx(String key, Xxx data):向Bundle放入Int、Long等各种类型的数据

   putSerializable(String key, Serializable data):向Bundle中放入一个可序列化的对象

   为了取出Bundle数据携带包里的数据,Bundle提供了如下方法:

   getXxx(String key):从Bundle取出Int、Long等各种类型的数据

   getSerializable(String key, Serializable data):从Bundle取出一个可序列化的对象

Person p =new Person(name.getText().toString(), passwd.getText().toString(), gender);
// 创建一个Bundle对象
Bundle data = new Bundle();
data.putSerializable("person", p);
// 创建一个Intent
Intent intent = new Intent(MainActivity.this, ResultActivity.class);
intent.putExtras(data);
// 启动intent对应的Activity
startActivity(intent);

 

   上面的程序中粗体字代码根据用户输入创建了一个Person对象,Person类只是一个简单的DTO对象,该Person类实现了java.io.Serializable接口,因此Person对象是可序列化的。 

//   获取启动该Result的Intent
Intent intent = getIntent();
//   直接通过Intent去取出它所携带的Bundle数据包中的数据
Person p = (Person) intent.getSerializableExtra("person");
name.setText(p.getName());
passwd.setText(p.getPass());
gender.setText(p.getGender());

 

  #启动其他Activity并返回结果:

    startActivityForResult(Intent intent, int requestCode),该方法用于启动指定Activity,而且期望获取指定Activity返回的结果,也是通过Bundle进行数据交换的。

    为了获取被启动的Activity所返回的结果,需要从两方面着手:

    当前Activity需要重写onActivityResult(int requestCode, int resultCode, Intent intent), 当被启动的Activity返回结果时,该方法将会被触发(为了知道该方法是由哪个请求的结果所触发的,可利用requestCode请求码;为了知道返回的数据来自于哪个新的Activity,可利用resultCode结果码)。

    被启动的Activity需要调用setResult()方法设置处理结果。

10、Activity和Task的启动模式:

    通常情况下,一个应用有一个Task,这个Task就是为了完成某个工作的一系列Activity的集合,而这些Activity又被组织成了堆栈的形式。当一个Activity启动时,就会把它压入该Task的堆栈,而当用户在该Activity中按返回键,或者代码中finish掉时,就会将它从该 Task的堆栈中弹出。

    在实际项目中我们应该根据特定的需求为每个活动指定恰当的启动模式。

    启动模式一共有四种:standard、singleTop、singleTask 和 singleInstance。

    可以在AndroidManifest.xml中通过给<activity>标签指定android:launchMode属性来选择启动模式。

    #standard: 是活动默认的启动模式。在standard模式下,每当启动一个新的活动,它就会在返回栈中入栈,并处于栈顶的位置,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。

    #singleTop: 在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。

    #singleTask: 每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。

    #singleInstance: 指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。假设我们的程序中有一个好的是允许其他程序调用的,如果想实现其他程序和我们的程序可以共享这个活动的实例,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用同一个返回栈,也就解决了共享活动实例的问题。

    当在一个返回栈里的活动界面按下BACK键,会逐一清空当前栈的活动,再清空另一个返回栈的栈顶活动。

11、关于SharedPreferences:

      有时候,应用程序有少量的数据需要保存,而且这些数据的格式很简单:都是普通的字符串标量类型的值等,比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果等)、小游戏的玩家积分等,对于这种数据,Android提供了SharedPreferences进行保存。

      保存的数据主要是类似于配置信息格式的数据,因此它保存的数据主要是简单类型的key-value对。

      SharedPreferences接口主要负责读取应用程序的Preferences数据,它提供了如下常用方法来访问SharedPreferences中的key-value对:

      boolean contains(String key):判断SharedPreferences是否包含特定key的数据。

      abstract Map<String, ?> getAll():获取SharedPreferences数据里全部的key-value对。

      boolean getXxx(String key, xxx defValue):获取SharedPreferences数据里指定key对应的value。如果该key不存在,返回默认值defValue。其中xxx可以是boolean、float、int、long、String等各种基本类型的值。

      SharedPreferences接口本身并没有提供写入数据的能力,而是通过 SharedPreferences的内部接口, SharedPreferences调用edit()方法即可获取它所对应的Editor对象

      Editor提供了如下方法来向 SharedPreferences写入数据:

      SharedPreferences.Editor clear(): 清空 SharedPreferences里所有数据。

      SharedPreferences.Editor putXxx(String key, xxx value): 向 SharedPreferences存入指定key对应的数据。

      SharedPreferences.Editor remove(String key): 删除 SharedPreferences里指定key对应的数据项。

      boolean commit(): 当Editor编辑完成后,调用该方法提交修改。

       SharedPreferences本身是一个接口,程序无法直接创建 SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日" + "hh:mm:ss");
//  存入当前时间
editor.putString("time", sdf.format(new Date()));

 

       由于 SharedPreferences并不支持写入Date类型的值,故程序使用了SimpleDateFormat将Date格式化成字符串后写入。 

       SharedPreferences数据总是以XML格式保存。

       为了读取其他程序的 SharedPreferences,可按如下步骤进行:

      (1) 需要创建其他程序对应的Context: org.crazyit.io就是其他程序的包名——实际上Android系统就是以应用程序的包名来作为该程序的标识的,Context代表了访问该Android应用的全局信息的接口。

useCount = createPackageContext("org.crazyit.io", Context.CONTEXT_IGNORE_SECURITY);

 

      (2) 调用其他应用程序的Context的getSharedPreferences(String name, int mode) 即可获取相应的 SharedPreferences对象。

      (3) 写入数据可调用 SharedPreferences的edit()方法获取相应的Editor即可。