您的位置:首页 > 移动开发 > Android开发

Android 笔记:2015.09.23

2015-09-23 14:36 337 查看
大多数 app并不需要实现这个方法,因为局部类的references会随着activity的销毁而销毁,并且我们的activity应该在onPause()与onStop()中执行清除activity资源的操作。然而,如果activity含有在onCreate调用时创建的后台线程,或者是其他有可能导致内存泄漏的资源,则应该在OnDestroy()时进行资源清理,杀死后台线程。

因为系统在activity停止时会在内存中保存Activity的实例,所以有时不需要实现onStop(),onRestart()甚至是onStart()方法. 因为大多数的activity相对比较简单,activity会自己停止与重启,我们只需要使用onPause()来停止正在运行的动作并断开系统资源链接。

当activity调用onStop()方法, activity不再可见,并且应该释放那些不再需要的所有资源。一旦activity停止了,系统会在需要内存空间时摧毁它的实例(和栈结构有关,通常back操作会导致前一个activity被销毁)。极端情况下,系统会直接杀死我们的app进程,并不执行activity的onDestroy()回调方法, 因此我们需要使用onStop()来释放资源,从而避免内存泄漏。(这点需要注意)

onRestart()方法则是只在activity从stopped状态恢复时才会被调用,因此我们可以使用它来执行一些特殊的恢复(restoration)工作,请注意之前是被stopped而不是destrory。

使用onRestart()来恢复activity状态是不太常见的,因此对于这个方法如何使用没有任何的guidelines。然而,因为onStop()方法应该做清除所有activity资源的操作,我们需要在重启activtiy时重新实例化那些被清除的资源,同样, 我们也需要在activity第一次创建时实例化那些资源。

介于上面的原因,应该使用onStart()作为onStop()所对应方法。因为系统会在创建activity与从停止状态重启activity时都会调用onStart()。也就是说,我们在onStop里面做了哪些清除的操作,就该在onStart里面重新把那些清除掉的资源重新创建出来。

比如你动态注册一个广播接收者,只有此 Activity 在前台时才有意义(例如,接到广播后会弹出一个对话框),此时就需要在 onStart 中注册广播,在 onStop 中解除广播注册。

Caution: 你的Activity会在每次旋转屏幕时被destroyed与recreated。当屏幕改变方向时,系统会Destory与Recreate前台的activity,因为屏幕配置被改变,你的Activity可能需要加载另一些替代的资源(例如layout).

(通常来说,跳转到其他的activity或者是点击Home都会导致当前的activity执行onSaveInstanceState,因为这种情况下的activity都是有可能会被destory并且是需要保存状态以便后续恢复使用的,而从跳转的activity点击back回到前一个activity,那么跳转前的activity是执行退栈的操作,所以这种情况下是不会执行onSaveInstanceState的,因为这个activity不可能存在需要重建的操作)

onRestoreInstanceState()方法会在 onStart() 方法之后执行. 系统仅仅会在存在需要恢复的状态信息时才会调用 onRestoreInstanceState() ,因此不需要检查 Bundle 是否为null。

记住在执行fragment事务时,如移除或者替换,我们经常要适当地让用户可以向后导航与"撤销"这次改变。为了让用户向后导航fragment事务,我们必须在FragmentTransaction提交前调用addToBackStack()方法。

Note:当移除或者替换一个fragment并把它放入返回栈中时,被移除的fragment的生命周期是stopped(不是destoryed).当用户返回重新恢复这个fragment,它的生命周期是restarts。如果没有把fragment放入返回栈中,那么当它被移除或者替换时,其生命周期是destoryed。

通常fragment之间可能会需要交互,比如基于用户事件改变fragment的内容。所有fragment之间的交互需要通过他们关联的activity,两个fragment之间不应该直接交互。

getSharedPreferences() — 如果需要多个通过名称参数来区分的shared preference文件, 名称可以通过第一个参数来指定。可在app中通过任何一个Context 执行该方法。

getPreferences() — 当activity仅需要一个shared preference文件时。因为该方法会检索activity下默认的shared preference文件,并不需要提供文件名称。

所有的Android设备均有两个文件存储区域:"internal" 与 "external" 。 这两个名称来自于早先的Android系统,当时大多设备都内置了不可变的内存(internal storage)及一个类似于SD card(external storage)这样的可卸载的存储部件。之后有一些设备将"internal" 与 "external" 都做成了不可卸载的内置存储,虽然如此,但是这一整块还是从逻辑上有被划分为"internal"与"external"的。只是现在不再以是否可卸载进行区分了。

getFilesDir() : 返回一个File,代表了我们app的internal目录。

getCacheDir() : 返回一个File,代表了我们app的internal缓存目录。请确保这个目录下的文件能够在一旦不再需要的时候马上被删除,并对其大小进行合理限制,例如1MB 。系统的内部存储空间不够时,会自行选择删除缓存文件。

如果需要缓存一些文件,可以使用createTempFile()。

无论是使用 getExternalStoragePublicDirectory() 来存储可以共享的文件,还是使用 getExternalFilesDir() 来储存那些对于我们的app来说是私有的文件,有一点很重要,那就是要使用那些类似DIRECTORY_PICTURES 的API的常量。那些目录类型参数可以确保那些文件被系统正确的对待。例如,那些以DIRECTORY_RINGTONES 类型保存的文件就会被系统的media scanner认为是ringtone而不是音乐。

-- http://hukai.me/android-training-course-in-chinese/basics/data-storage/files.html
如果事先知道想要保存的文件大小,可以通过执行getFreeSpace() or getTotalSpace() 来判断是否有足够的空间来保存文件,从而避免发生IOException。那些方法提供了当前可用的空间还有存储系统的总容量。

然而,系统并不能保证可以写入通过getFreeSpace()查询到的容量文件, 如果查询的剩余容量比我们的文件大小多几MB,或者说文件系统使用率还不足90%,这样则可以继续进行写的操作,否则最好不要写进去。

Note:并没有强制要求在写文件之前去检查剩余容量。我们可以尝试先做写的动作,然后通过捕获 IOException 。这种做法仅适合于事先并不知道想要写的文件的确切大小。例如,如果在把PNG图片转换成JPEG之前,我们并不知道最终生成的图片大小是多少。

在不需要使用某些文件的时候应删除它。删除文件最直接的方法是直接执行文件的delete()方法。

myFile.delete();
如果文件是保存在internal storage,我们可以通过Context来访问并通过执行deleteFile()进行删除

myContext.deleteFile(fileName);

Note: 当用户卸载我们的app时,android系统会删除以下文件:

所有保存到internal storage的文件。

所有使用getExternalFilesDir()方式保存在external storage的文件。

然而,通常来说,我们应该手动删除所有通过 getCacheDir() 方式创建的缓存文件,以及那些不会再用到的文件。

SQL Injection:(随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但由于程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码时没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入)

至于另外一些需要extra数据的implicit intent,我们可以使用 putExtra() 方法来添加那些数据。 默认的,系统会根据Uri数据类型来决定需要哪些合适的MIME type。如果我们没有在intent中包含一个Uri, 则通常需要使用 setType() 方法来指定intent附带的数据类型。设置MIME type 是为了指定应该接受这个intent的activity。

Note: 请尽可能的将Intent定义的更加确切。例如,如果想要使用ACTION_VIEW 的intent来显示一张图片,则还应该指定 MIME type 为image/*.这样能够阻止其他能够 "查看" 其他数据类型的app(比如一个地图app) 被这个intent叫起。

尽管Android系统会确保每一个确定的intent会被系统内置的app(such as the Phone, Email, or Calendar app)之一接收,但是我们还是应该在触发一个intent之前做验证是否有App接受这个intent的步骤。

Caution: 如果触发了一个intent,而且没有任何一个app会去接收这个intent,则app会crash。

为了验证是否有合适的activity会响应这个intent,需要执行queryIntentActivities() 来获取到能够接收这个intent的所有activity的list。若返回的List非空,那么我们才可以安全的使用这个intent。例如:

PackageManager packageManager = getPackageManager();

List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);

boolean isIntentSafe = activities.size() > 0;
若activity中的intent filter满足以下intent对象的标准,系统就能够把特定的intent发送给activity:

Action:一个想要执行的动作的名称。通常是系统已经定义好的值,如ACTION_SEND或ACTION_VIEW。 在intent filter中通过<action>指定它的值,值的类型必须为字符串,而不是API中的常量(看下面的例子)

Data:Intent附带数据的描述。在intent filter中通过<data>指定它的值,可以使用一个或者多个属性,我们可以只定义MIME type或者是只指定URI prefix,也可以只定义一个URI scheme,或者是他们综合使用。

Category:提供一个附加的方法来标识这个activity能够handle的intent。通常与用户的手势或者是启动位置有关。系统有支持几种不同的categories,但是大多数都很少用到。而且,所有的implicit intents都默认是 CATEGORY_DEFAULT 类型的。在intent filter中用<category>指定它的值。

Note:为了接受implicit intents, 必须在我们的intent filter中包含 CATEGORY_DEFAULT 的category。startActivity()和startActivityForResult()方法将所有intent视为声明了CATEGORY_DEFAULT category。如果没有在的intent filter中声明CATEGORY_DEFAULT,activity将无法对implicit intent做出响应。

如果只是纯粹想要返回一个int来表示某些返回的result数据之一,则可以设置result code为任何大于0的数值。如果我们返回的result只是一个int,那么连intent都可以不需要返回了,可以调用setResult()然后只传递result code如下:

setResult(RESULT_COLOR_RED);
finish();
Note:我们没有必要在意自己的activity是被用startActivity() 还是 startActivityForResult()方法所叫起的。系统会自动去判断该如何传递result。在不需要的result的case下,result会被自动忽略。

接收的程序需要有访问URI资源的权限。下面有一些方法来处理这个问题:

将数据存储在ContentProvider中,确保其他程序有访问provider的权限。较好的提供访问权限的方法是使用 per-URI permissions,其对接收程序而言是只是暂时拥有该许可权限。类似于这样创建ContentProvider的一种简单的方法是使用FileProvider helper类。

使用MediaStore系统。MediaStore系统主要用于音视频及图片的MIME类型。但在Android3.0之后,其也可以用于存储非多媒体类型。

要插入每一个页面的子视图,你需要把这个layout与PagerAdapter挂钩。有两种adapter(适配器)你可以用:

FragmentPagerAdapter

在同级屏幕(sibling screen)只有少量的几个固定页面时,使用这个最好。

FragmentStatePagerAdapter

当根据对象集的数量来划分页面,即一开始页面的数量未确定时,使用这个最好。当用户切换到其他页面时,fragment会被销毁来降低内存消耗。

为了让Android Developer Tools能够识别你的view,你必须至少提供一个constructor,它包含一个Contenx与一个AttributeSet对象作为参数。这个constructor允许layout editor创建并编辑你的view的实例。

styleable实例的名字,通常与自定义的view名字一致。尽管这并没有严格规定要遵守这个convention,但是许多流行的代码编辑器都依靠这个命名规则来提供statement completion。

为了避免输入长串的namespace名字,示例上面使用了xmlns指令,这个指令可以指派custom作为http://schemas.android.com/apk/res/com.example.customviewsnamespace的别名。

请注意,如果你的view是一个inner class,你必须指定这个view的outer class。同样的,如果PieChart有一个inner class叫做PieView。为了使用这个类中自设的属性,你应该使用com.example.customviews.charting.PieChart$PieView.

TypedArray对象是一个共享资源,必须被在使用后进行回收。

Attributes是一个强大的控制view的行为与外观的方法,但是他们仅仅能够在view被初始化的时候被读取到。为了提供一个动态的行为,需要暴露出一些合适的getter 与setter方法。

请注意,在setShowText方法里面有调用invalidate()) and requestLayout()). 这两个调用是确保稳定运行的关键。当view的某些内容发生变化的时候,需要调用invalidate来通知系统对这个view进行redraw,当某些元素变化会引起组件大小变化时,需要调用requestLayout方法。调用时若忘了这两个方法,将会导致hard-to-find bugs。

注意<service>标签并没有包含任何intent filter。因为发送任务给IntentService的Activity需要使用显式Intent,所以不需要filter。这也意味着只有在同一个app或者其他使用同一个UserID的组件才能够访问到这个Service。

避免频繁调用 findViewById() 的方法之一,就是使用 View Holder(视图占位符)设计模式。

Handler和AsyncTask:这俩类都是用来实现异步的,其中AsyncTask的集成度较高,使用简单,Handler则需要手动写Runnable或者Thread的代码;另外,由于AsyncTask内部实现了一个非常简单的线程池,实际上是只适用于轻量级的异步操作的,一般不应该用于网络操作。

ProGuard会对我们的代码进行检索,删除一些无用的代码,并且会对类、字段、方法等进行重命名,重命名之后的类、字段和方法名都会比原来简短很多,这样的话也就对内存的占用变得更少了。”

导致内存泄漏最主要的原因就是某些长存对象持有了一些其它应该被回收的对象的引用,导致垃圾回收器无法去回收掉这些对象,那也就出现内存泄漏了。比如说像Activity这样的系统组件,它又会包含很多的控件甚至是图片,如果它无法被垃圾回收器回收掉的话,那就算是比较严重的内存泄漏情况了。

我们还可以覆写titlebar中的任何一个layout属性,如layout_gravity、layout_margin等,而非layout属性则无法在<include>标签当中进行覆写。另外需要注意的是,如果我们想要在<include>标签当中覆写layout属性,必须要将layout_width和layout_height这两个属性也进行覆写,否则覆写效果将不会生效。

注意这里我对ViewStub的实例进行了一个非空判断,这是因为ViewStub在XML中定义的id只在一开始有效,一旦ViewStub中指定的布局加载之后,这个id也就失败了,那么此时findViewById()得到的值也会是空。

策略模式用接口实现算法的扩展性。

模板方法模式用继承的方式实现方法的扩展性。

每一个Library和每一个App都是单独的Project。

根据Gradle的要求,每一个Project在其根目录下都需要有一个build.gradle。

build.gradle文件就是该Project的编译脚本,类似于Makefile。

如果选择的Minimum Required SDK为8,而Target SDK大于它的话,系统会自动在项目中导入Support v4包;

tools:context="activity name"这一句不会被打包进APK。

只是ADT的Layout Editor在你当前的Layout文件里面设置对应的渲染上下文,说明你当前的Layout所在的渲染上下文是activity name对应的那个activity,如果这个activity在manifest文件中设置了Theme,那么ADT的Layout Editor会根据这个Theme来渲染你当前的Layout。

就是说如果你设置的MainActivity设置了一个Theme.Light(其他的也可以),那么你在可视化布局管理器里面看到的背景阿控件阿什么的就应该是Theme.Light的样子。仅用于给你看所见即所得的效果而已。

尽管Fragment对象独立于activity对象,它可以被使用于多个activity中,一个给定的fragment实例还是直接被绑定到包含它的activity实例中的。

在fragment中通过getActivity()可以获取activity实例,然后与activity中的成员进行交互操作。例如:

View listView = getActivity().findViewById(R.id.list);


同样的,activity可以通过FragmentManager获取fragment的引用。例如:

ExampleFragment fragment = (ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: