以下是Android基础知识笔记。
添加ActionBar
ActionBar的覆盖叠加
问题:当本人添加
传入的intent数据不显示。
兼容不同的设备
使用字符串替代资源实现支持多国语言
在res/中创建一个额外的values目录,以连字符和ISO国家代码结尾命名。使用字符资源
在xml文件中,通过元素的name属性来引用字符串资源。 在源代码中可以通过R.string.
语法来引用字符串资源。 适配不同的屏幕
创建不同的layout
创建不同的bitmap
适配不同的系统版本
确保新版本的app支持90%设备的使用。指定最小的目标API级别
在运行时检查系统版本
管理Activity的生命周期
指定你的程序首次启动的Activity
系统会调用你的app里面的被声明为“launcher”(or”main“)activity中的onCreate()方法。
如果你的程序没有一个activity声明了
MAIN action
或者LAUNCHER
category,那么在设备的主界面列表不会呈现你的app图标。
创建一个新的实例
问题:示例代码有问题。
- 无论如何,系统会调用新的activity实例中的onCreate()方法。
- onCreate里面尽量少做事情,避免程序启动太久都看不到界面。
- onStart之后开始被用户可见,但是onResume()会迅速执行,使activity停留在Resume()状态。
销毁Activity
- onDestroy()作为activity要从系统中完全移除的信号。
- 在onCreate创建了后台线程,以及可能导致内存泄露的资源等,你就应该在onDestroy()时杀死他们。
- 如果activity只是做简单的逻辑跳转功能,它用来决定跳转到哪的一个activity,那么,在onCreate调用finish方法,会让系统直接跳过其他方法执行onDestroy方法。
暂停与恢复Activity
- 被其它组件阻塞的情况下,只要activity部分可见,之前的activity一直处在Paused状态。
- 如果这个activity完全阻塞不可见,它会进入Stop状态。
- 你应该实现onResume()来初始化那些你在onPause方法中释放掉的组件。
停止与重启Activity初探
- 出现的一些关键场景:
- 使用app时,接到一个电话。
- 启动新的Activity时,当前的Activity会stop,点击back后,第一个会被重启。
- app在最近的app菜单,通过手机界面图标启动或者最近的程序中启动。
- 无论什么原因导致Activity停止,系统总是会在onStop()之前调用onPause方法的。
- 调用onRestart后会迅速调用onStart和onResume方法。
停止你的activity
- 避免内存泄露,为什么会内存泄露?因为极端情况下,在不需要activity时会摧毁,摧毁的时候不执行onDestroy方法。所以你需要在onStop()方法中释放资源。
启动与重启你的activity
- onRestart()方法只是在activity从stopped状态恢复时才会被调用。
- 因为回到activity之前需要过一段时间,所以onStart()方法是一个比较好的来验证某些系统特性是否可用方法。
重新创建Activity
- 正常destory的场景:
- 通过返回按钮或者通过调用finish()。
- 处于stop状态长期不用。
- 前台需要更多的系统资源所以关闭后台进程。
- 如果系统不是主动结束的时候(姑且认为是主动,比如按回按钮,调用finish()等),系统在销毁之前会保存记录数据(这些数据可能是描述状态用的),这是一个存放在Bunlde对象中的键值对。
- 重建的时候,可能需要更多的之前状态的信息,首先,为了恢复View的状态,需要为每个View指定一个ID。
- 这里有一个很”神秘”的回调函数,需要重写。这个方法叫做:onSaveinstanceState(),只要系统离开activity的时候,就会调用这个函数,把信息传递给Bundle对象,当重建的时候,就会调用onRestoreState()方法。
- 执行onSaveInstanceState()的场景:
- 跳转到其他activity
- 点击Home
- back不会出现这种情况,因为这属于退栈,不会执行该函数,因为通常认为,不会出现这种操作。
- 恢复数据可以在onCreate()和onRestoreInstanceState()实现,前者总是在后者前执行。
##使用Fragment建立动态UI
Fragment像一个嵌套的activity,拥有自己的布局以及管理自己的生命周期。在activity中可以与其他的fragments生成不同的组合。
创建一个Fragment
- 拥有生命周期,接受自己的输入事件,运行过程中添加移除。
- API版本大于11可以不使用Support Library,也可以使用v7 appcompat library.
- 和activity的区别之一:你必须重写onCreateView()回调方法,这是唯一一个需要重写的回调方法。
- 用XML将fragment添加到activity.可以在activity的XML布局文件中定义。
- API11版本以下的系统需要activity继承FragmentActivity,而如果版本大于等于11,你可以使用普通的Activity.
- 如果使用的是v7 appcompat library,需要继承ActionBarActivity,后者是FragmentActivity的一个子类。
- 如果是使用XML布局文件的方式添加进activity,是不可以动态移除的。如果是要交互的时候把fragment切入与切出,必须在activity启动后添加。
- 当屏幕是large时,按目录中的large字符来区分。
建立灵活动态的UI
- 支持范围广泛的屏幕尺寸时,可以重用fragment.
- FragmentManager可以动态的添加移除fragment.
- 如果你打算在activity的生命周期中替换fragment,可以在activity运行时动态添加fragment.
- 使用FragmentManager创建FragmentTransaction对象。
- fragment的layout在view中。
- 添加fragment的具体步骤:
- 程序中首先确定activity的布局使用了FragmeLayout.
- 用
saveInstanceState != null
判断是否存了先前的状态。 - 在activity的onCreate方法初始化fragment实例。
- 将activity的Intent参数传给生成的Fragment对象。
- 将Fragment对象添加到布局文件中。
- fragment替换的具体步骤:
- 将add()替换成replace()
- 为了使客户能向后导航和撤销,可以用FragmentTRansaction提交前调用addTbackStack()方法,该方法可以传参给这次更改的事务命名(String类型)。
- fragments之间的交互:
- fragment之间不应该直接交互,应该通过关联的activity.
- 在fragment中定义一个接口,用onAttach()来捕获activity的接口实现。
- 在fragment中有各种触发事件的类,在这些触发事件的类中调用前面的接口实现。
- 消息传递给Fragment
- 使用findFragmentById()获取Fragment的实例。
- 直接调用Fragment的public方法向Fragment传递消息。(比如一个activity中的另一个Fragment).
数据保存
通过activity那一部分可以了解到,onPause()是可以保存一些信息的。但是Android也有一些专门保存数据的方法。
保存到Preference
- SharedPreferences可以用来保存键值对量级的需求,他可以是私有的,也可以是共享的状态。
- 不要与preference混淆,后者帮助设置用户配置的页面。
- getPreference()方法如果省略了第一个参数(activity的shared preference文件),这个方法会默认检索该activity下的该文件。
- 如果设置了Context.MODE_PRIVATE,表示这个文件只能这个app访问了。如果输设置为MODE_WORLD_READABLE或者MODE_WORLD_WRITEABLE模式,那么任何app知道文件名就能访问。
- 写文件,可以通过edit()创建SharedPreferences.Editor来实现。
- 提交使用apply()方法来替代commit(),可能阻塞UI线程,但是如果不出于线程同步的要求可以使用。
- 读文件,用getInt()和getString()方法来读取。在读取时,存在一个默认的value作为查找的key不存在时函数的返回值,default.
保存到文件
应用场景:网络中交换的数据,图片文件。
- 两个存储区域:
- Internal storage:1.总是可用;2.只能被app访问;3.卸载app后相关文件都清楚干净;4.如果你希望不被其它用户以及其它app访问数据,应该保存在这里。
- External storage:1.并非总是可用,比如通过USB挂在的时候;2.都可以访问的区域,所以你不能拥有绝对的控制权;3.卸载app时,仅仅会删除external根目录下的相关文件(getExternalFilesDir()).
- app默认安装在internal storage,但是可以在manifest中声明android:install.ocation属性来安装到external storage中。
- 获得写External存储权限:在manifest文件中请求权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 尽量声明读权限,尽管在现有版本并非必要。
- getFileDir()代表app的internal目录,getCacheDir()返回缓存目录,并提供了一些方法对文件进行存储。
- 保存文件到External Storage,因为可能挂在,所以应该先查询状态,用getExternalStorageState()方法来实现。返回的是MEDIA_MOUNTED的话,表示可以读写。
- external storage目录对用户和其它app来说有两种:
- Public files:卸载后应该保留。特定方法getExternalStoragePublicDirectory()方法实现,有两个可指定的参数分别是DIRECTORY_MUSIC以及DIRECTORY_PICTURES.
- Private files:卸载后应该随之删除。
- 应该手动删除所有通过getCacheDir()方式创建的缓存文件。
保存到数据库
相较于前两种存储方法,数据库方便存储结构化的数据。大多数都有APIs封装好了。
- 由于保存的区域默认是私有的,所以android会保存db到你的程序private的空间上。
- 通过实现BaseColumns接口,可帮助您的数据库与 Android 框架协调工作。
- 要使用SQLiteOpenHelper,需要创建一个替代onCreate()、onUpgrade()和 onOpen()回调方法的子类。也可以通过版本号结合onDowngrade()来更新数据库。
- 通过将一个ContentValues对象传递至insert()将数据插入数据库。
- 要从数据库中读取信息,需使用query()方法,查询使用Cursor对象来返回。
与其它应用交互
Intent的发送
- 一个Intent可以显示的指明需要启动的模块,也可以隐式的指明处理那种类型的操作。
- 带有action的intent能使当前app跳转到其他app。有的时候你并不知道对方组件的名字,这个时候可以使用隐式的intent.
- 例如:Intent callIntent = new Intent(Intent.ACTION_DIAL, number)样式。
- 如果没有用Uri,则可以使用setType()方法来指定intent附带的数据类型。
- 在API Level 14以上可以使用intent for Calendar.
- 设MIME type 为哪activity应该这intent.
- 应该处罚intent之前验证是否有App接受这个intent,如果没有app接收,那么app会crash.
- 要确认可以安全的使用这个intent,需要使用queryIntentActivities()获得一个能接收的list,然后通过数组的大小来查看是不是为空。
- 通过前面的步骤后,最后一步,启动app,运行startActivity(intent).
Intent过滤
你的app如果需要在别人分享时出现在待选对话框,那么你的app应该能接收ACTION_SEND的Intent.
- 必须定义好action和data,如果activity中的intent filter满足以下对象的标准,那么就可以:
- Action,在intent filt用
指定它的值,值为字符串。这些值都是已经定义好了的。 - Data,可以是多个属性。
- Category,一般不怎么用得到,都有一个默认值,这主要是和用户的手势以及启动位置有关。
- 如果出现了能响应ACTION_SEND和ACTION_SENDTO两种“name”,那么你必须用两个filter来分开表示。
- Action,在intent filt用
- 获取action,并且读取intent的内容。方法是getIntent().
- 可以在任何生命周期使用getIntent(),但是最好是在onCreat()和onStart()执行。
- 返回Result,使用setResult(),并且总是指定result code.通常是RESULT_OK或者RESULT_CANCELED.
探究碎片
动态添加碎片
实现一个例子,用两个fragment对activity进行分屏,左边的fragment上面有一个按钮,通过按下这个按钮,将右边的fragment进行替换。
事实上,这个按钮还是注册在activity上面的,按照书中所说,动态加载fragment需要5步:
- 创建碎片实例;
- 获得fragmentManager对象;
- 用这个对象开启一个事务;
- 通过这个事务的replace()方法替换实例;
- 提交事务;
通过这个实验,产生了疑问,fragment到底是替换还是覆盖??因为替换为第三个fragment后,出现了一个现象,那就是前一个fragment的一部分图片仍然显示,背景颜色也是原有颜色。后来将新的fragment的颜色改为了黄色,这个时候才没有出现前一个fragment.如图所示:
.
碎片中模拟返回栈
在前面的例子中,如果按下back键程序会直接退出。添加生成的事务的方法addTobackStack(null);
但是我在我的模拟器上试验没有成功。
碎片的生命周期
- 运行状态
碎片可见,相关联的活动处于运行状态,则该碎片处于运行状态。 - 暂停状态
当一个活动进入暂停状态,他所关联的所见碎片都会进入暂停状态。 - 停止状态
当一个活动进入停止状态时,关联的碎片都进入到了停止状态,或者通过remove()和replace()方法将碎片移除,然后在事务提交之前调用addTobackStack()方法,也会进入停止状态。 - 销毁状态
如果关联的activity销毁了,name这些碎片也销毁。如果在事务提交之前没有调用addToBackStack(),也会进入到销毁状态。
详解广播机制
- 标准广播
- 有序广播
接受广播
- 在代码中注册,动态注册
- 在AndroidManifest.xml中注册,静态注册
- 动态注册的广播接收器都要取消注册
- 访问一些关键信息需要在配置文件中声明才可以
- 静态注册的缺点是程序启动了才能收到广播,这样不能进行一些接受操作,比如要实现开机自启动
内容提供器
内容提供器主要用于在不同的应用程序之间实现数据共享的功能,允许一个程序访问另一个程序中的数据,同时能保证数据的安全性。
- 一种是现有的内容提供器
- 一种是自己创建的内容提供器
- 具体操作有一套现有的APIs,充分利用了ContentResolve类
探究服务
适合于去执行不需要和用户交互而且要长期运行的任务。
服务不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程,所以程序进程被杀掉了,那么该进程的服务也会停止。
多线程
- 用Runnable()方法启动线程的方式是:new Thread(yourThread).start()
- Android不允许在子线程对UI进行操作
- 解析异步消息处理机制的四个方法
- Message:可以携带少量消息,有what字段,arg1,arg2还有obj…
- Handler:处理和发送消息
- MessageQueue:消息队列,每个线程只会有一个MessageQueue对象
- Looper:进行无限循环,去发现并取出MessageQueue中的消息
- AsyncTask:
服务基本用法
- onCreate():在服务创建时调用
- onStartCommand():在每次服务启动时调用,一旦启动就立即执行操作的话,可以使用这个
- onDestroy():在服务销毁的时候调用
- 服务需要在AndroidManifest.xml中注册才能生效,四大组件都需要这样
- 启动服务
- 启动服务很简单,只要配合好Intent就行了,类似启动另一个Activity,但是这里使用startService()
停止服务: - 同样的,如果停止服务,使用stopService()。服务也可以自己停下来,只要在自身任何位置调用stopSelf()方法就可以停止这个服务。
- 启动服务很简单,只要配合好Intent就行了,类似启动另一个Activity,但是这里使用startService()
- onCreate和onStartCommand的区别??我觉得类似Activity中的onCreate和onStart.如果没有创讲过服务,那么两者都要执行。而服务创建好后,多次点击启动的那个button,只执行后者了
- 服务是在活动里启动的,但是启动了服务后,活动与服务基本就没什么关系了。当我把Activity destroy以后,具体看图(服务1):
以及图(服务2):
从图(服务00)和图(服务01)可以看出,后台中还有一个服务在跑,中间其实经过了一次小小的重新创建,process曾经变为了0,为了证实这一点,可以看图(服务3)和图(服务03)的结果,图(服务3)如下:
图(服务4):
你会发现,当我关掉Activity后,onCreate()又执行了一次。
网络技术
WebView的用法
首先研究基本的语法,在layout中加入一个WebView的控件,这个很容易,其次是在设置文件中添加一个permission权限,来访问网络功能,接下来主要分析一下activity中的代码:
这这几行代码中,setJavaScriptEnabled()使WebView支持JavaScript脚本。
webView.loadUrl(“http://www.baidu.com");使得立即加载百度首页页面。
这里使用WebViewClient的作用是希望目标页面能在WebView直接显示,而不需要打开其它的浏览器,具体的做法是把方法的返回值设为true.当然,前面会运用view.loadUrl(url)来让函数传入参数后再加载新的网页。
使用HttpURLConnection
这里举一个例子,用一个Button控件来发送请求,然后通过HTTP协议接收目标网站响应的内容,并呈现在界面上。这里主要关注sendRequestWithHttpURLConnection()方法:
现在来分析一下这段代码:
- openConnection():用来获得connection实例
- setRequestMethod():设置请求方法,通常有post和get
- setConnectTimeout()和setReadTimeout():分别设置链接超时和读取超时
- getInputStream():获取服务器返回的输入流
后面就是对输入流进行读取了,然后就是把返回的信息保存在Message实例中
使用HttpClient
Android中另一种发送请求的方式是采用HttpClient.但是这种方法主要是在Android api9以下采用,Apache公司维护它的热情也不高。
解析XML格式数据
网络传输时最常用的格式有两种,分别是XML和JSON.
- Pull解析方式
在书上,作者采用了Apache服务器,这里我们简单的实用Django自带的服务器进行测试。
在Django工程目录下新建一个static文件夹,然后在该文件夹下新建一个get_data.xml,填写XML格式的内容:
浏览器显示如图所示(Safari呈现):
这里我们继续使用HttpURLConnection的那个项目开发,重点关注XML的解析。
在genymotion中指向localhost的地址不是10.0.2.2,而是10.0.3.2…
在原有的代码中,我们获得了一个叫response的String对象,然后将该对象传入一个叫做parseXMLWithPull()的方法中,用该方法来解析XML文件。
我们具体来看parseXMLWithPull()方法是如何来解析的:
- XmlPullParserFactory:这个类用于创建XmlPull解析器
- XmlPullParser:这是一个定义解析功能的接口,通过多态,和1中的类一起产生一个XmlPullParser的对象
- setInput()方法:将获得的Xml数据设置到2中产生的对象,以进行解析。
- getEventType():获得解析事件,一些基本的事件如下:
- START_DOCUMENT:判断事件为文档开始事件
- END_DOCUMENT:判断事件为文档结束事件
- START_TAG:标签文件开始事件
- END_TAG:标签文件结束事件
- TEXT:文本事件
解析JSON格式数据
JSON的优点是体积小,省流量,但是缺点是语义性差。
这里简单说说使用GSON来解析数据。
首先安装GSON插件,如图所示:
如果是单个GSON数据,可以有如下示例:
12Gson gson = new Gson();Person person = gson.fromJson(jsonData, Person.class);如果是一个GSON数组,可以有如下示例:
1List<Person> people = gson.fromJson(jsonData, new TypeToken<List<Person>>() {}.getType());
这样就生成了对象的一个List了。
参考
pull的详细原理:http://www.android100.org/html/201406/07/20575.html
GSON简单示例:http://blog.csdn.net/dakaring/article/details/46300963