Android学习笔记



Android开发前的注意事项
硬件限制带来的设计考虑事项:
  1. 高效,主要体现在代码的优化;
  2. 考虑有限的能力,主要是移动手机的存储、处理能力的限制;
  3. 为不同的屏幕进行设计;
  4. 考虑低效率,高延迟,如很多用户仍使用2G网络等;
  5. 考虑需要多少花费,如尽可能少地传送(网络)数据,通过缓存数据和地理编码结果来消除冗余或重复查找,尊重用户对后台数据传输的参数设置等。
考虑用户环境,保证应用程序:
  1. 可以预测,并且行为得当;
  2. 流畅地从后台切换到前台;
  3. 交互合理,以多种方式通知用户;
  4. 呈现出直观而有吸引力的用户界面;
  5. 快速响应。
google android 设计理念要求:
  1. 快速和高效;
  2. 响应快速;
  3. 数据保持新鲜;
  4. 安全;
  5. 连贯流畅的用户体验;
  6. 提供可访问性(指设计时考虑由于视力、肢体或年龄等问题导致无法正常使用的用户,利用android的可访问层为其提供服务,如文本转换语音、触摸反馈等)。

Android应用程序和Activity
  1. Android的软件栈:Android学习笔记_第1张图片
  2. Android应用程序的组成部分:Activity、Service、
    Content Provider
    、Intent、Broadcast Receiver、Widget、Notification;
  3. Manifest文件:自身属性(package、versionCode、versionName、installLocation
    ),包含标签(uses-sdk、uses-configuration、uses-feature、supports-srceens、supports-gl-texture、uses-permission、permission、application)其中application标签包含了常用的Activity、service、receiver和provider标签。
  4. 分离资源:按资源类型分类存放在res/values(strings.xml、colors.xml、dimens.xml、arrays.xml、styles.xml等)、res/drawable(图片文件)、res/layout(布局xml文件)、res/anim(动画xml文件,单逐帧动画存放在drawable文件夹中)、res/menu(菜单布局xml文件)中,通过‘-’连接符在文件夹后面加上限定符来为不同的语言和硬件创建资源,如drawable-ldpi、values-fr-rCA。
  5. 应用程序的优先级和进程状态:一个应用程序的优先级等同于它的多个组件之中最高的优先级,进程被终止的顺序由它们的应用程序的优先级所决定。进程状态分为Active进程(关键优先级)、可见进程(高优先级)、启动Service进程
    (高优先级)
    、后台进程(低优先级)
    、空进程
    (低优先级)。
  6. Application:一个Android应用程序只有一个Application对象,可以利用它完成以下3项工作:
    • 对Android运行时广播的应用程序级事件(如低内存)做出响应
    • 在应用程序组件之间传递对象
    • 管理和维护多个应用程序组件使用的资源
  7. Activity:
  • Activity栈。Activity的状态是由它在Activity栈中的位置所决定的,Activity栈是当前所有正在运行的Activity的后进先出的集合。
    • Android学习笔记_第2张图片
    • Activity有四种基本状态:
      • 活动状态(Running)。位于屏幕最前端时,此时处于可见状态,和用户可交互的状态。
      • 暂停状态(Paused)。当Acitivy被另一个透明的或者非全屏的Activity覆盖时的状态叫Paused状态,虽然可见但不可交互。
      • 停止状态(Stop)。当Activity被另外一个Activity覆盖、界面不可见时处于Stop状态。
      • 非活动状态(Killed)。Activity被系统杀死或者跟本没启动时就是Killed状态。
    • Activity的生存期:完整生存期、可见生存期、活动生存期。
    • Activity的生命周期:
      • Android学习笔记_第3张图片

Android布局
  • 常用布局:FrameLayout、LinearLayout、RelativeLayout、GridLayout等
  • RelativeLayout用到的一些重要的属性:
    • 第一类:属性值为true或false
      • android:layout_centerHrizontal                               水平居中
      • android:layout_centerVertical                                 垂直居中
      • android:layout_centerInparent                               相对于父元素完全居中
      • android:layout_alignParentBottom                         贴紧父元素的下边缘
      • android:layout_alignParentLeft                               贴紧父元素的左边缘
      • android:layout_alignParentRight                             贴紧父元素的右边缘
      • android:layout_alignParentTop                               贴紧父元素的上边缘
      • android:layout_alignWithParentIfMissing                 如果对应的兄弟元素找不到的话就以父元素做参照物
    • 第二类:属性值必须为id的引用名“@id/id-name”
      • android:layout_below                          在某元素的下方
      • android:layout_above                          在某元素的的上方
      • android:layout_toLeftOf                       在某元素的左边
      • android:layout_toRightOf                     在某元素的右边
      • android:layout_alignTop                      本元素的上边缘和某元素的的上边缘对齐
      • android:layout_alignLeft                      本元素的左边缘和某元素的的左边缘对齐
      • android:layout_alignBottom                 本元素的下边缘和某元素的的下边缘对齐
      • android:layout_alignRight                    本元素的右边缘和某元素的的右边缘对齐
    • 第三类:属性值为具体的像素值,如30dip,40px
      • android:layout_marginBottom              离某元素底边缘的距离
      • android:layout_marginLeft                   离某元素左边缘的距离
      • android:layout_marginRight                 离某元素右边缘的距离
      • android:layout_marginTop                   离某元素上边缘的距离
  • 优化布局:
    • 减少冗余布局,减少布局嵌套(少于10层),使用<merge>和<include>标签复用布局。
    • 避免使用过多的View(布局包含的View少于80),可以使用ViewStub延迟填充布局。
    • 使用Lint工具来分析布局的性能问题和检测其他问题。

Android 控件
  • 常用控件:Button、TextView、EditView、CheckBox、RadioGroup、ImageView、ImageButton、Spinner、AutoCompleteTextView、DatePicker、TimePicker、ProgressBar、SeekBar、RatingBar、ListView、Gallery、GridView。
  • AutoCompleteTextView:android:completionThreshold=”1″ 属性设置了一个阀值,规定用户打了多少字符之后才出现自动提示,默认值是2。
  • 使用Gallery来展示画廊,android:spacing="5dp" 属性则是用来设置元素之间的间隔。

Fragment
  • Fragment的生命周期:
    • Android学习笔记_第4张图片
  • Fragment Transaction:可以使用FragmentTransaction来在一个Activity内添加、删除和替换Fragment。
  • Fragment与Activity之间的接口:在Fragment中创建一个callback接口,而主Activity必须实现它,从而达到互相通信。
  • Android Fragment类:DialogFragment、ListFragment、WebViewFragment。
  • 若最低版本支持Android 3.0以上,则使用android.app.FragmentManager,否则使用 android.support.v4.app.FragmentManager。

创建新视图(View)
  • 修改现有的视图:通过继承现有的视图来定制其外观和行为。
  • 创建复合控件:通过扩展一个ViewGroup(通常是一个布局)来创建复合控件。
  • 定制新视图:通过重写View的onMeasure和onDraw方法来绘画自定义的控件。

Intent
  • 使用Intent来启动Activity(显式和隐式)
  • linkify:在TextView(及派生类)中通过RegEx模式匹配来创建超链接。使用Linkify,addLinks方法或android:autoLink属性来建立链接。
  • 使用Intent广播事件,通过注册Broadcast Receiver来监听广播。
    • 广播普通的Intent是无序的,随机的,可以通过sendOrderedBroadcast方法来发送有序广播,按优先级顺序被传递给所有具有合适权限的已注册的接收器(可以Broadcast Receiver的Intent Filter manifest节点中使用android:priority属性来指定其权限,值越大,优先级越高)。
    • Sticky Intent是Broadcast Intent的有用变体,可以保存它们最后一次广播的值,并且当有一个新的接收器被注册接收该广播时,它们会把这些值作为Intent返回。通过sendStickyBroadcast方法来广播Sticky Intent,不是必须制定一个接收器来获得Sticky Intent的当前值。
  • Local Broadcast Manager:
    • 使用Local Broadcast Manager需要导入Android Support包。
    • 局部广播更高效,也保证其它应用程序接收不到你广播的Intent。
    • 使用Local Broadcast Manager的sendBroadcastSync方法来发送同步广播,直到每个已注册的接收器都收到广播后才解除阻塞。
  • Pending Intent:提供了一种创建可由其它应用程序在稍晚时间触发的Intent的机制,通常用于包装在响应将来的事件时触发的Intent,例如单击Widget或Notification。

Intent Filter
  • 使用Intent Filter为隐性Intent服务,activity、service、receiver、provider标签下都可包含intent-filter标签。
    • 隐式启动一定要在intent filter中加入android.intent.category.DEFAULT:理论上来说,如果intent不指定category,那么无论intent filter的内容是什么都应该是匹配的。但是,如果是implicit(隐式)intent,android默认给加上一个CATEGORY_DEFAULT,这样的话如果intent filter中没有android.intent.category.DEFAULT这个category的话,匹配测试就会失败。所以,如果你的Activity支持接收implicit intent的话就一定要在intent filter中加入android.intent.category.DEFAULT。(Main Activity除外)
  • Intent Filter的属性:动作action、基本数据data、类别category、附加数据extra(数据保存在Bundle对象中)、指定组件component、数据类型type。

Broadcast Receiver
  • 在代码中注册Broadcast Receiver:只会在包含它的应用程序组件运行时才响应Intent。
  • 在manifest中注册Broadcast Receiver,通过这种方式注册,即使当应用程序被终止或者未启动时,也可以接收Broadcast Intent。
  • 监听本地的Broadcast Intent。
  • 使用Broadcast Intent监控设备的状态变化。
  • 使用Package Manager的setComponentEnabledSetting方法在运行时管理Manifest Receiver(其他组件也可以)。

Download Manager
  • 通过getSystemService(Context.DOWNLOAD_SERVICE)获得DownloadManager的实例,设置DownLoadManager.Request对象下载文件。通过注册一个Receiver来接收下载完成的通知,在注册一个Receiver来接收点击通知后的响应。
  • 自定义DownloadManager Notification,通过request的setTitle、setDescription、setNotificationVisibility等方法定义通知的状态。
  • 指定下载位置:通过request的setDestinationUri方法可以在外部存储上指定一个任意路径,通过setDestinationInExternalFilesDir方法在外部存储文件夹(通过Environment静态变量指定)中存储一个文件,通过setDestinationInExternalPublicDir方法在外部存储器的公共目录下指定一个文件夹(通过Environment静态变量指定)来存储文件。
  • 取消下载并删除下载文件:使用DownloadManager的remove方法。
  • 查询下载:通过设置DownLoadManager.Query对象,然后只用DownLoadManager的query方法查询,使用cursor游标遍历查找。

Shared Preference、保存状态文件系统
  • 通过getSharedPreferences方法取得SharedPreference对象,使用SharedPreference.Editor对象修改SharedPreference里的数据,使用getXXX方法来获取数据。
  • 首选项框架:由PreferenceScreen、PreferenceActivity和PreferenceFragment、PreferenceHeader、SharedPreference四个部分组成,通过定义相应的res/xml下的文件可以生产系统样式的PreferenceScreen,首选使用这个框架来创建自己应用的设置页面。
  • 持久化应用程序实例的状态:
    • 使用SharedPreference保存Activity的状态:通过调用Activity.getPreferences方法获得Activity自身范围的SharedPreference对象,不需要知道一个SharedPreference名称,其名称就是调用Activity的类名。这种方式适用于Activity由用户关闭的(back键)或者通过代码中调用finish关闭的情况。
    • 使用生命周期处理程序保存和还原Activity实例状态:通过重写Activity的onSaveInstanceState事件的处理程序,使用Bundle参数来保存UI实例的值。这种方式适用于由系统为了释放资源在运行时终止了Activity的情况。
    • 使用生命周期处理程序保存和还原Fragment实例状态:通过在Fragment的onCreate处理程序内调用setRetainInstance方法指定当关联的Activity被重新创建时保留Fragment实例。这个过程Fragment的onDestroy和onCreate不会被调用,但是其它生命周期处理程序仍然会被调用。
  • 将静态文件作为资源添加:通过将文件放置res/raw文件夹中,调用Resource对象的openRawResource方法,以R.raw.文件名(不带扩展名)作为参数,获得其InputStream对象。
  • 在Android文件系统下的操作:
    • 使用特定于应用程序的文件夹存储文件:
      • 内部存储:通过上下文提供的getFilesDir方法获得包含有指向内部的应用程序文件存储目录的绝对路径的File对象。
      • 外部存储:通过上下文提供的getExternalFilesDir方法获得包含有指向外部的应用程序文件存储目录的绝对路径的File对象。
      • 存储在以上两个目录的文件会在应用程序被卸载后一起被删除。另外,可以通过Environment.getExternalStorageDirectory方法获得外部存储的根路径,但这个路径下的文件不会随着应用程序被卸载后一起被删除。
    • 创建私有的应用程序文件,Android提供了openFileInput和openFileOutput方法来简化操作应用程序沙箱中的私有文件,通过getFilesDir方法可以获得使用openFileOutput所创建的文件的绝对路径。
    • 使用应用程序文件缓存:Android土工了一个可以管理的内部缓存和一个不能管理的外部缓存,分别调用getCacheDir和getExternalCacheDir获得包含其绝对路径的File对象。存储在这两个缓存文件夹的文件都会随着应用程序被卸载后一起被删除。内部缓存会在当系统运行在低可用存储空间的时候随机删除其文件夹下的文件,外部缓存则不会。
    • 存储公共可读的文件:以上存储特定于应用程序的文件通常不会被媒体扫描或被其它应用程序访问的,但是可通过Environment.getExternalStoragePublicDirectory方法可以用来找到存储应用程序公共可读的文件的路径。Environment提供了一系列DIRECTORY_XXXX字符串常量来统一公共的资源目录,可以作为Environment.getExternalStoragePublicDirectory、Context.getFilesDir和Context.getExternalFilesDir等方法的参数使用。

SQLite数据库和Content Provider
  • Content Value:ContentValue用来向SQLite数据库的表中插入新的行,每一个ContentValues对象都将一个表行表示为列名到值的映射。
  • Cursor:SQLite数据库查询作为Cursor对象返回,然后从Cursor中提取值。
  • 使用SQLite数据库:通过实现SQLiteOpenHelper抽象类的onCreate和onUpdate方法来实现创建、打开和升级数据库。调用SQLiteOpenHelper的getWritableDatabase(可写)或者getreadableDatabase(只读)来打开和获得数据库操作实例。通过调用数据库操作实例:
    • query(表名,列名字符串数组,where子句,where参数,groupBy语句,having语句,order语句)方法来查询数据;
    • insert(表名,null,ContentValues对象)方法来添加数据;
    • update(表名,ContentValues对象,where子句,where参数)方法来更新数据;
    • delete((表名,where子句,where参数)方法来删除数据。
  • 创建Content Provider:要想创建一个新的Content Provider,需要继承ContentProvider抽象类:
    • 首先在Manifest中注册Content Provider:
      • <provider
      •     android:name=".ToDoContentProvider"
      •     android:authorities="com.mythos.provider.todolist"
      •     android:exported="true" />;设置为true允许其他应用程序访问数据
    • 发布Content Provider的URI地址,定义一个URI Matcher来分析URL的形势,确定URI是请求所有数据还是单行数据;
    • 重写onCreate处理程序来初始化底层的数据源,一般是SQLite则初始化SQLiteOpenHelper;
    • 重写query方法,使用SQLiteQueryBuilder辅助类来执行查询;
    • 重写getType方法来返回基于传入的URI的正确MIME类型;
    • 重写update、delete、insert等修改数据库的事务方法,最好都调用Content Resolver的notifyChange方法通知所有已注册的ContentObserver。
  • 在Content Provider中存储文件:数据库里保存文件通常保存的是路径(一个合适的URI),通常表中文件类型的数据列名取名为_data。然后通过重写ContentProvider的openFile方法来提供一个ParceFileDescriptor对象。
  • 使用Content Provider:使用上下文的getContentResolver方法来获得一个ContentResolver对象实例:
    • 通过调用ContentResolver对象的query(URI,列名字符串数组,where子句,where参数,order语句)方法来查询ContentProvider公布的数据;默认情况下,ContentResolver是在应用程序的主线程上执行查询和事务操作的,为了确保应用程序的向英雄,应该通过子线程异步执行;
    • 使用Cursor Loader异步查询内容(非主线程):通过实现LoaderManager.LoaderCallbacks<Cursor>接口的onCreateLoader(初始化并返回一个CursorLoader对象)、onLoadFinished和onLoaderReset方法,然后作为LoaderManager对象的initLoader(int id,Bundle b,LoaderCallbacks lc)方法的参数进行CursorLoader的初始化并返回一个Loader对象;LoaderManager对象可以通过上下文的getLoaderManager方法获得;
    • 使用ContentResolver对象的insert、delete和update等事务方法来添加、删除和更新Content Provider的内容:
      • Content Resolver提供了两种方法来插入新记录——insert和bulkInsert;insert方法接收一个ContentValues对象,插入单行数据并返回新记录的URI;bulkInsert方法接收一个ContentValues对象数组,差不多多行数据并返回成功添加的行数;
      • 调用ContentResolver的delete方法,传入要删除的URI和where子句参数,若URI包含ID则删除一行数据,若不包含ID则依据where子句匹配条件来删除多行数据,并返回成功删除的行数;
      • 调用ContentResolver的update方法,传入要更新的URI、更新的ContentValues对象和where子句作为参数,若URI包含ID则更新一行数据,若不包含ID则依据where子句匹配条件来更新多行数据,并返回成功更新的行数;
  • 访问Content Provider中存储的文件:使用ContentResolver对象的openOutputStream方法来把一个新文件插入Content Provider中;使用ContentResolver对象的openInputStream方法来访问Content Provider中存储的文件。
  • 利用ContentProvider将搜索功能添加到应用程序中。
  • 访问本地的ContentProvider:
    • 使用Media Store Content Provider;
    • 使用Contacts Contract Content Provider:
      • 通过使用ContactsContract三个子类实现访问:Data、RawContacts和Contacts;
      • 要访问联系人详情,需要在应用程序的清单文件中包含READ_CONTACTS use-permission;
      • 基于Intent机制,使用先有的联系人应用程序(通常是本地应用程序)来查看、插入或选择一个联系人,这是一种最佳实践做法。
      • 要直接修改和增加联系人详情,需要在应用程序的清单文件中包含WRITE_CONTACTS use-permission;
    • 使用Calendar Content Provider:
      • 要访问Calendar Content Provider,需要在应用程序的清单文件中包含READ_CALENDAR use-permission;使用ContentResolver,通过他们的CONTENT_URI常量来查询任何CalendarProvider表。包括Calendars、Events、Instances、Attendees、Reminders表。
      • 使用Intent创建和编辑日历项,这是一种最佳实践做法。注意,Intent机制只对编辑时间的开始和结束时间提供支持。
      • 要直接修改和增加日历项,需要在应用程序的清单文件中包含WRITE_CALENDAR use-permission;

Service
  • Service是一种长生命周期的组件,用来执行一些持续性的,可能耗时的工作。
  • 创建和控制Service:
    • 通过继承Service类,重写onCreate和onBind方法,必须在应用程序清单中注册service节点;
    • 执行一个Service并控制它的重新启动行为:通过重写onStartCommand方事件处理程序来执行Service任务,然后通过返回的值来控制当Service被运行时终止后,系统应该如何相应Service的重新启动,三个值包括:
      • START_STICKY:
      • START_NOT_STICKY:
      • START_REDELIVER_INTENT:
    • 启动和停止Service:
      • 在Activity中调用startService启动Service(显示和隐式);
      • 在Activity中调用stopService停止Service(显示和隐式);
      • 当一个Service被启动后,再调用starService方法来执行onStartCommand处理操作,此时并不会再次调用Service的onCreate方法,所以无论重复调用多少次StartService,只要调用一次stopService就会终止对应的Service。
    • 自终止Service:由于Service具有高优先级,它们通常不会轻易被运行至终止,因此可以调用Service的stopSelf方法自终止,从而避免系统资源的浪费和电量的消耗。此时可以不传递参数从而强制终止,然而,如果service正在同时处理多个对onStartCommand的请求,那么不应在处理完一个请求之后就停止service,因为这是可能已经又收到了新的启动请求在第一个完成后停止将会结束掉第二个)。要避免这个问题,可以使用stopSelf(int startId)来保证你的停止请求对应于你最近的开始请求。也就是,当你调用stopSelf(int startId)时,你传递开始请求的ID传递给onStartCommand()的startId)给service,如果service在你调用stopSelf(int startId)之前收到一了个新的开始请求,发现ID不同,于是service将不会停止。
  • 绑定Service到Activity:
    • 首先要让一个Service支持绑定,需要实现Service的onBind方法,并返回一个IBinder对象,可以使用内部类并初始化对象获得;
    • Service与每一个组件连接表示为一个ServiceConnection对象,因此需要在组件内使用内部类并初始化对象获得一个ServiceConnection对象,并实现ServiceConnection的onServiceConnected和onServiceDisconnected方法。在onServiceConnected方法里使用IBinder对象的getService方法获得对Service实例的引用;
    • 要执行绑定,需要在Activity中调用bindService(intent,serviceConnection,Context.BIND_AUTO_CREATE)方法,其中Context.BIND_AUTO_CREATE是一个标志位,表示在Activity和Service建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。4.0之后引入了其它含义的标志位包括:
      • Context.BIND_ADJUST_WITH_ACTIVITY;
      • Context.BIND_IMPORTANT和Context.BIND_ABOVE_CLIENT
      • Context.BIND_NOT_FOREGROUND;
      • Context.BIND_WAIVE_PRIORITY;
  • 创建前台Service:在必要时候提高Service的优先级,可以通过Service的startForeground方法设置该Service在前台运行,将一个Service设为前台运行可以有效地避免运行时在释放资源的时候终止这个Service,但是很占用资源。同时可以调用StopForeground方法把Service移到后台。
  • 使用AsyncTask运行异步任务:通过扩展AsyncTask<A,B,C>类并重写doInBackground(执行后台处理任务,通过punishProgress方法传递参数给onProgressUpdate方法,执行完毕返回值传递给onPostExecute方法)、onProgressUpdate(执行过程中更新UI)和onPostExecute(执行结束后更新UI)方法,其中A、B、C分别为泛型定义类型,同时也是以上三个方法的参数类型。创建好异步任务后,在UI线程中新建实例调用execute方法执行即可,每一个AsyncTask实例只能执行一次execute方法。AsyncTask类更适合简单的后台任务,但多任务异步操作就会变得复杂,可以考虑使用handler方式代替。
  • Intent Service是一个非常方便的包装类,它为更具需求执行一组任务的后台Service实现了最佳实践模式,如Internet的循环更新或者复杂的数据处理。通过扩展IntentService并重写onHandleIntent方法,每次启动IntentService时传入的Intent会被放入一个队列中,被onHandleIntent方法逐个在后台处理,当所有传入的Intent都被处理后,IntentService会自动终止自己。IntentService是创建按需或固定时间间隔执行一组任务的这种Service的最佳方法。
  • Loader:用于在UI元素(如Activity和Fragment)中进行异步数据加载的最佳实践技术,如CursorLoader。
  • 使用Thread线程类和Handler类手动创建线程和GUI线程同步。
  • 使用Alarm是一种在预先确定时间或时间间隔激活Intent的方式:
    • 使用AlarmManager的set方法并给它指定一个Alarm类型、触发时间和一个要激活的PendingIntent来创建一个普通的Alarm,同一个PendingIntent只能创建一个Alarm,多次创建会覆盖前一个Alarm;
    • 调用AlarmManager的cancel(pendingIntent)方法取消pendingIntent对应的Alarm;
    • 调用AlarmManager的setRepeating(精确时间间隔)或setInexactRepeating(不精确时间间隔)方法来创建重复Alarm。取消的方式与普通Alarm相同;
    • 精确时间的重复Alarm会消耗更多的电量,尽量减少Alarm频率,尽量使用没有精确指定时间的重复Alarm;
    • Alarm是在应用程序之外操作,所以即使应用程序关闭,它们依然能够运作;
    • Alarm和BroadcastReceiver一起使用时更加强大,允许设置能够激活广播Intent、启动Service,甚至启动Activity的Alarm等;
    • Alarm是降低应用程序资源需求的极为有效的方式,它允许停止Service和清除定时器,同时仍然可以执行调度的操作;
    • 可以使用Alarm实现基于网络查找的更新。

Notification
  • Notification Manager是用来处理Notification的系统Service,可以触发新的、修改现有的、删除不再需要的Notification。
  • 使用Notification Builder来创建Notification是Android3.0之后的首选方式。使用NotificationBuilder的setOnlyAlertOnce方法来更新Notification而不触发任务关联的闪灯、音频或振动。使用setAutoCancel(true)方法来配置Notification在被单击后自动取消自己。
  • 通过嗲用Notification的setLatestEventInfo方法使用标准的Notification UI布局。
  • 通过使用Remote Views对象来创建自定义的Notification UI布局。

屏幕分辨率设计
  • 使用密度无关的像素:使用dp单位指定布局尺寸、View大小或者图像大小;使用sp单位指定文本大小。
  • 使用像素密度的资源限定符:
    • res/drawable-ldpi:为120dpi左右的屏幕提供低密度资源;
    • res/drawable-mdpi:为160dpi左右的屏幕提供中等密度资源;
    • res/drawable-tvdpi:为213dpi左右的屏幕提供中等度资源,这是在API 13之后为了优化面向电视的应用程序而引入的;
    • res/drawable-hdpi:为240dpi左右的屏幕提供高密度资源;
    • res/drawable-xhdpi:为320dpi左右的屏幕提供超高密度资源;
    • res/drawable-nodpi:用于不管主屏幕密度如何都不能缩放的资源;
  • 通过创建可缩放的布局、指定支持的屏幕尺寸(利用清单文件中的supports-screen元素)、创建可缩放的图形资源(利用自定义xml文件的shape节点来自定义Drawable)和NiePatch图像来为不同屏幕大小提供支持和优化。

优化用户体验
  • 确保可访问性
    • 为非触屏设备提供导航。
    • 为每个View提供文本描述。
  • 控制设备振动
    • 需要有android.permission.VIBRATE权限。
    • 通过Vibrator Service的vibrate方法来启动设备的振动,抵用cancel方法取消振动。
  • 使用动画:
    • 补间View动画:可以定义一系列关于位置、大小、旋转和透明度的变化。使用AnimationSet类可以管理多个动画作为集合运行,调用View的startAnimation方法可以启动动画或动画集合。使用动画监听器AnimationListener来创建动画过程触发的事件。可用的动画类型:
      • AlphaAnimation(透明度)
      • RotateAnimation(旋转)
      • ScaleAnimation(缩放)
      • TranslateAnimation(移动)
    • 逐帧动画:
      • 在res/drawable下使用xml文件,使用<animation-list>来定义一组图片集合,通过setBackgroundResource方法设置为一个View的背景。
      • 可以使用getBackground来获得View的背景AnimationDrawable实例,然后调用start方法启动动画。
    • 差值属性动画:
      • 使用ObjectAnimator来实现一个View对象的某个属性的差值变化,这个View对象必须包含这个属性的getter/setter方法。
      • 可以通过在res/animator下使用xml文件,定义一个ObjectAnimator,使用AnimatorInflator.loadAnimator方法获得一个ObjectAnimator实例,然后使用setTarget方法将它应用到一个View对象上。
      • 默认情况下使用的是AccelerateDecelerateInterpolator插值器的变化效果,可以通过setInterpolator方法来设置新的变化效果。
      • 通过AnimatorSet类创建复杂、互相关联的动画,使用start来执行动画序列。
      • 使用Animator.AnimatorListener类来处理差值属性动画的事件处理程序。

Canvas绘图
  • 使用Canvas类:提供了一系列drawXXX方法来实现绘图设计。
  • 使用Paint类:
    • 通过setColor选择一个Paint的颜色;
    • 通过setStyle控制Paint对象的样式;
    • 使用setAlpha设置Paint对象的透明度;
    • 使用Shader类来定义渐变填充,是在2D图像中添加深度和文理的最佳方式之一:BitmapShader、ComposeShader和三种渐变Shader(线性、辐射和扫描);
    • 使用Shader TileMode来决定如何处理Shader填充的剩余区域;
    • 使用MaskFilter类可以为Paint对象分配边缘效果,实际上是对一个Paint的alpha通道应用转换
    • 使用ColorFilter对一个Paint的RGB通道应用转换;
    • 使用PathEffect来控制绘制轮廓线的方式;
    • 通过修改Paint的Xfermode来影响在画布中已有的图像上面绘制新的颜色的方式;
    • 使用抗锯齿效果来提高Paint质量
  • Canvas绘图的最佳实践:使用Canvas自定义绘图是非常消耗处理器资源的,而且低效的绘图方法会阻塞GUI线程,会对应用程序的相应造成很不利的影响。所以使用Canvas绘图时我们需要考虑:
    • 考虑大小和方向(屏幕分辨率和尺寸大小等);
    • 只创建一次静态对象;
    • 记住onDraw是很耗费资源的,可以考虑使用画布转换(rotate和translate),使用动画,使用位图、.9和Drawable资源来代替实现效果;
    • 避免重复绘制。
  • 硬件加速:通过硬件加速这种新的渲染方式能够让应用程序加速2D绘图,大多数情况下硬件加速是可用的(自定义View除外),但是回大量消耗处理器资源;
  • 使用SurfaceView可以在后台线程绘图,完全支持OpenGL ES库,可以用来绘制3D图像等;

创建交互式控件
  • 重写Activity和VIew的onTouchEvent事件处理程序,当触摸屏被触摸、释放和拖动时触发,也可以注册OnTouchListener监听器实现。
  • 重写Activity和VIew的onKeyDown和onKeyUp事件处理程序,当硬件安静被按下和释放时触发,也可以注册OnKeyListener监听器实现。
  • 重写Activity和VIew的onTrackballEvent事件处理程序,当轨迹球移动时触发。

高级Drawable资源
  • 由XML定义的复合Drawable;
  • 变换Drawable:
    • ScaleDrawable(<scale>标签)
    • RotateDrawable(<rotate>标签)
  • 层Drawable(<layer-list>标签)
  • 状态列表Drawable(<selector>标签
  • 级别列表Drawable(<level-list>标签

硬件传感器
  • 使用传感器和传感器管理器SensorManager,我们操作的是Sersor对象,它描述了它代表的硬件传感器的属性。Sersor类提供了一组常量,表示多种传感器类型,但是注意,设备上的硬件决定了应用程序中可以使用的传感器类型。
    • 虚拟传感器:提供简化的、经过校正的或者复合的数据,使用传感器更易于在一些应用程序上使用。
    • 通过调用SensorManager的getSersorList方法,并传入Sensor.TYPE_ALL参数可以得到设备上可用的传感器,传入特定的类型参数则获得特定类型的传感器的实现列表;使用getDefaultSensor方法可以找到指定类型的传感器的默认实现;
    • 应该检查任何必须传感器的可用性,如果传感器不存在,那么应用程序应该以正常方式退出。
  • 监视传感器:
    • 通过SensorManager的registerLisenert方法注册SensorEventListener,实现onSensorChange(监视传感器信息改变)和onAccuracyChange(监视传感器精度改变)方法;
    • onSensorChange方法中SensorEvent参数的四种属性:
      • sensor:传感器对象;
      • accuracy:表示精确度;
      • values:检测到的新值;
      • timestamp:检测时间;
    • 注册时可以设置更新速率参数,为了减少开销,最好选择可接受的最慢的速率:
      • SENSOR_DELAY_FASTEST:指定可以实现的最快更新速率;
      • SENSOR_DELAY_GAME:指定适合控制游戏的更新速率;
      • SENSOR_DELAY_NORMAL:指定默认的更新速率;
      • SENSOR_DELAY_UI:指定适合更新UI的更新速率;
    • 在应用程序不再需要接受更新时,注销传感器的监听器也很重要,分别在Activity的onResume和onPause中注册和注销传感器的事件监听器是一种好的做法。
  • 使用传感器:
    • 确定设备相对于自然方向的显示方向,返回的传感器值总是相对于设备的自然方向测量的,可以使用默认的Display对象的getRotation方法获得当前屏幕的旋转方向。
    • 使用加速计检测加速度变化;
    • 使用加速计和磁场传感器计算方向;
    • 基于设备的自然方向重新映射方向参考坐标系,使用SensorManager的remapCoordinateSyatem方法;
    • 使用气压计传感器检测当前气压,可以确定当前海拔;

基于位置的服务LBS
  • 位置管理器:使用getSystemService获得LocationManager实例。使用LBS之前,需要在manifest文件中添加uses-permission标签获取fine权限(高精度)或者coarse权限(低精度)。
  • 位置提供器:
    • 根据不同的设备,可以有多种技术来确定当前位置,即有多种位置提供器。LocationManager提供三个常量通过调用getProvider来返回三种位置提供器:
      • LocationManager.GPS_PROVIDER(需要fine权限)
      • LocationManager.NETWORK_PROVIDER(需要coarse权限)
      • LocationManager.PASSIVE_PROVIDER(需要fine权限
    • 通过getProviders获得所有可用的位置提供器,根据一个boolean参数返回是否启用的提供器,false则相当于getAllProviders。
  • 通过条件查找位置提供器:使用Criteria类实例来设置匹配的条件,通过调用getBestProvider来获取最匹配的位置提供器,可以可以通过调用getProviders来获得所有符合条件的提供器。
  • 确定当前位置:
    • 获得上一次确定的位置:通过调用getLastKnownLocation获得某个提供器上一次确定的位置
    • 通过注册LocationListener,使用LocationManager的requestLocationUpdates方法设置更新相隔的最短时间和最短距离参数来定期更新位置。可以使用不同的最短时间和最短距离,或不同的LocationProvider,通过同一个或者不同的LocationListener请求多个位置更新。
    • 通过指定PendingIntent,使用LocationManager的requestLocationUpdates方法设置更新相隔的最短时间和最短距离参数来定期更新位置。必须把BroadcastReceiver添加到应用程序的manifest文件中,新位置存在intent的一个extra中,key为KEY_LOCATION_CHANGED。
    • 在不需要更新位置的时候,要使用removeUpdates来停止位置更新。
    • 如果及时性没有那么重要,则可以使用Passice Location Provider来更新位置,当且仅当其他应用程序请求位置更新时,Passice Location Provider才会更新位置。因为有可能是任意的位置提供器,所以需要fine权限。
    • 单次位置更新:使用requestSingleUpdate方法来请求单次位置更新,单次位置更新同样可以使用监听器或者PendingIntent的方式。无论选择哪种方式,在单次更新后都需要注销接收器。
  • 位置更新的最佳实践,考虑以下因素:
    • 耗电量与精度;
    • 启动时间;
    • 更新频率;
    • 提供器可用性。
  • 使用近距离提醒:通过设置一个中心点(经纬度)、距离半径和提醒的超时时间,指定一个PendingIntent,使用LocationManager的addProximityAlert方法来添加一个近距离提醒,还需要创建一个BroadcastReceiver接收提醒。当设备越过半径边界时,接收器会收到一个带有extra的Intent,存放进入(ture)或者是离开(false)近距离范围,key为LocationManager.KEY_PROXIMITY_ENTERING。

Google Map
  • 添加Google Map库:在manifest文件application节点中添加<uses-library android:name="com.google.android.maps">,然后可以使用Geocoder类进行前向地理编码和反向地理编码。
  • MapView:首先从Android开发人员网站获取一个APIkey,通过扩展MapActivity来包含MapView(也可以扩展MapFragment或MapFragmentActivity,但需要导入支持包),通过配置MapView可以选择显示卫星视图、接到视图和语气的交通覆盖等,还可以使用MapVIew获得中心点和当前课件的纬度和经度范围,也可以设置显示标准的缩放控件。
  • MapController:使用MapController来平移和缩放MapView。
  • 覆盖Overlay:使用Overlay来向MapView添加多层注释绘图和单击处理方法。
    • 扩展Overlay并重写draw方法来绘制注释和重写onTap方法处理用户单击注释;
    • 通过投影Projection类来进行地理位置和屏幕坐标之间转换
    • 通过MapView的getOverlays获得当前显示的覆盖列表,然后进行添加和移除Overlay;
    • 使用MyLocationOverlay在MapView中显示当前位置和方向;
    • 使用ItemizedOverlay来把标记图片和相关的文本分配给特定的地理位置;
    • 通过设置MapView.LayoutParams参数,调用MapView的addView方法可以把视图固定在一个屏幕位置或者是一个地理位置。

Media Player
  • 音频播放:通过调用MediaPlayer.create来初始化,调用setDataSource和prepare来准备音频播放。
  • 视频播放有两种技术:
    • 使用VideoView来初始化和准备视频播放;
    • 使用自定义的SurfaceView和实现SurfaceHolder.callback来初始化和准备视频播放;
  • 使用MediaController控制MediaPlayer的播放:
    • 若是使用VideoView,通过调用VideoVIew的setMediaController方法即可使用MediaController;
    • 此外,MediaController可以控制任何的MediaPlayer,需要实现一个新的MediaPlayerControl与相应的MediaPlayer关联起来。
    • 使用setAnchorView方法设置当MediaController可见时包含在那个视图中,调用show和hide显示和隐藏。
  • MediaPlayer提供了一系列方法用来控制输出音量、设置屏幕亮度、设置循环状态等,设置音频流(AudioManager.STREAM_MUSIC)来响应音量控制,设置Receiver来相应Media播放控件。
  • 通过AudioManager的requestAudioFocus和指定一个AudioFocusChangeListener来请求和管理音频焦点。
  • 通过注册一个Receiver来监听当音频输出发生改变时暂停播放。
  • 使用RemoteControlClient配置远程播放控件,如锁频播放控件。
  • 当不需要使用MediaPlayer时,应该调用release来释放资源。

录音、拍摄和录像
  • 使用AudioRecord和AudioTrack来录制和播放原始音频。
  • 使用SoundPool来管理多个混合)音频的同时播放。
  • 可以使用Android 4.0.3之后提供的Equalizer、Virtualizer、BassBoost、PresetReverb、EnvironmentalReveb的音效类来设置AudioTrack或MediaPlayer的播放音效。
  • 拍摄照片:使用startActivityForResult(intent,CODE)来启动摄像头用自带的Camera拍照(其中intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)),默认是获得缩略图,要获得原图需要指定一个用于存储图像的目标文件并封装为URI,放在intent的putExtra中,key为MediaStore.EXTRA_OUTPUT。
  • 使用Camera类来直接访问摄像头,通过设置Camera.Pararmeters对象的参数来管理摄像头的配置,并可以使用摄像头预览、进行面部识别和面部特征,调用Camera的takePicture方法拍摄并保存照片。
  • 使用ExifInterface类来读取并写入JAEG EIXF图像的详细信息。
  • 录制视频:使用startActivityForResult(intent,CODE)来启动自带的视频录制器(其中intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE)),录制后视频URI在intent的getData中作为返回。启动视频录制捕获动作Intent可以包含以下三个extra:
    • MediaStore.EXTRA_OUTPUT:默认情况录像保存在默认媒体库,若想保存在其他地方可通过此extra指定一个URI;
    • MediaStore.EXTRA_OUTPUT:通过此extra指定录像质量,0表示低质量,1表示高质量;
    • MediaStore.EXTRA_OUTPUT:通过此extra指定录像的最大长度,单位为秒。
  • 若想要替换原生视频录制器,或者需要对视频捕获UI或录制设置进行更细致的控制,可以使用MediaRecorder类。
  • 使用MediaScannerConnection类将录制的任何一种新媒体添加到媒体库中。

蓝牙
  • 使用蓝牙的前提是需要在manifest文件中包含BLUETOOTH权限,若要修改蓝牙设备属性,则需要BLUETOOTH_ADMIN权限。
  • 蓝牙设备适配器:通过调用BluetoothAdapter.getDefaultAdapter方法获得默认的蓝牙适配器,Bluetooth Adaoter提供了用于读取并设置本地蓝牙硬件的属性的方法。若用户禁用了蓝牙功能,为了启用BluetoothAdapter,可以使用startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENBLE), REQUEST_CODE)启动一个系统Preference Activity提示用户打开蓝牙请求确认。一般情况下,应当注册一个BroadcastReceiver来监听ACTION_STATE_CHANG接收蓝牙设备开启状态的变化。
  • 蓝牙的可发现性:
    • 通过BluetoothAdapter的getScanMode方法来获得相应的扫描模式:
      • SCAN_MODE_CONNECTABLE_DISCOVERABLE:可以被任何设备发现;
      • SCAN_MODE_CONNECTABLE_:可以被先前已连接绑定过的设备发现
      • SCAN_MODE_NONE:可发现性被关闭。
    • 一般情况下,设备的可发现性是被关闭的,可以使用startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE), REQUEST_CODE)启动一个系统Preference Activity提示用户将在指定持续时间内打开可发现机制。默认为120s,可以通过Intent来修改时间。
    • 此外,可以注册一个BroadcastReceiver来监听ACTION_SCAN_MODE_CHANG接收蓝牙设备可发现性状态的变化。
  • 发现远程的设备:调用BluetoothAdapter的startDiscovery方法来启动发现过程,嗲用cancelDiscovery进行取消。发现过程是异步进行的,Android使用Broadcast Intent来通知发现过程的启动和结束以及扫描过程中发现的远程设备,可以通过创建Broadcast Receiver来监听发现过程的变化和发现的设备(被封装在BluetoothDevice对象中)。
  • 蓝牙通信:
    • 蓝牙通信是互相匹配的双向过程,任何一方都可以作为服务器,另一方作为客户端请求连接,连接完成后就变为两个BluetoothSocket客户端的传输的过程。
    • 首先,打开一个Bluetooth Server Socket Listener:为了使BluetoothAdapter作为服务器,需要调用其listenUsingRfcommWithServiceRecord方法来获得一个BluetoothServerSocket对象调用accept方法来监听连接请求。
    • 选择远程蓝牙设备进行通信:有多种方法获得BluetoothDevice对象,上面通过Receiver接收是一种,还可以使用Bluetooth的getRemoteDevice方法获得指定了硬件地址的设备。
    • 打开一个客户端Bluetooth Socket连接:通过调用获得的BluetoothDevice的createRfcommSocketToServiceRecord方法创建BluetoothSocket,调用其connect方法启动连接请求。
    • 使用Bluetooth Socket传输通信:通过BluetoothSocket的getInputStream和getOutputStream来处理数据传输。

网络和WIFI
  • 使用ConnectivityManager监视网络连接状态、配置故障转移设置以及控制网络无线电:
    • 4.0之前的版本通过getBackgroundDataSetting方法判断用户是否设置了当应用程序不可见且不在前台是不进行网络传输数据,4.0之后的版本让用户对应用程序使用网络数据的方法有了更多的控制,包括设置单独数据限制和限制后台数据。
    • 对于优化用户对应用程序网络传输的体验:
      • 将要传输的数据降到最低;
      • 根据连接类型修改数据使用方法;
      • 提供可修改数据使用方式(例如后台更行频率)的用户首选项。
    • 使用ConnectivityManager的getActiveNetworkInfo和getNetworkInfo方法配合BroadcastReceiver来查找和监视网络连接。
  • 大多数情况,使用ConnectivityManager监视WiFi连接的变化是一种最佳实践。但也可以使用WiFiManager来配置WiFi网络连接、管理当前的WiFi连接、扫描接入点以及监视WiFi连接变化:
    • 每当WiFi连接状态发生变化时,WiFiManager就会广播Intent,可根据不同的Action来监听当前Wif连接状态和信号强度等变化;
    • 使用getConnectionInfo来获得WIFiInfo对象从而获得更详细的WIFi连接状态信息。
    • 通过WiFiManager的startScan方法进行接入点扫描,使用接收器来接收扫描结果,使用WiFiManager的getScanResult方法获得扫描结果。
    • 使用getConfiguredNetworks获得当前网络配置列表,使用enableNetwork来启用某个网络。
    • 通过设置WiFiConfiguration对象来创建网络配置,调用WiFiManager的addNetwork、updateNetwork和removeNetwork来添加、更新和删除配置,更改后必须调用saveConfiguration保存配置。
  • WiFi Direct传输数据:4.0之后引入了对WiFi Direct的支持,与蓝牙相比,WiFi Direct更加快速可靠,工作距离更远。
    • 通过WifiP2pManager来初始化WiFi Direct框架,使用ActionListener来监听执行某些动作是否成功(如找到和链接对等设备时),成功则广播一个Broadcast Intent,通过接收器接收可以得到与这些动作相关的信息。
    • 使用ACTION_WIRELESS_SETTINGS来启动设置屏幕从而让用户启用WiFi Direct,并通过注册一个接收器来监视其状态。
    • 初始化WiFi Direct框架并启用设备及其对等设备上的WiFi Direct设置后,就可以开始搜索和连接传输数据了:
      • 发现对等设备:调用WifiP2pManager的discoverPeers方法扫描对等设备,通过广播进行搜索。使用WifiP2pManager.requestPeers方法请求当前发现的对等设备的列表;
      • 连接对等设备:通过WifiP2pManager的connect方法与对等设备建立连接,远程设备会被提示接受连接请求,若连接成功将使用WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION 的Intent动作在两个设备上广播,可以使用WifiP2pManager.requestConnectionInfo方法在接收器中查询连接的详细信息;
      • 对等设备之间的数据传输:通过创建标准的Java套接字ServerSocket和ClientSocket进行数据传输(也有tetanus的特定数据传输实现。)

NFC近场通信
  • NFC是一种短距离、少量数据的传输技术,使用NFC需要具有NFC manifest权限。
  • 读取NFC标签:当Android设备扫描一个NFC标签时,会由其标签分派系统使用Intent来启动一个应用程序来接收数据,所以需要添加一个尽可能具体的Activity Intent Filter来监听Intent。
  • 使用前台分派系统:通过前台分派系统,可以指定特定的一个具有高优先级的Activity使得当它位于前台是成伟默认接收标签的应用程序。使用NFCAdapter的enable/disableForegroundDispatch方法可以切换前台分派系统,分别写在onResume和onPause方法中。
  • Android Beam:4.0之后引入的Android Beam提供了一套简单的API,用于使用NFC的两个设备之间传输数据:
    • 创建Android Beam消息:创建一个NdefMessage对象,并在其中创建至少一个NdefRecord(必须制定它表示的记录类型、一个MIME类型、一个ID和有效载荷);
    • 分配Android Beam有效载荷:使用NFCAdapter的setNdefPushMessage或者setNdefpushMessageCallback方法分配有效载荷;
    • 接受Android Beam消息:与NFC标签接收类似,添加一个尽可能具体的Activity Intent Filter来监听Intent,并加上分配的有效载荷对应的MIME Type。

Android的安全性
  • Android的安全性是基于Linux内核的,每个应用程序在安装时,都会分配给它一个唯一的Linux用户ID,这个ID具有隔离进程和它所创建的资源的作用。
  • 要使用其它应用程序授权保护的组件,需要使用<uses-permission>在manifest文件中指定对应权限字符串。
  • 在向一个应用程序的组件分配权限之前,需要使用<permission>在manifest文件中定义权限,可以指定权限字符串、允许的访问级别、一个标签以及一个包含了对授予这个权限的风险进行了说明的外部资源。
Android开发前的注意事项
硬件限制带来的设计考虑事项:
  1. 高效,主要体现在代码的优化;
  2. 考虑有限的能力,主要是移动手机的存储、处理能力的限制;
  3. 为不同的屏幕进行设计;
  4. 考虑低效率,高延迟,如很多用户仍使用2G网络等;
  5. 考虑需要多少花费,如尽可能少地传送(网络)数据,通过缓存数据和地理编码结果来消除冗余或重复查找,尊重用户对后台数据传输的参数设置等。
考虑用户环境,保证应用程序:
  1. 可以预测,并且行为得当;
  2. 流畅地从后台切换到前台;
  3. 交互合理,以多种方式通知用户;
  4. 呈现出直观而有吸引力的用户界面;
  5. 快速响应。
google android 设计理念要求:
  1. 快速和高效;
  2. 响应快速;
  3. 数据保持新鲜;
  4. 安全;
  5. 连贯流畅的用户体验;
  6. 提供可访问性(指设计时考虑由于视力、肢体或年龄等问题导致无法正常使用的用户,利用android的可访问层为其提供服务,如文本转换语音、触摸反馈等)。

Android应用程序和Activity
  1. Android的软件栈:
  2. Android应用程序的组成部分:Activity、Service、
    Content Provider
    、Intent、Broadcast Receiver、Widget、Notification;
  3. Manifest文件:自身属性(package、versionCode、versionName、installLocation
    ),包含标签(uses-sdk、uses-configuration、uses-feature、supports-srceens、supports-gl-texture、uses-permission、permission、application)其中application标签包含了常用的Activity、service、receiver和provider标签。
  4. 分离资源:按资源类型分类存放在res/values(strings.xml、colors.xml、dimens.xml、arrays.xml、styles.xml等)、res/drawable(图片文件)、res/layout(布局xml文件)、res/anim(动画xml文件,单逐帧动画存放在drawable文件夹中)、res/menu(菜单布局xml文件)中,通过‘-’连接符在文件夹后面加上限定符来为不同的语言和硬件创建资源,如drawable-ldpi、values-fr-rCA。
  5. 应用程序的优先级和进程状态:一个应用程序的优先级等同于它的多个组件之中最高的优先级,进程被终止的顺序由它们的应用程序的优先级所决定。进程状态分为Active进程(关键优先级)、可见进程(高优先级)、启动Service进程
    (高优先级)
    、后台进程(低优先级)
    、空进程
    (低优先级)。
  6. Application:一个Android应用程序只有一个Application对象,可以利用它完成以下3项工作:
    • 对Android运行时广播的应用程序级事件(如低内存)做出响应
    • 在应用程序组件之间传递对象
    • 管理和维护多个应用程序组件使用的资源
  7. Activity:
  • Activity栈。Activity的状态是由它在Activity栈中的位置所决定的,Activity栈是当前所有正在运行的Activity的后进先出的集合。
    • Activity有四种基本状态:
      • 活动状态(Running)。位于屏幕最前端时,此时处于可见状态,和用户可交互的状态。
      • 暂停状态(Paused)。当Acitivy被另一个透明的或者非全屏的Activity覆盖时的状态叫Paused状态,虽然可见但不可交互。
      • 停止状态(Stop)。当Activity被另外一个Activity覆盖、界面不可见时处于Stop状态。
      • 非活动状态(Killed)。Activity被系统杀死或者跟本没启动时就是Killed状态。
    • Activity的生存期:完整生存期、可见生存期、活动生存期。
    • Activity的生命周期:

Android布局
  • 常用布局:FrameLayout、LinearLayout、RelativeLayout、GridLayout等
  • RelativeLayout用到的一些重要的属性:
    • 第一类:属性值为true或false
      • android:layout_centerHrizontal                               水平居中
      • android:layout_centerVertical                                 垂直居中
      • android:layout_centerInparent                               相对于父元素完全居中
      • android:layout_alignParentBottom                         贴紧父元素的下边缘
      • android:layout_alignParentLeft                               贴紧父元素的左边缘
      • android:layout_alignParentRight                             贴紧父元素的右边缘
      • android:layout_alignParentTop                               贴紧父元素的上边缘
      • android:layout_alignWithParentIfMissing                 如果对应的兄弟元素找不到的话就以父元素做参照物
    • 第二类:属性值必须为id的引用名“@id/id-name”
      • android:layout_below                          在某元素的下方
      • android:layout_above                          在某元素的的上方
      • android:layout_toLeftOf                       在某元素的左边
      • android:layout_toRightOf                     在某元素的右边
      • android:layout_alignTop                      本元素的上边缘和某元素的的上边缘对齐
      • android:layout_alignLeft                      本元素的左边缘和某元素的的左边缘对齐
      • android:layout_alignBottom                 本元素的下边缘和某元素的的下边缘对齐
      • android:layout_alignRight                    本元素的右边缘和某元素的的右边缘对齐
    • 第三类:属性值为具体的像素值,如30dip,40px
      • android:layout_marginBottom              离某元素底边缘的距离
      • android:layout_marginLeft                   离某元素左边缘的距离
      • android:layout_marginRight                 离某元素右边缘的距离
      • android:layout_marginTop                   离某元素上边缘的距离
  • 优化布局:
    • 减少冗余布局,减少布局嵌套(少于10层),使用<merge>和<include>标签复用布局。
    • 避免使用过多的View(布局包含的View少于80),可以使用ViewStub延迟填充布局。
    • 使用Lint工具来分析布局的性能问题和检测其他问题。

Android 控件
  • 常用控件:Button、TextView、EditView、CheckBox、RadioGroup、ImageView、ImageButton、Spinner、AutoCompleteTextView、DatePicker、TimePicker、ProgressBar、SeekBar、RatingBar、ListView、Gallery、GridView。
  • AutoCompleteTextView:android:completionThreshold=”1″ 属性设置了一个阀值,规定用户打了多少字符之后才出现自动提示,默认值是2。
  • 使用Gallery来展示画廊,android:spacing="5dp" 属性则是用来设置元素之间的间隔。

Fragment
  • Fragment的生命周期:
  • Fragment Transaction:可以使用FragmentTransaction来在一个Activity内添加、删除和替换Fragment。
  • Fragment与Activity之间的接口:在Fragment中创建一个callback接口,而主Activity必须实现它,从而达到互相通信。
  • Android Fragment类:DialogFragment、ListFragment、WebViewFragment。
  • 若最低版本支持Android 3.0以上,则使用android.app.FragmentManager,否则使用 android.support.v4.app.FragmentManager。

创建新视图(View)
  • 修改现有的视图:通过继承现有的视图来定制其外观和行为。
  • 创建复合控件:通过扩展一个ViewGroup(通常是一个布局)来创建复合控件。
  • 定制新视图:通过重写View的onMeasure和onDraw方法来绘画自定义的控件。

Intent
  • 使用Intent来启动Activity(显式和隐式)
  • linkify:在TextView(及派生类)中通过RegEx模式匹配来创建超链接。使用Linkify,addLinks方法或android:autoLink属性来建立链接。
  • 使用Intent广播事件,通过注册Broadcast Receiver来监听广播。
    • 广播普通的Intent是无序的,随机的,可以通过sendOrderedBroadcast方法来发送有序广播,按优先级顺序被传递给所有具有合适权限的已注册的接收器(可以Broadcast Receiver的Intent Filter manifest节点中使用android:priority属性来指定其权限,值越大,优先级越高)。
    • Sticky Intent是Broadcast Intent的有用变体,可以保存它们最后一次广播的值,并且当有一个新的接收器被注册接收该广播时,它们会把这些值作为Intent返回。通过sendStickyBroadcast方法来广播Sticky Intent,不是必须制定一个接收器来获得Sticky Intent的当前值。
  • Local Broadcast Manager:
    • 使用Local Broadcast Manager需要导入Android Support包。
    • 局部广播更高效,也保证其它应用程序接收不到你广播的Intent。
    • 使用Local Broadcast Manager的sendBroadcastSync方法来发送同步广播,直到每个已注册的接收器都收到广播后才解除阻塞。
  • Pending Intent:提供了一种创建可由其它应用程序在稍晚时间触发的Intent的机制,通常用于包装在响应将来的事件时触发的Intent,例如单击Widget或Notification。

Intent Filter
  • 使用Intent Filter为隐性Intent服务,activity、service、receiver、provider标签下都可包含intent-filter标签。
    • 隐式启动一定要在intent filter中加入android.intent.category.DEFAULT:理论上来说,如果intent不指定category,那么无论intent filter的内容是什么都应该是匹配的。但是,如果是implicit(隐式)intent,android默认给加上一个CATEGORY_DEFAULT,这样的话如果intent filter中没有android.intent.category.DEFAULT这个category的话,匹配测试就会失败。所以,如果你的Activity支持接收implicit intent的话就一定要在intent filter中加入android.intent.category.DEFAULT。(Main Activity除外)
  • Intent Filter的属性:动作action、基本数据data、类别category、附加数据extra(数据保存在Bundle对象中)、指定组件component、数据类型type。

Broadcast Receiver
  • 在代码中注册Broadcast Receiver:只会在包含它的应用程序组件运行时才响应Intent。
  • 在manifest中注册Broadcast Receiver,通过这种方式注册,即使当应用程序被终止或者未启动时,也可以接收Broadcast Intent。
  • 监听本地的Broadcast Intent。
  • 使用Broadcast Intent监控设备的状态变化。
  • 使用Package Manager的setComponentEnabledSetting方法在运行时管理Manifest Receiver(其他组件也可以)。

Download Manager
  • 通过getSystemService(Context.DOWNLOAD_SERVICE)获得DownloadManager的实例,设置DownLoadManager.Request对象下载文件。通过注册一个Receiver来接收下载完成的通知,在注册一个Receiver来接收点击通知后的响应。
  • 自定义DownloadManager Notification,通过request的setTitle、setDescription、setNotificationVisibility等方法定义通知的状态。
  • 指定下载位置:通过request的setDestinationUri方法可以在外部存储上指定一个任意路径,通过setDestinationInExternalFilesDir方法在外部存储文件夹(通过Environment静态变量指定)中存储一个文件,通过setDestinationInExternalPublicDir方法在外部存储器的公共目录下指定一个文件夹(通过Environment静态变量指定)来存储文件。
  • 取消下载并删除下载文件:使用DownloadManager的remove方法。
  • 查询下载:通过设置DownLoadManager.Query对象,然后只用DownLoadManager的query方法查询,使用cursor游标遍历查找。

Shared Preference、保存状态文件系统
  • 通过getSharedPreferences方法取得SharedPreference对象,使用SharedPreference.Editor对象修改SharedPreference里的数据,使用getXXX方法来获取数据。
  • 首选项框架:由PreferenceScreen、PreferenceActivity和PreferenceFragment、PreferenceHeader、SharedPreference四个部分组成,通过定义相应的res/xml下的文件可以生产系统样式的PreferenceScreen,首选使用这个框架来创建自己应用的设置页面。
  • 持久化应用程序实例的状态:
    • 使用SharedPreference保存Activity的状态:通过调用Activity.getPreferences方法获得Activity自身范围的SharedPreference对象,不需要知道一个SharedPreference名称,其名称就是调用Activity的类名。这种方式适用于Activity由用户关闭的(back键)或者通过代码中调用finish关闭的情况。
    • 使用生命周期处理程序保存和还原Activity实例状态:通过重写Activity的onSaveInstanceState事件的处理程序,使用Bundle参数来保存UI实例的值。这种方式适用于由系统为了释放资源在运行时终止了Activity的情况。
    • 使用生命周期处理程序保存和还原Fragment实例状态:通过在Fragment的onCreate处理程序内调用setRetainInstance方法指定当关联的Activity被重新创建时保留Fragment实例。这个过程Fragment的onDestroy和onCreate不会被调用,但是其它生命周期处理程序仍然会被调用。
  • 将静态文件作为资源添加:通过将文件放置res/raw文件夹中,调用Resource对象的openRawResource方法,以R.raw.文件名(不带扩展名)作为参数,获得其InputStream对象。
  • 在Android文件系统下的操作:
    • 使用特定于应用程序的文件夹存储文件:
      • 内部存储:通过上下文提供的getFilesDir方法获得包含有指向内部的应用程序文件存储目录的绝对路径的File对象。
      • 外部存储:通过上下文提供的getExternalFilesDir方法获得包含有指向外部的应用程序文件存储目录的绝对路径的File对象。
      • 存储在以上两个目录的文件会在应用程序被卸载后一起被删除。另外,可以通过Environment.getExternalStorageDirectory方法获得外部存储的根路径,但这个路径下的文件不会随着应用程序被卸载后一起被删除。
    • 创建私有的应用程序文件,Android提供了openFileInput和openFileOutput方法来简化操作应用程序沙箱中的私有文件,通过getFilesDir方法可以获得使用openFileOutput所创建的文件的绝对路径。
    • 使用应用程序文件缓存:Android土工了一个可以管理的内部缓存和一个不能管理的外部缓存,分别调用getCacheDir和getExternalCacheDir获得包含其绝对路径的File对象。存储在这两个缓存文件夹的文件都会随着应用程序被卸载后一起被删除。内部缓存会在当系统运行在低可用存储空间的时候随机删除其文件夹下的文件,外部缓存则不会。
    • 存储公共可读的文件:以上存储特定于应用程序的文件通常不会被媒体扫描或被其它应用程序访问的,但是可通过Environment.getExternalStoragePublicDirectory方法可以用来找到存储应用程序公共可读的文件的路径。Environment提供了一系列DIRECTORY_XXXX字符串常量来统一公共的资源目录,可以作为Environment.getExternalStoragePublicDirectory、Context.getFilesDir和Context.getExternalFilesDir等方法的参数使用。

SQLite数据库和Content Provider
  • Content Value:ContentValue用来向SQLite数据库的表中插入新的行,每一个ContentValues对象都将一个表行表示为列名到值的映射。
  • Cursor:SQLite数据库查询作为Cursor对象返回,然后从Cursor中提取值。
  • 使用SQLite数据库:通过实现SQLiteOpenHelper抽象类的onCreate和onUpdate方法来实现创建、打开和升级数据库。调用SQLiteOpenHelper的getWritableDatabase(可写)或者getreadableDatabase(只读)来打开和获得数据库操作实例。通过调用数据库操作实例:
    • query(表名,列名字符串数组,where子句,where参数,groupBy语句,having语句,order语句)方法来查询数据;
    • insert(表名,null,ContentValues对象)方法来添加数据;
    • update(表名,ContentValues对象,where子句,where参数)方法来更新数据;
    • delete((表名,where子句,where参数)方法来删除数据。
  • 创建Content Provider:要想创建一个新的Content Provider,需要继承ContentProvider抽象类:
    • 首先在Manifest中注册Content Provider:
      • <provider
      •     android:name=".ToDoContentProvider"
      •     android:authorities="com.mythos.provider.todolist"
      •     android:exported="true" />;设置为true允许其他应用程序访问数据
    • 发布Content Provider的URI地址,定义一个URI Matcher来分析URL的形势,确定URI是请求所有数据还是单行数据;
    • 重写onCreate处理程序来初始化底层的数据源,一般是SQLite则初始化SQLiteOpenHelper;
    • 重写query方法,使用SQLiteQueryBuilder辅助类来执行查询;
    • 重写getType方法来返回基于传入的URI的正确MIME类型;
    • 重写update、delete、insert等修改数据库的事务方法,最好都调用Content Resolver的notifyChange方法通知所有已注册的ContentObserver。
  • 在Content Provider中存储文件:数据库里保存文件通常保存的是路径(一个合适的URI),通常表中文件类型的数据列名取名为_data。然后通过重写ContentProvider的openFile方法来提供一个ParceFileDescriptor对象。
  • 使用Content Provider:使用上下文的getContentResolver方法来获得一个ContentResolver对象实例:
    • 通过调用ContentResolver对象的query(URI,列名字符串数组,where子句,where参数,order语句)方法来查询ContentProvider公布的数据;默认情况下,ContentResolver是在应用程序的主线程上执行查询和事务操作的,为了确保应用程序的向英雄,应该通过子线程异步执行;
    • 使用Cursor Loader异步查询内容(非主线程):通过实现LoaderManager.LoaderCallbacks<Cursor>接口的onCreateLoader(初始化并返回一个CursorLoader对象)、onLoadFinished和onLoaderReset方法,然后作为LoaderManager对象的initLoader(int id,Bundle b,LoaderCallbacks lc)方法的参数进行CursorLoader的初始化并返回一个Loader对象;LoaderManager对象可以通过上下文的getLoaderManager方法获得;
    • 使用ContentResolver对象的insert、delete和update等事务方法来添加、删除和更新Content Provider的内容:
      • Content Resolver提供了两种方法来插入新记录——insert和bulkInsert;insert方法接收一个ContentValues对象,插入单行数据并返回新记录的URI;bulkInsert方法接收一个ContentValues对象数组,差不多多行数据并返回成功添加的行数;
      • 调用ContentResolver的delete方法,传入要删除的URI和where子句参数,若URI包含ID则删除一行数据,若不包含ID则依据where子句匹配条件来删除多行数据,并返回成功删除的行数;
      • 调用ContentResolver的update方法,传入要更新的URI、更新的ContentValues对象和where子句作为参数,若URI包含ID则更新一行数据,若不包含ID则依据where子句匹配条件来更新多行数据,并返回成功更新的行数;
  • 访问Content Provider中存储的文件:使用ContentResolver对象的openOutputStream方法来把一个新文件插入Content Provider中;使用ContentResolver对象的openInputStream方法来访问Content Provider中存储的文件。
  • 利用ContentProvider将搜索功能添加到应用程序中。
  • 访问本地的ContentProvider:
    • 使用Media Store Content Provider;
    • 使用Contacts Contract Content Provider:
      • 通过使用ContactsContract三个子类实现访问:Data、RawContacts和Contacts;
      • 要访问联系人详情,需要在应用程序的清单文件中包含READ_CONTACTS use-permission;
      • 基于Intent机制,使用先有的联系人应用程序(通常是本地应用程序)来查看、插入或选择一个联系人,这是一种最佳实践做法。
      • 要直接修改和增加联系人详情,需要在应用程序的清单文件中包含WRITE_CONTACTS use-permission;
    • 使用Calendar Content Provider:
      • 要访问Calendar Content Provider,需要在应用程序的清单文件中包含READ_CALENDAR use-permission;使用ContentResolver,通过他们的CONTENT_URI常量来查询任何CalendarProvider表。包括Calendars、Events、Instances、Attendees、Reminders表。
      • 使用Intent创建和编辑日历项,这是一种最佳实践做法。注意,Intent机制只对编辑时间的开始和结束时间提供支持。
      • 要直接修改和增加日历项,需要在应用程序的清单文件中包含WRITE_CALENDAR use-permission;

Service
  • Service是一种长生命周期的组件,用来执行一些持续性的,可能耗时的工作。
  • 创建和控制Service:
    • 通过继承Service类,重写onCreate和onBind方法,必须在应用程序清单中注册service节点;
    • 执行一个Service并控制它的重新启动行为:通过重写onStartCommand方事件处理程序来执行Service任务,然后通过返回的值来控制当Service被运行时终止后,系统应该如何相应Service的重新启动,三个值包括:
      • START_STICKY:
      • START_NOT_STICKY:
      • START_REDELIVER_INTENT:
    • 启动和停止Service:
      • 在Activity中调用startService启动Service(显示和隐式);
      • 在Activity中调用stopService停止Service(显示和隐式);
      • 当一个Service被启动后,再调用starService方法来执行onStartCommand处理操作,此时并不会再次调用Service的onCreate方法,所以无论重复调用多少次StartService,只要调用一次stopService就会终止对应的Service。
    • 自终止Service:由于Service具有高优先级,它们通常不会轻易被运行至终止,因此可以调用Service的stopSelf方法自终止,从而避免系统资源的浪费和电量的消耗。此时可以不传递参数从而强制终止,然而,如果service正在同时处理多个对onStartCommand的请求,那么不应在处理完一个请求之后就停止service,因为这是可能已经又收到了新的启动请求在第一个完成后停止将会结束掉第二个)。要避免这个问题,可以使用stopSelf(int startId)来保证你的停止请求对应于你最近的开始请求。也就是,当你调用stopSelf(int startId)时,你传递开始请求的ID传递给onStartCommand()的startId)给service,如果service在你调用stopSelf(int startId)之前收到一了个新的开始请求,发现ID不同,于是service将不会停止。
  • 绑定Service到Activity:
    • 首先要让一个Service支持绑定,需要实现Service的onBind方法,并返回一个IBinder对象,可以使用内部类并初始化对象获得;
    • Service与每一个组件连接表示为一个ServiceConnection对象,因此需要在组件内使用内部类并初始化对象获得一个ServiceConnection对象,并实现ServiceConnection的onServiceConnected和onServiceDisconnected方法。在onServiceConnected方法里使用IBinder对象的getService方法获得对Service实例的引用;
    • 要执行绑定,需要在Activity中调用bindService(intent,serviceConnection,Context.BIND_AUTO_CREATE)方法,其中Context.BIND_AUTO_CREATE是一个标志位,表示在Activity和Service建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。4.0之后引入了其它含义的标志位包括:
      • Context.BIND_ADJUST_WITH_ACTIVITY;
      • Context.BIND_IMPORTANT和Context.BIND_ABOVE_CLIENT
      • Context.BIND_NOT_FOREGROUND;
      • Context.BIND_WAIVE_PRIORITY;
  • 创建前台Service:在必要时候提高Service的优先级,可以通过Service的startForeground方法设置该Service在前台运行,将一个Service设为前台运行可以有效地避免运行时在释放资源的时候终止这个Service,但是很占用资源。同时可以调用StopForeground方法把Service移到后台。
  • 使用AsyncTask运行异步任务:通过扩展AsyncTask<A,B,C>类并重写doInBackground(执行后台处理任务,通过punishProgress方法传递参数给onProgressUpdate方法,执行完毕返回值传递给onPostExecute方法)、onProgressUpdate(执行过程中更新UI)和onPostExecute(执行结束后更新UI)方法,其中A、B、C分别为泛型定义类型,同时也是以上三个方法的参数类型。创建好异步任务后,在UI线程中新建实例调用execute方法执行即可,每一个AsyncTask实例只能执行一次execute方法。AsyncTask类更适合简单的后台任务,但多任务异步操作就会变得复杂,可以考虑使用handler方式代替。
  • Intent Service是一个非常方便的包装类,它为更具需求执行一组任务的后台Service实现了最佳实践模式,如Internet的循环更新或者复杂的数据处理。通过扩展IntentService并重写onHandleIntent方法,每次启动IntentService时传入的Intent会被放入一个队列中,被onHandleIntent方法逐个在后台处理,当所有传入的Intent都被处理后,IntentService会自动终止自己。IntentService是创建按需或固定时间间隔执行一组任务的这种Service的最佳方法。
  • Loader:用于在UI元素(如Activity和Fragment)中进行异步数据加载的最佳实践技术,如CursorLoader。
  • 使用Thread线程类和Handler类手动创建线程和GUI线程同步。
  • 使用Alarm是一种在预先确定时间或时间间隔激活Intent的方式:
    • 使用AlarmManager的set方法并给它指定一个Alarm类型、触发时间和一个要激活的PendingIntent来创建一个普通的Alarm,同一个PendingIntent只能创建一个Alarm,多次创建会覆盖前一个Alarm;
    • 调用AlarmManager的cancel(pendingIntent)方法取消pendingIntent对应的Alarm;
    • 调用AlarmManager的setRepeating(精确时间间隔)或setInexactRepeating(不精确时间间隔)方法来创建重复Alarm。取消的方式与普通Alarm相同;
    • 精确时间的重复Alarm会消耗更多的电量,尽量减少Alarm频率,尽量使用没有精确指定时间的重复Alarm;
    • Alarm是在应用程序之外操作,所以即使应用程序关闭,它们依然能够运作;
    • Alarm和BroadcastReceiver一起使用时更加强大,允许设置能够激活广播Intent、启动Service,甚至启动Activity的Alarm等;
    • Alarm是降低应用程序资源需求的极为有效的方式,它允许停止Service和清除定时器,同时仍然可以执行调度的操作;
    • 可以使用Alarm实现基于网络查找的更新。

Notification
  • Notification Manager是用来处理Notification的系统Service,可以触发新的、修改现有的、删除不再需要的Notification。
  • 使用Notification Builder来创建Notification是Android3.0之后的首选方式。使用NotificationBuilder的setOnlyAlertOnce方法来更新Notification而不触发任务关联的闪灯、音频或振动。使用setAutoCancel(true)方法来配置Notification在被单击后自动取消自己。
  • 通过嗲用Notification的setLatestEventInfo方法使用标准的Notification UI布局。
  • 通过使用Remote Views对象来创建自定义的Notification UI布局。

屏幕分辨率设计
  • 使用密度无关的像素:使用dp单位指定布局尺寸、View大小或者图像大小;使用sp单位指定文本大小。
  • 使用像素密度的资源限定符:
    • res/drawable-ldpi:为120dpi左右的屏幕提供低密度资源;
    • res/drawable-mdpi:为160dpi左右的屏幕提供中等密度资源;
    • res/drawable-tvdpi:为213dpi左右的屏幕提供中等度资源,这是在API 13之后为了优化面向电视的应用程序而引入的;
    • res/drawable-hdpi:为240dpi左右的屏幕提供高密度资源;
    • res/drawable-xhdpi:为320dpi左右的屏幕提供超高密度资源;
    • res/drawable-nodpi:用于不管主屏幕密度如何都不能缩放的资源;
  • 通过创建可缩放的布局、指定支持的屏幕尺寸(利用清单文件中的supports-screen元素)、创建可缩放的图形资源(利用自定义xml文件的shape节点来自定义Drawable)和NiePatch图像来为不同屏幕大小提供支持和优化。

优化用户体验
  • 确保可访问性
    • 为非触屏设备提供导航。
    • 为每个View提供文本描述。
  • 控制设备振动
    • 需要有android.permission.VIBRATE权限。
    • 通过Vibrator Service的vibrate方法来启动设备的振动,抵用cancel方法取消振动。
  • 使用动画:
    • 补间View动画:可以定义一系列关于位置、大小、旋转和透明度的变化。使用AnimationSet类可以管理多个动画作为集合运行,调用View的startAnimation方法可以启动动画或动画集合。使用动画监听器AnimationListener来创建动画过程触发的事件。可用的动画类型:
      • AlphaAnimation(透明度)
      • RotateAnimation(旋转)
      • ScaleAnimation(缩放)
      • TranslateAnimation(移动)
    • 逐帧动画:
      • 在res/drawable下使用xml文件,使用<animation-list>来定义一组图片集合,通过setBackgroundResource方法设置为一个View的背景。
      • 可以使用getBackground来获得View的背景AnimationDrawable实例,然后调用start方法启动动画。
    • 差值属性动画:
      • 使用ObjectAnimator来实现一个View对象的某个属性的差值变化,这个View对象必须包含这个属性的getter/setter方法。
      • 可以通过在res/animator下使用xml文件,定义一个ObjectAnimator,使用AnimatorInflator.loadAnimator方法获得一个ObjectAnimator实例,然后使用setTarget方法将它应用到一个View对象上。
      • 默认情况下使用的是AccelerateDecelerateInterpolator插值器的变化效果,可以通过setInterpolator方法来设置新的变化效果。
      • 通过AnimatorSet类创建复杂、互相关联的动画,使用start来执行动画序列。
      • 使用Animator.AnimatorListener类来处理差值属性动画的事件处理程序。

Canvas绘图
  • 使用Canvas类:提供了一系列drawXXX方法来实现绘图设计。
  • 使用Paint类:
    • 通过setColor选择一个Paint的颜色;
    • 通过setStyle控制Paint对象的样式;
    • 使用setAlpha设置Paint对象的透明度;
    • 使用Shader类来定义渐变填充,是在2D图像中添加深度和文理的最佳方式之一:BitmapShader、ComposeShader和三种渐变Shader(线性、辐射和扫描);
    • 使用Shader TileMode来决定如何处理Shader填充的剩余区域;
    • 使用MaskFilter类可以为Paint对象分配边缘效果,实际上是对一个Paint的alpha通道应用转换
    • 使用ColorFilter对一个Paint的RGB通道应用转换;
    • 使用PathEffect来控制绘制轮廓线的方式;
    • 通过修改Paint的Xfermode来影响在画布中已有的图像上面绘制新的颜色的方式;
    • 使用抗锯齿效果来提高Paint质量
  • Canvas绘图的最佳实践:使用Canvas自定义绘图是非常消耗处理器资源的,而且低效的绘图方法会阻塞GUI线程,会对应用程序的相应造成很不利的影响。所以使用Canvas绘图时我们需要考虑:
    • 考虑大小和方向(屏幕分辨率和尺寸大小等);
    • 只创建一次静态对象;
    • 记住onDraw是很耗费资源的,可以考虑使用画布转换(rotate和translate),使用动画,使用位图、.9和Drawable资源来代替实现效果;
    • 避免重复绘制。
  • 硬件加速:通过硬件加速这种新的渲染方式能够让应用程序加速2D绘图,大多数情况下硬件加速是可用的(自定义View除外),但是回大量消耗处理器资源;
  • 使用SurfaceView可以在后台线程绘图,完全支持OpenGL ES库,可以用来绘制3D图像等;

创建交互式控件
  • 重写Activity和VIew的onTouchEvent事件处理程序,当触摸屏被触摸、释放和拖动时触发,也可以注册OnTouchListener监听器实现。
  • 重写Activity和VIew的onKeyDown和onKeyUp事件处理程序,当硬件安静被按下和释放时触发,也可以注册OnKeyListener监听器实现。
  • 重写Activity和VIew的onTrackballEvent事件处理程序,当轨迹球移动时触发。

高级Drawable资源
  • 由XML定义的复合Drawable;
  • 变换Drawable:
    • ScaleDrawable(<scale>标签)
    • RotateDrawable(<rotate>标签)
  • 层Drawable(<layer-list>标签)
  • 状态列表Drawable(<selector>标签
  • 级别列表Drawable(<level-list>标签

硬件传感器
  • 使用传感器和传感器管理器SensorManager,我们操作的是Sersor对象,它描述了它代表的硬件传感器的属性。Sersor类提供了一组常量,表示多种传感器类型,但是注意,设备上的硬件决定了应用程序中可以使用的传感器类型。
    • 虚拟传感器:提供简化的、经过校正的或者复合的数据,使用传感器更易于在一些应用程序上使用。
    • 通过调用SensorManager的getSersorList方法,并传入Sensor.TYPE_ALL参数可以得到设备上可用的传感器,传入特定的类型参数则获得特定类型的传感器的实现列表;使用getDefaultSensor方法可以找到指定类型的传感器的默认实现;
    • 应该检查任何必须传感器的可用性,如果传感器不存在,那么应用程序应该以正常方式退出。
  • 监视传感器:
    • 通过SensorManager的registerLisenert方法注册SensorEventListener,实现onSensorChange(监视传感器信息改变)和onAccuracyChange(监视传感器精度改变)方法;
    • onSensorChange方法中SensorEvent参数的四种属性:
      • sensor:传感器对象;
      • accuracy:表示精确度;
      • values:检测到的新值;
      • timestamp:检测时间;
    • 注册时可以设置更新速率参数,为了减少开销,最好选择可接受的最慢的速率:
      • SENSOR_DELAY_FASTEST:指定可以实现的最快更新速率;
      • SENSOR_DELAY_GAME:指定适合控制游戏的更新速率;
      • SENSOR_DELAY_NORMAL:指定默认的更新速率;
      • SENSOR_DELAY_UI:指定适合更新UI的更新速率;
    • 在应用程序不再需要接受更新时,注销传感器的监听器也很重要,分别在Activity的onResume和onPause中注册和注销传感器的事件监听器是一种好的做法。
  • 使用传感器:
    • 确定设备相对于自然方向的显示方向,返回的传感器值总是相对于设备的自然方向测量的,可以使用默认的Display对象的getRotation方法获得当前屏幕的旋转方向。
    • 使用加速计检测加速度变化;
    • 使用加速计和磁场传感器计算方向;
    • 基于设备的自然方向重新映射方向参考坐标系,使用SensorManager的remapCoordinateSyatem方法;
    • 使用气压计传感器检测当前气压,可以确定当前海拔;

基于位置的服务LBS
  • 位置管理器:使用getSystemService获得LocationManager实例。使用LBS之前,需要在manifest文件中添加uses-permission标签获取fine权限(高精度)或者coarse权限(低精度)。
  • 位置提供器:
    • 根据不同的设备,可以有多种技术来确定当前位置,即有多种位置提供器。LocationManager提供三个常量通过调用getProvider来返回三种位置提供器:
      • LocationManager.GPS_PROVIDER(需要fine权限)
      • LocationManager.NETWORK_PROVIDER(需要coarse权限)
      • LocationManager.PASSIVE_PROVIDER(需要fine权限
    • 通过getProviders获得所有可用的位置提供器,根据一个boolean参数返回是否启用的提供器,false则相当于getAllProviders。
  • 通过条件查找位置提供器:使用Criteria类实例来设置匹配的条件,通过调用getBestProvider来获取最匹配的位置提供器,可以可以通过调用getProviders来获得所有符合条件的提供器。
  • 确定当前位置:
    • 获得上一次确定的位置:通过调用getLastKnownLocation获得某个提供器上一次确定的位置
    • 通过注册LocationListener,使用LocationManager的requestLocationUpdates方法设置更新相隔的最短时间和最短距离参数来定期更新位置。可以使用不同的最短时间和最短距离,或不同的LocationProvider,通过同一个或者不同的LocationListener请求多个位置更新。
    • 通过指定PendingIntent,使用LocationManager的requestLocationUpdates方法设置更新相隔的最短时间和最短距离参数来定期更新位置。必须把BroadcastReceiver添加到应用程序的manifest文件中,新位置存在intent的一个extra中,key为KEY_LOCATION_CHANGED。
    • 在不需要更新位置的时候,要使用removeUpdates来停止位置更新。
    • 如果及时性没有那么重要,则可以使用Passice Location Provider来更新位置,当且仅当其他应用程序请求位置更新时,Passice Location Provider才会更新位置。因为有可能是任意的位置提供器,所以需要fine权限。
    • 单次位置更新:使用requestSingleUpdate方法来请求单次位置更新,单次位置更新同样可以使用监听器或者PendingIntent的方式。无论选择哪种方式,在单次更新后都需要注销接收器。
  • 位置更新的最佳实践,考虑以下因素:
    • 耗电量与精度;
    • 启动时间;
    • 更新频率;
    • 提供器可用性。
  • 使用近距离提醒:通过设置一个中心点(经纬度)、距离半径和提醒的超时时间,指定一个PendingIntent,使用LocationManager的addProximityAlert方法来添加一个近距离提醒,还需要创建一个BroadcastReceiver接收提醒。当设备越过半径边界时,接收器会收到一个带有extra的Intent,存放进入(ture)或者是离开(false)近距离范围,key为LocationManager.KEY_PROXIMITY_ENTERING。

Google Map
  • 添加Google Map库:在manifest文件application节点中添加<uses-library android:name="com.google.android.maps">,然后可以使用Geocoder类进行前向地理编码和反向地理编码。
  • MapView:首先从Android开发人员网站获取一个APIkey,通过扩展MapActivity来包含MapView(也可以扩展MapFragment或MapFragmentActivity,但需要导入支持包),通过配置MapView可以选择显示卫星视图、接到视图和语气的交通覆盖等,还可以使用MapVIew获得中心点和当前课件的纬度和经度范围,也可以设置显示标准的缩放控件。
  • MapController:使用MapController来平移和缩放MapView。
  • 覆盖Overlay:使用Overlay来向MapView添加多层注释绘图和单击处理方法。
    • 扩展Overlay并重写draw方法来绘制注释和重写onTap方法处理用户单击注释;
    • 通过投影Projection类来进行地理位置和屏幕坐标之间转换
    • 通过MapView的getOverlays获得当前显示的覆盖列表,然后进行添加和移除Overlay;
    • 使用MyLocationOverlay在MapView中显示当前位置和方向;
    • 使用ItemizedOverlay来把标记图片和相关的文本分配给特定的地理位置;
    • 通过设置MapView.LayoutParams参数,调用MapView的addView方法可以把视图固定在一个屏幕位置或者是一个地理位置。

Media Player
  • 音频播放:通过调用MediaPlayer.create来初始化,调用setDataSource和prepare来准备音频播放。
  • 视频播放有两种技术:
    • 使用VideoView来初始化和准备视频播放;
    • 使用自定义的SurfaceView和实现SurfaceHolder.callback来初始化和准备视频播放;
  • 使用MediaController控制MediaPlayer的播放:
    • 若是使用VideoView,通过调用VideoVIew的setMediaController方法即可使用MediaController;
    • 此外,MediaController可以控制任何的MediaPlayer,需要实现一个新的MediaPlayerControl与相应的MediaPlayer关联起来。
    • 使用setAnchorView方法设置当MediaController可见时包含在那个视图中,调用show和hide显示和隐藏。
  • MediaPlayer提供了一系列方法用来控制输出音量、设置屏幕亮度、设置循环状态等,设置音频流(AudioManager.STREAM_MUSIC)来响应音量控制,设置Receiver来相应Media播放控件。
  • 通过AudioManager的requestAudioFocus和指定一个AudioFocusChangeListener来请求和管理音频焦点。
  • 通过注册一个Receiver来监听当音频输出发生改变时暂停播放。
  • 使用RemoteControlClient配置远程播放控件,如锁频播放控件。
  • 当不需要使用MediaPlayer时,应该调用release来释放资源。

录音、拍摄和录像
  • 使用AudioRecord和AudioTrack来录制和播放原始音频。
  • 使用SoundPool来管理多个混合)音频的同时播放。
  • 可以使用Android 4.0.3之后提供的Equalizer、Virtualizer、BassBoost、PresetReverb、EnvironmentalReveb的音效类来设置AudioTrack或MediaPlayer的播放音效。
  • 拍摄照片:使用startActivityForResult(intent,CODE)来启动摄像头用自带的Camera拍照(其中intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)),默认是获得缩略图,要获得原图需要指定一个用于存储图像的目标文件并封装为URI,放在intent的putExtra中,key为MediaStore.EXTRA_OUTPUT。
  • 使用Camera类来直接访问摄像头,通过设置Camera.Pararmeters对象的参数来管理摄像头的配置,并可以使用摄像头预览、进行面部识别和面部特征,调用Camera的takePicture方法拍摄并保存照片。
  • 使用ExifInterface类来读取并写入JAEG EIXF图像的详细信息。
  • 录制视频:使用startActivityForResult(intent,CODE)来启动自带的视频录制器(其中intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE)),录制后视频URI在intent的getData中作为返回。启动视频录制捕获动作Intent可以包含以下三个extra:
    • MediaStore.EXTRA_OUTPUT:默认情况录像保存在默认媒体库,若想保存在其他地方可通过此extra指定一个URI;
    • MediaStore.EXTRA_OUTPUT:通过此extra指定录像质量,0表示低质量,1表示高质量;
    • MediaStore.EXTRA_OUTPUT:通过此extra指定录像的最大长度,单位为秒。
  • 若想要替换原生视频录制器,或者需要对视频捕获UI或录制设置进行更细致的控制,可以使用MediaRecorder类。
  • 使用MediaScannerConnection类将录制的任何一种新媒体添加到媒体库中。

蓝牙
  • 使用蓝牙的前提是需要在manifest文件中包含BLUETOOTH权限,若要修改蓝牙设备属性,则需要BLUETOOTH_ADMIN权限。
  • 蓝牙设备适配器:通过调用BluetoothAdapter.getDefaultAdapter方法获得默认的蓝牙适配器,Bluetooth Adaoter提供了用于读取并设置本地蓝牙硬件的属性的方法。若用户禁用了蓝牙功能,为了启用BluetoothAdapter,可以使用startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENBLE), REQUEST_CODE)启动一个系统Preference Activity提示用户打开蓝牙请求确认。一般情况下,应当注册一个BroadcastReceiver来监听ACTION_STATE_CHANG接收蓝牙设备开启状态的变化。
  • 蓝牙的可发现性:
    • 通过BluetoothAdapter的getScanMode方法来获得相应的扫描模式:
      • SCAN_MODE_CONNECTABLE_DISCOVERABLE:可以被任何设备发现;
      • SCAN_MODE_CONNECTABLE_:可以被先前已连接绑定过的设备发现
      • SCAN_MODE_NONE:可发现性被关闭。
    • 一般情况下,设备的可发现性是被关闭的,可以使用startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE), REQUEST_CODE)启动一个系统Preference Activity提示用户将在指定持续时间内打开可发现机制。默认为120s,可以通过Intent来修改时间。
    • 此外,可以注册一个BroadcastReceiver来监听ACTION_SCAN_MODE_CHANG接收蓝牙设备可发现性状态的变化。
  • 发现远程的设备:调用BluetoothAdapter的startDiscovery方法来启动发现过程,嗲用cancelDiscovery进行取消。发现过程是异步进行的,Android使用Broadcast Intent来通知发现过程的启动和结束以及扫描过程中发现的远程设备,可以通过创建Broadcast Receiver来监听发现过程的变化和发现的设备(被封装在BluetoothDevice对象中)。
  • 蓝牙通信:
    • 蓝牙通信是互相匹配的双向过程,任何一方都可以作为服务器,另一方作为客户端请求连接,连接完成后就变为两个BluetoothSocket客户端的传输的过程。
    • 首先,打开一个Bluetooth Server Socket Listener:为了使BluetoothAdapter作为服务器,需要调用其listenUsingRfcommWithServiceRecord方法来获得一个BluetoothServerSocket对象调用accept方法来监听连接请求。
    • 选择远程蓝牙设备进行通信:有多种方法获得BluetoothDevice对象,上面通过Receiver接收是一种,还可以使用Bluetooth的getRemoteDevice方法获得指定了硬件地址的设备。
    • 打开一个客户端Bluetooth Socket连接:通过调用获得的BluetoothDevice的createRfcommSocketToServiceRecord方法创建BluetoothSocket,调用其connect方法启动连接请求。
    • 使用Bluetooth Socket传输通信:通过BluetoothSocket的getInputStream和getOutputStream来处理数据传输。

网络和WIFI
  • 使用ConnectivityManager监视网络连接状态、配置故障转移设置以及控制网络无线电:
    • 4.0之前的版本通过getBackgroundDataSetting方法判断用户是否设置了当应用程序不可见且不在前台是不进行网络传输数据,4.0之后的版本让用户对应用程序使用网络数据的方法有了更多的控制,包括设置单独数据限制和限制后台数据。
    • 对于优化用户对应用程序网络传输的体验:
      • 将要传输的数据降到最低;
      • 根据连接类型修改数据使用方法;
      • 提供可修改数据使用方式(例如后台更行频率)的用户首选项。
    • 使用ConnectivityManager的getActiveNetworkInfo和getNetworkInfo方法配合BroadcastReceiver来查找和监视网络连接。
  • 大多数情况,使用ConnectivityManager监视WiFi连接的变化是一种最佳实践。但也可以使用WiFiManager来配置WiFi网络连接、管理当前的WiFi连接、扫描接入点以及监视WiFi连接变化:
    • 每当WiFi连接状态发生变化时,WiFiManager就会广播Intent,可根据不同的Action来监听当前Wif连接状态和信号强度等变化;
    • 使用getConnectionInfo来获得WIFiInfo对象从而获得更详细的WIFi连接状态信息。
    • 通过WiFiManager的startScan方法进行接入点扫描,使用接收器来接收扫描结果,使用WiFiManager的getScanResult方法获得扫描结果。
    • 使用getConfiguredNetworks获得当前网络配置列表,使用enableNetwork来启用某个网络。
    • 通过设置WiFiConfiguration对象来创建网络配置,调用WiFiManager的addNetwork、updateNetwork和removeNetwork来添加、更新和删除配置,更改后必须调用saveConfiguration保存配置。
  • WiFi Direct传输数据:4.0之后引入了对WiFi Direct的支持,与蓝牙相比,WiFi Direct更加快速可靠,工作距离更远。
    • 通过WifiP2pManager来初始化WiFi Direct框架,使用ActionListener来监听执行某些动作是否成功(如找到和链接对等设备时),成功则广播一个Broadcast Intent,通过接收器接收可以得到与这些动作相关的信息。
    • 使用ACTION_WIRELESS_SETTINGS来启动设置屏幕从而让用户启用WiFi Direct,并通过注册一个接收器来监视其状态。
    • 初始化WiFi Direct框架并启用设备及其对等设备上的WiFi Direct设置后,就可以开始搜索和连接传输数据了:
      • 发现对等设备:调用WifiP2pManager的discoverPeers方法扫描对等设备,通过广播进行搜索。使用WifiP2pManager.requestPeers方法请求当前发现的对等设备的列表;
      • 连接对等设备:通过WifiP2pManager的connect方法与对等设备建立连接,远程设备会被提示接受连接请求,若连接成功将使用WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION 的Intent动作在两个设备上广播,可以使用WifiP2pManager.requestConnectionInfo方法在接收器中查询连接的详细信息;
      • 对等设备之间的数据传输:通过创建标准的Java套接字ServerSocket和ClientSocket进行数据传输(也有tetanus的特定数据传输实现。)

NFC近场通信
  • NFC是一种短距离、少量数据的传输技术,使用NFC需要具有NFC manifest权限。
  • 读取NFC标签:当Android设备扫描一个NFC标签时,会由其标签分派系统使用Intent来启动一个应用程序来接收数据,所以需要添加一个尽可能具体的Activity Intent Filter来监听Intent。
  • 使用前台分派系统:通过前台分派系统,可以指定特定的一个具有高优先级的Activity使得当它位于前台是成伟默认接收标签的应用程序。使用NFCAdapter的enable/disableForegroundDispatch方法可以切换前台分派系统,分别写在onResume和onPause方法中。
  • Android Beam:4.0之后引入的Android Beam提供了一套简单的API,用于使用NFC的两个设备之间传输数据:
    • 创建Android Beam消息:创建一个NdefMessage对象,并在其中创建至少一个NdefRecord(必须制定它表示的记录类型、一个MIME类型、一个ID和有效载荷);
    • 分配Android Beam有效载荷:使用NFCAdapter的setNdefPushMessage或者setNdefpushMessageCallback方法分配有效载荷;
    • 接受Android Beam消息:与NFC标签接收类似,添加一个尽可能具体的Activity Intent Filter来监听Intent,并加上分配的有效载荷对应的MIME Type。

Android的安全性
  • Android的安全性是基于Linux内核的,每个应用程序在安装时,都会分配给它一个唯一的Linux用户ID,这个ID具有隔离进程和它所创建的资源的作用。
  • 要使用其它应用程序授权保护的组件,需要使用<uses-permission>在manifest文件中指定对应权限字符串。
  • 在向一个应用程序的组件分配权限之前,需要使用<permission>在manifest文件中定义权限,可以指定权限字符串、允许的访问级别、一个标签以及一个包含了对授予这个权限的风险进行了说明的外部资源。

你可能感兴趣的:(Android学习笔记)