Android 官方培训文档 笔记 (6-10)

6、Android联系人与位置信息  GMS

6.1Android 联系人信息
Contacts Provider 是用户联系人信息的集中仓库,它包含了来自联系人应用与社交应用的联系人数据。
在我们的应用中,我们可以通过调用ContentResolver()方法或者通过发送Intent给联系人应用来访问
Contacts provider的信息。

6.1.1获取联系人列表
注:这一课的所有例子都使用CursorLoader获取Contacts Provider中的数据。CursorLoader在一个与UI
相独立的工作线程进行查询操作。这保证了数据查询不会降低UI响应的事件,以免引起糟糕的用户体验。

请求读取联系人的权限

6.2Android位置信息

7、Android 可穿戴应用
8、Android TV应用
9、Android 企业级应用

10、Android 交互设计 
10.1设计高效的导航
10.1.1 规划界面和他们之间的关系
多数APP都有一种内在的信息模型,它都被表示成一个用对象类型构成的树或图。更浅显的说,你可以画
一个具有不同类型信息的图,这些信息代表用户在你app里用户与之互动的各种东西。软件工程师和数据
结构师经常使用实例-关系图描述一个应用的信息模型。

创建一个界面列表
3到4英寸的屏幕通常只适合每次展示蛋哥纵向内容视窗,一个列表,或某列表项的具体信息。所以在这些
悲伤,界面通常对应于信息层次上的某一级。
更大的诸如 平板和电视上的屏幕通常会有更多的可用界面控件,并且他们能够展示多个内容视窗。横屏中
,视窗从左到右以细节成都递增的顺序排列。
为了符合这用户期望,通常很有必要为平板提供多个信息视窗来避免留下过多空白或无意间进入尴尬的交互。

实现提醒:当决定好了区分使用单视窗布局和多视窗布局的屏幕大小基准线后,你就可以为不同屏幕大小区间
或最低屏幕宽度提供不同的布局了。

实现提醒:单一界面被实现为Activity的子类,单独的内容视窗则可实现为Fragment的子类。

10.2实现高效导航

10.2.1使用Tabs创建Swipe视图 
Swipe View提供在同级屏幕中的横向导航,例如通过横向切屏手势切换的tab(一种称作横向分页的模式)

实现Swipe View 

10.2.4 提供向后的导航
向后导航是用户根据屏幕历史记录返回之前所查看的界面,所有的Android设备都可以为这种导航提供后退
按钮,所以你的app不需要在UI中添加后退按钮。
在几乎所有情况下,当用户在应用中进行导航时,系统会保存activity的后退栈。这样当用户点击后退按钮时
,系统可以正确地向后导航。但是有少数集中情况需要手动指定app的后退操作,来提供更好的用户体验。

手动指定后退操作需要的导航模式:
1、当用户从notification (通知),app widget,navigation drawer 直接进入深层次activity
2、用户在fragment之间切换的某些情况
3、当用户在webview中对网页进行导航。

为深度链接合并新的后退栈 
一般而言,当用户从一个activity导航到下一个时,系统会递增得创建后退栈。但是当用户从一个自己的任务中
启动activity的深度链接进入app,你就有必须要去同步新的后退栈,因为新的activity是运行在一个没有任何
后退栈的任务中。
例如,当用户从通知进入你的app中的深层activity时,你应该添加别的activity到你的任务的后退栈中,这样当
点击后退时向上导航,而不是退出app。

在启动activity时创建后退栈
当发生用户进入app的事件时,开始添加activity到后退栈中,就是说,使用TaskStackBuilder API定义每个放到
新后退栈的activity,不使用startActivity();然后使用startactivity来启动目标activity,或者调用getPendingIntent()
来创建响应的activity .
//当用户选择通知时,启动activity的Intent
Intent intent = new Intent( this , BuildNavigation. class );
          
  //使用TaskStackBuilder创建后退栈,并获取PendingIntent
 PendingIntent pendingIntent =TaskStackBuilder.create( this )
  //添加所有OfferUpNavigation的父activity到栈中
  //然后再添加OfferUpNavigation自己
 .addNextIntentWithParentStack(intent)
 .getPendingIntent(0, PendingIntent. FLAG_UPDATE_CURRENT );
          
  NotificationCompat.Builder builder= new NotificationCompat.Builder( this );
  builder.setContentIntent(pendingIntent);
产生的Pendingitent不仅指定了启动哪个activity还制定了要插入任务的后退栈。所以当activity被启动时,点击back
向后导航至每个activiyy的父activity

为Fragment实现向后导航 
当在app中使用Fragment,个别的FragmentTrancsaction 对象可以代表要就加入后退栈中变化的内容。例如,如果
你要在手机上通过交换fragment实现一个master/detail flow,你就要保证点击back按钮可以从detail screen返回到master screen。
要这么做,你可以在提交事务之前调用addtoBcakStack()。
当后退栈中FragmentTransaction对象并且用户点击back按钮时,FragmentManager会从后退栈中弹出最近的事务,然后执行
反向操作,(例如如果事务添加了一个Fragment,那么就删除一个fragment)。
注:当事务用作水平导航或者修改内容外观时,不要将这个事务添加到后退栈中。

为WebView实现向后导航 
如果你的应用一部分包含在WebView中,可以通过浏览器历史使用Back。要这么做,如果webview有历史记录,你可以重写onBackPressed()
并代理给WebView


10.2.5 导航至外部Activities
有很多情况,是从别的应用下降至你的应用信息层次,再到activity。例如,当正在浏览手机通讯录中联系信息的detail screen,子屏幕详细显示由
社交网络提供的最近文章,子屏幕可就可以属于一个社交网络应用。

当启动一个应用的activity来允许用户说话,发邮件或选择一个照片附近,如果用户是从启动器(设备Home屏幕)重启你的应用,你一般不会希望
用户返回别的activity。如果点击你的应用图标又回到“发邮件”的屏幕。

为防止这种情况的发生,只需要添加FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET标记到用来启动外部activity的intent中


10.3  通知提示用户


10.3.1建立Notification 

创建Notification Builder
定义Notification 的Action(行为)
尽管在Notification中Action是可选的,但是你应该至少添加一种Action。一种Action可以让用户从Notification直接进入你应用内的activity,在这个
activity中他们可以查看引起Notification的事件或者做下一步的处理。在Notification中,action本身由PendingIntent定义的,PendingInten包含了一个
启动你应用内activity的Intent。

如何构建一个PendingIntent取决于你要启动的activity的类型。当从Notification中启动一个activity时,你必须保存用户的导航体验。在下面的代码中,
点击Notification启动一个新的activity,这个activity有效地扩展了Notification的行为。在这种情形下,就没必要人为地去创建一个返回栈。

设置Notification的点击行为
发布Notifacation

10.3.2启动activity时保留导航
部分设计一个notification的目的是为了保持用户导航体验 

常规的activity
你启动的activity是你application工作流中的一部分 
特定的activity
用户只能从notification中启动,才能看到这个

设置一个常规的activity  PendingIntent

设置一个特定的activity  PendingIntent 
y一个特定的Activity不需要一个返回栈,所以你不需要在mainfest中定义Activity的层次,以及你不需要调用addParentStack()方法去构建一个返回栈。
作为代替,你需要mainfest设置Activity任务选项,以及调用getactivity创建PendingIntent。

注:

application是由四大组件组成的。在app安装时,系统会读取manifest的信息,将所有的组件解析出来,以便在运行时对组件进行实例化和调度。

而task是在程序运行时,只针对activity的概念。说白了,task是一组相互关联的activity的集合它是存在于framework层的一个概念,控制界面的跳转和返回。这个task存在于一个称为back stack的数据结构中,也就是说,framework是以栈的形式管理用户开启的activity。这个栈的基本行为是,当用户在多个activity之间跳转时,执行压栈操作,当用户按返回键时,执行出栈操作。  task是可以跨应用的,这正是task存在的一个重要原因。有的Activity,虽然不在同一个app中,但为了保持用户操作的连贯性,把他们放在同一个任务中
一个应用程序的所有组件运行在同一个进程中。但是这种情况也有例外,即,应用程序中的不同组件可以运行在不同的进程中。只需要在manifest中用process属性指定组件所运行的进程的名字。

1、在mainfest中,在Activity的标签中增加下列属性,android:name="activityclass"activity的完整的类名。android:taskAffinity结合你在代码里设置的
FLAG_ACTIVITY_NEW_TASK标识,确保这个acitivty不会进入application的默认任务。任何与application的默认任务有密切关系的任务都不会受到影响。
android:excludeFromRecents="true"将新任务从最近列表中排除,目的是为了防止用户不小心返回到它。

Android Framework既能在同一个任务中对Activity进行调度,也能以Task为单位进行整体调度。在启动模式为standard或singleTop时,一般是在同一个任务中对Activity进行调度,而在启动模式为singleTask或singleInstance是,一般会对Task进行整体调度

对Task进行整体调度包括以下操作:

  1. 按Home键,将之前的任务切换到后台
  2. 长按Home键,会显示出最近执行过的任务列表
  3. 在Launcher或HomeScreen点击app图标,开启一个新任务,或者是将已有的任务调度到前台
  4. 启动singleTask模式的Activity时,会在系统中搜寻是否已经存在一个合适的任务,若存在,则会将这个任务调度到前台以重用这个任务。如果这个任务中已经存在一个要启动的Activity的实例,则清除这个实例之上的所有Activity,将这个实例显示给用户。如果这个已存在的任务中不存在一个要启动的Activity的实例,则在这个任务的顶端启动一个实例。若这个任务不存在,则会启动一个新的任务,在这个新的任务中启动这个singleTask模式的Activity的一个实例。
  5. 启动singleInstance的Activity时,会在系统中搜寻是否已经存在一个这个Activity的实例,如果存在,会将这个实例所在的任务调度到前台,重用这个Activity的实例(该任务中只有这一个Activity),如果不存在,会开启一个新任务,并在这个新任务中启动这个singleInstance模式的Activity的一个实例。

10.3.3 更新Notification

改变一个Notification 
想要设置一个可以被更新的notification,需要在发布它的时候调用NotificationManager.notify(id,notification)方法为它指定一个notification id。更新一个已经发布的Notification,需要更新或者创建一个NotificationCompat.Builder对象,并从这个对象创建一个Notification对象,然后用与先前一样的id去发布这个Notification。

10.3.4 使用BigView 风
10.3.5 显示Notification进度

10.4 增加搜索功能 
10.4.1建立搜索功能 
10.4.2保存并搜索数据
10.4.3保持向下兼容

10.5使得你的app内容可被google 搜索 
为你的深度连接添加Intent filter
当你把包含有指定activity内容的URI的intent filter添加到你的app mainfest后,Android就可以在你的app运行时,为app与匹配的intent建立路径 
注:对一个URI pattern ,intent filter 可以只包含一个单一的data元素,创建不同的intent filter 来匹配额外的URI pattern.

从传入的intent读取数据


管理系统的UI
System Bar是用来展示通知、表现设备状态和完成设备导航的屏幕区域。通常上来说,系统栏(System bar )包括状态栏和导航栏,他们一般都是与程序
同时显示在屏幕上的。而照片、视频等这类沉浸式的应用可以临时弱化系统蓝图标来创造一个更加专注的体验环境,甚至可以完全隐藏系统Bar。

淡化系统Bar
本课程将向你讲解如何在Android 4.0与更高的系统版本上淡化系统栏。
但你使用这个方法的时候,内容区域并不会发生大小的变化,只是系统的图标会收起来。一旦用户触摸状态栏或者是导航栏的时候,这两个系统栏就又都会完全显示(透明度)。
这种方法的优势是系统栏仍然课件,但是它们的细节被隐藏掉了,因此可以在不牺牲快捷访问系统栏的情况下创建一个沉浸式的体验。

注意以下几点:
1、一旦UI标签被清楚,如果你还想隐藏状态栏就必须再次设定它。详细可以看第五节如何监听并响应UI可见性的变化。
2、在不同的地方设置UI标签是有所区别的。如果你在Activity的onCreate()方法中隐藏系统栏,当用户按下home键时,系统就会重新显示。当用户再重新打开activity的时候,
oncreate()不会被调用,所以系统蓝还会保持课件。如果你想让在butongactivity之间切换时,系统UI保持不变,你需要在onResume()与onWindowFocusChanged()里
设定UI标签。
3、setSystemUIVisibility()仅仅在被调用的View显示的时候才会生效。
4、当从View导航到别的地方时,用setSystemUiVisibility()设置的标签会被清除。

同步状态栏与Action Bar的变化 
设置System_ui_FLAG_LAYOUT_FULLSCREEN来让你的activity使用的屏幕区域与设置SYSTEM_UI_FLAG_FULLSCREEN时的区域系统。当你需要隐藏系统UI时,使用SYSTEM_UI_
FLAG_SCREEN。这个操作也同时隐藏了action_bar,当同时显示与隐藏ActionBar与状态栏的时候,使用一个动画来让它们相互协调。
View decroView=getWindow().getDecorView();
      int uiOptions=View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_FULLSCREEN
     decroView.setSystemUiVisibility(uiOptions);
              
     }
当使用这个方法时,触摸屏幕的任何一个区域都会使导航栏(与状态栏)重新显示。用户交互会使这个标签被清除。

2)让内容显示在导航栏之后 

&和&&都是逻辑运算符,都是判断两边同时真则为真,否则为假;但是&&当第一个条件不成之后,后面的条件都不执行了,而&则还是继续执行,直到整个条件语句执行完为止。如&&例子中的i++>5被执行了,而i++<9并没有被执行,这就是他们的差别。&例子中的i++>5和i++<9都被执行了。

或就是表示前后表达式只要有一个是true,结果就是true,所有的表达式都是false,结果才是false; 
短路与表示只要第一个表达式是true,则程序就不会去执行其他的表达式判断,相反,如果不是短路或的话,则即使第一个是true,也会都去执行其他的表达式进行判断 

无论我们是否使用Gesture.OnGestureListener类,最好都实现onDown()函数并返回true。这是因为所有的手势都是由onDown()消息开始的。如果让
onDown()函数返回false,就像GestureDetector.SimpleOnGestureListener类中默认实现的那样,系统会假定我们想忽略剩余的手势,GestureDector.OnGestureListener中其他的函数也永远不会被调用。这可能会导致我们的app出现意想不道德问题。


追踪手势移动
每当当前的触摸位置、压力、大小发生变化时,ACTION_MOVE事件都会触发onTouchEvent()函数。正如检测常用的手势中描述的那样,触摸事件全部都记录在onTouchEvent()函数的MotionEvent函数中。
因为基于手指的触摸的交互方式并不总是非常精确,所以检测触摸事件更多的基于手势移动。而非简单地基于触摸。为了帮助app区分基于移动的手势(如滑动)和非移动手势(如简单地点击),Android引入了
“touch stop”的概念。touch slop是指,在被识别为基于移动的手势前,用户触摸可移动的那一段像素距离。

根据应用的需求,有多种追踪手势移动的方式可以选择。比如:
1、追踪手指的起始和终止位置(比如,在屏幕上的对象从A点移动到B点)
2、根据x,y轴坐标,追踪手势移动的方向 
3、追踪历史状态。我们可以通过调用MotionEvent的getHistorySizeZ()方法,来获得一个手势的历史尺寸。我们可以通过移动事件的getHistorical<Value>系统函数,来获得事件之前的位置、尺寸、事件以及按压力。
当我们需要绘制用户手指痕迹时,历史状态非常有用,比如触摸会吐。
4、追踪手指在触摸屏上滑过的速度。

追踪速度 
我们可以简单地用基于距离,或(和)基于手指移动方向的移动手势。但是速度经常也是追踪手势特性的一个决定性因素,甚至是判断一个手势是否发生的依据。为了让计算速度更容易,Android提供了VelocityTracker类
以及VelocityTrackerCompat类。VelocityTracker类可以帮主我们追踪触摸事件中的速度因素。如果速度是手势的一个判断标准,比如快速滑动(fling),那么这些类很有用。

滚动手势动画
在Android中,通常使用ScrollView类实现滚动(scroll)。任何可能超过父类边界的布局,都应该嵌套在ScrollView中,来提供一个由系统框架管理的可滚动的View。仅在特殊情形下,我们才需要一个自定义scroller。
为了收集数据来产生滚动动画,以响应一个触摸事件,我们可以使用scrollers(Scroller或OverScroller)。这两个类很相似,但overScroller有一些函数,能在平移或快速滑动手势后,向用户支出已经达到内容的边缘。InteractiveChart
在用户到达内容的边缘时显示“发光效果”。
通过使用平台标准的滚蛋物理因素(摩擦、速度等),scroller被用来随着时间的推移产生滚动动画。实际上,scroller本身不会绘制任何东西,scroller只是随着时间的推移,追踪滚动的偏移量,但它们不会自动地把这些位置应用到View上。

追踪多点
当多个手指同事触摸屏幕时,系统会产生如下的触摸事件:
1、ACTION_DOWN:针对触摸屏幕的第一个点,此事件是手势的开端,第一触摸点的数据在MotionEvent中的索引是0.
2、ACTION_POINTER_DOWN:针对第一点后,出现在屏幕上额外的点。这个点的数据在MotionEvent中的索引,可以通过getActionIndex()获得。
3、ACTION_MOVE:在按下手势期间发生变化。
4、ACTION_POINTER_UP:当非主要点离开屏幕时,发送此事件。
5、ACTION_UP:当最后一点离开屏幕时发送此事件。

我们可以通过各个点的索引以及id,单独地追踪Motionevent的每个点。
Index:MotionEvent把各个点的信息都存储在一个数组中。点的索引值就是它在数组中的位置。大多数用来与点交互的MotionEvent都是以索引值而不是点的ID作为参数的。
ID:每个店也都有一个ID映射,该ID映射在整个手势期间一直存在,以便我们单独追踪每个点。

每个独立的点在移动事件中出现的次序是不固定的。因此,从一个事件到另一个事件,点的索引值是可以改变的,但点的id在它的生命周期内是保证不会改变的。使用getPointerID()可以
获得一个点的ID,在手势随后的移动事件中,就可以用该ID来追踪这个点。对于随后一系列的事件,可以使用findPointerIndex()函数,来获得对应给定ID的点在移动事件中的索引值。

获取MotionEvent的动作
我们应该总是使用getActionMasked()函数(或者用MotionEventCompat.getActionMasked()这个兼容版本)来获取MotionEvent的动作(action)。与旧的getAction()函数不同的是,
getActionMasked()是设计用来处理多点触摸的。它会返回执行过的动作的掩码值,不包括点的索引位。然后我们可以使用getActionIndex()来获得与该动作关联的点的索引值。

拖拽与缩放
拖拽一个对象
对于触摸手势来说,一个很常见的操作是在屏幕上拖拽一个对象。接下来的代码段让用于可以拖拽屏幕上的图片。
注意:
1、拖拽操作即使有额外的手指放置到屏幕上了,app也必须保持对最初的点(手指)的追踪。比如,想象在拖拽图片时,用户放置了第二根手指在屏幕上,并且抬起了第一个手指。如果我们的app
只是单独的追踪每个店,它会把第二个点当作默认的点,并且把图片移到该点的位置。
2、为了防止这种情况发生,我们的app需要区分初始点以及随后任意的触摸点。要做到这一点,它需要追踪处理多触摸手势提到过的ACTION_POINTER_DOWN和ACTION_POINTER_UP事件。每当
第二个手指按下或拿起时, ACTION_POINTER_DOWN  和  ACTION_POINTER_UP  事件就会传递给 onTouchEvent() 回调函数。
3、当ACTION_POINTER_UP事件发生时,示例程序会移动对该点的索引值的引用,确保操作中的点的ID不会引用已经不在触摸屏上的触摸点。这种情况下,app会选择另一个触摸点来作为操作中(active)
的点,并保存它当前的x、y值。由于在action_move事件时,这个保存的位置会被用来计算屏幕上的对象将要移动的距离,所以app会始终根据正确触摸点来计算移动的距离。

使用触摸手势进行缩放
GestureDetector可以帮助我们检测Android中的常见手势。对于缩放,Android也提供了ScaleGestureDetector类。当我们想让view能识别额外的手势时,我们可以同时使用GestureDecetor和ScaleGestureDecetor类。
为了报告检测到的手势事件,手势检测需要一个作为构造参数的listener对象。ScaleGestureDecetor使用ScaleGestureDetector.onScaleGestureListener。andorid提供了ScaleGestureDecetor.SimpleOnScaleGestrurListener
类,如果我们不是关注所有的手势事件,我们可以继承它。

在ViewGroup中截获触摸事件
每当在ViewGroup(包括它的子View)的表面上检测到一个触摸事件,onInterceptTouchEvent()都会被调用。如果onInterceptTouchEvevnt()返回true,MotionEvent就被截获了,这表示他不会被传递给其他子View,而是传递给
该父View自身的onTouchEvent()方法。
onIntercepTouchEvent()方法让父View能够在它的子View之前处理触摸事件。如果我们让onInterceptTouchEvent()返回true。则之前处理触摸事件的子View会收到Action_Cancel事件,并且该点之后的事件会被发送给该父View自身的
onTouchEvent()函数,进行常规处理。onInterceptTouchEvent()也可以返回false,这样事件沿View从层级分发到目标前,父View可以简单地观察该事件。这里的目标是指,通过onTouchEvent()处理消息时间的view。

接下来的代码段中,MyViewGroup继承自ViewGroup。MyViewGroup有多个子View。如果我们在某个子View上水平地拖动手指,该子View不会接收到触摸事件,而是应该由MyViewGroup处理这些触摸事件来滚动它的内容。然而,如果我们
点击view中的button,或垂直的滚动子View,则父View不会截获这些触摸事件,因为子View本身就是预定目标。

创建后台服务
Intentservice为在单一后台线程中执行任务提供了一种直接的实现方式。它可以处理一个耗时的任务并确保不影响到UI的响应性。另外IntentService有下面几个局限性:
1、不可以直接和UI做交互。为了把他执行的结果体现在UI上,需要把结果返回给Activity。
2、工作任务队列是顺序执行的,如果一个任务正在IntentService中执行,此时你再发送一个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕才开始执行。
3、正在执行的任务无法打断。

保持屏幕常亮
某些应用需要保持屏幕常亮,比如游戏与视频应用。最好的方式是在你的Activity中使用FLAG_KEEP_SCREEN_ON属性。

调度重复的闹钟 
闹钟(基于AlarmManager)给予你一种在应用使用期之外执行与时间相关的操作的方法。你可以使用闹钟初始化一个长时间的操作。
闹钟具有如下特性:
1、允许你通过预设事件或者设定某个事件间隔,来触发Intent。
2、你可以将它与BroadcastReceiver相结合,来启动服务并执行其他操作。
3、可在应用范围之外执行,所以你可以在你的应用没有运行或设备处于睡眠状态的情况下,使用它来触发事件或行为。
4、帮助你的应用最小化资源需求,你可以使用闹钟调度你的任务,来替代计时器或者长时间连续运行的后台服务。

管理应用的内存
Random Access Memory(RAM)在任何软件开发换进中都是十分宝贵的。尽管Android的Davlik虚拟机扮演了常规了垃圾回收角色,但并不意味着你可以忽视app的内存分配及释放的时机与地点。
为了GC能够从app中及时回收内存,我们需要注意避免内存泄漏。

第1部分 Android 是如何管理内存的
Android并没有为内存提供交换区,但是它有使用paging与memory-mapping(mapping)的机制来管理内存,这意味着任何你修改的内存(无论是通过分配新的对象还是去访问mmaped pages 中的
内容都会贮存在RAM中,而且不能被paged out  。因为唯一完整释放内存的方法是释放那些你可能hold住的对象的引用,但这个引用没有被任何其他对象所引用的时候,它就能GC回收了。)

1)共享内存,
Android 通过下面几个方式在不同的进程中实现共享RAM:
1、每个app的进程都是从一个叫做Zygote的进程中分支出来的。 Zygote进程会在系统启动并且载入通用的framwork的代码与资源之后开始启动,为了启动一个新的程序进程,系统会fork zygote进程生成一个新的
进程,然后在新的进程中加载并运行app的代码。这使得大多数的RAM pages被用来分配给framework的代码,同时,使得RAM资源能够在英勇的所有进程中共享。
大多数static的数据被mapped到一个进程中。这不仅仅使得同样的数据能够在进程间进行共享,而且使得它能够在需要的时候被paged out。例如下面几种static的数据:
2、大多数static的数据被mapped到一个进程中,这不仅仅使得同样的数据能够在进程间进行共享,而且使得它能够在需要的时候被paged out 。例如 下面集中 static的数据 
(1)Dalvik代码(放在一个预连接好的。odex文件中以便直接mapping)
(  2 )appresources(通过把资源表结构设计成便于mapping的数据结构,另外还可以通过把apk中的文件aligning的操作来优化)
3、在很多情况下,Android 通过显式的分配共享内存区域来实现一些动态RAM区域能够在不同进程间进行共享。

2)分配与回收内存
1、每一个进程的Dalvik heap都有一个受限的虚拟内存范围,这就是逻辑上堆内存,它有一个系统为它所定义的上限。
2、逻辑上讲的heap size和实际物理上使用的内存数量是不等的。Android会计算一个叫做Propotional set Size 的值,它记录了那些和其他进程进行共享的内存大小
3、Davlik heap与逻辑上的heap size 不吻合,这意味着 android并不会去做heap中的碎片整理用来关闭空闲区域。android仅仅会在heap的尾端出现不实用的控件时才会做收缩逻辑heap size大小的动作。在垃圾回收之后,
Dalvik会遍历heap并找出不使用的pages,然后使用madvise把那些pages返回给kernal。

3)限制应用的内存
你也许想要查询当前设备的heap size 限制大小是多少,然后绝地沟cache的大小。可以通过getMemoryClass()来查询。这个方法会返回一个证书,表明你的应用的heap size限制是多少MB

4)切换应用
Android 并不会在用户切换不同应用时候做交换内存的操作。android会把那些不包含foregrond组件的进程放到LRU cache中。
如果你的应用中有一个缓存的进程,这个进程会占用暂时不需要使用到的内存,这个暂时不需要使用的进程,它被保留在内存中,这会对系统的整体性能有影响。因此当系统开始进入低内存状态时,它会由系统根据LRU规则与其他因素
综合考虑之后,决定杀掉某些进程。

第2部分:你的应用该如何管理内存
1)珍惜services资源
当你启动一个service,系统会倾向为了保留这个service而一直保留service所在的进程,这使得进程的运行代价很高。因为系统没有办法把service所占用的RAM空间腾出来给其他组件。另外service还不能被paged out 。
限制你的service,最好是使用intentservice 。
2)当UI隐藏时释放内存
为了能够接受到用户离开你的UI时的通知,你需要实现activity里面的Ontrimmemory()回调方法。
3)
  • TRIM_MEMORY_RUNNING_MODERATE:你的app正在运行并且不会被列为可杀死的。但是设备此时正运行于低内存状态下,系统开始触发杀死LRU Cache中的Process的机制。
  • TRIM_MEMORY_RUNNING_LOW:你的app正在运行且没有被列为可杀死的。但是设备正运行于更低内存的状态下,你应该释放不用的资源用来提升系统性能(但是这也会直接影响到你的app的性能)。
  • TRIM_MEMORY_RUNNING_CRITICAL:你的app仍在运行,但是系统已经把LRU Cache中的大多数进程都已经杀死,因此你应该立即释放所有非必须的资源。如果系统不能回收到足够的RAM数量,系统将会清除所有的LRU缓存中的进程,并且开始杀死那些之前被认为不应该杀死的进程,例如那个包含了一个运行态Service的进程。

同样,当你的app进程正在被cached时,你可能会接受到从onTrimMemory()中返回的下面的值之一:

  • TRIM_MEMORY_BACKGROUND: 系统正运行于低内存状态并且你的进程正处于LRU缓存名单中最不容易杀掉的位置。尽管你的app进程并不是处于被杀掉的高危险状态,系统可能已经开始杀掉LRU缓存中的其他进程了。你应该释放那些容易恢复的资源,以便于你的进程可以保留下来,这样当用户回退到你的app的时候才能够迅速恢复。
  • TRIM_MEMORY_MODERATE: 系统正运行于低内存状态并且你的进程已经已经接近LRU名单的中部位置。如果系统开始变得更加内存紧张,你的进程是有可能被杀死的。
  • TRIM_MEMORY_COMPLETE: 系统正运行与低内存的状态并且你的进程正处于LRU名单中最容易被杀掉的位置。你应该释放任何不影响你的app恢复状态的资源。
因为onTrimMemory()的回调是在 API 14才被加进来的,对于老的版本,你可以使用 onLowMemory)回调来进行兼容。onLowMemory相当与 TRIM_MEMORY_COMPLETE
Note:  当系统开始清除LRU缓存中的进程时,尽管它首先按照LRU的顺序来操作,但是它同样会考虑进程的内存使用量。因此消耗越少的进程则越容易被留下来。

4)检查你应该使用多少的内存 
正如前面提到的,每一个android设备都会有不同的RAM总大小与可用空间,因此不同设备为app提供了不同大小的heap限制。
不要轻易的因为你需要使用大量的内存而去请求一个大的heap size。只有当你清楚的知道哪里会使用大量的内存并且这些内存必须被保留时才去使用large heap。
5)避免bitmaps的浪费 
Note: 在Android 2.3.x (API level 10)及其以下, bitmap对象的pixel data是存放在native内存中的,它不便于调试。然而,从Android 3.0(API level 11)开始,bitmap pixel data是分配在你的app的Dalvik heap中, 这提升了GC的工作效率并且更加容易Debug。因此如果你的app使用bitmap并在旧的机器上引发了一些内存问题,切换到3.0以上的机器上进行Debug。
6)使用优化的数据容器 
利用android  framework里面优化过的容器类,例如spareArray ,sparseBooleanArray,与LongSparseArray
7) 请注意内存开销
enums的内存消耗通常是 static constants 的2倍 
在java的每一个类都会使用大概500bytes 
每一个类的实例花销是12-16bytes
往hashmap添加一个entry需要额外占用一个32bytes的entry对象。
16)使用多进程 

为多线程创建管理器 
一个线程池能运行多个并行的任务实例,因此你要保证你的代码是线程安全的。从而你需要给会被多个线程访问的变量附上同步代码快、当一个线程再对一个变量进行写操作时,通过这个方法将能阻止另一个现场对该变量进行读取操作。典型的,这种情况会发生在静态变量上,但同样它也能突然发生在任意一个只实例化一次。

定义线程池类

当系统检测到下面的条件之一会显示ANR的对话框:
1、对输入事件(例如硬件点击或者屏幕触摸事件),5秒内无响应
2、BroadReceiver不能够在10秒内结束接收到任务。

增加响应性
1、如果你的程序需要响应正在后台加载的任务,在你的UI中可以显示ProgressBar来显式进度
2、对游戏程序,在工作线程中执行计算的任务。
3、如果你的程序在启动阶段有一个耗时的初始化操作,可以考虑显示一个闪屏。要么,尽快的显示主界面,然后马上显示一个加载的对话框,异步加载数据。

JNI 
JNI 全称为 java native interface 。它为托管代码与本地代码提供了一种交互方式。它是与厂商无关的,支持从动态共享库中加载代码,虽然这样会稍显麻烦,但有时这是相当有效地。

Java VM 与 JNI Env
JNI定义了两种关键数据结构,"java VM"和“JNIEnv”,它们本质上都是指向函数表指针的指针(在C++版本中,它们被定义为类,该类包含一个指向函数表的指针,以及一些列可以通过这个函数表间接地访问对应的JNI函数的成员函数)。
JavaVM提供“调用接口”函数,允许你创建和销毁一个java VM,理论上你可以在一个进程中拥有多个JavaVM对象,但安卓只允许一个。
JNIEnv提供了大部分JNI功能。你定义的所有本地函数都会接受JNIEnv作为第一个函数。
JNIEnv是用作线程局部存储。因此,你不能在线程间共享一个JNIEnv变量。

线程
所有的线程都是Linux线程,由内核统一调度。

Symmetric Multi-Processor对称多处理器 
1.1)内存一致性模型
描述了硬件架构如何确保内存访问的一致性。例如,如果你对地址A进行了一个赋值,然后对地址B也进行了赋值,那么内存一致性模型就需要确保每一个CPU都需要知道刚才的操作复制和操作顺序。
1、所有的内存操作每次只能执行一个  2、 所有的操作,在单核CPU上,都是顺序执行的。
 
使用HTTPS与SSL
SSL:安全套接层,是一个常见的用来加密客户端和服务器通信的模块。但是应用程序错误地使用SSL可能会导致应用程序的数据在网络中被恶意攻击者拦截。
概念

无法识别证书机构



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值