首页 文章 精选 留言 我的

精选列表

搜索[独立开发者],共10000篇文章
优秀的个人博客,低调大师

Android开发者指南(4) —— Application Fundamentals(二)

线程安全方法(Thread-safe methods) 在一些情况下,你所实现的方法有可能会被多于一个的线程所调用,所以它们必须被写成线程安全的。 对于我们上一节所讨论的RPC机制中的可以被远程调用的方法来说,这是必须首先考虑的。如果针对一个IBinder对象中实现的方法的调用源自这个IBinder对象所在的进程时,这个方法将会在调用者的线程中执行。然而,如果这个调用源自其它的进程,则这个方法将会在一个线程池中选出的线程中运行,这个线程池由Android加以管理,并与IBinder存在于同一进程内;这个方法不会在进程的主线程内执行。反过来说,一个服务的onBind()方法应为服务进程的主线程所调用,而实现了由onBind()返回的对象(比如说,一个实现了RPC方法的Stub的子类)的方法将为池中的线程所调用。因为服务可以拥有多于一个的客户端,而同一时间,也会有多个池中的线程调用同一个IBinder方法。因此IBinder方法必须实现为线程安全的。 类似的,一个内容提供者能接受源自其它进程的请求数据。尽管ContentResolver和ContentProvider类隐藏了交互沟通过程的管理细节,ContentProvider会由query(),insert(),delete(),update()和getType()方法来相应这些请求,而这些方法也都是由那个内容提供者的进程中所包涵的线程池提供的,而不是进程的主线程本身。所以这些有可能在同一时间被很多线程调用的方法也必须被实现为线程安全的。 组件生命周期(Component Lifecycles) 应用程序组件有其生命周期──由Android初始化它们以相应intent直到这个实例被摧毁。在此之间,它们有时是激活的有时则相反。或者,如果它是一个activity,则是可为用户所见或者不能。这一节讨论了activity、服务以及广播接收器的生命周期,包括它们在生命周期中的状态、在状态之间转变时通知你的方法、以及当这些进程被关闭或实例被摧毁时,这些状态产生的效果。 Activity生命周期(Activity lifecycle) 一个activity主要有三个状态: *当在屏幕前台时(位于当前任务堆栈的顶部),它是活跃或运行的状态。它就是相应用户操作的activity。 *当它失去焦点但仍然对用户可见时,它处于暂停状态。即是:在它之上有另外一个activity。这个activity也许是透明的,或者未能完全遮蔽全屏,所以被暂停的activity仍对用户可见。暂停的activity仍然是存活状态(它保留着所有的状态和成员信息并连接至窗口管理器),但当系统处于极低内存的情况下,仍然可以杀死这个activity。 *如果它完全被另一个activity覆盖是,它处于停止状态。它仍然保留所有的状态和成员信息。然而它不在为用户可见,所以它的窗口将被隐藏,如果其它地方需要内存,则系统经常会杀死这个activity。 如果一个activity处于暂停或停止状态,系统可以通过要求它结束(调用它的finish()方法)或直接杀死它的进程来将它驱出内存。当它再次为用户可见的时候,它只能完全重新启动并恢复至以前的状态。 当一个activity从这个状态转变到另一个状态时,它被以下列protected方法所通知: void onCreate(Bundle savedInstanceState) void onStart() void onRestart() void onResume() void onPause() void onStop() void onDestroy() 你可以重载所有这些方法以在状态改变时进行合适的工作。所有的activity都必须实现onCreate()用以当对象第一次实例化时进行初始化设置。很多activity会实现onPause()以提交数据变化或准备停止与用户的交互。 调用父类(Calling into the superclass) 所有activity生命周期方法的实现都必须先调用其父类的版本。比如说: 总得来说,这七个方法定义了一个activity完整的生命周期。实现这些方法可以帮助你监察三个嵌套的生命周期循环: *一个activity完整的生命周期自第一次调用onCreate()开始,直至调用onDestroy()为止。activity在onCreate()中设置所有“全局”状态以完成初始化,而在onDestroy()中释放所有系统资源。比如说,如果activity有一个线程在后台运行以从网络上下载数据,它会以onCreate()创建那个线程,而以onDestroy()销毁那个线程。 *一个activity的可视生命周期自onStart()调用开始直到相应的onStop()调用。在此期间,用户可以在屏幕上看到此activity,尽管它也许并不是位于前台或者正在与用户做交互。在这两个方法中,你可以管控用来向用户显示这个activity的资源。比如说,你可以在onStart()中注册一个BroadcastReceiver来监控会影响到你UI的改变,而在onStop()中来取消注册,这时用户是无法看到你的程序显示的内容的。onStart()和onStop()方法可以随着应用程序是否为用户可见而被多次调用。 *一个activity的前台生命周期自onResume()调用起,至相应的onPause()调用为止。在此期间,activity位于前台最上面并与用户进行交互。activity会经常在暂停和恢复之间进行状态转换──比如说当设备转入休眠状态或有新的activity启动时,将调用onPause()方法。当activity获得结果或者接收到新的intent的时候会调用onResume()方法。因此,在这两个方法中的代码应当是轻量级的。 下图展示了上述循环过程以及activity在这个过程之中历经的状态改变。着色的椭圆是activity可以经历的主要状态。矩形框代表了当activity在状态间发生改变的时候,你进行操作所要实现的回调方法。 下表详细描述了这些方法,并在activity的整个生命周期中定位了它们。 方法 描述 是否可被杀死(Killable?) 下一个 onCreate() 在activity第一次被创建的时候调用。这里是你做所有初始化设置的地方──创建视图、绑定数据至列表等。如果曾经有状态记录(参阅后述Saving Activity State。),则调用此方法时会传入一个包含着此activity以前状态的包对象做为参数。 接下来始终遵循调用onStart()。 否 onStart() onRestart() 在activity停止后,在再次启动之前被调用。 接下来始终遵循调用onStart()。 否 onStart() onStart() 当activity正要变得为用户所见时被调用。 当activity转向前台时接下来调用onResume(),在activity变为隐藏时接下来调用onStop()。 否 onResume() 或 onStop() onResume() 在activity开始与用户进行交互之前被调用。此时activity位于堆栈顶部,并接受用户输入。 接下来始终遵循调用onPause()。 否 onPause() onPause() 当系统将要启动另一个activity时调用。此方法主要用来将未保存的变化进行持久化,停止类似动画这样耗费CPU的动作等。这一切动作应该在短时间内完成,因为下一个activity必须等到此方法返回后才会继续。 当activity重新回到前台时接下来调用onResume()。当activity变为用户不可见时接下来调用onStop()。 是 onResume() 或 onStop() onStop() 当activity不再为用户可见时调用此方法。这可能发生在它被销毁或者另一个activity(可能是现存的或者是新的)回到运行状态并覆盖了它。 如果activity再次回到前台跟用户交互则接下来调用onRestart(),如果关闭activity则接下来调用onDestroy()。 是 onRestart() or onDestroy() onDestroy() 在activity销毁前调用。这是activity接收的最后一个调用。这可能发生在activity结束(调用了它的finish()方法)或者因为系统需要空间所以临时的销毁了此acitivity的实例时。你可以用isFinishing()方法来区分这两种情况。 是 无 请注意上表中可被杀死一列。它标示了在方法返回后,还没执行activity的其余代码的任意时间里,系统是否可以杀死包含此activity的进程。三个方法(onPause()、onStop()和onDestroy())被标记为“是”。onPause()是三个中的第一个,它也是唯一一个在进程被杀死之前必然会调用的方法──onStop()和onDestroy()有可能不被执行。因此你应该用onPause()来将所有持久性数据(比如用户的编辑结果)写入存储之中。 在可被杀死一列中标记为“否”的方法在它们被调用时将保护activity所在的进程不会被杀死。所以只有在onPause()方法返回后到onResume()方法被调用时,一个activity才处于可被杀死的状态。在onPause()再次被调用并返回之前,它不会被系统杀死。 如后面一节进程和生命周期所述,即使是在这里技术上没有被定义为“可杀死”的activity仍然有可能被系统杀死──但这仅会发生在实在没有其它方法的极端情况之下。 保存activity状态(Saving activity state) 当系统而不是用户自己出于回收内存的考虑,关闭了一个activity之后。用户会期望当他再次回到那个activity的时候,它仍保持着上次离开时的样子。 为了获取activity被杀死前的状态,你应该为activity实现onSaveInstanceState()方法。Android在activity有可能被销毁之前(即onPause()调用之前)会调用此方法。它会将一个以名称-值对方式记录了activity动态状态的Bundle对象传递给该方法。当activity再次启动时,这个Bundle会传递给onCreate()方法和随着onStart()方法调用的onRestoreInstanceState(),所以它们两个都可以恢复捕获的状态。 与onPause()或先前讨论的其它方法不同,onSaveInstanceState()和onRestoreInstanceState()并不是生命周期方法。它们并不是总会被调用。比如说,Android会在activity易于被系统销毁之前调用onSaveInstanceState(),但用户动作(比如按下了BACK键)造成的销毁则不调用。在这种情况下,用户没打算再次回到这个activity,所以没有保存状态的必要。 因为onSaveInstanceState()不是总被调用,所以你应该只用它来为activity保存一些临时的状态,而不能用来保存持久性数据。而是应该用onPause()来达到这个目的。 服务生命周期(Coordinating activities) 服务以两种方式使用: *它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf()或Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。 *它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。 这两种模式并不是完全分离的。你可以绑定至一个用startService()启动的服务。比如说,一个后台音乐播放服务可以调用startService()并传递给它一个包含欲播放的音乐列表的Intent对象来启动。不久,当用户想要对播放器进行控制或者查看当前播放曲目的详情时,会启用一个activity,调用bindService()连接到服务来完成操作。在这种情况下,直到绑定连接关闭stopService()才会真正停止一个服务。 与activity一样,服务也有一系列你可以实现以用于监控其状态变化的生命周期方法。但相对于activity要少一些,只有三个,而且,它们是public属性,并非protected: void onCreate() void onStart(Intent intent) void onDestroy() 倚仗实现这些方法,你监控服务的两个嵌套的生命周期循环: *服务的完整生命周期始于调用onCreate()而终于onDestroy()方法返回。如同activity一样,服务在onCreate()里面进行它自己的初始化,而在onDestroy()里面释放所有资源。比如说,一个音乐回放服务可以在onCreate()中创建播放音乐的线程,而在onDestroy()中停止这个线程。 *服务的活跃生命周期始于调用onStart()。这个方法用于处理传递给startService()的Intent对象。音乐服务会打开Intent来探明将要播放哪首音乐,并开始播放。 服务停止时没有相应的回调方法──不存在onStop()方法。 onCreate()和onDestroy()方法在所有服务中都会被调用,无论它们是由Context.startService()还是由Context.bindService()所启动的。而onStart()仅会被startService()所启用的服务调用。 如果一个服务允许别的进程绑定,则它还会有以下额外的回调方法以供实现: IBinder onBind(Intent intent) boolean onUnbind(Intent intent) void onRebind(Intent intent) 传递给bindService的Intent的对象也会传递给onBind()回调方法,而传递给unbindService()的Intent对象同样传递给onUnbind()。如果服务允许绑定,onBind()将返回一个供客户端与服务进行交互的通讯渠道。如果有新的客户端连接至服务,则onUnbind()方法可以要求调用onRebind()。 下图描绘了服务的回调方法。尽管图中对由startService和startService方法启动的服务做了区分,但要记住,不论一个服务是怎么启动的,它都可能允许客户端的连接,所以任何服务都可以接受onBind()和onUnbind()调用。 广播接收器生命周期(Broadcast receiver lifecycle) 广播接收器只有一个回调方法: void onReceive(Context curContext, Intent broadcastMsg) 当广播消息抵达接收器时,Android调用它的onReceive()方法并将包含消息的Intent对象传递给它。广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态。 拥有一个活跃状态的广播接收器的进程被保护起来而不会被杀死。但仅拥有失活状态组件的进程则会在其它进程需要它所占有的内存的时候随时被杀掉。 这种方式引出了一个问题:如果响应一个广播信息需要很长的一段时间,我们一般会将其纳入一个衍生的线程中去完成,而不是在主线程内完成它,从而保证用户交互过程的流畅。如果onReceive()衍生了一个线程并且返回,则包涵新线程在内的整个进程都被会判为失活状态(除非进程内的其它应用程序组件仍处于活跃状态),于是它就有可能被杀掉。这个问题的解决方法是令onReceive()启动一个新服务,并用其完成任务,于是系统就会知道进程中仍然在处理着工作。 下一节中,我们会讨论更多进程易误杀的问题。 进程与生命周期(Processes and lifecycles) Android系统会尽可能长的延续一个应用程序进程,但在内存过低的时候,仍然会不可避免需要移除旧的进程。为决定保留或移除一个进程,Android将每个进程都放入一个“重要性层次”中,依据则是它其中运行着的组件及其状态。重要性最低的进程首先被消灭,然后是较低的,依此类推。重要性共分五层,依据重要性列表如下: 1.前台进程是用户操作所必须的。当满足如下任一条件时,进程被认为是处于前台的: *它运行着正在与用户交互的activity(Activity对象的onResume()方法已被调用)。 *一个正在与用户交互的activity使用着它提供的一个服务。 *它包含着一个正在执行生命周期回调方法(onCreate()、onStart()或onDestroy())的Service对象。 *它包含着一个正在执行onReceive()方法的BroadcastReceiver对象。 任一时间下,仅有少数进程会处于前台,仅当内存实在无法供给它们维持同时运行时才会被杀死。一般来说,在这种情况下,设备已然处于使用虚拟内存的状态,必须要杀死一些前台进程以用户界面保持响应。 2.可视进程没有前台组件,但仍可被用户在屏幕上所见。当满足如下任一条件时,进程被认为是可视的: *它包含着一个不在前台,但仍然为用户可见的activity(它的onPause()方法被调用)。这种情况可能出现在以下情况:比如说,前台activity是一个对话框,而之前的activity位于其下并可以看到。 *它包含了一个绑定至一个可视的activity的服务。 可视进程依然被视为是很重要的,非到不杀死它们便无法维持前台进程运行时,才会被杀死。 3.服务进程是由startService()方法启动的服务,它不会变成上述两类。尽管服务进程不会直接为用户所见,但它们一般都在做着用户所关心的事情(比如在后台播放mp3或者从网上下载东西)。所以系统会尽量维持它们的运行,除非系统内存不足以维持前台进程和可视进程的运行需要。 4.背景进程包含目前不为用户所见的activity(Activity对象的onStop()方法已被调用)。这些进程与用户体验没有直接的联系,可以在任意时间被杀死以回收内存供前台进程、可视进程以及服务进程使用。一般来说,会有很多背景进程运行,所以它们一般存放于一个LRU(最后使用)列表中以确保最后被用户使用的activity最后被杀死。如果一个activity正确的实现了生命周期方法,并捕获了正确的状态,则杀死它的进程对用户体验不会有任何不良影响。 5.空进程不包含任何活动应用程序组件。这种进程存在的唯一原因是做为缓存以改善组件再次于其中运行时的启动时间。系统经常会杀死这种进程以保持进程缓存和系统内核缓存之间的平衡。 Android会依据进程中当前活跃组件的重要程度来尽可能高的估量一个进程的级别。比如说,如果一个进程中同时有一个服务和一个可视的activity,则进程会被判定为可视进程,而不是服务进程。 此外,一个进程的级别可能会由于其它进程依赖于它而升高。一个为其它进程提供服务的进程级别永远高于使用它服务的进程。比如说,如果A进程中的内容提供者为进程B中的客户端提供服务,或进程A中的服务为进程B中的组件所绑定,则A进程最低也会被视为与进程B拥有同样的重要性。 为运行着一个服务的进程重要级别总高于一个背景activity。所以一个activity以启动一个服务的方式启动一个长时间运行过程比简单的衍生一个线程来进行处理要好。尤其是当处理过程比activity本身存在时间要长的情况之下。我们以背景音乐播放和上传一个相机拍摄的图片至网站上为例。使用服务则不论activity发生何事,都至少可以保证操作拥有“服务进程”的权限。如上一节广播接收器生命周期所提到的,这也正是广播接收器使用服务,而不是使用线程来处理耗时任务的原因。 本文转自over140 51CTO博客,原文链接:http://blog.51cto.com/over140/582308,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android开发者指南(29) —— USB Host and Accessory

USB主从设备 Android支持各种USB外围设备,通过两种模式来支持Android USB外设(实现了Android外设协议的硬件):USB外设模式和USB主机模式。在USB外设模式下,外部USB硬件(装有Android的设备要连接的外部设备)充当USB主机。外设的例子包括机器人、扩展插座、诊断和音乐设备、电子报亭、读卡器等其他设备。这种模式给予不具备主机功能的Android设备以与USB硬件交互的能力。Android USB外设必须设计用来与装有Android的设备一起工作,并且必须遵循Android外设通讯协议。在USB主机模式下,装有Android的设备扮演着主机的角色。这种设备的例子包括数码像机,键盘,鼠标和游戏手柄。那些适应面很广的USB设备仍可以与Android应用交互,前提是这些Android应用可以正确的与这些设备通讯。 图1展示了两种模式的异同。当Android设备处于主机模式时,它扮演USB主机角色并为总线供电。当Android设备处于附件模式时,被连接的USB硬件(在这种情况下是一个Android USB附件)扮演主机角色并给总线供电。 图1. USB主从模式 USB外设和主机模式在Android 3.1 (API level 12)或更高的平台中直接支持。USB外设模式作为一个外设库也被回馈到Android 2.3.4 (API level 10)来支持更广泛的设备。设备厂商可以选择是否在设备的系统镜像中包含附加库。 注意:对USB主机和外设模式的支持最终取决于设备的硬件,不管平台的等级(是多少)。你可以通过<uses-feature>元素过滤那些支持USB主机和外设的设备。查看USB外设和主机文档获取更多详细信息。 调试注意事项 当调试那些使用了USB外设和主机特性的应用时,你很有可能把你的USB硬件连接到你的Android设备上,这将阻止你通过USB建立adb到Android设备的连接。你通过网络仍可以访问adb。通过网络连接adb: 通过USB将Android设备连接到电脑。 从SDK的platform-tools目录,在命令行输入adb tcpip 5555 输入:adb connect <设备的IP地址>:5555,你现在将被连接到Android设备并能像adb logcat一样发出通用的adb命令。 要设置你的设备监听USB,输入adb usb。 本文转自over140 51CTO博客,原文链接:http://blog.51cto.com/over140/716803,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android开发者指南(18) —— Web Apps Overview

Web Apps Overview 译者署名:happyjiahan 审核:铁骑_PuLee 版本:Android 3.2 r1 在android上发布一个应用程序一般有两种方式:一种是基于客户端模式(Client-Side模式)的应用程序(基于客户端的应用程序需要用AndroidSDK来开发,并且需要在用户的设备上安装一个以.apk为后缀名的文件),另一种是基于浏览器的web应用程序(基于浏览器的应用程序的开发需要遵循web标准,通过一个web浏览器来访问你开发的应用程序,不需要在用户的设备上安装其他任何程序)。 图1.你可以通过两种方式向用户提供你的web内容:一种是通过传统的浏览器的方式,另一种则是通过在Android的应用程序的布局文件中包含一个WebView组件的方式来实现。 那么在你的软件开发过程中,你究竟是应该选择基于客户端的模式(C/S)还是基于浏览器的模式(B/S)呢?其实这个问题要考虑很多个因素,要视你所开发的软件来确定选择哪种模式更合适。这不是我们当前讨论的重点,下面我们来看一下Android为我们提供了哪些方便我们进行web程序开发的支持吧! *支持一系列视窗属性,这些属性允许你根据屏幕的大小正确的确定你的web程序的窗口大小。 *支持css和javascript特性,这些特性能使你可以根据屏幕的像素密度来使用不同的样式和图片资源。 因此,在你决定为android开发一个web应用的时候,可以先不考虑支持多种屏幕方面的问题。因为让你的web页面在各种android设备的屏幕上有很好的效果已经很容易了。 Android提供的另外一个很好的特性就是你现在不必纯粹的在客户端或者纯粹的在web上构建你的应用,你可以将这两者融合在一起。你可以开发一个基于客户端的android应用,但是在这个应用中嵌入了一些web页面(你可以在你的android应用中使用WebView)。图1形象化的展示了你如何通过浏览器或者android应用程序来访问web页面。然而,你不应该开发一个android应用简单到只是为了运行web网站。与此相反,嵌入到你的android应用程序中的web页面应该是专门为某种应用场景设计的。你也可以在android应用程序和你的web页面之间定义一个接口,这个接口允许你web页面中的javascript调用你的android应用程序中的API。 从Android 1.0开始,WebView已经能够在android应用程序的布局文件中嵌入web内容并通过javascript调用android api。在android增加了对不同分辨率的屏幕的支持后,android 2.0在WebKit框架中添加了允许在网页中指定视窗属性的支持,并且能够查询屏幕的分辨率,这样就能够更好的修改上文提到的那些样式和图片资源。因为这些特性都是Android中WebKit框架的一部分,所以不管是Android浏览器还是WebView在视图接口和屏幕分辨率方面都具有相同的特性。 如果你想为Android设备开发web应用,你应该阅读下面的文档: Targeting Screens from Web Apps 如何让你的web应用能够非常合适的呈现在Android设备上,并且能够支持多种屏幕分辨率呢?如果你正在创建一个的web应用并且希望自己的应用至少能够在Android设备上运行(假设你的应用完全部署在网络上),特别是如果你针对的是移动终端或者打算使用WebView,那么这个文档介绍的信息对你来说非常重要。 Building Web Apps in WebView 如何使用WebView将网页嵌入到Android应用中以及如何使用JavaScript调用Android API。 Debugging Web Apps 如何使用JavaScript控制台API调试web应用。 Best Practices for Web Apps 它列举了一系列你应该遵循的实践技巧,帮助你创建出可以在Android设备上高效运行的web应用。 本文转自over140 51CTO博客,原文链接:http://blog.51cto.com/over140/628187,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android开发者指南(7) —— App Install Location

正文 自API Level 8开始,你可以允许你的应用安装至扩展存储(例如,SD卡)。这是一个可选功能,你可以在你应用的manifest属性android:installLocation里设定。如果你没设定这个属性,那么你的应用将被安装到内置存储,而且将不允许移动到扩展存储上。 为了允许系统可以在扩展存储上安装你的应用,修改你的manifest文件,在<manifest>元素中包含android:installLocation属性,设置其值为"preferExternal"或"auto"。例如: <manifestxmlns:android=http://schemas.android.com/apk/res/android android:installLocation="preferExternal" ...> 如果你定义了"preferExternal",意味着你要求你的应用安装至扩展存储,但是系统不能保证应用肯定会安装至扩展存储。如果扩展存储没有空间了,系统将把应用安装到内置存储。用户可以在两个位置之间移动你的应用。 如果你定义了"auto",表示你的应用可能会安装在扩展存储,但是对安装位置没有特别的偏好。系统将基于很多因素决定你的应用安装到哪里。用户同样可以将应用在两个位置之间移动。 当你的应用安装在扩展存储上: *只要扩展存储已经挂载在设备上,对应用的性能都没有影响。 *.apk文件保存在扩展存储上,但是所有的用户私有数据,数据库,优化过的.dex文件和释放的原生代码都保存在内置存储空间上。 *存储你应用的唯一容器是被一个随机生成的KEY加密存放的,仅仅能被最初安装的设备进行解密操作。因此,安装在SD卡上的应用仅仅针对一个设备可以工作。 *用户可以通过系统设置移动你的应用到内置存储。 警告:当用户启用USB大容量存储以共享文件给计算机或者通过系统设置卸载SD卡,外置存储从设备卸载并且所有运行在外置存储的应用立刻都被结束。 向后兼容Backward Compatibility 将你的应用安装至扩展存储的功能是运行API Level 8(Android 2.2)及以上版本的设备才有效的。使用API Level 8之前的版本编译的已存在的应用,将一直安装在内置存储,并且无法移动至扩展存储(即使设备上运行的是API Level 8版本的系统)。然而,如果你的应用计划支持低于8的API Level,你可以选择针对API Level 8及更高版本支持此特性,并且继续保持与低于API level 8的设备兼容。 为了允许安装在扩展存储并且保持与API Level 8或更低版本兼容: *在<manifest>元素中,包含值为"auto"或"preferExternal"的android:installLocation属性。 *继续保持你的android:minSdkVersion属性不变(小于8的值)并且确定你的应用代码只使用与此level保持兼容的API。 *为了编译你的应用,更改你的build target为API Level 8。这步操作是必须的,因为旧的Android库无法理解android:installLocation属性,并且当该属性存在时,也不会编译你的应用。 当你的应用安装到API Level低于8的设备上时,android:installLocation属性被忽略,并且应用会被安装至内置存储上。 注意:尽管XML标记,例如这个将被之前的平台忽略,但你还是要小心不要使用API Level 8中的编程API,除非你在你的代码中提供向后兼容。关于在应用代码中创建向后兼容的信息,请参考Backward Compatibility这篇文章。 不应当安装在扩展存储的应用 Applications That Should NOT Install on External Storage 当用户启用USB大容量存储来给他们的计算机共享文件时(卸载或移除扩展存储),任何安装在扩展存储上并正在运行的应用都会被结束。实际上此时系统并不知道应用程序的存在,直到大容量存储关闭,或者扩展存储重新挂载到设备上。除了杀死该应用程序使它对用户不可用,它还会使用更严重地方式中断某些类型的应用程序。为了使你的应用始终如你所期望的那样运行,当你使用了下面任何一种特性,那你就不应当允许你的应用安装到扩展存储上去,以避免产生当扩展存储被卸载时所导致的后果: 服务Services 当扩展存储被卸载时,你正在运行的Service将被结束并且不会再重新启动。你可以注册ACTION_EXTERNAL_APPLICATIONS_AVAILABLE广播(broadcast) Intent,当安装在扩展存储上的应用对系统重新有效时,会通知你的应用。在那个时候,你可以重新启动你的Service。 定时服务Alarm Services 你注册到AlarmManager的闹钟会被取消。当扩展存储重新挂载时,你必须手工重新注册。 输入法引擎Input Method Engines 你的输入法(IME)将被替换为默认输入法。当扩展存储重新挂载,用户可以打开系统设置以重新启用你的输入法。 壁纸Live Wallpapers 你正在运行的Live Wallpaper会被替换为默认的。当扩展存储被挂载时,用户可以重新选择Live Wallpaper。 Live Folders 你的Live Folder将被从home屏幕被移除。当扩展存储被挂载上时,用户可以重新添加Live Folder到Home界面。 应用程序部件App Widgets 你的App Widget将被从Home界面移除,当扩展存储被挂载时,在系统重置Home应用之前,用户将无法使用你的App Widget(通常直到系统重启)。 Account Managers 在扩展存储被挂载之前,你使用AccountManager创建的账户都是不可见的。 Sync Adapters 在扩展存储被挂载之前,你的AbstractThreadedSyncAdapter和所有相关的同步功能将无法工作。 Device Administrators 你的DeviceAdminReceiver和它的管理能力会被禁止,这会导致设备功能产生无法预料的结果,这种现象会持续到扩展存储重新挂载为止。 Broadcast Receivers listening for "boot completed" T系统在扩展存储挂载到设备前发送广播ACTION_BOOT_COMPLETED。所以如果你的应用安装到扩展存储上,它拥有也接收不到这个广播。 如果你的应用使用的上面列表中的任何一种特性,那你就不应该允许你的应用安装到扩展存储上去。默认情况下,系统将不允许你的应用安装至扩展存储,所以你不需要担心你已存在的应用。然而,如果你不确定你的应用是否永远不会安装到扩展存储上去,那么你可以通过定义android:installLocation值为"internalOnly"来确保其安装至内置存储。尽管这不会改变默认的行为,但它明确的指出,你的应用只会被安装在内置存储上并且作为提醒你和其他开发人员已经做出决定。 应当安装在扩展存储的应用 Applications That Should Install on External Storage 简单来说,任何没有使用上一章节功能列表中的应用安装在扩展存储上都是安全的。大型的游戏更是常见的应该允许安装至扩展存储的应用类型,因为游戏当处于非激活状态时,通常不需要提供额外的服务。当扩展存储无效后,游戏进程被结束,这并不会带来明显的影响,当存储重新有效后,用户可以重新启动游戏(假设游戏在正常的Activity lifecycle中保存了状态)。 如果你的应用的APK文件大小为几兆(M),那你就需要认真考虑是否启用应用安装至扩展存储了,这样的话用户可以保留他们的内置存储空间。 本文转自over140 51CTO博客,原文链接:http://blog.51cto.com/over140/582281,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android开发者指南(15) —— Managing Virtual Devices

管理虚拟设备 一个Android虚拟设备(AVD)就是一个仿真器配置。通过对硬件和软件配置进行定义,就能用Android仿真器来模拟一个实际的设备。 创建AVD最简单的方法就是使用图形化的AVD管理器。它既可以通过在Eclipse中点击Window > Android SDK and AVD Manager来启动,也可以通过在命令行中调用Android SDK的tools目录下的android工具来启动。 创建AVD也可以通过在命令行中给android工具传入适当的选项参数来实现。有关如何用这种方法来创建AVD的更多信息,请查阅从命令行管理虚拟设备。 一个AVD由以下内容组成: *一个硬件配置文件:它定义了虚拟设备的硬件功能。例如,可以定义该设备是否有一个摄像头,它是否使用一个物理的QWERTY键盘或拨号盘,它有多少内存,等等。 *映射到一个系统映像:你可以定义将要运行在虚拟设备上的Android平台的版本。你可以选择标准Android平台的一个版本,也可以选择被打包在SDK附加组件中的系统映像。 *其它选项:你可以指定仿真器运行此AVD时使用的皮肤,它可以让你控制屏幕尺寸,外观,等等。你还可以指定AVD使用的模拟SD卡。 *开发机器上的一个专用存储区域:设备的用户数据(被安装的应用程序,设置,等等)和模拟SD卡都存储在这个区域中。 基于想要模拟的设备类型,可以根据需要创建多个AVD。为了彻底地测试应用程序,需要为每个特定的设备配置都创建一个AVD(例如不同的屏幕尺寸和平台版本)。并在每个AVD上对应用程序进行测试,以确保其兼容性。 当你为AVD选择系统映像时,需要记住以下几点: *目标设备的API Level很重要,因为应用程序在一个低于所需API Level的系统映像上是不能运行的。应用程序所需的最低API Level由它的manifest文件中的minSdkVersion属性指定。有关系统API Level和应用程序minSdkVersion之间关系的更多信息,请查阅指定最小系统。 *至少创建一个AVD,其目标设备的API Level要高于应用程序所需。因为这样可以测试应用程序的向前兼容性。向前兼容性测试可以确保下载过你的应用程序的用户能够接收到系统更新,从而使你的应用程序能继续正常运行。 *如果你的应用程序在manifest文件中声明了uses-library元素,此应用程序就只能运行在提供了扩展库的系统映像中。如果你想在仿真器上运行应用程序,就需要追寻一个包含了所需库的AVD。通常,创建这样的AVD需要使用一个专用于此AVD平台的附加组件(例如,Google APIs附加组件包含了Google Maps库)。 要继续学习如何使用图形化工具管理AVD,请查阅用AVD管理器管理AVD。要继续学习如何在命令行管理AVD,请查阅从命令行管理AVD。 本文转自over140 51CTO博客,原文链接:http://blog.51cto.com/over140/598176,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android 开发者应该使用 FlatBuffers 替代 JSON ?

你可能会问,既然我们已经有很标准的JSON以及转换库比如GSON和Jackson,为什么还要使用新的工具呢? 不妨先试一下FlatBuffers,然后你就会发现它比JSON快得多。 FlatBuffers是什么? FlatBuffers是一个高效的跨平台序列化类库,可以在C++、C#、C、Go、Java、JavaScript、PHP和Python中使用。是Google开发的,是为了应用在游戏开发,以及其他注重性能的应用上。 为什么要使用FlatBuffers? 不需要解析/拆包就可以访问序列化数据 — FlatBuffers与其他库不同之处就在于它使用二进制缓冲文件来表示层次数据,这样它们就可以被直接访问而不需解析与拆包,同时还支持数据结构进化(前进、后退兼容性)。 内存高效速度快 — 访问数据时只需要访问内存中的缓冲区。它不需要多余的内存分配(至少在C++是这样,其他语言中可能会有变动)。FlatBuffers还适合配合 mmap或数据流使用,只需要缓冲区的一部分存储在内存中。访问时速度接近原结构访问,只有一点延迟(一种虚函数表vtable),是为了允许格式升级以 及可选字段。FlatBuffers适合那些花费了大量时间和空间(内存分配)来访问和构建序列化数据的项目,比如游戏以及其他对表现敏感的应用。可以参 考这里的基准。 灵活 — 由于有可选字段,你不但有很强的升级和回退兼容性(对于历史悠久的游戏尤其重要,不用为了每个版本升级所有数据),在选择要存储哪些数据以及设计数据结构时也很自由。 轻量的code footprint — FlatBuffers只需要很少量的生成代码,以及一个表示最小依赖的很小的头文件,很容易集成。细节上可以看上面的基准页。 强类型 — 编译时报错,而不需要自己写重复的容易出错的运行时检查。它可以自动生成有用的代码。 使用方便 — 生成的C++代码允许精简访问与构建代码。还有可选的用于实现图表解析、类似JSON的运行时字符串展示等功能的方法。(后者比JSON解析库更快,内存效率更高) 代码跨平台且没有依赖 — C++代码可以运行在任何近代的gcc/clang和VS2010上。同时还有用于测试和范例的构建文件(Android中.mk文件,其他平台是cmake文件)。 都有谁使用FlatBuffers? BobbleApp,印度第一贴图App。我们在BobbleApp中使用FlatBuffers后App的性能明显增强。 Cocos2d-x,第一开源移动游戏引擎,使用FlatBuffers来序列化所有的游戏数据。 Facebook使用FlatBuffers在Android App中进行客户端服务端的沟通。他们写了一篇文章来描述FlatBuffers是如何加速加载内容的。 Google的Fun Propulsion Labs在他们所有的库和游戏中大量使用FlatBuffers。 App性能有多大提高? 解析速度 解析一个20KB的JSON流(这差不多是BobbleApp的返回大小)需要35ms,超过了UI刷新间隔也就是16.6ms。如果解析JSON的话,我们就在滑动时就会因为要从磁盘加载缓存而导致掉帧(视觉上的卡顿)。 解析器初始化 一个JSON解析器需要先构建字段映射再进行解析,这会花100ms到200ms,很明显的拖缓App启动时间。 垃圾回收 在解析JSON时创建了很多小对象,在我们的试验中,解析20kb的JSON流时,要分配大约100kb的瞬时存储,对Java内存回收造成很大压力。 FlatBuffers vs JSON 我尝试使用FlatBuffers和JSON解析4mb的JSON文件。 FlatBuffers花了1-5ms,JSON花了大约2000ms。在使用FlatBuffers期间Android App中没有GC,而在使用JSON时发生了很多次GC。在使用JSON时UI完全卡住,所以真实使用时只能在后台线程进行解析。 如何使用FlatBuffer呢? 我在我的GitHub中写了一个示例,里面手把手教你如何使用FlatBuffer。 文章转载自 开源中国社区[http://www.oschina.net]

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册