androidBasic
一、 概述
android是一种基于Linux的自由开源代码的操作系统,主要用于移动设备,如智能手机和平板电脑。
1. 手机通信技术代世
手机根据通信技术的代世分为1G,2G,3G,4G,未来向5G发展。第1代通信技术使用模拟网络传递信号,如早期的大哥大,类似简单的无线电双工电台。第2代技术主要是gsm、chma,比如moto、nokia等的手机采用这个频段的无线技术,速度约在10kb左右。第3代使用的技术主要是wcdma、cdma2000,传输速度远远快于2G,约为100kb左右,图片和语音的传播质量提高,主要产品如android和ios,电容屏、多点触控技术和手势操作等技术促进手机智能化。第4代,速度比3G提高了10倍左右,视频的传播质量提高。预计未来5G的速度比4G提高10倍左右,预计有可能促进VR的普及化。
2. android操作系统和版本
android系统最初由安迪-鲁宾等人开发。2005年8月被google公司收购。android的版本从1.5开始基本是都是以食物命名。4.0以下的使用率较低,主要以4.0以上为主。
3. android系统的体系结构
如下表:
APPLICATION |
|||||||||||||
Home |
Contacts |
Phone |
Browser |
…… |
|||||||||
APPLICATION FRAMEWORK |
|||||||||||||
Activity Manager |
Window Manager |
Content Manager |
View Manager |
||||||||||
Package Manager |
Telephone Manager |
Resource Manager |
Location Manager |
Notification Manager |
|||||||||
LIBRARIES |
ANDROID RUNTIME |
||||||||||||
Surface Manager |
Media Framework |
SQLite |
Core Libraries |
||||||||||
OpenGL|ES |
FreeType |
WebKit |
Dalvik Virtual Machine |
||||||||||
SGL |
SSL |
Libc |
|||||||||||
LINUX KERNEL |
|||||||||||||
Display Driver |
Camera Driver |
Flash Memery Driver |
Binder(IPC) Driver |
||||||||||
Keypad Driver |
WiFi Driver |
Audio Drivers |
Power Management |
application framework应用框架层,可以是采用java编写的api。这一层表现在最高层应用层,向下可以调用c、c++、java编写的库文件。
4. android虚拟机
java代码需要运行在jvm虚拟机上。android中的java代码运行在dalvik虚拟机上。由于利益的关系,google没有使用jvm而是自己开发了dvm。两者的比较是,jvm基于的架构是栈,也就是内存,dvm基于寄存器,也就是cpu;jvm编译后的文件是从java编译为.class或这.jar;dvm编译后的文件是从java编译为.class,然后为.dex或者.odex。从4.4以后,采用art模式,android runtime来替代dalvik(可选的),以提高系统的运行速度;5.0之后放弃了art彻底替代dalvik,dalvik被放弃使用。java是解释性语言,就是说在编译后,运行时需要将二进制的字节码文件翻译为机器码才能被设备读取,art虚拟机的优化处理是在安装程序时就将字节码翻译为机器码,相比dalvik的边翻译边运行就提高了速度,区别是程序的安装速度满了,程序占的空间也相对变大了,是一种用空间换时间的策略。
5. 搭建集成开发环境
常用的集成开发环境有eclipse和google发布的android studio。
google提供了android的软件开发包。从官网下载4.0以上版本的sdk包。sdk包的目录结构为:
add-ons,是google提供的相关的服务,比如goole地图、google云、google推送、用户统计。
build-tools,编译工具,对android项目打包。
docs,android帮助文档。
extras,android的支持库,对低的版本的支持补丁或者对其他设备或接口的支持。intel的硬件加速器。
platforms,android的平台。
platform-tools,与平台相关的打包的文件。
sources,android应用框架层相关的源码。
system-images,android自带的虚拟机,不同的系统的镜像。
temp,临时文件夹。
tools,一些相关的工具。
使用sdk自带的sdk manager可以下载安装和更新sdk。需要通过设置代理下载。通过tools>options设置。
在eclipse中安装adt插件,如果不能在线安装,可以离线安装,下载一个ADT包,然后,在eclipse中help>install new software>add>archive>选择adt压缩包的路径>ok>选中develop tools>next>next>安装>安装完毕,重启eclipse。
6. android虚拟机
安装好ADT插件后,在控制面板就看到Android sdk manager和android virtual device manager的图标,如果没有,设置window>perspective>customize perspective>action set availability>选中这两项的选项。然后,点击android virtual device manager,create虚拟设备,分辨率选择3.2寸的;cpu架构选择arm或者intel的,一般是arm的,如果要使用intel加速器要选intel的cpu;摄像头选择none,做摄像头相关的开发需要用真机;内存选项都默认。设置好后,start可以开启设备。再create一个设备,开启,两者可以互通电话和发信息。
7. ddms透视图
Dalvik Debug Monitor Services,Dalvik虚拟机调试监控服务。在Java EE按钮隔壁有DDMS按钮,如果没有在window>perspective>open perspective>other中可以找到。打开ddms视图,可以看到devices选项卡,显示当前启动的移动设备。在选项卡中screen capture可以截屏,file explore可以查看设备的文件目录,data>app目录下放安装的应用,data>data放设备本身程序的文件,mnt>sdcard是sdcard的路径。emulator control是虚拟机的控制器,可以给指定的设备打电话、发短信、设置位置信息等。
8. 第一个android程序
file>new>android application project。填写应用名,应用名和项目名一致,包名采用域名反转方式、不可以出现中文字符。next,勾选创建自定义登陆图标,创建activity,在工作空间创建项目;next,设置一个图标;next,选择一个activity;next,给activity命名;finish。
创建完成后,通过graphical layout视图可以看预览,activity_main.xml是布局的xml文档,MainActivity.java是程序代码,右键项目run as>android application,可以运行到avd上。
Android程序的目录结构
scr |
程序源文件 |
|
gen |
自动生成的java文件 |
|
Android4.x.x |
编译依赖的jar包 |
|
Android Private Libraries |
android支持库 |
|
assets |
不被编译的文件 |
|
bin |
编译后的内容放在bin目录 |
|
libs |
项目依赖的其他库文件 |
|
res resource 项目相关的资源 |
drawable |
像素率,放置图片的文件 |
layout |
布局文件夹,放布局的文件 |
|
menu |
菜单,一般是3个按钮 |
|
values |
值,放一些相关的值的文件 |
|
values-v11 |
放版本适配的文件 |
|
values-v14 |
放版本适配的文件 |
|
values-w820dp |
放屏幕适配的文件 |
|
AndroidManifest.xml |
项目的清单文件,注意包名的重名问题 |
9. android项目打包
打包就是做成apk文件,也就是app安装包。一个android项目的唯一性的标志是项目的包名和签名,右键项目>android tools>export signed application packeg,生成项目签名包,进行打包。需要注意的是keystore文件不能丢失,密码不能丢失,有效期设置的时间足够长。打包后,通过adb,android debug bridge,android调试桥发布到设备上。
10. adb指令
进入命令行。
adb kill-server,关闭adb服务。通过任务管理器可以查看服务进程,一般在第一个进程的位置。
adb start-server,开启adb服务。
adb uninstall 项目的包名,卸载app。
adb install res目录下的apk文件,安装app。
adb push 本地pc文件全路径 avd路径(sdcard/),将本地pc的文件推到avd设备。
adb pull avd路径(sdcard/1.apk) 本地pc路径可以(重命名)C:\Users\Administrator\Desktop\2.apk,从avd设备拖拽文件到本地pc。注意,avd的路径使用/。
adb shell,进入avd的shell,实际上avd是一个linux设备,可以使用linux命令操作。
11. 编写一个电话拨号器
首先,画ui,画用户界面,在layout->xml布局文件中做;
然后,对着ui写java代码;
然后,部署到设备看效果。
在eclipse中创建项目。
在res>layout>activity_main.xml中,画ui。
RelativeLayout是一个相对布局的容器,里面的TextView用于显示文本,用户不可以修改的。TextView标签下,layout_width表示布局宽度,layout_height表示布局的高度,wrap_content表示自适应内容大小,match_parent表示适应父级组件大小。通过修改RelativeLayout中的内边距标签可以修改TextView中文本框与父级的距离:
android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"
在TextView标签的text标签中设置要显示的文本内容,比如:
<TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:text="请输入电话号码"/>
然后,添加EditText,相当于web中的文本域,添加一个Button。
最终ui的xml为:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.dailer.MainActivity" ><TextViewandroid:id="@+id/textView1"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:text="请输入电话号码"/><EditTextandroid:id="@+id/editText1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_below="@+id/textView1"android:hint="请输入电话号"android:ems="10" ><requestFocus /></EditText><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_below="@+id/editText1"android:text="拨打号码" /></RelativeLayout>
编写java代码
Activity类很重要,代表了一个用户的界面,每一个android界面都对应一个activity。activity可以创建一个窗口上加载用户的界面(UI),这个界面就是用来跟用户交互的。
当Activity创建的时候调用onCreate方法。
public class MainActivity extendsActionBarActivity {privateEditText editText;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);editText= (EditText) findViewById(R.id.editText1);Buttonbutton = (Button) findViewById(R.id.button1);button.setOnClickListener(newMyOnClickListener());}privateclass MyOnClickListener implements OnClickListener {@SuppressLint("NewApi")@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stub//按钮点击事件//1,获取用户的输入//空校验StringinputNum = editText.getText().toString();if(inputNum.isEmpty()) {//Toast,向界面输出一个短暂的提示Toast.makeText(MainActivity.this,"输入不能为空",Toast.LENGTH_SHORT).show();System.out.println("输入不能为空");}else {System.out.println("打电话");//开启打电话的avtivity,Intent对要进行的操作的抽象描述。可以开启一个ActivityIntentintent = new Intent();intent.setAction(Intent.ACTION_CALL);//给意图设置内容Uridata = Uri.parse("tel:" + inputNum);intent.setData(data);startActivity(intent);}}}}
出于经济安全的考虑,有些应用需要获得权限后才能执行,打电话需要获得打电话的权限,在清单文件中设置,AndroidManifest.xml>permissions>add>users permission>选择CALL_PHONE>保存。
运行app测试。
注意,在拨号中对于美国认定的紧急号码不能直接播出,需要手动点击拨打。
12. 点击事件的4种写法
第一种,通过内部类的方式,如上。
第二种,通过匿名内部类的方式,优点是节省内存。
button.setOnClickListener(new OnClickListener(){@SuppressLint("NewApi")@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stub//TODO Auto-generated method stub// 按钮点击事件//1,获取用户的输入//空校验StringinputNum = editText.getText().toString();if(inputNum.isEmpty()) {//Toast,向界面输出一个短暂的提示Toast.makeText(MainActivity.this,"输入不能为空",Toast.LENGTH_SHORT).show();System.out.println("输入不能为空");}else {System.out.println("打电话");//开启打电话的avtivity,Intent对要进行的操作的抽象描述。可以开启一个ActivityIntentintent = new Intent();intent.setAction(Intent.ACTION_CALL);//给意图设置内容Uridata = Uri.parse("tel:" + inputNum);intent.setData(data);startActivity(intent);}}});
第三种写法,activity类实现OnClickListener接口。
public class MainActivity extendsActionBarActivity implements OnClickListener {privateEditText editText;privateButton button;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);editText= (EditText) findViewById(R.id.editText1);button= (Button) findViewById(R.id.button1);button.setOnClickListener(this);}@SuppressLint("NewApi")@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stub//TODO Auto-generated method stub//按钮点击事件//1,获取用户的输入//空校验StringinputNum = editText.getText().toString();if(inputNum.isEmpty()) {//Toast,向界面输出一个短暂的提示Toast.makeText(this,"输入不能为空",Toast.LENGTH_SHORT).show();System.out.println("输入不能为空");}else {System.out.println("打电话");//开启打电话的avtivity,Intent对要进行的操作的抽象描述。可以开启一个ActivityIntentintent = new Intent();intent.setAction(Intent.ACTION_CALL);//给意图设置内容Uridata = Uri.parse("tel:" + inputNum);intent.setData(data);startActivity(intent);}}}
如果一个主控件中有多个button按钮,当这个主控件实现了点击侦听接口后,为了区别是哪个按钮的事件,可以使用onClick()方法的参数,来区分。
private class MyOnClickListener implements OnClickListener {@SuppressLint("NewApi")@Overridepublicvoid onClick(View v) {//获得控件的id,根据id执行不同的操作intid = v.getId();switch(id){caseR.id.button1:break;caseR.id.button2:break;caseR.id.button3:break;}
第四种,布局文件中添加onclick属性
<Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@+id/editText1"android:layout_below="@+id/editText1"android:onClick="call"android:text="拨打此号" />
然后在主控件中编写call方法
publicvoid call(View v) {Stringnumber = editText.getText().toString().trim();if(TextUtils.isEmpty(number)) {Toast.makeText(this,"输入不能为空",Toast.LENGTH_SHORT).show();}else {Intentintent = new Intent();intent.setAction(Intent.ACTION_CALL);intent.setData(Uri.parse("tel:"+ number));startActivity(intent);}}
注意,参数一定要是View类型的,且方法名和布局文件中定义的一致。
第四种方法比较少用,由于使得类的内部内容显得不清晰。
13. 五种布局
1) 线性布局
只能在垂直方向或水平方向布局的,通过设置属性orientation的值为垂直或水平。
<?xml version="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="第一个textview"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="第一个textview"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="第一个textview"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="第一个textview"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="第一个textview"/></LinearLayout>
2) 相对布局
创建项目时默认创建的布局文件就是相对布局的。相对布局是相对某个主件的布局,通过指定相对组件的距离可以设置主件的位置。相对布局,摆放控件的位置更灵活。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.fivelayout.MainActivity" ><TextViewandroid:id="@+id/textview1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/hello_world" /><TextViewandroid:id="@+id/textview2"android:layout_below="@id/textview1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview2" /><TextViewandroid:id="@+id/textview3"android:layout_toRightOf="@id/textview1"android:layout_alignBottom="@id/textview1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview3" /><TextViewandroid:id="@+id/textview4"android:layout_toRightOf="@id/textview3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview4" /><TextViewandroid:id="@+id/textview5"android:layout_centerInParent="true"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview5" /><TextViewandroid:id="@+id/textview6"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview6" /></RelativeLayout>
3) 帧布局
可以通过组合属性将控件摆放在屏幕的类似九宫格的位置。使用外边距可以更灵活的摆放。
<?xml version="1.0"encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" ><TextViewandroid:id="@+id/textview1"android:layout_gravity="bottom"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="text1" /><TextViewandroid:id="@+id/textview2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right"android:text="text2" /><TextViewandroid:id="@+id/textview3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:text="text3" /><TextViewandroid:id="@+id/textview4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical|center"android:text="text4" /><TextViewandroid:id="@+id/textview5"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="100dp"android:text="text5" /></FrameLayout>
4) 表格布局
通过表格的行和列来布局控件。
<?xml version="1.0"encoding="utf-8"?><TableLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" ><TableRow> <TextViewandroid:id="@+id/textview1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview1" /><TextViewandroid:id="@+id/textview2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview2" /><TextViewandroid:id="@+id/textview3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview3" /></TableRow><TextViewandroid:id="@+id/textview4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview4" /></TableLayout>
5) 绝对布局
通过绝对的坐标来定位控件。绝对布局对不同分辨率尺寸的屏幕适应性较差,已经很少被使用了。
<?xml version="1.0"encoding="utf-8"?><AbsoluteLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" ><TextViewandroid:id="@+id/textview1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="textview1" /><TextViewandroid:id="@+id/textview3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_x="113dp"android:layout_y="43dp"android:text="textview3" /><TextViewandroid:id="@+id/textview2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_x="12dp"android:layout_y="54dp"android:text="textview2" /></AbsoluteLayout>
五种布局的使用度从高到低排序是:相对》线性》帧》表格》绝对。
14. 测试
从代码可以见的角度,测试分为黑盒测试和白盒测试。黑盒测试,看不见代码的情况下,通过点击等方式测试软件,自动化测试等。白盒测试,可以看见代码的,比如单元测试、集成测试、系统测试等。从对软件的施力程度,分为压力测试、冒烟测试等,压力测试,在高并发访问或者高频率使用软件的情况下的反应,android中使用的monkey测试,monkey 1000或者monkey -p 包名 次数,对某一个应用的点击;冒烟测试,就是不停的测试直到软件崩溃。
在android中使用单元测试。
编写一个测试类继承AndroidTextCase类,然后在清单文件中添加Instrumentation,包名填写测试类在的包,类名为android.test.InstrumentationTestRunner。在清单文件的application标签中添加<uses-libraryandroid:name="android.test.runner" />。在测试类中编写方法,右键run as>Android JUnit Test进行测试。
15. 日志猫
日志猫logcat可以查看文件的输出以及错误警告等提醒。使用saved filter可以添加过滤条件。可以选择日志的级别进行过滤。也可以使用Log类的方法进行过滤。
protectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);System.out.println("输出日志");Log.v("MainActivity","vervbose");Log.d("MainActivity","debug");Log.i("MainActivity","info");Log.w("MainActivity","warning");Log.e("MainActivity","error");}
一般,为了方便项目在测试和上线之间显示或不显示logcat数据,不会直接使用Log类的方法,而是通过一个工具类,设置开关间接使用Log类的方法。这样,在测试时,打开开关,可以查看输出;项目上线后,关闭开关,可以不显示测试时出现的logcat输出。这样,主要是为了避免直接使用Log方法,而待上线后需要大量工作去注释这些方法。
public class LogUtils {privatestatic boolean openLog = true;publicstatic void LOGV(String tag, String msg) {if(openLog) {Log.d(tag,msg);}}publicstatic void LOGD(String tag, String msg) {if(openLog) {Log.d(tag,msg);}}publicstatic void LOGI(String tag, String msg) {if(openLog) {Log.d(tag,msg);}}publicstatic void LOGW(String tag, String msg) {if(openLog) {Log.d(tag,msg);}}publicstatic void LOGE(String tag, String msg) {if(openLog) {Log.d(tag,msg);}}}
二、 数据存储
关于android系统的数据存储方面的东西。
1. 一个用到数据存储的案例
使用java的io流来存储用户数据的登陆案例。
首先,UI设计。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.logintest1.MainActivity" ><TextViewandroid:id="@+id/title"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="登录界面" /><EditTextandroid:id="@+id/username"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/title"android:hint="请输入用户名"/><EditTextandroid:id="@+id/password"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/username"android:inputType="textPassword"android:hint="请输入密码"/><CheckBoxandroid:id="@+id/isShow"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/password"android:text="勾选保存密码"/><Buttonandroid:id="@+id/loginButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_below="@id/password"android:text="登陆"/></RelativeLayout>
编写主类入口方法,实现业务逻辑
protectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//获取用户名输入框控件finalEditText usernameV = (EditText) findViewById(R.id.username);finalEditText passwordV = (EditText) findViewById(R.id.password);finalCheckBox isShowV = (CheckBox) findViewById(R.id.isShow);Stringresult = UserUtils.getInfo();if(result != null) {String[]results = result.split(":");if("true".equals(results[2])) {usernameV.setText(results[0]);passwordV.setText(results[1]);isShowV.setChecked(true);}}ButtonloginV = (Button) findViewById(R.id.loginButton);//给登陆按钮添加点击监听loginV.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stub//检验用户名和密码Stringusername = usernameV.getText().toString().trim();Stringpassword = passwordV.getText().toString().trim();if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {Toast.makeText(MainActivity.this,"用户名或密码不能为空",Toast.LENGTH_SHORT).show();}else {BooleanisShow = isShowV.isChecked();Toast.makeText(MainActivity.this,isShow.toString(),Toast.LENGTH_SHORT).show();Toast.makeText(MainActivity.this,"登陆成功",Toast.LENGTH_SHORT).show();//在用户设备上保存用户名和密码booleansaveResult = UserUtils.saveInfo(username, password,isShow);if(saveResult) {Toast.makeText(MainActivity.this,"保存成功",Toast.LENGTH_SHORT).show();}else {Toast.makeText(MainActivity.this,"保存失败",Toast.LENGTH_SHORT).show();}}}});}
编写使用io流的工具
/*** 用户工具类**@author Administrator*@version 1.0*/public class UserUtils {/*** 保存用户名和密码** @param username* ,用户名* @param password* ,密码* @param isShow* @return,保存成功,反悔true;否则返回fasle*/publicstatic boolean saveInfo(String username, String password,BooleanisShow) {//TODO Auto-generated method stubStringinfo = username + ":" + password + ":" + isShow.toString();Filefile = new File("data/data/com.example.logintest1/info.data");try{BufferedWriterbw = new BufferedWriter(new FileWriter(file));bw.write(info);bw.newLine();bw.close();returntrue;}catch (IOException e) {e.printStackTrace();returnfalse;}}/*** 获取用户信息的方法** @return 返回用户信息的字符串*/publicstatic String getInfo() {Filefile = new File("data/data/com.example.logintest1/info.data");try{BufferedReaderbr = new BufferedReader(new FileReader(file));Stringinfo = null;if((info = br.readLine()) != null) {returninfo;}returnnull;}catch (IOException e) {e.printStackTrace();returnnull;}}}
2. 通过上下文获取常见目录
通过上下文类Context可以访问跟当前应用相关的信息,也可以访问当前应用的私有资源和类,也可以做系统级的调用,比如开启另外一个avtivity、发广播和开启服务。
修改后的保存数据和获取数据的方法
publicstatic boolean saveInfoByContext(Context context, String username,Stringpassword, Boolean isShow) {//TODO Auto-generated method stubStringinfo = username + ":" + password + ":" + isShow.toString();//File file = new File("data/data/com.example.logintest1/info.data");//File file = new File(context.getFilesDir().getAbsoluteFile()//+ "/info.data");try{//BufferedWriter bw = new BufferedWriter(new FileWriter(file));FileOutputStreamfos = context.openFileOutput("info2.data",Context.MODE_PRIVATE);BufferedWriterbw = new BufferedWriter(new OutputStreamWriter(fos));bw.write(info);bw.newLine();bw.close();returntrue;}catch (IOException e) {e.printStackTrace();returnfalse;}}publicstatic String getInfoByContext(Context context) {//File file = new File("data/data/com.example.logintest1/info.data");try{FileInputStreamfis = context.openFileInput("info2.data");BufferedReaderbr = new BufferedReader(new InputStreamReader(fis));Stringinfo = null;if((info = br.readLine()) != null) {returninfo;}returnnull;}catch (IOException e) {e.printStackTrace();returnnull;}}
3. 数据保存至sd卡
使用Environment类获取sd卡的一些数据。
public voidonClick(View v) {//TODO Auto-generated method stub//检验用户名和密码Stringusername = usernameV.getText().toString().trim();Stringpassword = passwordV.getText().toString().trim();booleansaveResult = false;BooleanisShow = isShowV.isChecked();if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {Toast.makeText(MainActivity.this,"用户名或密码不能为空",Toast.LENGTH_SHORT).show();}else {if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){Toast.makeText(MainActivity.this,isShow.toString(),Toast.LENGTH_SHORT).show();Toast.makeText(MainActivity.this,"登陆成功",Toast.LENGTH_SHORT).show();//在用户设备上保存用户名和密码//boolean saveResult = UserUtils.saveInfo(username,//password,//isShow);//boolean saveResult = UserUtils.saveInfoByContext(//MainActivity.this, username, password, isShow);saveResult= UserUtils.saveInfo2sd(username, password,isShow);Toast.makeText(MainActivity.this,saveResult? "保存到sd卡成功" : "保存到sd卡失败",Toast.LENGTH_SHORT).show();}else {saveResult= UserUtils.saveInfoByContext(MainActivity.this,username, password, isShow);Toast.makeText(MainActivity.this,saveResult? "保存到内存卡成功" : "保存到内存卡失败",Toast.LENGTH_SHORT).show();}}}
保存数据到sd卡的方法
publicstatic boolean saveInfo2sd(String username, String password,BooleanisShow) {//TODO Auto-generated method stubStringinfo = username + ":" + password + ":" + isShow.toString();//File file = new File("mnt/sdcard/info.data");FilesdDir = Environment.getExternalStorageDirectory().getAbsoluteFile();Filefile = new File(sdDir, "info.data");try{BufferedWriterbw = new BufferedWriter(new FileWriter(file));bw.write(info);bw.newLine();bw.close();returntrue;}catch (IOException e) {e.printStackTrace();returnfalse;}}
4. 获取sd卡的大小
protectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextViewfreeSpaceV = (TextView) findViewById(R.id.sdFreeSpace);TextViewtotalSpaceV = (TextView) findViewById(R.id.sdTotalSpace);FilestorageDir = Environment.getExternalStorageDirectory();longtotalSpace = storageDir.getTotalSpace();longfreeSpace = storageDir.getFreeSpace();totalSpace/= (1024 * 1024);freeSpaceV.setText("剩余空间:" + Long.toString(freeSpace) + "bytes");totalSpaceV.setText("总空间:" + Long.toString(totalSpace) + "Mb");}
5. android中常用的像素单位
长度单位,px和dp。px,pixel,表示像素,是绝对的单位,屏幕适配性不好。dp,device in dependent pixels,虚拟的像素,在不同的像素密度的设备上会自动适配。sp,类似dp,会根据用户的字体大小偏好来缩放,常用于表示字体大小。
6. 文件权限
android的操作系统的内核是linux内核,linux对文件的权限有严格的控制。每个应用的权限一般仅限于该应用读写,其他用户也就是其他应用,没有任何权限。为了访问其他应用的私有文件,需要通过root用户修改这个应用的文件的权限,使用adb shell获得root的shell。使用chmod -R xxx 文件名,修改权限。
publicvoid read(View v) {Filefile = new File("data/data/com.example.logintest1/info.data");try{BufferedReaderbr = new BufferedReader(new FileReader(file));Stringresult = br.readLine();Toast.makeText(this,result, Toast.LENGTH_SHORT).show();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();Toast.makeText(this,"访问失败",Toast.LENGTH_SHORT).show();}}
7. 通过上下文保存文件的模式
通过上下文保存文件可以设置为不同的模式,主要是关于文件的权限方面的。
设计保存按钮的UI。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.contextSaveMode.MainActivity"android:orientation="vertical" ><Buttonandroid:id="@+id/privateB"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="private" /><Buttonandroid:id="@+id/append"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="append" /><Buttonandroid:id="@+id/world_readable"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="world_readable" /><Buttonandroid:id="@+id/worldWritable"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="worldWritable" /></LinearLayout>
编写主类入口方法,给按钮添加事件
protectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButtonprivateB = (Button) findViewById(R.id.privateB);ButtonappendB = (Button) findViewById(R.id.append);Buttonworld_readableB = (Button) findViewById(R.id.world_readable);ButtonworldWritable = (Button) findViewById(R.id.worldWritable);privateB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubtry{FileOutputStreamfos = openFileOutput("private.txt",MODE_PRIVATE);fos.write("private".getBytes());fos.close();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();}}});appendB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubtry{FileOutputStreamfos = openFileOutput("append.txt",MODE_APPEND);fos.write("append".getBytes());fos.close();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();}}});world_readableB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubtry{FileOutputStreamfos = openFileOutput("world_readableB.txt",MODE_WORLD_READABLE);fos.write("world_readableB".getBytes());fos.close();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();}}});worldWritable.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubtry{FileOutputStreamfos = openFileOutput("worldWritable.txt",MODE_WORLD_WRITEABLE);fos.write("worldWritable".getBytes());fos.close();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();}}});}
运行程序,到文件夹查看保存的文件的权限。一般常用的是append模式或者private模式,两者的区别是append是追加写,private是覆盖写。
8. SharedPreferences
访问或修改配置数据的接口,通过Context对象的getSharedPreferences(String name,int mode)方法可以获得这个接口对象。是轻量级的存储数据的API,通过xml文件存储数据。
使用SharedPreferences保存用户登陆信息。
privateEditText usernameV;privateEditText passwordV;privateCheckBox isShowV;privateSharedPreferences sp;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);usernameV= (EditText) findViewById(R.id.username);passwordV= (EditText) findViewById(R.id.password);isShowV= (CheckBox) findViewById(R.id.isShow);ButtonloginV = (Button) findViewById(R.id.login);loginV.setOnClickListener(this);sp= this.getSharedPreferences("info", MODE_PRIVATE);booleanisShow = sp.getBoolean("isShow", false);if(isShow) {Stringusername = sp.getString("username", "");Stringpassword = sp.getString("password", "");usernameV.setText(username);passwordV.setText(password);isShowV.setChecked(true);}}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubStringusername = usernameV.getText().toString().trim();Stringpassword = passwordV.getText().toString().trim();BooleanisShow = isShowV.isChecked();if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {Toast.makeText(this,"用户名或密码不能为空",Toast.LENGTH_SHORT).show();}else {Editoreditor = sp.edit();if(isShow) {editor.putString("username",username);editor.putString("password",password);}editor.putBoolean("isShow",isShow.booleanValue());editor.commit();}}
9. xml序列化
将数据输出为xml格式的文件,就是xml序列化的表现之一。
使用StringBuilder拼接xml字符串。
ArrayList<SMS>smslist = new ArrayList<SMS>();@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);for(int i = 0; i < 20; i++) {SMSsms = new SMS();sms.from= "1000" + i;sms.content= "nihao" + i;sms.time= "19:40:" + i;smslist.add(sms);}for(SMS sms : smslist) {System.out.println(sms);}ButtonsaveB = (Button) findViewById(R.id.saveB);saveB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {// TODOAuto-generated method stubStringBuildersb = new StringBuilder();sb.append("<?xmlversion=\"1.0\" encoding=\"utf-8\"?>");sb.append("<SMSList>");for(SMS sms : smslist) {sb.append("<SMS><from>").append(sms.from).append("</from><content>").append(sms.content).append("</content><time>").append(sms.time).append("</time></SMS>");}sb.append("</SMSList>");Stringxml = sb.toString();try{FileOutputStreamfos = openFileOutput("sms.xml",MODE_PRIVATE);fos.write(xml.getBytes());fos.close();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();}}});}
使用XmlSerializer类序列化xml
Button saveB2 =(Button) findViewById(R.id.saveB2);saveB2.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stub//获取xml序列化器类XmlSerializerserializer = Xml.newSerializer();try{serializer.setOutput(openFileOutput("smslist2.xml",MODE_PRIVATE),"utf-8");//写文档开始的声明serializer.startDocument("utf-8",true);serializer.startTag(null,"SMSLIST");for(SMS sms : smslist) {serializer.startTag(null,"SMS");serializer.startTag(null,"from");serializer.text(sms.from);serializer.endTag(null,"from");serializer.startTag(null,"content");serializer.text(sms.content);serializer.endTag(null,"content");serializer.startTag(null,"time");serializer.text(sms.time);serializer.endTag(null,"time");serializer.endTag(null,"SMS");}serializer.endTag(null,"SMSLIST");serializer.endDocument();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();}}});
云备份等服务可以使用xml序列化文件,将xml文件上传到云服务器。
10. pull解析xml文件
Button parseB =(Button) findViewById(R.id.parse);parseB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {ArrayList<SMS>smslist = null;SMSsms = null;//TODO Auto-generated method stubXmlPullParserxpp = Xml.newPullParser();try{xpp.setInput(openFileInput("smslist2.xml"),"utf-8");inteventType = xpp.getEventType();while(eventType != xpp.END_DOCUMENT) {switch(eventType) {caseXmlPullParser.START_TAG:if("SMSLIST".equals(xpp.getName())) {smslist= new ArrayList<SMS>();}else if ("SMS".equals(xpp.getName())) {sms= new SMS();}else if ("from".equals(xpp.getName())) {sms.from= xpp.nextText();}else if ("content".equals(xpp.getName())) {sms.content= xpp.nextText();}else if ("time".equals(xpp.getName())) {sms.time= xpp.nextText();}break;caseXmlPullParser.END_TAG:if("SMS".equals(xpp.getName())) {smslist.add(sms);}break;}eventType= xpp.next();}//for (SMS sm : smslist) {//System.out.println(sm);//}Toast.makeText(MainActivity.this,smslist.toString(),Toast.LENGTH_SHORT).show();}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}});}
11. SQLite
lite表示轻量级的,SQLite是一个轻量级的数据库,主要表现在体积小,适合嵌入式设备,绿色免安装,扩平台,单一文件。不足是多线程操作比较差,对sql的支持不全面,有的sql是不支持的,比如alter,多表查询等。
1) 使用SQLiteOpenHelper类操作sqlite数据库。
创建一个项目,创建一个类继承自SQLiteOpenHelpter。
public class MyOpenHelper extendsSQLiteOpenHelper {publicMyOpenHelper(Context context) {super(context,"ten1.db", null, 1, null);//TODO Auto-generated const ructor stub}@Overridepublicvoid onCreate(SQLiteDatabase db) {//TODO Auto-generated method stubdb.execSQL("createtable info(_id integer primary key autoincrement,name varchar(20),phonevarchar(20));");System.out.println("oncreate被调用");}//当版本号发生变化时,就会调用数据库升级的方法@Overridepublicvoid onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {//TODO Auto-generated method stub//表结构修改,比如增加字段//db.execSQL("alter table info add age integer");//获取版本号是为了统计用户版本分布System.out.println("oldversion:"+ oldVersion + ";newversion:"+newVersion);}@Overridepublicvoid onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {//TODO Auto-generated method stub//super.onDowngrade(db, oldVersion, newVersion);System.out.println("onDowngrade");}}
在项目的主控件类的入口方法中调用sqllite数据库对象和方法。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyOpenHelpermyOpenHelper = new MyOpenHelper(this);myOpenHelper.getWritableDatabase();}}
2) 直接执行sql语句操作
增查改删
Button queryB =(Button) findViewById(R.id.query);ButtoninsertB = (Button) findViewById(R.id.insert);ButtonupdateB = (Button) findViewById(R.id.update);ButtondeleteB = (Button) findViewById(R.id.delete);queryB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stub//获取sqlitedatebase对象SQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();//String[] strs = { "zhangsan", "23" };Cursorresult = database.rawQuery("select * from info", null);System.out.println("总记录数:" + result.getCount());while(result.moveToNext()) {Stringname = result.getString(1);Stringphone = result.getString(result.getColumnIndex("phone"));intage = result.getInt(3);System.out.println("name:"+ name + "--phone:" + phone+"--age:" + age);}result.close();database.close();}});insertB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubSQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();database.execSQL("insertinto info (name,phone,age) values ('zhangsan','xiaomi',23)");database.execSQL("insertinto info (name,phone,age) values ('lisi','meizu',34)");database.close();}});updateB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubSQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();database.execSQL("updateinfo set age=100 where name = 'zhangsan'");database.close();}});deleteB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubSQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();database.execSQL("deletefrom info where name = 'lisi'");database.close();}});
3) 使用google封装的api操作数据库
// 使用google封装的api操作数据库ButtongqueryB = (Button) findViewById(R.id.gquery);ButtonginsertB = (Button) findViewById(R.id.ginsert);ButtongupdateB = (Button) findViewById(R.id.gupdate);ButtongdeleteB = (Button) findViewById(R.id.gdelete);gqueryB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubSQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();Cursorresult = database.query(false, "info", new String[] {"name","phone", "age" }, "name=?",newString[] { "maliu" }, null, null, null, null, null);while(result.moveToNext()) {Stringname = result.getString(0);Stringphone = result.getString(result.getColumnIndex("phone"));intage = result.getInt(2);System.out.println("name:"+ name + "--phone:" + phone+"--age:" + age);}result.close();database.close();}});ginsertB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubSQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();StringnullColumnHack = null;ContentValuesvalues = new ContentValues();values.put("name","maliu");values.put("age",25);values.put("phone","1234");longid = database.insert("info", nullColumnHack, values);System.out.println("当前插入的数据的id:" + id);database.close();}});gdeleteB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubSQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();intcount = database.delete("info", "name = ?",newString[] { "zhangsan" });Toast.makeText(MainActivity.this,"删除了" + count +"条数据",Toast.LENGTH_SHORT).show();database.close();}});gupdateB.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubSQLiteDatabasedatabase = myOpenHelper.getReadableDatabase();ContentValuesvalues = new ContentValues();values.put("age",100);intcount = database.update("info", values, "name=?",newString[] { "maliu" });Toast.makeText(MainActivity.this,"更新了" + count +"条数据",Toast.LENGTH_SHORT).show();database.close();}});
对比原始api和google封装的api,返回值上,原始的没有,google的有;当操作大量数据时,原始的较快;sql的灵活性,原始的api可以任意写,google的有一定的限制;使用原始sql,可能会写错,而且几率大于google提供的api。使用原始sql操作时,可以先在可视化软件中测试下,通过后再复制sql语句,减小出错率。
12. 数据库事务
事务,通俗的解释,就是一系列操作,要么全部成功,要么全部失败。
android中使用事务的案例。
创建MyOpenHelper,初始化方法中,创建表和插入数据。
然后,在主类中执行事务操作。
public class MainActivity extends Activity {privateMyOpenHelper openHelper;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);openHelper= new MyOpenHelper(this);}publicvoid transact(View v) {SQLiteDatabasedatabase = openHelper.getReadableDatabase();database.beginTransaction();try{database.execSQL("updateinfo set money=money-100 where name=?",newString[] { "张三" });inta = 10 / 0;database.execSQL("updateinfo set money=money+100 where name=?",newString[] { "李四" });database.setTransactionSuccessful();}catch (Exception e) {Toast.makeText(this,"服务器忙,异常码1234",Toast.LENGTH_SHORT).show();}finally {database.endTransaction();database.close();}}publicvoid query(View v) {SQLiteDatabasedatabase = openHelper.getReadableDatabase();Cursorresult = database.rawQuery("select * from info", null);while(result.moveToNext()) {Stringname = result.getString(result.getColumnIndex("name"));Stringmoney = result.getString(3);System.out.println("name:"+ name + "--money:" + money);}result.close();database.close();}}
三、 ListView
列表显示,是app中广泛使用的展示页面的api。以垂直滚动列表的形式展示条目。一般配合ListAdapter使用。
1. ListView的入门使用
创建项目,在UI文件创建一个Listview。
在入口方法中给ListView添加条目。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ListViewlistview = (ListView) findViewById(R.id.listview);//创建一个列表适配器,给listview提供数据listview.setAdapter(newMyListAdapter());}privateclass MyListAdapter extends BaseAdapter {@Overridepublic int getCount() {//TODO Auto-generated method stubreturn5;}@OverridepublicObject getItem(int position) {//TODO Auto-generated method stubreturnnull;}@Overridepubliclong getItemId(int position) {//TODO Auto-generated method stubreturn0;}@OverridepublicView getView(int position, View convertView, ViewGroup parent) {//TODO Auto-generated method stubTextViewtextview1 = new TextView(MainActivity.this);textview1.setText("这是第" + position + "个条目");returntextview1;}}}
在上述的例子中,如果给ListView添加大量的条目,那么在使用app的过程中,如果快速的滚动条目,会由于dvm来不及回收滚出屏幕的条目的占用的内存资源,而导致出现内存溢出的异常。
优化的方法是,一个屏幕能够显示的条目的量是有限的,对于大量的条目,比如说100000条,如果每条都创建一个TextView对象,就容易造成垃圾回收不及时的内存溢出,可以创建屏幕最大显示量稍多的TextView的数量,然后重复利用这些TextView而不回收。android的ListAdapter提供了实现这样操作的方法。
主要就是修改ListAdapter的getView方法
public ViewgetView(int position, View convertView, ViewGroup parent) {//TODO Auto-generated method stubTextViewtextview1 = null;if(convertView == null) {//为空说明没有可用的对象,则创建新的view对象textview1= new TextView(MainActivity.this);System.out.println("创建新的对象:" + position);}else {//如果不为空,则当前的converView可以重复使用textview1= (TextView) convertView;System.out.println("重用旧的对象");}textview1.setText("这是第" + position + "个条目");returntextview1;}}
2. 显示复杂条目
创建项目,主界面UI添加一个ListView。
创建一个xml文件,作为ListView内条目的模版。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:padding="5dp"><ImageViewandroid:id="@+id/iv_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher"/><TextViewandroid:id="@+id/iv_title"android:layout_toRightOf="@id/iv_icon"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="标题"android:textSize="20sp"android:padding="5dp"/><TextViewandroid:id="@+id/iv_content"android:layout_toRightOf="@id/iv_icon"android:layout_below="@id/iv_title"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="内容内容"android:textSize="10sp"android:textColor="#88000000"android:padding="5dp"/></RelativeLayout>
在入口方法中实现给UI添加模版条目。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ListViewlv_list = (ListView) findViewById(R.id.lv_list);lv_list.setAdapter(newMyAdapter());}privateclass MyAdapter extends BaseAdapter {@Overridepublicint getCount() {//TODO Auto-generated method stubreturn10;}@OverridepublicObject getItem(int position) {//TODO Auto-generated method stubreturnnull;}@Overridepubliclong getItemId(int position) {//TODO Auto-generated method stubreturn0;}@OverridepublicView getView(int position, View convertView, ViewGroup parent) {//TODO Auto-generated method stubViewview = null;if(convertView == null) {view= View.inflate(MainActivity.this, R.layout.item, null);}else {view= convertView;}returnview;}}}
3. 获取LayoutInflate的三种方法
public View getView(int position, View convertView, ViewGroupparent) {//TODO Auto-generated method stubViewview = null;if(convertView == null) {//view = View.inflate(MainActivity.this, R.layout.item, null);LayoutInflaterinflater = LayoutInflater.from(MainActivity.this);view= inflater.inflate(R.layout.item, null);LayoutInflaterinflater2 = getLayoutInflater();//google的源代码中使用getSystemService这个方法获取的LayoutInflater,google推荐使用这个方法LayoutInflaterinflater3 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);}else {view= convertView;}returnview;
4. Adapter子类展示数据
一般使用ArrayAdapter和SimpleAdapter。ArrayAdapter适合数据比较简单的布局,比如只有一个TextView的内容需要修改时。SimpleAdapter适合布局比较复杂的情况,比如一个新闻界面或博客文章界面等。
使用ArrayAdapter的例子。
创建项目,在主UIxml中添加一个ListView,创建一个只有一个TextView的线性布局文件。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal" ><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher"/><TextViewandroid:id="@+id/tv_text"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>
在入口方法中,给UI添加数据。
protectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ListViewlv_list = (ListView) findViewById(R.id.lv_list);String[]objects = { "hello", "爪哇", "安卓", "配合片", "磁盘片" };ArrayAdapter<String>adapter = new ArrayAdapter<String>(this,R.layout.item,objects);lv_list.setAdapter(adapter);ArrayAdapter<String>adapter2 = new ArrayAdapter<String>(this,R.layout.item2,R.id.tv_text, objects);lv_list.setAdapter(adapter2);}
SimpleAdapter的例子
创建一个项目,在主UI添加一个ListView,创建一个item作为内容的模版。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"android:padding="5dp" ><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher"/><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20sp"android:text="标题"/><TextViewandroid:id="@+id/tv_content"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="12sp"android:text="内容内容内容内容太"android:textColor="#88000000"android:layout_marginTop="2dp"/></LinearLayout></LinearLayout>
在入口方法中给界面添加数据
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ListViewlv_list = (ListView) findViewById(R.id.lv_list);List<Map<String,String>> data = new ArrayList<Map<String, String>>();String[]from = { "title", "content" };int[]to = { R.id.tv_title, R.id.tv_content };Map<String,String> item1 = new HashMap<String, String>();item1.put("title","新闻1");item1.put("content","内容1内容1内容1内容1");data.add(item1);Map<String,String> item2 = new HashMap<String, String>();item2.put("title","新闻2");item2.put("content","内容2内容2内容2内容2");data.add(item2);Map<String,String> item3 = new HashMap<String, String>();item3.put("title","新闻3");item3.put("content","内容3内容3内容3内容3");data.add(item3);SimpleAdapteradapter = new SimpleAdapter(this, data, R.layout.item,from,to);lv_list.setAdapter(adapter);}
5. ListView展示数据库数据
创建一个项目,创建UI主界面,里面两个按钮一个ListView。
创建一个javabean。
创建一个模版文件。
创建数据库操作类。
public class MyOpenHelper extendsSQLiteOpenHelper {publicMyOpenHelper(Context context) {super(context,"ten.db", null, 1);//TODO Auto-generated constructor stub}@Overridepublicvoid onCreate(SQLiteDatabase db) {//TODO Auto-generated method stubdb.execSQL("createtable info(_id integer primary key autoincrement,name varchar(20),phonevarchar(20))");}@Overridepublicvoid onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {//TODO Auto-generated method stub}}
在主控件中编写业务代码
public class MainActivity extends Activity {privateMyOpenHelper openHelper;privateArrayList<User> userList = new ArrayList<User>();privateListView lv_list;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);lv_list= (ListView) findViewById(R.id.lv_list);Buttonbtn_insert = (Button) findViewById(R.id.btn_insert);Buttonbtn_query = (Button) findViewById(R.id.btn_query);openHelper= new MyOpenHelper(this);}publicvoid insert(View v) {SQLiteDatabasedatabase = openHelper.getWritableDatabase();database.execSQL("insertinto info values(null,'照样','12342')");database.execSQL("insertinto info values(null,'上述','345334')");database.execSQL("insertinto info values(null,'滚动','5676765')");database.execSQL("insertinto info values(null,'更好','4565645')");database.execSQL("insertinto info values(null,'改天','4586786')");database.close();}publicvoid query(View v) {SQLiteDatabasedatabase = openHelper.getWritableDatabase();Cursorresult = database.rawQuery("select * from info", null);while(result.moveToNext()) {Useruser = new User();intid = result.getInt(result.getColumnIndex("_id"));Stringname = result.getString(result.getColumnIndex("name"));Stringphone = result.getString(result.getColumnIndex("phone"));user.id = id;user.name= name;user.phone= phone;userList.add(user);}result.close();database.close();for(User user : userList) {System.out.println(user);}lv_list.setAdapter(newMyAdapter());}privateclass MyAdapter extends BaseAdapter {@Overridepublicint getCount() {//TODO Auto-generated method stubreturnuserList.size();}@OverridepublicObject getItem(int position) {//TODO Auto-generated method stubreturnuserList.get(position);}@Overridepubliclong getItemId(int position) {//TODO Auto-generated method stubreturnposition;}@OverridepublicView getView(int position, View convertView, ViewGroup parent) {//TODO Auto-generated method stubViewview = null;if(convertView == null) {view= View.inflate(MainActivity.this, R.layout.item, null);}else {view= convertView;}TextViewtv_name = (TextView) view.findViewById(R.id.tv_name);TextViewtv_phone = (TextView) view.findViewById(R.id.tv_phone);tv_name.setText(userList.get(position).name);tv_phone.setText(userList.get(position).phone);returnview;}}}
四、 网络编程
关于网络方面的编程。
1. 网页源码查看器
创建项目,设计UI
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.htmlcodeviewer.MainActivity" ><EditTextandroid:id="@+id/et_url"android:layout_width="match_parent"android:layout_height="wrap_content"android:inputType="textUri"android:hint="请输入网址"/><Buttonandroid:id="@+id/btn_show"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="查看"android:layout_below="@id/et_url"/><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@id/btn_show"><TextViewandroid:id="@+id/tv_code"android:layout_width="match_parent"android:layout_height="match_parent" /></ScrollView> </RelativeLayout>
编写入口方法
public class MainActivity extends Activity {privateEditText et_url;privateButton btn_show;privateTextView tv_code;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if(android.os.Build.VERSION.SDK_INT > 9) {StrictMode.ThreadPolicypolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();StrictMode.setThreadPolicy(policy);}et_url= (EditText) findViewById(R.id.et_url);btn_show= (Button) findViewById(R.id.btn_show);tv_code= (TextView) findViewById(R.id.tv_code);btn_show.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubStringurlStr = et_url.getText().toString().trim();if(urlStr == null) {return;}else {try{URLurl = new URL(urlStr);HttpURLConnectionurlConnection = (HttpURLConnection) url.openConnection();urlConnection.setRequestMethod("GET");urlConnection.setConnectTimeout(10000);intresCode = urlConnection.getResponseCode();if(resCode == 200) {InputStreaminputStream = urlConnection.getInputStream();Stringresult = Utils.getStringFromStream(inputStream);tv_code.setText(result);}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}});}}
编写将流转化为字符串的工具类
public class Utils {publicstatic String getStringFromStream(InputStream inputStream) {//TODO Auto-generated method stubByteArrayOutputStreambaos = new ByteArrayOutputStream();intlen = -1;byte[]buffer = new byte[1024];try{while((len = inputStream.read(buffer)) != -1) {baos.write(buffer,0, len);}inputStream.close();byte[]byteArray = baos.toByteArray();returnnew String(byteArray, "utf-8");}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();returnnull;}}}
2. android4.0以上主线程联网问题
联网是一个比较耗时的操作。4.0后联网必须要在子线程中,否则抛出android.os.NetworkOnMainThreadException。如果主线程被阻塞5秒以上,就会抛出Activity/Application not responding,ANR异常。如果多次抛出ANR异常,会降低产品的可靠度,导致被用户抛弃。为了避免ANR,需要把耗时的操作放到子线程中,联网就是一种耗时的操作。为了改善使用体验,4.0后联网必须要在子线程中实现。为了减少内存开销,可以使用匿名内部类的方式,在主线程方法中开启子线程联网。
需要注意的是子线程不能修改UI,需要在主线程中修改,这是因为多线程中容易出现安全问题,为了避免多个线程修改同一个UI导致矛盾的UI,所以这样规定,修改UI只能通过主线程,所以主线程又叫UI线程。由于数据是在子线程中获取的,但是子线程不能修改UI,所以需要跨线程传递数据,通知主线程获取数据并给主线程数据,需要使用Handler类。
public class MainActivity extends Activity {privateEditText et_url;privateButton btn_show;privateTextView tv_code;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {super.handleMessage(msg);tv_code.setText((String)msg.obj);};};@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//if (android.os.Build.VERSION.SDK_INT > 9) {//StrictMode.ThreadPolicy policy = new//StrictMode.ThreadPolicy.Builder()//.permitAll().build();//StrictMode.setThreadPolicy(policy);//}Stringname = Thread.currentThread().getName();Toast.makeText(this,name, Toast.LENGTH_SHORT).show();et_url= (EditText) findViewById(R.id.et_url);btn_show= (Button) findViewById(R.id.btn_show);tv_code= (TextView) findViewById(R.id.tv_code);btn_show.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {newThread() {publicvoid run() {StringurlStr = et_url.getText().toString().trim();if(urlStr == null) {return;}else {try{URLurl = new URL(urlStr);HttpURLConnectionurlConnection = (HttpURLConnection) url.openConnection();urlConnection.setRequestMethod("GET");urlConnection.setConnectTimeout(10000);intresCode = urlConnection.getResponseCode();if(resCode == 200) {InputStreaminputStream = urlConnection.getInputStream();Stringresult = Utils.getStringFromStream(inputStream);//子线程不能直接修改UI,通知主线程获取数据,并且把数据给主线程//tv_code.setText(result);Messagemsg = new Message();msg.obj= result;handler.sendMessage(msg);}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}}.start();//TODO Auto-generated method stub}});}}
3. 图片查看器
使用消息机制获取网络资源图片。
public class MainActivity extends Activityimplements OnClickListener {privateEditText et_url;privateButton btn_show;privateImageView iv_pic;finalprivate int GET_DATA_SUCCESS = 1;finalprivate int INET_ERROR = 2;finalprivate int SERVER_ERROR = 3;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {switch(msg.what) {caseGET_DATA_SUCCESS:Bitmapbitmap = (Bitmap) msg.obj;iv_pic.setImageBitmap(bitmap);break;caseINET_ERROR:Toast.makeText(MainActivity.this,"网络异常",Toast.LENGTH_SHORT).show();break;caseSERVER_ERROR:Toast.makeText(MainActivity.this,"服务器返回异常",Toast.LENGTH_SHORT).show();break;default:break;}};};@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_url= (EditText) findViewById(R.id.et_url);btn_show= (Button) findViewById(R.id.btn_show);iv_pic= (ImageView) findViewById(R.id.iv_pic);btn_show.setOnClickListener(this);}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubnewThread() {publicvoid run() {Stringpath = et_url.getText().toString().trim();try{URLurl = new URL(path);HttpURLConnection connection= (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setReadTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {InputStreaminputStream = connection.getInputStream();Bitmapbitmap = BitmapFactory.decodeStream(inputStream);Messagemsg = Message.obtain();msg.what= GET_DATA_SUCCESS;msg.obj= bitmap;handler.sendMessage(msg);}else {Messagemsg = Message.obtain();msg.what= SERVER_ERROR;handler.sendMessage(msg);}}catch (Exception e) {//TODO Auto-generated catch blockMessagemsg = Message.obtain();msg.what= INET_ERROR;handler.sendMessage(msg);}};}.start();}}
4. 给图片查看器添加图片缓存
在获取图片的输入流之后,输出到文件,保存在设备中。
public class MainActivity extends Activityimplements OnClickListener {privateEditText et_url;privateButton btn_show;privateImageView iv_pic;finalprivate int GET_DATA_SUCCESS = 1;finalprivate int INET_ERROR = 2;finalprivate int SERVER_ERROR = 3;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {switch(msg.what) {caseGET_DATA_SUCCESS:Bitmapbitmap = (Bitmap) msg.obj;iv_pic.setImageBitmap(bitmap);Toast.makeText(MainActivity.this,"从网络获取",Toast.LENGTH_SHORT).show();break;caseINET_ERROR:Toast.makeText(MainActivity.this,"网络异常",Toast.LENGTH_SHORT).show();break;caseSERVER_ERROR:Toast.makeText(MainActivity.this,"服务器返回异常",Toast.LENGTH_SHORT).show();break;default:break;}};};@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_url= (EditText) findViewById(R.id.et_url);btn_show= (Button) findViewById(R.id.btn_show);iv_pic= (ImageView) findViewById(R.id.iv_pic);btn_show.setOnClickListener(this);}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubfinalFile file = new File(getCacheDir(), "tomcat.png");if(file != null && file.length() > 0) {Bitmapbitmap = BitmapFactory.decodeFile(file.getAbsolutePath());iv_pic.setImageBitmap(bitmap);Toast.makeText(this,"使用缓存",Toast.LENGTH_SHORT).show();return;}newThread() {publicvoid run() {Stringpath = et_url.getText().toString().trim();try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setReadTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {InputStreaminputStream = connection.getInputStream();FileOutputStreamfos = new FileOutputStream(file);byte[]bts = new byte[1024];intlen = -1;while((len = inputStream.read(bts)) != -1) {fos.write(bts,0, len);}fos.close();//Bitmap bitmap =//BitmapFactory.decodeStream(inputStream);Bitmapbitmap = BitmapFactory.decodeFile(file.getAbsolutePath());Messagemsg = Message.obtain();msg.what= GET_DATA_SUCCESS;msg.obj= bitmap;handler.sendMessage(msg);}else {Messagemsg = Message.obtain();msg.what= SERVER_ERROR;handler.sendMessage(msg);}}catch (Exception e) {//TODO Auto-generated catch blockMessagemsg = Message.obtain();msg.what= INET_ERROR;handler.sendMessage(msg);}};}.start();}}
5. Handler的第二个常用功能,在未来的某个时间执行一个消息
这个功能相当于延迟执行消息,可以用来做倒计时、定时发布等。
public class MainActivity extends Activity {privateTextView tv_text;privateint count = 60;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {countDown();};};@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_text= (TextView) findViewById(R.id.tv_text);Messagemsg = Message.obtain();handler.sendMessageDelayed(msg,1000);}publicvoid countDown() {count--;if(count == -1) {Toast.makeText(this,"时间到",Toast.LENGTH_SHORT).show();return;}tv_text.setText(Integer.toString(count));Messagemsg = Message.obtain();handler.sendMessageDelayed(msg,1000);}}
可以使用sendEmptyMessageDelayed()来简化操作。
public class MainActivity extends Activity {privateTextView tv_text;privateint count = 60;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {countDown();};};@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv_text= (TextView) findViewById(R.id.tv_text);//Message msg = Message.obtain();//handler.sendMessageDelayed(msg, 1000);handler.sendEmptyMessageDelayed(1,1000);}publicvoid countDown() {count--;if(count < 0) {Toast.makeText(this,"时间到",Toast.LENGTH_SHORT).show();return;}else {tv_text.setText(Integer.toString(count));//Message msg = Message.obtain();//handler.sendMessageDelayed(msg, 1000);handler.sendEmptyMessageDelayed(1,1000);}}}
6. runOnUiThread方法
Activity的一个方法,作用是可以让其中的Runnable对象的方法运行在UI线程中。如果当前线程是主线程,则立即执行run方法;如果不是,就会把Runnable对象丢到消息队列,最终在主线程中执行。
runOnUiThread(newRunnable() {@Overridepublicvoid run() {//TODO Auto-generated method stub//无论这个方法在那个线程,这里的代码一定会执行在主线程里Toast.makeText(MainActivity.this,"网路连接失败", Toast.LENGTH_SHORT).show();}});
如果子线程也想更新UI,如果是比较简单的业务逻辑,可以使用runOnUiThread()。
7. 新闻客户端
创建项目,设计UI,在主控件中添加一个ListView。准备伪服务端数据xml文件和图片,放到tomcat的webapps的ROOT目录下。
然后,创建条目模版。为了方便获取图片,使用了开源项目android-smart-image-view-1.0.0。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content" ><com.loopj.android.image.SmartImageViewandroid:id="@+id/iv_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher"/><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toRightOf="@id/iv_icon"android:text="标题"android:layout_marginLeft="5dp"android:textSize="16sp"/><TextViewandroid:id="@+id/tv_content"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toRightOf="@id/iv_icon"android:layout_below="@id/tv_title"android:text="内容内容内容"android:layout_marginLeft="5dp"android:textSize="12sp"/><TextViewandroid:id="@+id/tv_comment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/tv_content"android:layout_alignParentRight="true"android:text="评论"android:textSize="10sp"/></RelativeLayout>
创建条目的javabean文件
public class NewsItem {publicString title;publicString description;publicString img;publicString type;publicString comment;@OverridepublicString toString() {return"NewsItem [title=" + title + ", description=" + description+", img=" + img + ", type=" + type + ", comment="+ comment+"]";}}
编写业务逻辑
public class MainActivity extends Activity {protectedstatic final int GET_DATA = 1;privateListView lv_list;privateString path = "http://192.168.0.100:8080/news.xml";privateArrayList<NewsItem> itemList = new ArrayList<NewsItem>();privateMyAdapter adapter;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {switch(msg.what) {case GET_DATA:lv_list.setAdapter(adapter);break;}};};@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);lv_list= (ListView) findViewById(R.id.lv_list);adapter= new MyAdapter();initData();}privatevoid initData() {//TODO Auto-generated method stubnewThread() {publicvoid run() {try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {InputStreaminputStream = connection.getInputStream();XmlPullParserpullParser = Xml.newPullParser();pullParser.setInput(inputStream,"utf-8");inteventType = pullParser.getEventType();NewsItemitem = null;while(eventType != XmlPullParser.END_DOCUMENT) {switch(eventType) {caseXmlPullParser.START_TAG:if("item".equals(pullParser.getName())) {item= new NewsItem();}else if ("title".equals(pullParser.getName())) {item.title= pullParser.nextText();}else if ("description".equals(pullParser.getName())){item.description= pullParser.nextText();}else if ("image".equals(pullParser.getName())) {item.img= pullParser.nextText();}else if ("type".equals(pullParser.getName())) {item.type= pullParser.nextText();}else if ("comment".equals(pullParser.getName())){item.comment= pullParser.nextText();}break;caseXmlPullParser.END_TAG:if("item".equals(pullParser.getName())) {itemList.add(item);}break;default:break;}eventType= pullParser.next();}for(NewsItem news : itemList) {System.out.println(news);}handler.sendEmptyMessage(GET_DATA);}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}};}.start();}privateclass MyAdapter extends BaseAdapter {@Overridepublicint getCount() {//TODO Auto-generated method stubreturnitemList.size();}@OverridepublicObject getItem(int position) {//TODO Auto-generated method stubreturnitemList.get(position);}@Overridepubliclong getItemId(int position) {//TODO Auto-generated method stubreturnposition;}@OverridepublicView getView(int position, View convertView, ViewGroup parent) {//TODO Auto-generated method stubViewview = null;if(convertView == null) {view= View.inflate(MainActivity.this, R.layout.item, null);}else {view= (View) convertView;}TextViewtv_title = (TextView) view.findViewById(R.id.tv_title);TextViewtv_content = (TextView) view.findViewById(R.id.tv_content);TextViewtv_comment = (TextView) view.findViewById(R.id.tv_comment);SmartImageViewiv_icon = (SmartImageView) view.findViewById(R.id.iv_icon);NewsItemitem = itemList.get(position);tv_title.setText(item.title);tv_content.setText(item.description);if("1".equals(item.type)) {tv_comment.setText(item.comment+ "条评论");tv_comment.setTextColor(Color.BLACK);}else if ("2".equals(item.type)) {tv_comment.setText("独家");tv_comment.setTextColor(Color.RED);}else if ("3".equals(item.type)) {tv_comment.setText("专题");tv_comment.setTextColor(Color.BLUE);}else if ("4".equals(item.type)) {tv_comment.setText("专栏");tv_comment.setTextColor(Color.CYAN);}iv_icon.setImageUrl(item.img);returnview;}}}
8. 自定义实现SmartImageView的加载图片功能
也是继承ImageView,自定义设置图片url的方法。
创建一个MySmartImageView类,设计使用自定义ImageView的UI。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.mysmartimageview.MainActivity" ><com.example.mysmartimageview.MySmartImageViewandroid:id="@+id/siv_pic"android:layout_width="wrap_content"android:layout_height="wrap_content"/></RelativeLayout>
编写自定义类的方法
public class MySmartImageView extends ImageView{protectedstatic final int GET_PIC_SUCCESS = 1;protectedstatic final int GET_PIC_FAILED = -1;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {switch(msg.what) {caseGET_PIC_SUCCESS:Bitmapbm = (Bitmap) msg.obj;setImageBitmap(bm);break;caseGET_PIC_FAILED:setImageResource(msg.arg1);break;}};};publicMySmartImageView(Context context) {super(context);//TODO Auto-generated constructor stub}publicMySmartImageView(Context context, AttributeSet attrs) {super(context,attrs);//TODO Auto-generated constructor stub}publicMySmartImageView(Context context, AttributeSet attrs, int defStyle) {super(context,attrs, defStyle);//TODO Auto-generated constructor stub}publicvoid setImageUrl(final String path) {newThread() {publicvoid run() {try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {InputStreaminputStream = connection.getInputStream();Bitmapbitmap = BitmapFactory.decodeStream(inputStream);Messagemsg = Message.obtain();msg.obj= bitmap;handler.sendMessage(msg);}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}.start();}//设置默认图方法publicvoid setImageUrl(final String path, final int resId) {newThread() {publicvoid run() {try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {InputStreaminputStream = connection.getInputStream();Bitmapbitmap = BitmapFactory.decodeStream(inputStream);Messagemsg = Message.obtain();msg.what= GET_PIC_SUCCESS;msg.obj= bitmap;handler.sendMessage(msg);}else {//没有成功获取到图片Messagemsg = Message.obtain();msg.what= GET_PIC_FAILED;msg.arg1= resId;handler.sendMessage(msg);}}catch (Exception e) {//TODO Auto-generated catch blockMessagemsg = Message.obtain();msg.what= GET_PIC_FAILED;msg.arg1= resId;handler.sendMessage(msg);}}}.start();}}
在入口方法中测试
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MySmartImageViewsiv_pic = (MySmartImageView) findViewById(R.id.siv_pic);//siv_pic.setImageUrl("http://192.168.0.100:8080/img/c.jpg");siv_pic.setImageUrl("http://192.168.0.100:8080/img/c.jpg",R.drawable.ic_launcher);}}
9. 使用HttpURLConnection提交参数到服务端
创建一个项目,编写一个用户登陆的UI界面。
使用get方式提交参数。
编写业务逻辑
public class MainActivity extends Activityimplements OnClickListener {privateString path = "http://192.168.0.100:8080/login/LoginServlet";privateEditText et_username;privateEditText et_password;privateButton btn_login;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_username= (EditText) findViewById(R.id.et_username);et_password= (EditText) findViewById(R.id.et_password);btn_login= (Button) findViewById(R.id.btn_login);btn_login.setOnClickListener(this);}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubnewThread() {publicvoid run() {Stringusername = et_username.getText().toString().trim();Stringpassword = et_password.getText().toString().trim();if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {showToast("用户名或密码不能为空");return;}StringtempPath = path + "?username=" + username +"&password="+password;try{URLurl = new URL(tempPath);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {InputStreaminputStream = (InputStream) connection.getInputStream();Stringresult = Utils.getStringFromStream(inputStream);showToast(result);}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}};}.start();}privatevoid showToast(final String str) {runOnUiThread(newRunnable() {@Overridepublicvoid run() {//TODO Auto-generated method stubToast.makeText(MainActivity.this,str, Toast.LENGTH_SHORT).show();}});}}
解决上述业务中的get参数到服务端的中文乱码问题
String tempPath = path + "?username="+ URLEncoder.encode(username, "utf-8") + "&password=" + URLEncoder.encode(password,"utf-8");
使用post方式提交参数
在UI界面添加一个做post提交登陆的按钮。
new Thread() {publicvoid run() {Stringusername = et_username.getText().toString().trim();Stringpassword = et_password.getText().toString().trim();if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)) {showToast("用户名或密码不能为空");return;}try{Stringparams = "username="+URLEncoder.encode(username, "utf-8")+"&password="+URLEncoder.encode(password, "utf-8");URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("POST");connection.setConnectTimeout(10000);//设置post请求相关的请求头connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");connection.setRequestProperty("Content-Length",Integer.toString(params.length()));connection.setDoOutput(true);connection.getOutputStream().write(params.getBytes());intcode = connection.getResponseCode();if(code == 200) {InputStreaminputStream = (InputStream) connection.getInputStream();Stringresult = Utils.getStringFromStream(inputStream);showToast(result);}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}};}.start();
注意由于使用url传输,需要使用UrlEncoder类编码,以防止Content-Length出现异常。
10. HttpClient
HttpClient是封装的http请求与响应操作的api,简化了代码。
创建项目,设计UI。
编写业务逻辑代码。
public class MainActivity extends Activity {privateButton btn_loginPost;privateButton btn_login;privateEditText et_password;privateEditText et_username;privateString path = "http://192.168.0.100:8080/login/LoginServlet";@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_username= (EditText) findViewById(R.id.et_username);et_password= (EditText) findViewById(R.id.et_password);et_username.setText("abc哈哈");btn_login= (Button) findViewById(R.id.btn_login);btn_loginPost= (Button) findViewById(R.id.btn_loginPost);btn_login.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubnewThread() {publicvoid run() {Stringusername = et_username.getText().toString().trim();Stringpassword = et_password.getText().toString().trim();if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)) {showToast("用户名或密码不能为空");return;}try{DefaultHttpClienthttpClient = new DefaultHttpClient();Stringuri = path + "?username="+URLEncoder.encode(username, "utf-8")+"&password="+URLEncoder.encode(password, "utf-8");HttpGetrequest = new HttpGet(uri);HttpResponseresponse = httpClient.execute(request);intcode = response.getStatusLine().getStatusCode();if(code == 200) {InputStreaminputStream = response.getEntity().getContent();Stringresult = Utils.getStringFromStream(inputStream);showToast(result);}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}};}.start();}});btn_loginPost.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubnewThread() {publicvoid run() {Stringusername = et_username.getText().toString().trim();Stringpassword = et_password.getText().toString().trim();if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)) {showToast("用户名或密码不能为空");return;}try{DefaultHttpClienthttpClient = new DefaultHttpClient();HttpPostpost = new HttpPost(path);List<BasicNameValuePair>parameters = new ArrayList<BasicNameValuePair>();BasicNameValuePairusernamePair = new BasicNameValuePair("username",username);BasicNameValuePairpasswordPair = new BasicNameValuePair("password",password);parameters.add(usernamePair);parameters.add(passwordPair);HttpEntityentity = new UrlEncodedFormEntity(parameters,"utf-8");post.setEntity(entity);HttpResponseresponse = httpClient.execute(post);intcode = response.getStatusLine().getStatusCode();if(code == 200) {InputStreaminputStream = response.getEntity().getContent();Stringresult = Utils.getStringFromStream(inputStream);showToast(result);}}catch (Exception e) {e.printStackTrace();}};}.start();}});}publicvoid showToast(final String str) {runOnUiThread(newRunnable() {@Overridepublicvoid run() {//TODO Auto-generated method stubToast.makeText(MainActivity.this,str, Toast.LENGTH_SHORT).show();}});}}
11. 使用开源项目提交参数
使用android-async-http-1.4.8。1.4.9会报莫名其妙的错误。
业务逻辑代码
public class MainActivity extends Activity {privateEditText et_username;privateEditText et_password;privateButton btn_login;privateButton btn_loginPost;privateString path = "http://192.168.0.100:8080/login/LoginServlet";@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_username= (EditText) findViewById(R.id.et_username);et_password= (EditText) findViewById(R.id.et_password);et_username.setText("abc哈哈");btn_login= (Button) findViewById(R.id.btn_login);btn_loginPost= (Button) findViewById(R.id.btn_loginPost);btn_login.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubStringusername = et_username.getText().toString().trim();Stringpassword = et_password.getText().toString().trim();if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {showToast("用户名或密码不能为空");return;}Stringurl;try{url= path + "?username="+URLEncoder.encode(username, "utf-8")+"&password="+URLEncoder.encode(password, "utf-8");AsyncHttpClientclient = new AsyncHttpClient();client.get(url,new AsyncHttpResponseHandler() {@Overridepublicvoid onSuccess(int arg0, Header[] arg1,byte[]response) {//TODO Auto-generated method stubtry{Stringresult = new String(response, "utf-8");showToast(result);}catch (UnsupportedEncodingException e) {//TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublicvoid onFailure(int arg0, Header[] arg1,byte[]arg2, Throwable arg3) {//TODO Auto-generated method stub}});}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}});btn_loginPost.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubStringusername = et_username.getText().toString().trim();Stringpassword = et_password.getText().toString().trim();if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {showToast("用户名或密码不能为空");return;}AsyncHttpClientclient = new AsyncHttpClient();RequestParamsparams = new RequestParams();params.put("username",username);params.put("password",password);client.post(path,params, new AsyncHttpResponseHandler() {@Overridepublicvoid onSuccess(int arg0, Header[] arg1,byte[]response) {//TODO Auto-generated method stubStringresult;try{result= new String(response, "utf-8");showToast(result);}catch (UnsupportedEncodingException e) {//TODO Auto-generated catch blocke.printStackTrace();}}@Overridepublicvoid onFailure(int arg0, Header[] arg1, byte[] arg2,Throwablearg3) {//TODO Auto-generated method stub}});}});}publicvoid showToast(final String str) {runOnUiThread(newRunnable() {@Overridepublicvoid run() {//TODO Auto-generated method stubToast.makeText(MainActivity.this,str, Toast.LENGTH_SHORT).show();}});}privateclass MyHandler extends AsyncHttpResponseHandler {@Overridepublicvoid onFailure(int arg0, Header[] arg1, byte[] arg2,Throwablearg3) {//TODO Auto-generated method stub}@Overridepublicvoid onSuccess(int arg0, Header[] arg1, byte[] response) {//TODO Auto-generated method stubtry{Stringresult = new String(response, "utf-8");showToast(result);}catch (UnsupportedEncodingException e) {// TODO Auto-generated catchblocke.printStackTrace();}}}}
12. 多线程下载
使用多线程下载在带宽限制内可以提高下载速度。使用多线程下载,要求服务端支持多线程读,客户端支持多线程写,具体就是,服务端支持conn.setRequestProperty(“Range”,…);客户端使用javaSE的RandomAccessFile对象,随机读写文件的任意位置。
创建一个项目,编写下载的java代码。
public class MultiThreadDownload {privatestatic String path = "http://192.168.0.100:8080/xfplay9.9991.exe";privatestatic int threadCount = 3;publicstatic void main(String[] args) {try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {intfileLength = connection.getContentLength();StringfileName = getFileName(path);RandomAccessFilefile = new RandomAccessFile(fileName, "rw");file.setLength(fileLength);intblockSize = fileLength / threadCount;for(int i = 0; i < threadCount; i++) {intstartIndex = i * blockSize;intendIndex = startIndex + blockSize - 1;if(i == threadCount - 1) {endIndex =fileLength - 1;}newDownloadThread(startIndex, endIndex, i).start();}}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}privatestatic class DownloadThread extends Thread {privateint startIndex;privateint endIndex;privateint threadID;publicDownloadThread(int startIndex, int endIndex, int threadID) {super();this.startIndex= startIndex;this.endIndex= endIndex;this.threadID= threadID;}publicvoid run() {try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);connection.setRequestProperty("Range","bytes=" + startIndex+"-" + endIndex);if(connection.getResponseCode() == 206) {System.out.println("线程:" + threadID);InputStreaminputStream = connection.getInputStream();intlen = -1;byte[]bytes = new byte[1024];RandomAccessFilefile = new RandomAccessFile(getFileName(path),"rw");file.seek(startIndex);while((len = inputStream.read(bytes)) != -1) {file.write(bytes,0, len);}file.close();System.out.println("线程:" + threadID + "下载结束");}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}publicstatic String getFileName(String path) {String[]result = path.split("/");returnresult[result.length - 1];}}
13. 断点续传
从意外或非意外导致的中断的位置,在下一次传输时继续传。比如某些下载软件,下载一个大的文件,下载了一半后,先暂停,然后关闭了计算机,下次打开计算机,打开软件,可以接着继续下载,就是断点续传。或者,在下载过程中,意外关机了,再次开机后,接着下载也是断点续传。
实现断点续传的原理是通过一个日志文件记录每个线程下载了多少数据,中断后再次下载,读取日志文件从记录下的文件读取数据作为开始写入的文件。需要注意的是,日志文件的保存最好使用RandomAccessFile的rwd模式确保每次写入都同步保存到磁盘而不是磁盘的缓存。
public class MultiThreadDownload2 {privatestatic String path = "http://192.168.0.100:8080/xfplay9.9991.exe";privatestatic int threadCount = 3;publicstatic void main(String[] args) {try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {intfileLength = connection.getContentLength();StringfileName = getFileName(path);RandomAccessFilefile = new RandomAccessFile(fileName, "rw");file.setLength(fileLength);intblockSize = fileLength / threadCount;for(int i = 0; i < threadCount; i++) {intstartIndex = i * blockSize;intendIndex = startIndex + blockSize - 1;if(i == threadCount - 1) {endIndex= fileLength - 1;}newDownloadThread(startIndex, endIndex, i).start();}}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}privatestatic class DownloadThread extends Thread {privateint startIndex;privateint endIndex;privateint threadID;publicDownloadThread(int startIndex, int endIndex, int threadID) {super();this.startIndex= startIndex;this.endIndex= endIndex;this.threadID= threadID;}publicvoid run() {try{//读出记录下来的位置FilebreakPointFile = new File(getFileName(path) + threadID+".log");if(breakPointFile != null && breakPointFile.length() > 0) {FileInputStreamfis = new FileInputStream(breakPointFile);byte[]bts = new byte[1024];intlen = -1;StringBuildersb = new StringBuilder();while((len = fis.read(bts)) != -1) {sb.append(newString(bts, 0, len));}startIndex= Integer.parseInt(sb.toString());}System.out.println(startIndex);URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);connection.setRequestProperty("Range","bytes=" + startIndex+"-" + endIndex);if(connection.getResponseCode() == 206) {System.out.println("线程:" + threadID + "开始下载" + startIndex);InputStreaminputStream = connection.getInputStream();intlen = -1;byte[]bytes = new byte[1024];RandomAccessFilefile = new RandomAccessFile(getFileName(path),"rw");file.seek(startIndex);intcount = 0;while((len = inputStream.read(bytes)) != -1) {file.write(bytes,0, len);count+= len;intposition = count + startIndex;RandomAccessFiletemp = new RandomAccessFile(getFileName(path)+ threadID + ".log", "rwd");temp.write(String.valueOf(position).getBytes());}file.close();System.out.println("线程:" + threadID + "下载结束");}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}publicstatic String getFileName(String path) {String[]result = path.split("/");returnresult[result.length - 1];}}
14. android实现多线程下载
创建项目,设计UI。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:orientation="vertical"tools:context="com.example.multithreaddownloadandroid.MainActivity"><EditTextandroid:id="@+id/et_url"android:layout_width="match_parent"android:layout_height="wrap_content"android:inputType="textUri"android:hint="请输入地址"/><EditTextandroid:id="@+id/et_count"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入线程个数"/><Buttonandroid:id="@+id/btn_download"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="下载" /><LinearLayoutandroid:id="@+id/ll_progress"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"></LinearLayout></LinearLayout>
创建进度条文件
<?xml version="1.0"encoding="utf-8"?><ProgressBarxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"style="@android:style/Widget.ProgressBar.Horizontal"></ProgressBar>
编写业务代码
public class MainActivity extends Activityimplements OnClickListener {privateEditText et_url;privateEditText et_count;privateButton btn_download;privateLinearLayout ll_progress;privateString path;privateint threadCount;privateint blockSize;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_url= (EditText) findViewById(R.id.et_url);et_count= (EditText) findViewById(R.id.et_count);btn_download= (Button) findViewById(R.id.btn_download);ll_progress= (LinearLayout) findViewById(R.id.ll_progress);btn_download.setOnClickListener(this);}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubpath= et_url.getText().toString().trim();ll_progress.removeAllViews();StringcountStr = et_count.getText().toString().trim();intcount = Integer.parseInt(countStr);threadCount= count;for(int i = 0; i < count; i++) {View.inflate(getApplicationContext(),R.layout.item, ll_progress);}newThread() {publicvoid run() {try{URLurl = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);intcode = connection.getResponseCode();if(code == 200) {intfileLength = connection.getContentLength();StringfileName = getFileName(path);RandomAccessFilefile = new RandomAccessFile(fileName,"rw");file.setLength(fileLength);blockSize= fileLength / threadCount;for(int i = 0; i < threadCount; i++) {intstartIndex = i * blockSize;intendIndex = startIndex + blockSize - 1;if(i == threadCount - 1) {endIndex= fileLength - 1;}ProgressBarpb = (ProgressBar) ll_progress.getChildAt(i);pb.setMax(endIndex- startIndex);newDownloadThread(startIndex, endIndex, i).start();}}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}.start();}privateclass DownloadThread extends Thread {privateint startIndex;privateint endIndex;privateint threadID;privateProgressBar pb;publicDownloadThread(int startIndex, int endIndex, int threadID) {super();this.startIndex= startIndex;this.endIndex= endIndex;this.threadID= threadID;this.pb= (ProgressBar) ll_progress.getChildAt(threadID);}publicvoid run() {try{//读出记录下来的位置FilebreakPointFile = new File(getFileName(path) + threadID+".log");if(breakPointFile != null && breakPointFile.length() > 0) {FileInputStreamfis = new FileInputStream(breakPointFile);byte[]bts = new byte[1024];intlen = -1;StringBuildersb = new StringBuilder();while((len = fis.read(bts)) != -1) {sb.append(newString(bts, 0, len));}startIndex= Integer.parseInt(sb.toString());}System.out.println(startIndex);URL url = new URL(path);HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");connection.setConnectTimeout(10000);connection.setRequestProperty("Range","bytes=" + startIndex+"-" + endIndex);if(connection.getResponseCode() == 206) {System.out.println("线程:" + threadID + "开始下载" + startIndex);InputStreaminputStream = connection.getInputStream();intlen = -1;byte[]bytes = new byte[1024 * 500];RandomAccessFilefile = new RandomAccessFile(getFileName(path),"rw");file.seek(startIndex);intcount = 0;RandomAccessFiletemp = new RandomAccessFile(getFileName(path)+ threadID + ".log", "rwd");while((len = inputStream.read(bytes)) != -1) {file.write(bytes,0, len);count+= len;intposition = count + startIndex;pb.setProgress(position- threadID * blockSize);temp.write(String.valueOf(position).getBytes());}file.close();System.out.println("线程:" + threadID + "下载结束");if(breakPointFile != null) {breakPointFile.delete();}}}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}privateString getFileName(String path) {String[]result = path.split("/");returngetCacheDir().getAbsolutePath() + "/"+result[result.length - 1];}}
15. 使用开源项目实现多线程下载
使用XUtils-master。
创建项目,导入源码,设计UI。
编写业务逻辑
public class MainActivity extends Activity {privateEditText et_url;privateButton btn_download;privateProgressBar pb_progress;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_url= (EditText) findViewById(R.id.et_url);btn_download= (Button) findViewById(R.id.btn_download);pb_progress= (ProgressBar) findViewById(R.id.pb_progress);btn_download.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubStringpath = et_url.getText().toString().trim();HttpUtilsutils = new HttpUtils();utils.download(path,getFileName(path), true,newRequestCallBack<File>() {@Overridepublicvoid onSuccess(ResponseInfo<File>responseInfo) {//TODO Auto-generated method stubToast.makeText(MainActivity.this,"下载完成",Toast.LENGTH_SHORT).show();}@Overridepublicvoid onFailure(HttpException error,Stringmsg) {//TODO Auto-generated method stub}@Overridepublicvoid onLoading(long total, long current,booleanisUploading) {//TODO Auto-generated method stubpb_progress.setMax((int)total);pb_progress.setProgress((int)current);}});}});}privateString getFileName(String path) {String[]result = path.split("/");returngetCacheDir().getAbsolutePath() + "/"+result[result.length - 1];}}
16. 上下文
Application对象表示当前应用。生命周期最长。使用这个对象的getApplicationContext获得的上下文的生命周期最长。使用getApplicationContext获得的上下文随应用的存亡,不会发生内存泄漏,相比使用this方法获得上下文可能发生内存泄漏。大部分情况下使用getApplicationContext是安全的,但是操作对话框的使用this是安全的。
五、 Activity页面跳转和数据传递
Activity,是四大组件之一。
1. 创建一个Activity
当创建一个项目时,会自动生成一个主Activity继承自Activity。自己创建一个Activity。继承Activity,重写onCreate方法。
创建一个layout文件。
在android清单文件中,声明对应的activity节点。在activity节点下,可以声明一个intent-filter,如果intent-filter配置为下述内容,则这个activity会最为启动项并在桌名创建一个图标。
<intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER"/></intent-filter>
在activity节点下可以声明label、icon项,如果这个activity是启动activity,则icon和label会显示在桌名上,如果没有声明这两项,则默认使用应用的label和icon。
<applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activityandroid:name=".MainActivity"android:label="第一个activity"android:icon="@drawable/a"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><activityandroid:name="com.example.createnewactivity.SecondActivity"android:label="第二个activity"android:icon="@drawable/b"><!-- <intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER"/></intent-filter> --></activity></application>
2. 隐式意图打开activity
通过设置要跳转到的页面的意图过滤器,在主界面或者跳转前页面中,通过匹配目标页面的意图过滤器,打开目标页面activity。这种方式称为隐式意图方式。
<activityandroid:name="com.example.createnewactivity.SecondActivity"android:label="第二个activity"android:icon="@drawable/b"><intent-filter><actionandroid:name="com.ten.second"/><categoryandroid:name="android.intent.category.DEFAULT" /><dataandroid:scheme="hello"/></intent-filter><intent-filter><actionandroid:name="com.ten.second2"/><categoryandroid:name="android.intent.category.DEFAULT" /><dataandroid:scheme="hello"android:mimeType="he/hehe"/></intent-filter></activity>
可以设置多个意图过滤器,通过匹配这些过滤器都可以打开activity
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn_open= (Button) findViewById(R.id.btn_open);btn_open.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubIntentintent = new Intent();intent.setAction("com.ten.second");intent.setData(Uri.parse("hello:world"));startActivity(intent);}});btn_open2= (Button) findViewById(R.id.btn_open2);btn_open2.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubIntentintent = new Intent();intent.setAction("com.ten.second2");//intent.setData(Uri.parse("hello:world"));//intent.setType("he/hehe");intent.setDataAndType(Uri.parse("hello:world"),"he/hehe");startActivity(intent);}});}
3. 显式意图打开activity
显式意图就是在创建意图时或者创建意图后,指定要打开的activity的类字节码文件或者包名类名来打开。需要在清单文件中声明要打开的activity。
创建一个activity及其xml文件,在清单文件中声明这个activity。
<activityandroid:name="com.example.createnewactivity.ThirdActivity"></activity>
在打开前页面设置按钮,为按钮添加显示意图。
publicvoid onClick(View v) {//TODO Auto-generated method stubIntentintent = new Intent(getApplicationContext(), ThirdActivity.class);startActivity(intent);}publicvoid otherApp(View v) {Intentintent = new Intent();intent.setClassName("com.example.httpclientcommitparams","com.example.httpclientcommitparams.MainActivity");startActivity(intent);}
一般,在应用中打开activity使用显示意图。如果需要其他应用打开本应用的activity使用隐式意图,以便不随意泄漏本应用的包名和类名信息,或者要开启别的应用的activity使用隐式意图。
4. 随机计算小程序
设计一个小程序,输入姓名性别,然后将数据传递到另一个界面,给出一个随机数。
创建项目,设计两个activity的UI。
登陆界面的activity
public class MainActivity extends Activityimplements OnClickListener {privateButton btn_calc;privateRadioGroup gb_gender;privateEditText et_name;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_name= (EditText) findViewById(R.id.et_name);gb_gender= (RadioGroup) findViewById(R.id.rg_gender);btn_calc= (Button) findViewById(R.id.btn_calc);btn_calc.setOnClickListener(this);}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubStringname = et_name.getText().toString().trim();intgender_id = gb_gender.getCheckedRadioButtonId();Stringsex = null;if(gender_id == R.id.rb_male) {sex= "男";}else if (gender_id == R.id.rb_female) {sex= "女";}Intentintent = new Intent(getApplicationContext(),ResultActivity.class);intent.putExtra("name",name);intent.putExtra("gender",sex);startActivity(intent);}}
跳转页面的activity,注意在清单中添加这个activity的声明。
public class ResultActivity extends Activity {privateTextView tv_info;privateTextView tv_result;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {//TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.activity_result);Intentintent = getIntent();Stringname = intent.getStringExtra("name");Stringsex = intent.getStringExtra("gender");Stringinfo = "姓名:" + name +";性别:" + sex;tv_info= (TextView) findViewById(R.id.tv_info);tv_result= (TextView) findViewById(R.id.tv_result);tv_info.setText(info);intresult = (int) (Math.random() * 100) + 1;tv_result.setText(Integer.toString(result));}}
5. 短信大全小程序
使用条目的点击事件。
创建项目,设计UI,编写条目模版。
编写业务逻辑。
public class MainActivity extends Activity {privateString[] msgs = { "彩旗飘飘风中舞,男女老少喜盈盈。","北风呼啸不可畏,元旦到来春光近。","烦恼不顺如云烟,立刻消散好运临。", "气象更新今日始,幸福快乐新年迎。祝元旦快乐!", "北风呼啸不可畏,元旦到来春光近。","烦恼不顺如云烟,立刻消散好运临。", "气象更新今日始,幸福快乐新年迎。祝元旦快乐!", "北风呼啸不可畏,元旦到来春光近。","烦恼不顺如云烟,立刻消散好运临。", "气象更新今日始,幸福快乐新年迎。祝元旦快乐!" };privateListView lv_list;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);lv_list= (ListView) findViewById(R.id.lv_sms);ArrayAdapter<String>adapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.item, msgs);lv_list.setAdapter(adapter);lv_list.setOnItemClickListener(newOnItemClickListener() {@Overridepublicvoid onItemClick(AdapterView<?> parent, View view,intposition, long id) {//TODO Auto-generated method stubStringmsg = msgs[position];System.out.println(msg);Intentintent = new Intent();intent.setAction("android.intent.action.SEND");intent.setType("text/plain");intent.putExtra("sms_body",msg);startActivity(intent);}});}}
6. 短信发送器小程序
通过按钮添加联系人,通过快速回复按钮添加快速回复的短信内容,可以直接发送短信的应用。
创建项目,设计主界面UI,添加联系人界面UI和快速回复界面UI。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.msgsender2.MainActivity" ><Buttonandroid:id="@+id/btn_add"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:text="+" /><EditTextandroid:id="@+id/et_tel"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_toLeftOf="@id/btn_add"android:inputType="numberSigned"android:hint="请输入号码" /><EditTextandroid:id="@+id/et_msgcontent"android:layout_width="match_parent"android:layout_height="200dp"android:layout_below="@id/btn_add"android:layout_marginTop="10dp"android:gravity="top"android:hint="请输入短信内容"/><Buttonandroid:id="@+id/btn_reply"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/et_msgcontent"android:text="快速回复" /><Buttonandroid:id="@+id/btn_send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/btn_reply"android:text="发送" /></RelativeLayout>
快速回复界面和添加联系人界面都是一个ListView。
创建两个界面的item模版。
编写业务逻辑
主控件
public class MainActivity extends Activityimplements OnClickListener {privateEditText et_tel;privateEditText et_msgcontent;privateButton btn_send;privateButton btn_reply;privateButton btn_add;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_tel= (EditText) findViewById(R.id.et_tel);et_msgcontent= (EditText) findViewById(R.id.et_msgcontent);btn_add = (Button)findViewById(R.id.btn_add);btn_reply= (Button) findViewById(R.id.btn_reply);btn_send= (Button) findViewById(R.id.btn_send);btn_add.setOnClickListener(this);btn_reply.setOnClickListener(this);btn_send.setOnClickListener(this);}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubintid = v.getId();switch(id) {caseR.id.btn_add:Intentintent = new Intent(getApplicationContext(),ContractActivity.class);//startActivity(intent);startActivityForResult(intent,1);break;caseR.id.btn_reply:Intentintent2 = new Intent(getApplicationContext(),ReplyActivity.class);startActivityForResult(intent2,2);break;caseR.id.btn_send:SmsManagermanager = SmsManager.getDefault();StringdestinationAddress = et_tel.getText().toString().trim();Stringtext = et_msgcontent.getText().toString().trim();manager.sendTextMessage(destinationAddress,null, text, null, null);break;}}@Overrideprotectedvoid onActivityResult(int requestCode, int resultCode, Intent data) {//TODO Auto-generated method stubsuper.onActivityResult(requestCode,resultCode, data);System.out.println("onactivityresult");if(data == null) {return;}switch(requestCode) {case1:Stringtel = data.getStringExtra("tel");et_tel.setText(tel);break;case2:Stringmsg_content = data.getStringExtra("msgContent");et_msgcontent.setText(msg_content);break;}}}
添加联系人号码控件
public class ContractActivity extends Activity{privateListView lv_contract;privateArrayList<Contract> conList = new ArrayList<Contract>();@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {//TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.activity_contract);lv_contract= (ListView) findViewById(R.id.lv_contract);for(int i = 0; i < 20; i++) {Contractcon = new Contract();con.name= "abc" + i;con.tel= "1351234" + i;conList.add(con);}MyAdapteradapter = new MyAdapter();lv_contract.setAdapter(adapter);lv_contract.setOnItemClickListener(newOnItemClickListener() {@Overridepublicvoid onItemClick(AdapterView<?> parent, View view,intposition, long id) {//TODO Auto-generated method stubContractcontract = conList.get(position);Stringname = contract.name;Stringtel = contract.tel;Intentintent = new Intent();intent.putExtra("name",name);intent.putExtra("tel",tel);setResult(1,intent);finish();}});}privateclass MyAdapter extends BaseAdapter {@Overridepublicint getCount() {//TODO Auto-generated method stubreturnconList.size();}@OverridepublicObject getItem(int position) {//TODO Auto-generated method stubreturnconList.get(position);}@Overridepubliclong getItemId(int position) {//TODO Auto-generated method stubreturnposition;}@OverridepublicView getView(int position, View convertView, ViewGroup parent) {//TODO Auto-generated method stubViewview = null;if(convertView == null) {view= View.inflate(getApplicationContext(),R.layout.item_contract,null);}else {view= convertView;}TextViewcontract_name = (TextView) view.findViewById(R.id.contract_name);TextViewcontract_tel = (TextView) view.findViewById(R.id.contract_tel);contract_name.setText(conList.get(position).name);contract_tel.setText(conList.get(position).tel);returnview;}}}
快速回复控件
public class ReplyActivity extends Activity {privateString[] msgs = { "春回大地莺梳柳,","福到农家燕衔泥。", "小康入户春风暖;", "福字进门日子红。","三春巧画新时代", "四海高歌大浪潮","鸡犬相闻邻里睦;", "燕莺清唱画中欢。", "淘到春风归作宝","裹成福气寄回家","梅花一路追新梦", "竹叶两行入画图" };privateListView lv_reply;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {//TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.activity_reply);lv_reply= (ListView) findViewById(R.id.lv_reply);ArrayAdapter<String>adapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.item_reply, msgs);lv_reply.setAdapter(adapter);lv_reply.setOnItemClickListener(newOnItemClickListener() {@Overridepublicvoid onItemClick(AdapterView<?> parent, View view,intposition, long id) {//TODO Auto-generated method stubStringmsgContent = msgs[position];Intentintent = new Intent();intent.putExtra("msgContent",msgContent);setResult(2,intent);finish();}});}}
将两个控件加入到清单。测试。
7. Activity的生命周期
Activity的四个状态。处于前台的activity,是可见的,并且可以跟用户进行交互;处于暂停状态,可见,但不能被操作,当内存资源及其紧张时被杀死;处于停止状态,不可见,不可被操作;销毁状态,activity被系统杀死或者调用finish方法主动退出。
这四种状态就是Activity的生命周期。
创建一个项目,调用各个生命周期的方法。
onCreate当activity创建的时候执行这个方法。
onStart走完这个方法activity就被用户端看到。
onResume走这个方法使activity处于前台的状态,可见可被操作。
onPause这个方法使activity处于暂停状态,可见但不可被操作。
onStop这个方法使activity处于停止状态,不可见不可被操作。
onDestroy这个方法使activity被销毁,回收内存。
onRestart这个方法使activity从停止状态回到前台。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);System.out.println("oncreate");}publicvoid open(View v) {Intentintent = new Intent(getApplicationContext(),SecondActivity.class);startActivity(intent);}@Overrideprotectedvoid onStart() {//TODO Auto-generated method stubsuper.onStart();System.out.println("onStart");}@Overrideprotectedvoid onResume() {//TODO Auto-generated method stubsuper.onResume();System.out.println("onResume");}@Overrideprotectedvoid onPause() {//TODO Auto-generated method stubsuper.onPause();System.out.println("onPause");}@Overrideprotectedvoid onStop() {//TODO Auto-generated method stubsuper.onStop();System.out.println("onStop");}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();System.out.println("onDestroy");}@Overrideprotectedvoid onRestart() {//TODO Auto-generated method stubsuper.onRestart();System.out.println("onRestart");}}
在UI界面添加一个按钮,跳转到另一个界面,测试从停止状态回到前台。
创建一个透明应用,打开这个应用,并覆盖已经打开的测试应用,可以看到被测试应用处于暂停状态。
创建透明应用,需要在清单文件的application标签下添加:
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
生命周期方法的应用很普遍,比如在使用一个应用的过程中,来电话了,这个时候可以在应用的onStop或者onPause方法中做一些操作。比如操作数据库时,对于频繁使用数据库的应用,可以在打开数据库后,不立即close,而是在onDestroy的方法中关闭,减少频繁打开、关闭数据库,节省内存资源。比如一个聊天应用,当压到后台后,再次调回前台时,可以在onResume方法中,获取最新的聊天记录,在界面显示。
onResume加载数据、恢复播放的状态。
onStop,可以在这个方法中停止所有关于刷新界面的操作。
onDestroy,释放资源的操作在这个方法中执行。
对于屏幕旋转时,activity的生命周期,当屏幕旋转后,先销毁再新创建。对于多数应用,在使用过程中,如果销毁了activity,再创建可能产生意外的结果。解决这个问题的方式是,第一,禁止屏幕旋转,在清单文件中给对应的activity配置android:screenOrientation="portrait"。第二中方式,可以随屏幕旋转,但是不执行生命周期方法,在activity中配置android:configChanges="orientation|screenSize"。
8. Activity的任务栈
创建一个activity就会放在任务栈中。android使用栈结构保存activity的实例。在清单文件中可以配置activity的启动模式。
standard,标准模式,默认使用这种模式。
singleTop,在任务栈的栈顶只有一个实例。如果栈顶有一个activity,则在创建这个activity的对象不会成功。比如使用二维码扫描使用singleTop。
singleTask,在任务栈中只有一个实例。如果在栈中已经存在一个singleTask的activity的对象,那么再次开启这个activity不会创建一个新的对象而是把这个activity上的对象都弹出栈,而使用这个activity。较常用,比如应用的主界面可以使用singleTask。
singleInstance,在当前任务栈中只有一个实例,并且这个实例会创建一个单独的任务栈,这个栈中只有这一个实例。调用浏览器,就可以使用singleInstance。
六、 BroadcastReceiver
1. 广播接受者介绍
广播接受者,接受意图发布的广播。现实中广播的特点是,数据传递方向是单向的,需要调到固定的频率,一对多。android的广播的特点是,作用范围有限,仅限于当前的手机,系统把一些重要的操作,通过广播的形式通知给所有的应用。
2. ip拨号器
通过拨号器,自动给拨打的号码添加前缀,比如添加17951等。
创建项目,设计UI,一个号码输入框和提交保存的按钮。
编写保存用户输入的前缀的业务。
public class MainActivity extends Activity {privateButton btn_save;privateEditText et_prefix;privateSharedPreferences sp;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_prefix= (EditText) findViewById(R.id.et_prefix);btn_save= (Button) findViewById(R.id.btn_save);sp= getSharedPreferences("info", MODE_PRIVATE);btn_save.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubStringprefix = et_prefix.getText().toString().trim();if(TextUtils.isEmpty(prefix)) {Toast.makeText(MainActivity.this,"前缀不可以为空",Toast.LENGTH_SHORT);}else {sp.edit().putString("prefix",prefix).commit();}}});}}
创建一个广播接受对象,并在清单中声明之,给它添加打电话的意图过滤。
<receiverandroid:name="com.example.ipdailer.DailReceiver"><intent-filter ><actionandroid:name="android.intent.action.NEW_OUTGOING_CALL"/></intent-filter></receiver>
编写广播接受对象的业务逻辑
public class DailReceiver extendsBroadcastReceiver {privateSharedPreferences sp;@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubsp= context.getSharedPreferences("info", context.MODE_PRIVATE);Stringprefix = sp.getString("prefix", "12530");System.out.println("打电话进行中");Stringresult = getResultData();System.out.println(result);setResultData(prefix+ result);}}
3. sd卡状态监听
创建项目,创建一个sd卡的广播接收者,并将这个接受者声明在清单文件,给其添加意图过滤。
<receiverandroid:name="com.example.sdstatuslistener.SDCardStatusReceiver"><intent-filter ><actionandroid:name="android.intent.action.MEDIA_MOUNTED"/><actionandroid:name="android.intent.action.MEDIA_UNMOUNTED"/><data android:scheme="file"/></intent-filter></receiver>
编写接收业务
public class SDCardStatusReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubSystem.out.println("sd卡状态发生变化");Stringaction = intent.getAction();if("android.intent.action.MEDIA_MOUNTED".equals(action)) {System.out.println("sd卡被挂载");}else if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {System.out.println("sd卡被卸载");}}}
4. 短信监听
创建项目,创建短信广播接受者,将短信接受者添加到清单。
<receiverandroid:name="com.example.smslistener.SmsReceiver"><intent-filter ><actionandroid:name="android.provider.Telephony.SMS_RECEIVED" /></intent-filter></receiver>
编写业务逻辑
public class SmsReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubSystem.out.println("收到短信了");Object[]obj = (Object[]) intent.getExtras().get("pdus");for(Object o : obj) {SmsMessagesmsMessage = SmsMessage.createFromPdu((byte[]) o);Stringfrom = smsMessage.getOriginatingAddress();Stringsms = smsMessage.getMessageBody();System.out.println("from:"+ from + ";body:" + sms);}}}
5. 应用的卸载和安装的广播
针对应用的安装和卸载的广播接收,常用来做竞品分析。
创建项目,创建应用安装卸载的广播接收,添加到清单。
<receiverandroid:name="com.example.appinstalluninstall.AppInstallUninstall"><intent-filter ><action android:name="android.intent.action.PACKAGE_ADDED"/><actionandroid:name="android.intent.action.PACKAGE_INSTALL"/><actionandroid:name="android.intent.action.PACKAGE_REMOVED"/><data android:scheme="package"/></intent-filter></receiver>
编写业务逻辑
public class AppInstallUninstall extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringaction = intent.getAction();Uriuri = intent.getData();if("android.intent.action.PACKAGE_ADDED".equals(action)) {System.out.println("应用被添加" + uri);}else if ("android.intent.action.PACKAGE_INSTALL".equals(action)) {System.out.println("应用被安装");}else if ("android.intent.action.PACKAGE_REMOVED".equals(action)) {System.out.println("应用被卸载" + uri);}}}
6. 开机启动广播接收
创建项目,创建一个开机启动的广播接收,在清单文件中添加这个接受者。
<receiverandroid:name="com.example.bootreceiver.BootReceiver"><intent-filter ><action android:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver>
编写接收广播的业务
public class BootReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubSystem.out.println("开机");Intentintent2 = new Intent(context, MainActivity.class);intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent2);}}
7. 发送无序广播
指定action可以发送无序广播。
创建一个发送广播的项目,设计UI,添加一个发送广播的按钮。
编写这个按钮的发送业务逻辑,可以携带键值对内容。
publicvoid sendbroadcast(View v) {//TODO Auto-generated method stubIntentintent = new Intent();intent.setAction("com.ten.broadcast");intent.putExtra("hello","world");sendBroadcast(intent);}
再创建一个项目,接收广播,创建广播接收者,在清单中添加接收者,指定action为发送源的action。
编写接收的业务
public class CustomReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubSystem.out.println("收到无序广播");Stringv = intent.getStringExtra("hello");System.out.println(v);}}
8. 发送有序广播
有序广播可以指定接收的顺序。
创建一个发送有序广播的项目,设计UI,添加一个按钮,发送广播。
编写发送的业务逻辑
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid send(View v) {Intentintent = new Intent();intent.setAction("com.ten.sendmoney");StringreceiverPermission = null;BroadcastReceiverresultReceiver = new FinalReceiver();Handlerscheduler = null;intinitialCode = Activity.RESULT_OK;StringinitialData = "广播初始值100";BundleinitialExtras = null;sendOrderedBroadcast(intent,receiverPermission, resultReceiver,scheduler,initialCode, initialData, initialExtras);}}
创建一个最后的接收者,由于由系统维护,所以不需要在清单文件中声明之。
public class FinalReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringresult = getResultData();System.out.println(result);Toast.makeText(context,"final:" + result, Toast.LENGTH_SHORT).show();}}
创建一个接收有序广播的项目,创建多个广播接收者。
public class ProReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringdata = getResultData();Toast.makeText(context,data, Toast.LENGTH_SHORT).show();setResultData("经过pro后80");}}public class CityReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringdata = getResultData();Toast.makeText(context,data, Toast.LENGTH_SHORT).show();setResultData("经过ci后60");}}public class CountyReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringdata = getResultData();Toast.makeText(context,data, Toast.LENGTH_SHORT).show();setResultData("经过cy后40");}}public class CountryReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringdata = getResultData();Toast.makeText(context,data, Toast.LENGTH_SHORT).show();}}
在清单文件中,声明这些接收者,并配置优先级。
<receiverandroid:name="com.example.receiveorderbroadcast.ProReceiver"><intent-filter android:priority="100" ><action android:name="com.ten.sendmoney"/></intent-filter></receiver><receiverandroid:name="com.example.receiveorderbroadcast.CityReceiver"><intent-filter android:priority="80" ><action android:name="com.ten.sendmoney"/></intent-filter></receiver><receiverandroid:name="com.example.receiveorderbroadcast.CountyReceiver"><intent-filter android:priority="60" ><action android:name="com.ten.sendmoney"/></intent-filter></receiver><receiverandroid:name="com.example.receiveorderbroadcast.CountryReceiver"><intent-filter android:priority="40" ><action android:name="com.ten.sendmoney"/></intent-filter></receiver>
比较无序广播和有序广播。有序广播,可以在接收者中通过优先级设置广播接收顺序;无序广播是一起收到。有序广播可以在接收中,abortBroadcast();中断广播;无序广播不可以,否则报错。有序广播可以在接收过程中,修改广播内容;无序广播不可以。
9. 特殊广播接收者
四大组件中,只有广播接收者可以不在清单文件中注册。在清单文件中,通过声明一个receiver,指定一个intent-filter的方法是静态注册。对于某些频繁执行的操作,比如屏幕的点亮、灭和手机的锁定、解锁等,如果使用静态注册的方法接收这些操作的广播,那么,这些操作就会变得卡顿。对于这类操作,需要使用动态注册广播接收者。也就是在代码中注册广播接收者。
创建一个项目,创建一个关于屏幕点亮、关灭的广播接收者。
public class ScreenLightReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringaction = intent.getAction();if("android.intent.action.SCREEN_OFF".equals(action)) {System.out.println("灭");}else if ("android.intent.action.SCREEN_ON".equals(action)) {System.out.println("点亮");}}}
在入口方法中编写注册广播接收者的代码并在销毁方法中注销之。
public class MainActivity extends Activity {privateBroadcastReceiver receiver;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);receiver= new ScreenLightReceiver();IntentFilterfilter = new IntentFilter();filter.addAction("android.intent.action.SCREEN_ON");filter.addAction("android.intent.action.SCREEN_OFF");registerReceiver(receiver,filter);}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();unregisterReceiver(receiver);}}
10. 自动填写短信验证码
通过短信监听的广播接收者,获得短信源的号码,然后发起一个广播,将验证码广播出去,在主activity中,创建一个接收验证码广播的内部类,动态注册这个广播接收者,并在广播接收者的方法中,将验证码赋值给EditText控件。
创建短信监听的广播接收者,并在清单中注册。
public class SmsReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubSystem.out.println("收到短信了");Object[]obj = (Object[]) intent.getExtras().get("pdus");for(Object o : obj) {SmsMessagesmsMessage = SmsMessage.createFromPdu((byte[]) o);Stringfrom = smsMessage.getOriginatingAddress();Stringsms = smsMessage.getMessageBody();System.out.println("from:"+ from + ";body:" + sms);if("15555215556".equals(from)) {Intentintent2 = new Intent();intent2.setAction("com.ten.smscode");intent2.putExtra("code",sms);context.sendBroadcast(intent2);}}}}
在主activity中编写业务逻辑
public class MainActivity extends Activity {privateEditText et_code;privateBroadcastReceiver receiver;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_code= (EditText) findViewById(R.id.et_code);receiver= new DynamicSmsReceiver();IntentFilterfilter = new IntentFilter();filter.addAction("com.ten.smscode");registerReceiver(receiver,filter);}privateclass DynamicSmsReceiver extends BroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubStringcode = intent.getStringExtra("code");et_code.setText(code);System.out.println(code);}}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();unregisterReceiver(receiver);}}
七、 样式与主题
1. 样式
使用values>styles.xml文件配置一个style,可以给多个样式相同的控件使用这个style,如果这多个控件需要修改样式,只需要修改这个style配置文件,即可,减少工作量。比如:
配置了一个style
<stylename="myTextView"><itemname="android:layout_width">wrap_content</item><itemname="android:layout_height">wrap_content</item><itemname="android:textSize">20sp</item><itemname="android:textColor">#ffff00</item><itemname="android:background">#770000</item></style>
在使用style的UI中
<TextViewstyle="@style/myTextView"android:text="@string/hello_world" /><TextViewstyle="@style/myTextView"android:text="@string/hello_world" /><TextViewstyle="@style/myTextView"android:text="@string/hello_world" /><TextViewstyle="@style/myTextView"android:text="@string/hello_world" />
2. 主题
主题实际上也是一种样式,区别是,主题是作用在整个app上的。
比如,配置一个主题:
<stylename="myAppTheme"><item name="android:background">#770000</item></style>
在清单文件中,给application设置主题为myAppTheme。
android:theme="@style/myAppTheme">
八、 国际化
Internationalization i18n,国际化。主要是应用的语言的国际化,就是支持随着系统语言的变化而改变为相应的语言展示的。通过创建values-zh等文件夹,修改strings.xml等文件的内容,同时在应用中使用到文字等的地方采用@string/app_name的形式表示内容。关于语言的缩写,通过查看pc端网页浏览器中的相关设置可以找到。
九、 对话框
常用的对话框有普通对话框、单选对话框、对选对话框和进度条对话框。创建对话框的时候,需要使用上下文,这个上下文必须是activity。因为对话框需要显示到当前的activity中,是activity的一部分,创建对象的时候需要通知系统当前对话框显示到哪个activity。对于进度条对话框,可以在子线程中修改主线程activity的界面。
创建一个项目,设计UI,添加四个对话框的按钮。
<Button android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="normal"android:text="普通对话框"/><Button android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="singleChoice"android:text="单选对话框"/><Button android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="multiChoice"android:text="多选对话框"/><Button android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="progress"android:text="进度对话框"/><TextViewandroid:id="@+id/tv_hobby" android:layout_width="match_parent"android:layout_height="wrap_content"/>
编写业务逻辑
public class MainActivity extends Activity {privateLinearLayout ll_docker;privateTextView tv_hobby;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ll_docker= (LinearLayout) findViewById(R.id.ll_docker);tv_hobby= (TextView) findViewById(R.id.tv_hobby);}publicvoid normal(View v) {AlertDialog.Builderbuilder = new Builder(this);builder.setTitle("普通对话框");builder.setMessage("要显示的内容");builder.setPositiveButton("确定", new OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {//TODO Auto-generated method stubToast.makeText(MainActivity.this,"去顶了",Toast.LENGTH_SHORT).show();}});builder.setNegativeButton("取消", new OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {//TODO Auto-generated method stubToast.makeText(MainActivity.this,"取消了",Toast.LENGTH_SHORT).show();}});builder.show();}publicvoid singleChoice(View v) {AlertDialog.Builderbuilder = new Builder(this);builder.setTitle("请选择背景颜色");finalString[] items = { "红色", "蓝色", "黑色", "白色", "绿色" };finalMap<String, String> colorMap = new HashMap<String, String>();colorMap.put("红色", "#ff0000");colorMap.put("蓝色", "#ffff00");colorMap.put("黑色", "#ffffff");colorMap.put("白色", "#000000");colorMap.put("绿色", "#00ff00");builder.setSingleChoiceItems(items,4, new OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {//TODO Auto-generated method stubll_docker.setBackgroundColor(Color.parseColor(colorMap.get(items[which])));dialog.dismiss();}});builder.show();}publicvoid multiChoice(View v) {AlertDialog.Builderbuilder = new Builder(this);builder.setTitle("请选择爱好");finalString[] items = { "篮球", "足球", "排球", "乒乓球", "地球" };finalSet<Integer> choice = new HashSet<Integer>();builder.setMultiChoiceItems(items,null,newOnMultiChoiceClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which,booleanisChecked) {//TODO Auto-generated method stubif(isChecked) {choice.add(which);}else {if(choice.contains(which)) {choice.remove(which);}}StringBuildersb = new StringBuilder("爱好:");for(Integer i : choice) {sb.append(items[i]+ "、");}Stringresult = sb.substring(0,sb.length()- "、".length());tv_hobby.setText(result);}});builder.setPositiveButton("确定", new OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {//TODO Auto-generated method stubdialog.dismiss();}});builder.setNegativeButton("取消", new OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {//TODO Auto-generated method stubtv_hobby.setText("");}});builder.show();}publicvoid progress(View v) {finalProgressDialog dialog = new ProgressDialog(this);dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);dialog.setTitle("下载中");dialog.setMax(100);dialog.show();newThread() {publicvoid run() {for(int i = 0; i <= 100; i++) {dialog.setProgress(i);SystemClock.sleep(10);}dialog.dismiss();runOnUiThread(newRunnable() {@Overridepublicvoid run() {//TODO Auto-generated method stubToast.makeText(MainActivity.this,"下载完成",Toast.LENGTH_SHORT).show();}});}}.start();}}
十、 service服务
1. 进程
关于android中的进程和线程。当应用运行之后,系统就会创建一个linux进程,大部分情况下一个应用对应一个进程。每个进程开始有一个线程,默认下,所有的组件都运行在同一个进程和同一个线程中。四大组件都运行在主线程中。android系统会尽量保证每一个开启的进程尽量长的运行在系统上。
前台进程,当前用户正在操作的进程是前台进程。包括,当前进程中有activity处于可见可操作的状态,activity执行了onResume之后并且留在这个状态正在被用户操作;service执行生命周期方法以及广播接收者执行onReceive方法。前台进程几乎不会被杀死。
可视进程,有activity处于onPause状态,可见不可操作。比如有透明应用盖在一个应用上,或者有对话框盖在activity上。只有当前台进程内存不够时才会杀死可视进程。
服务进程,用startService开启的服务,并且运行在后台。
后台进程,activity处于onStop状态,但是没有被销毁。通常会有大量应用处于后台进程状态,哪个应用的进程被销毁取决于其LRU,最近最少使用。
空进程,没有任何组件在运行,保存这个空进程的目的,是为了缓存当前的进程,加快下次运行时间。
2. 开启服务
使用startService方法开启服务。执行的生命周期方法onCreate()->onStartCommand()->onStop()。结束服务使用stopService方法。
创建一个项目,UI中设计两个按钮,开启和关闭,编写一个服务,调用生命周期方法。
public class MyService extends Service {@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubSystem.out.println("onBind");returnnull;}@Overridepublicvoid onCreate() {//TODO Auto-generated method stubsuper.onCreate();System.out.println("onCreate");}@Overridepublicint onStartCommand(Intent intent, int flags, int startId) {//TODO Auto-generated method stubSystem.out.println("onStartCommand");returnsuper.onStartCommand(intent, flags, startId);}@Overridepublicvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();System.out.println("onDestroy");}}
在清单文件中注册这个服务。
<serviceandroid:name="com.example.startservice.MyService"></service>
编写业务
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid startservice(View v) {Intentservice = new Intent(this, MyService.class);startService(service);}publicvoid stopservice(View v) {Intentname = new Intent(this, MyService.class);stopService(name);}}
3. 电话录音机
利用服务,侦听接听的电话,进行录音。通过广播,开机,开启录音服务。
创建项目,创建一个侦听接电话的服务。在清单中注册这个服务。
public class RecordService extends Service {privateMediaRecorder recorder;@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubreturnnull;}@Overridepublicvoid onCreate() {//TODO Auto-generated method stubsuper.onCreate();TelephonyManagermanager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);MyPhoneStateListenerlistener = new MyPhoneStateListener();manager.listen(listener,PhoneStateListener.LISTEN_CALL_STATE);}privateclass MyPhoneStateListener extends PhoneStateListener {@Overridepublicvoid onCallStateChanged(int state, String incomingNumber) {switch(state) {caseTelephonyManager.CALL_STATE_IDLE:System.out.println("空闲");if(recorder != null) {try{recorder.stop();recorder.reset();recorder.release();}catch (Exception e) {e.printStackTrace();}}break;caseTelephonyManager.CALL_STATE_RINGING:System.out.println("响铃" + incomingNumber);recorder= new MediaRecorder();recorder.setAudioSource(MediaRecorder.AudioSource.MIC);recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);recorder.setOutputFile(getCacheDir()+"/"+incomingNumber+"-Date"+new SimpleDateFormat("yyyyMMddHHmmss").format(newDate(System.currentTimeMillis()))+".3gp");try{recorder.prepare();}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}break;caseTelephonyManager.CALL_STATE_OFFHOOK:System.out.println("接听" + incomingNumber);recorder.start();break;}}}}
创建一个接收开机完毕的广播,完成开启服务的业务,注册这个广播。
public class BootReceiver extendsBroadcastReceiver {@Overridepublicvoid onReceive(Context context, Intent intent) {//TODO Auto-generated method stubIntentintent2 = new Intent(context, RecordService.class);context.startService(intent2);}}
测试。
4. bindService开启服务
bindService开启的服务与当前的activity是绑定的,生命周期随当前acitivity。
创建一个项目,创建一个服务,并在清单中注册之。
public class BindService extends Service {@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubSystem.out.println("onbind");returnnew MyBinder();}@Overridepublicvoid onCreate() {//TODO Auto-generated method stubsuper.onCreate();System.out.println("oncreate");}@Overridepublicint onStartCommand(Intent intent, int flags, int startId) {//TODO Auto-generated method stubSystem.out.println("onStartCommand");returnsuper.onStartCommand(intent, flags, startId);}@Overridepublicvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();System.out.println("onDestroy");}privateclass MyBinder extends Binder {}}
编写开启服务和结束服务的逻辑。
public class MainActivity extends Activity {privateMyConnection conn;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid start(View v) {Intentservice = new Intent(this, BindService.class);conn= new MyConnection();bindService(service,conn, BIND_AUTO_CREATE);}privateclass MyConnection implements ServiceConnection {@Overridepublicvoid onServiceConnected(ComponentName name, IBinder service) {//TODO Auto-generated method stubSystem.out.println("onServiceConnected");}@Overridepublicvoid onServiceDisconnected(ComponentName name) {//TODO Auto-generated method stubSystem.out.println("onServiceDisconnected");}}publicvoid stop(View v) {unbindService(conn);}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();unbindService(conn);}}
比较startService和bindService
bindService的生命周期,是onCreate->onBind,onBind只会执行一次。开启的服务会绑定到当前的activity,随着activity的退出而需要结束服务。结束的时候,调用unbindService方法,只能调用一次,否则报错。
startService的生命周期,是onCreate->onStartCommand,onStartCommand会执行多次。开启的服务与activity没有绑定关系。结束的时候,调用stopService方法,可以调用多次,只有第一次有效。
5. bindService通过activity调用服务中的方法
使用service的onBind方法的返回值,在service中创建一个内部类继承Binder类,通过这个内部类将service类中的方法传递给activity。然后,在activity创建的实现ServiceConnection接口的类中,获取到service传递过来的内部类。使用这个获得的内部类调用相关方法。
public class BindService extends Service {@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubSystem.out.println("onbind");returnnew MyBinder();}publicvoid showToast(String str) {Toast.makeText(getApplicationContext(),str, Toast.LENGTH_SHORT).show();}@Overridepublicvoid onCreate() {//TODO Auto-generated method stubsuper.onCreate();System.out.println("oncreate");}@Overridepublicint onStartCommand(Intent intent, int flags, int startId) {//TODO Auto-generated method stubSystem.out.println("onStartCommand");returnsuper.onStartCommand(intent, flags, startId);}@Overridepublicvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();System.out.println("onDestroy");}publicclass MyBinder extends Binder {publicvoid callShowToast(String s) {showToast(s);}}}
编写调用方法的业务逻辑
public class MainActivity extends Activity {privateMyConnection conn;privateMyBinder mybinder;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid start(View v) {Intentservice = new Intent(this, BindService.class);conn= new MyConnection();bindService(service,conn, BIND_AUTO_CREATE);}privateclass MyConnection implements ServiceConnection {@Overridepublicvoid onServiceConnected(ComponentName name, IBinder service) {//TODO Auto-generated method stubSystem.out.println("onServiceConnected");mybinder= (MyBinder) service;//mybinder.callShowToast("haha");}@Overridepublicvoid onServiceDisconnected(ComponentName name) {//TODO Auto-generated method stubSystem.out.println("onServiceDisconnected");}}publicvoid stop(View v) {unbindService(conn);}publicvoid callServiceMethod(View v) {//BindService service = new BindService();mybinder.callShowToast("haha");}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();unbindService(conn);}}
6. 音乐播放器
创建项目,设计UI,编写播放控制服务,并注册。
public class MusicPlayerService extends Service{@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubreturnnew MyBinder();}publicclass MyBinder extends Binder {publicvoid callNext() {next();}publicvoid callPrev() {prev();}publicvoid callPlay() {play();}publicvoid callPause() {pause();}}@Overridepublicvoid onCreate() {//TODO Auto-generated method stubsuper.onCreate();System.out.println("准备一个音乐播放器");}publicvoid next() {Toast.makeText(getApplicationContext(),"播放下一首",Toast.LENGTH_SHORT).show();}publicvoid play() {Toast.makeText(getApplicationContext(),"播放开始",Toast.LENGTH_SHORT).show();}publicvoid prev() {Toast.makeText(getApplicationContext(),"播放上一首",Toast.LENGTH_SHORT).show();}publicvoid pause() {Toast.makeText(getApplicationContext(),"暂停",Toast.LENGTH_SHORT).show();}}
编写播放控制的业务
public class MainActivity extends Activity {privateMyBinder binder;privateMyConnection conn;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intentservice = new Intent(this, MusicPlayerService.class);conn= new MyConnection();bindService(service,conn, BIND_AUTO_CREATE);}publicvoid nextOne(View v) {binder.callNext();}publicvoid preOne(View v) {binder.callPrev();}publicvoid play(View v) {binder.callPlay();}publicvoid pause(View v) {binder.callPause();}privateclass MyConnection implements ServiceConnection {@Overridepublicvoid onServiceConnected(ComponentName name, IBinder service) {binder= (MyBinder) service;}@Overridepublicvoid onServiceDisconnected(ComponentName name) {//TODO Auto-generated method stub}}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();unbindService(conn);}}
使用bindService打开服务,问题是,播放功能随着退出应用而结束。
7. 混合方式开启服务
混合使用startService和bindService方法,既可以使得activity和service沟通,也可以使得service的生命不止于activity结束而结束。开启服务需要两种方式的开启都执行,顺序不分前后;结束服务需要两种方式的结束都执行,顺序不分前后。
创建一个项目,设计UI,为两种方式分别设计开始和结束的按钮,创建服务,并注册。
public class MixStartService extends Service {@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubSystem.out.println("onbind");returnnew MyBinder();}publicclass MyBinder extends Binder {publicvoid start() {Toast.makeText(getApplicationContext(),"开始",Toast.LENGTH_SHORT).show();}publicvoid stop() {Toast.makeText(getApplicationContext(),"停止",Toast.LENGTH_SHORT).show();}}@Overridepublicvoid onCreate() {//TODO Auto-generated method stubsuper.onCreate();System.out.println("onCreate");}@Overridepublicint onStartCommand(Intent intent, int flags, int startId) {//TODO Auto-generated method stubSystem.out.println("onStartCommand");returnsuper.onStartCommand(intent, flags, startId);}@Overridepublicvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();System.out.println("onDestroy");}}
编写业务逻辑
public class MainActivity extends Activity {privateMyBinder binder;privateMyConnection conn;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid start(View v) {Intentintent = new Intent(this, MixStartService.class);startService(intent);if(binder != null) {binder.start();}}publicvoid stop(View v) {Intentintent = new Intent(this, MixStartService.class);stopService(intent);if(binder != null) {binder.stop();}}publicvoid bind(View v) {Intentintent = new Intent(this, MixStartService.class);conn= new MyConnection();bindService(intent,conn, BIND_AUTO_CREATE);//binder.start();}publicvoid unbind(View v) {if(binder != null && conn != null) {binder.stop();unbindService(conn);conn= null;}}privateclass MyConnection implements ServiceConnection {@Overridepublicvoid onServiceConnected(ComponentName name, IBinder service) {binder= (MyBinder) service;System.out.println("onServiceConnected");}@Overridepublicvoid onServiceDisconnected(ComponentName name) {//TODO Auto-generated method stub}}}
8. AIDL
android interface definition language,android接口定义语言,让其他应用可以调用当前应用service中的方法。 为了让其他应用可以访问本应用程序提供的服务,android采用远程过程调用remote procedure call,rpc来实现。rpc是属于ipc,inter process communication进程间通信的表现形式。每个androud应用间的通信都是ipc,四大组件中的三个acitivity、broadcastreceiver、provider都可以独立实现ipc。跨进程间访问的服务使用aidl实现rpc。
通过提供远程服务实现跨进程调用服务方法。
创建一个提供远程服务的项目,编写服务类,并注册。
public class RemoteService extends Service {@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubreturnnew MyBinder();}publicclass MyBinder extends Stub {publicvoid callRemoteMethod() {remoteMethod();}}publicvoid remoteMethod() {System.out.println("remoteMethod");}}
编写一个接口,暴露要被调用的方法。
interface IService {void callRemoteMethod();}
将这个接口文件的后缀改为aidl,注意aidl文件不支持public关键字。修改后在gen目录下会自动创建一个对应的java文件,这个文件里面的抽象类Stub继承了Binder类实现了自定义的接口。然后,将服务类中自定义的Binder类继承这个Stub类。
创建调用远程服务方法的项目,建立一个被调用项目的aidl文件的包同名的包,并将aidl文件复制到这个包下。设计UI,添加一个调用方法的按钮。编写业务逻辑。
public class MainActivity extends Activity {privateIService iservice;privateMyConnection conn;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intentservice = new Intent();service.setAction("com.ten.remoteservice");conn= new MyConnection();bindService(service,conn, BIND_AUTO_CREATE);}publicvoid callremote(View v) {try{iservice.callRemoteMethod();}catch (RemoteException e) {//TODO Auto-generated catch blocke.printStackTrace();}}privateclass MyConnection implements ServiceConnection {@Overridepublicvoid onServiceConnected(ComponentName name, IBinder service) {iservice= Stub.asInterface(service);}@Overridepublicvoid onServiceDisconnected(ComponentName name) {//TODO Auto-generated method stub}}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();unbindService(conn);}}
测试。
十一、 内容相关组件
1. 内容提供者
内容提供者的主要作用,将自己的数据提供给其他应用使用。如果不使用内容提供者将本应用的数据提供出去,将是不安全的,比如。
创建一个项目,创建数据库表并插入数据。
public class MyOpenHelper extendsSQLiteOpenHelper {publicMyOpenHelper(Context context) {super(context,"ten.db", null, 1);//TODO Auto-generated constructor stub}@Overridepublicvoid onCreate(SQLiteDatabase db) {//TODO Auto-generated method stubdb.execSQL("createtable info(_id integer primary key autoincrement,name varchar(20),phonevarchar(20))");db.execSQL("altertable info add age integer");}@Overridepublicvoid onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {//TODO Auto-generated method stub}}
编写初始化数据表和插入数据的业务逻辑。
public class MainActivity extends Activity {privateSQLiteDatabase database;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyOpenHelperopenHelper = new MyOpenHelper(this);database= openHelper.getReadableDatabase();}publicvoid insert(View v) {database.execSQL("insertinto info (name,phone) values ('张四','13567899876')");database.execSQL("insertinto info (name,phone) values ('王武','13545699876')");database.execSQL("insertinto info (name,phone) values ('马流','1567899876')");database.execSQL("insertinto info (name,phone) values ('沼气','1678765676')");}publicvoid query(View v) {Cursorresult = database.rawQuery("select * from info", null);while(result.moveToNext()) {Stringname = result.getString(result.getColumnIndex("name"));Stringphone = result.getString(result.getColumnIndex("phone"));System.out.println(name+ "--" + phone);}}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();database.close();}}
为了能够是其他应用可以访问被应用的数据,将数据库文件权限修改为777,并删除对应的日志文件。
创建其他应用访问本应用的数据表。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid access(View v) {Stringpath = "/data/data/com.example.opendatabase/databases/ten.db";SQLiteDatabasedatabase = SQLiteDatabase.openDatabase(path, null,SQLiteDatabase.OPEN_READWRITE);Cursorresult = database.rawQuery("select * from info", null);while(result.moveToNext()) {Stringname = result.getString(result.getColumnIndex("name"));Stringphone = result.getString(result.getColumnIndex("phone"));System.out.println(name+ "--" + phone);}}}
显然,这种方式是不安全的。
内容提供者,专门向其他应用提供内容访问的类,并且具有一定的安全控制性。
使用内容提供者的例子。
创建一个项目初始化数据表。
public class MyOpenHelper extendsSQLiteOpenHelper {publicMyOpenHelper(Context context) {super(context,"ten.db", null, 1);//TODO Auto-generated constructor stub}@Overridepublicvoid onCreate(SQLiteDatabase db) {//TODO Auto-generated method stubdb.execSQL("createtable info(_id integer primary key autoincrement,name varchar(20),phonevarchar(20))");db.execSQL("insertinto info (name,phone) values ('张四','13567899876')");db.execSQL("insertinto info (name,phone) values ('王武','13545699876')");db.execSQL("insertinto info (name,phone) values ('马流','1567899876')");db.execSQL("insertinto info (name,phone) values ('沼气','1678765676')");}@Overridepublicvoid onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {//TODO Auto-generated method stub}}public class MainActivity extends Activity {privateSQLiteDatabase db;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyOpenHelperhelper = new MyOpenHelper(this);db= helper.getReadableDatabase();}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();db.close();}}
创建内容提供者类,并注册之。
public class MyProvider extends ContentProvider{privateMyOpenHelper helper;privatestatic final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);privatestatic final int QUERY_SUCESS = 0;privatestatic final int INSERT_MATCHED = 1;privatestatic final int DELETE_MATCHED = 2;privatestatic final int UPDATE_MATCHED = 3;static{sUriMatcher.addURI("com.ten.provider","query", QUERY_SUCESS);sUriMatcher.addURI("com.ten.provider","insert", INSERT_MATCHED);sUriMatcher.addURI("com.ten.provider","delete", DELETE_MATCHED);sUriMatcher.addURI("com.ten.provider","update", UPDATE_MATCHED);}@Overridepublicboolean onCreate() {helper= new MyOpenHelper(getContext());returnfalse;}@OverridepublicCursor query(Uri uri, String[] projection, String selection,String[]selectionArgs, String sortOrder) {//TODO Auto-generated method stubintresult = sUriMatcher.match(uri);if(result == QUERY_SUCESS) {SQLiteDatabasedb = helper.getReadableDatabase();Cursorquery = db.query("info", projection, selection,selectionArgs,null, null, sortOrder);returnquery;}else {thrownew IllegalStateException("口令不符");}}@OverridepublicString getType(Uri uri) {//TODO Auto-generated method stubreturnnull;}@OverridepublicUri insert(Uri uri, ContentValues values) {//TODO Auto-generated method stubintresult = sUriMatcher.match(uri);if(result == INSERT_MATCHED) {SQLiteDatabasedb = helper.getReadableDatabase();longinsert = db.insert("info", null, values);db.close();returnUri.parse(String.valueOf(insert));}else {returnnull;}}@Overridepublicint delete(Uri uri, String selection, String[] selectionArgs) {//TODO Auto-generated method stubintresult = sUriMatcher.match(uri);if(result == DELETE_MATCHED) {SQLiteDatabasedb = helper.getReadableDatabase();intdelete = db.delete("info", selection, selectionArgs);db.close();returndelete;}else {return-1;}}@Overridepublicint update(Uri uri, ContentValues values, String selection,String[]selectionArgs) {intresult = sUriMatcher.match(uri);if(result == UPDATE_MATCHED) {SQLiteDatabasedb = helper.getReadableDatabase();intupdate = db.update("info", values, selection, selectionArgs);db.close();returnupdate;}else {return-1;}}}
<providerandroid:name="com.example.contentprovider.MyProvider"android:authorities="com.ten.provider"android:exported="true"></provider>
创建另一个项目,编写业务逻辑,使用内容解析者获取其他项目内容提供者提供的数据。
publicclass MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid access(View v) {Stringpath = "/data/data/com.example.opendatabase/databases/ten.db";SQLiteDatabasedatabase = SQLiteDatabase.openDatabase(path, null,SQLiteDatabase.OPEN_READWRITE);Cursorresult = database.rawQuery("select * from info", null);while(result.moveToNext()) {Stringname = result.getString(result.getColumnIndex("name"));Stringphone = result.getString(result.getColumnIndex("phone"));System.out.println(name+ "--" + phone);}}publicvoid accessProvider(View v) {ContentResolvercontentResolver = getContentResolver();Uriuri = Uri.parse("content://com.ten.provider/query");Cursorresult = contentResolver.query(uri, null, null, null, null);while(result.moveToNext()) {Stringname = result.getString(result.getColumnIndex("name"));Stringphone = result.getString(result.getColumnIndex("phone"));System.out.println(name+ "--" + phone);}}publicvoid insert(View v) {ContentResolverresolver = getContentResolver();Uriurl = Uri.parse("content://com.ten.provider/insert");ContentValuesvalues = new ContentValues();values.put("name","礼拜");values.put("phone","135665456");Uriinsert = resolver.insert(url, values);System.out.println(insert);}publicvoid update(View v) {ContentResolverresolver = getContentResolver();Uriuri = Uri.parse("content://com.ten.provider/update");ContentValuesvalues = new ContentValues();values.put("phone","11111111");Stringwhere = "name = ?";String[]selectionArgs = { "沼气" };intupdate = resolver.update(uri, values, where, selectionArgs);Toast.makeText(getApplicationContext(),update + "", Toast.LENGTH_SHORT).show();}publicvoid delete(View v) {ContentResolverresolver = getContentResolver();Uriurl = Uri.parse("content://com.ten.provider/delete");Stringwhere = "name=?";String[]selectionArgs = { "马流" };intdelete = resolver.delete(url, where, selectionArgs);Toast.makeText(getApplicationContext(),delete + "", Toast.LENGTH_SHORT).show();}}
2. 通过内容提供者访问短信数据库
创建一个项目,设计UI,添加3个按钮。
编写一个短信的javabean。
public class Sms {publicString date;publicString address;publicString body;@OverridepublicString toString() {return"Sms [date=" + date + ", address=" + address + ",body=" + body+"]";}}
在清单文件,添加读写短信的权限。
编写业务逻辑。
public class MainActivity extends Activity {privateArrayList<Sms> smslist = new ArrayList<Sms>();@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid query(View v) {ContentResolverresolver = getContentResolver();Uriuri = Uri.parse("content://sms");String[]projection = { "address", "date", "body" };Cursorresult = resolver.query(uri, projection, null, null, null);while(result.moveToNext()) {Smssms = new Sms();Stringaddress = result.getString(result.getColumnIndex("address"));StringdateStr = result.getString(result.getColumnIndex("date"));Datedate = new Date(Long.parseLong(dateStr));StringdateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);Stringbody = result.getString(result.getColumnIndex("body"));sms.address= address;sms.body= body;sms.date= dateFormat;smslist.add(sms);System.out.println("address:"+ address + "||date:" + dateFormat+"||body:" + body);}for(Sms sms : smslist) {System.out.println(sms);}result.close();}publicvoid backup(View v) {XmlSerializerserializer = Xml.newSerializer();try{serializer.setOutput(openFileOutput("sms.xml",MODE_PRIVATE),"utf-8");serializer.startDocument("utf-8",null);serializer.startTag(null,"SmsList");for(Sms sms : smslist) {serializer.startTag(null,"sms");serializer.startTag(null,"address");serializer.text(sms.address);serializer.endTag(null,"address");serializer.startTag(null,"date");serializer.text(sms.date);serializer.endTag(null,"date");serializer.startTag(null,"body");serializer.text(sms.body);serializer.endTag(null,"body");serializer.endTag(null,"sms");}serializer.endTag(null,"SmsList");serializer.endDocument();}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}publicvoid insert(View v) {ContentResolverresolver = getContentResolver();Uriurl = Uri.parse("content://sms");ContentValuesvalues = new ContentValues();values.put("address","123456");values.put("date",String.valueOf(System.currentTimeMillis()));values.put("body","测试插入短信");Uriresult = resolver.insert(url, values);Toast.makeText(getApplicationContext(),result.toString(),Toast.LENGTH_SHORT).show();}}
3. 联系人
使用联系人的内容提供者可以实现查询联系人和保存联系人的功能。
创建一个项目,设计UI,添加查询按钮,输入框和保存按钮。在清单文件,添加读写联系人的权限。
创建一个联系人的javabean。
public class Contact {publicString name;publicString address;publicString phone;publicString email;@OverridepublicString toString() {return"Contact [name=" + name + ", address=" + address + ",phone="+phone + ", email=" + email + "]";}}
编写业务逻辑。
public class MainActivity extends Activity {ArrayList<Contact>contList = new ArrayList<Contact>();privateEditText et_name;privateEditText et_phone;privateEditText et_address;privateEditText et_email;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_name= (EditText) findViewById(R.id.et_name);et_phone= (EditText) findViewById(R.id.et_phone);et_address= (EditText) findViewById(R.id.et_address);et_email= (EditText) findViewById(R.id.et_email);}publicvoid queryContract(View v) {ContentResolverresolver = getContentResolver();Uriraw_contacts_uri = Uri.parse("content://com.android.contacts/raw_contacts");Uridata_uri = Uri.parse("content://com.android.contacts/data");String[]projection = { "contact_id" };Cursorcontact_id = resolver.query(raw_contacts_uri, projection, null,null,null);while(contact_id.moveToNext()) {Stringid = contact_id.getString(contact_id.getColumnIndex("contact_id"));System.out.println(id);String[]projection2 = { "data1", "mimetype" };Cursordatas = resolver.query(data_uri, projection2,"raw_contact_id=?",new String[] { id }, null);Contactcont = new Contact();while(datas.moveToNext()) {Stringdata1 = datas.getString(datas.getColumnIndex("data1"));Stringmimetype = datas.getString(datas.getColumnIndex("mimetype"));System.out.println(data1+ "--" + mimetype);//String[] names = datas.getColumnNames();//for (String name : names) {//System.out.println(name);//}if("vnd.android.cursor.item/phone_v2".equals(mimetype)) {cont.phone= data1;}else if ("vnd.android.cursor.item/postal-address_v2".equals(mimetype)){cont.address= data1;}else if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {cont.email= data1;}else if ("vnd.android.cursor.item/name".equals(mimetype)) {cont.name= data1;}}contList.add(cont);}contact_id.close();for(Contact cont : contList) {System.out.println(cont);}}publicvoid save(View v) {Stringname = et_name.getText().toString().trim();Stringphone = et_phone.getText().toString().trim();Stringaddress = et_address.getText().toString().trim();Stringemail = et_email.getText().toString().trim();ContentResolverresolver = getContentResolver();Uriraw_contact_uri = Uri.parse("content://com.android.contacts/raw_contacts");Cursordata_ids = resolver.query(raw_contact_uri,newString[] { "contact_id" }, null, null, null);intcount = data_ids.getCount();data_ids.close();System.out.println("count:"+ count);intid = count + 1;ContentValuesraw_values = new ContentValues();raw_values.put("contact_id",id);resolver.insert(raw_contact_uri,raw_values);HashMap<String,String> datas = new HashMap<String, String>();datas.put("vnd.android.cursor.item/name",name);datas.put("vnd.android.cursor.item/phone_v2",phone);datas.put("vnd.android.cursor.item/postal-address_v2",address);datas.put("vnd.android.cursor.item/email_v2",email);Uridata_uri = Uri.parse("content://com.android.contacts/data");Set<String>keys = datas.keySet();for(String key : keys) {ContentValuesvalues = new ContentValues();values.put("raw_contact_id",id);values.put("mimetype",key);values.put("data1",datas.get(key));resolver.insert(data_uri,values);}}}
4. 内容观察者
当数据发生改变的时候,内容解析者发出一个通知ContentResolver.notifyChange,通过内容解析者可以注册一个内容解析者。常用于数据库内容发生变化后,通知界面做相应变化。
在某个应用的内容提供者类中通过内容解析者发出一个通知变化
public Uri insert(Uri uri, ContentValues values) {//TODO Auto-generated method stubintresult = sUriMatcher.match(uri);if(result == INSERT_MATCHED) {SQLiteDatabasedb = helper.getReadableDatabase();longinsert = db.insert("info", null, values);db.close();getContext().getContentResolver().notifyChange(uri,null);returnUri.parse(String.valueOf(insert));}else {returnnull;}}
创建一个内容观察者的项目
编写观察的业务逻辑
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ContentResolverresolver = getContentResolver();Uriuri = Uri.parse("content://com.ten.provider/insert");MyObserverobserver = new MyObserver(new Handler());resolver.registerContentObserver(uri,false, observer);}privateclass MyObserver extends ContentObserver {publicMyObserver(Handler handler) {super(handler);//TODO Auto-generated constructor stub}@Overridepublicvoid onChange(boolean selfChange, Uri uri) {//TODO Auto-generated method stubSystem.out.println(uri);}}}
十二、 多媒体
关于图片、音频是视频相关的。
1. 计算机表示图片的方式
BMP,质量最高,用于计算机。
PNG,质量高,用于计算机、网络,android中的图片资源一般使用这种格式。
jpeg,较高质量,用于计算机、网络、电子右键。
gif,质量最差,用于计算机、网络、电子邮件、动图。
单色位图,一个像素用一位2进制数表示。比如,100*100像素的图片,大小为10000/8= 1250byte。
16色位图,一个像素16种颜色,也就是4位2进制数,100*100像素的图片,大小为10000*4/8 = 5000byte。
256色位图,一个像素256种颜色,也就是8位2进制数,也就是1byte,100*100像素的图片,大小为10000*8/8=10000byte。
24位位图,一个像素用24位2进制数表示,也就是3个字节,100*100像素的图片,大小为10000*24/8=30000byte。
anroid中的图片使用的argb8888,一个像素就是32位2进制数表示,也就是4个字节,100*100像素的图片,大小为10000*4= 40000byte。
2. 加载大图
可能会发生内存溢出的异常,因为手机出厂时,应用的对内存的大小已经固定,所以,只能通过压缩图片的方式来加载大图。
创建一个项目,设计UI,一个按钮一个图片控件。
编写业务逻辑。
public class MainActivity extends Activity {privateImageView iv_pic;privateString path = "mnt/sdcard/11.jpg";privateint width;privateint height;privatePoint p;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_pic= (ImageView) findViewById(R.id.iv_pic);height= getWindowManager().getDefaultDisplay().getHeight();width= getWindowManager().getDefaultDisplay().getWidth();System.out.println(width+ ":" + height);p= new Point();getWindowManager().getDefaultDisplay().getSize(p);System.out.println(p.x+ ":" + p.y);}publicvoid loadpic(View v) {//Bitmap bm = BitmapFactory.decodeFile(path);//iv_pic.setImageBitmap(bm);loadpic3();}publicvoid loadpic1() {BitmapFactory.Optionsoptions = new Options();options.inSampleSize= 2;Bitmapbm = BitmapFactory.decodeFile(path, options);iv_pic.setImageBitmap(bm);}publicvoid loadpic2() {BitmapFactory.Optionsopts = new Options();opts.inJustDecodeBounds= true;Bitmapbm = BitmapFactory.decodeFile(path, opts);intpicW = opts.outWidth;intpicH = opts.outHeight;if(bm == null) {System.out.println("图片的宽度:" + picW + ";图片的高度:" +picH);}if(picW > width || picH > height) {intwidthIndex = Math.round((float) picW / (float) width);intheightIndex = Math.round((float) picH / (float) height);opts.inSampleSize= Math.max(widthIndex, heightIndex);}opts.inJustDecodeBounds= false;bm= BitmapFactory.decodeFile(path, opts);iv_pic.setImageBitmap(bm);}publicvoid loadpic3() {BitmapFactory.Optionsopts = new Options();opts.inSampleSize= 1;Bitmapbm = null;inti = 1;for(;;) {try{opts.inSampleSize= i;bm= BitmapFactory.decodeFile(path, opts);break;}catch (Error e) {i*= 2;System.out.println("i="+ i);}}iv_pic.setImageBitmap(bm);}}
3. 创建图片副本
创建图片副本,使用到Canvas类。创建一个项目,设计UI一个图片显示控件,编写业务逻辑。
public class MainActivity extends Activity {privateImageView iv_image;privateString path;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_image= (ImageView) findViewById(R.id.iv_image);//iv_image.setImageResource(R.drawable.c);Bitmapbm = BitmapFactory.decodeResource(getResources(), R.drawable.c);//iv_image.setImageBitmap(bm);//bm.setPixel(40, 40, Color.RED);//iv_image.setImageBitmap(bm);Bitmapcopybm = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(),bm.getConfig());Canvascanvas = new Canvas(copybm);Matrixmatrix = new Matrix();Paintpaint = new Paint();canvas.drawBitmap(bm,matrix, paint);for(int i = 0; i < 20; i++) {copybm.setPixel(40+ i, 40 + i, Color.RED);}iv_image.setImageBitmap(copybm);}}
4. 处理图片
使用Matrix类的方法处理图片。
创建一个项目,设计UI,添加两个图片显示控件,编写业务逻辑。
public class MainActivity extends Activity {privateImageView iv_dest;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_dest= (ImageView) findViewById(R.id.iv_dest);Bitmapbm = BitmapFactory.decodeResource(getResources(), R.drawable.b);intpicW = bm.getWidth();intpicH = bm.getHeight();Bitmapcopybm = Bitmap.createBitmap(picW, picH, bm.getConfig());Canvascanvas = new Canvas(copybm);Matrixmatrix = new Matrix();//matrix.setRotate(60, picW / 2, picH / 2);//matrix.setTranslate(30, 20);//matrix.setScale(0.5F, 0.5F);//matrix.setScale(-1f, 1f);//matrix.postTranslate(copybm.getWidth(), 0);matrix.setScale(1F,-1F);matrix.postTranslate(0,copybm.getHeight());Paintpaint = new Paint();canvas.drawBitmap(bm,matrix, paint);iv_dest.setImageBitmap(copybm);}}
5. 画画板
简单的画画的功能,创建一个项目,设计UI,添加变化颜色和线条的按钮以及保存的按钮。
编写业务逻辑
public class MainActivity extends Activity {privateImageView iv_image;privateCanvas canvas;privatePaint paint;privateBitmap copybm;privateBitmap bm;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_image= (ImageView) findViewById(R.id.iv_image);bm= BitmapFactory.decodeResource(getResources(), R.drawable.bj);copybm= Bitmap.createBitmap(bm.getWidth(), bm.getHeight(),bm.getConfig());canvas= new Canvas(copybm);Matrixmatrix = new Matrix();paint= new Paint();canvas.drawBitmap(bm,matrix, paint);iv_image.setImageBitmap(copybm);iv_image.setOnTouchListener(newOnTouchListener() {privatefloat startX;privatefloat startY;@Overridepublic boolean onTouch(Viewview, MotionEvent event) {//TODO Auto-generated method stubintaction = event.getAction();switch(action) {caseMotionEvent.ACTION_DOWN:System.out.println("按下");startX= event.getX();startY= event.getY();break;caseMotionEvent.ACTION_MOVE:System.out.println("移动");floatx = event.getX();floaty = event.getY();System.out.println(x+ ":" + y);canvas.drawLine(startX,startY, x, y, paint);startX= x;startY= y;iv_image.setImageBitmap(copybm);break;caseMotionEvent.ACTION_UP:System.out.println("抬起");break;default:break;}returntrue;}});}publicvoid changeColor(View v) {paint.setColor(Color.RED);}publicvoid changeBold(View v) {paint.setStrokeWidth(5f);}publicvoid save(View v) {Filefile = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+ ".png");FileOutputStreamfos = null;try{fos= new FileOutputStream(file);copybm.compress(CompressFormat.PNG,100, fos);Intentintent = new Intent();intent.setAction(Intent.ACTION_MEDIA_MOUNTED);intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));sendBroadcast(intent);}catch (FileNotFoundException e) {//TODO Auto-generated catch blocke.printStackTrace();}finally {if(fos != null) {try{fos.close();}catch (IOException e) {//TODO Auto-generated catch blocke.printStackTrace();}}}}}
6. 撕衣服应用
利用两种内容大致相同的图片,一张覆盖另一张,使用触摸事件修改上图的透明度,看起来像撕衣服的效果。
创建项目,设计UI,编写业务逻辑。
public class MainActivity extends Activity {privateImageView iv_front;privateMatrix matrix;privatePaint paint;privateBitmap copybm;privateBitmap bm;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_front= (ImageView) findViewById(R.id.iv_front);bm= BitmapFactory.decodeResource(getResources(), R.drawable.s1);copybm= Bitmap.createBitmap(bm.getWidth(), bm.getHeight(),bm.getConfig());Canvascanvas = new Canvas(copybm);matrix= new Matrix();paint= new Paint();canvas.drawBitmap(bm,matrix, paint);iv_front.setImageBitmap(copybm);iv_front.setOnTouchListener(newOnTouchListener() {privatefloat startX;privatefloat startY;@Overridepublicboolean onTouch(View v, MotionEvent event) {//TODO Auto-generated method stubintaction = event.getAction();switch(action) {caseMotionEvent.ACTION_DOWN:break;caseMotionEvent.ACTION_MOVE:floatx = event.getX();floaty = event.getY();try{for(int i = -6; i < 7; i++) {for(int j = -6; j < 7; j++) {if(Math.sqrt(i * i + j * j) <= 6) {copybm.setPixel((int)x + i, (int) y + j,Color.TRANSPARENT);}}}}catch (Exception e) {}iv_front.setImageBitmap(copybm);break;caseMotionEvent.ACTION_UP:break;}returntrue;}});}}
7. 音乐播放器
使用MediaPlayer类实现一个音乐播放器,需要使用混合服务。
创建一个项目,设计UI,一个控制按钮和一个进度条。创建音乐控制的服务。
public class MusicPlayerService extends Service{privateString path = "mnt/sdcard/gbqq.mp3";privateMediaPlayer player;@OverridepublicIBinder onBind(Intent intent) {//TODO Auto-generated method stubreturnnew MyBinder();}publicclass MyBinder extends Binder {publicvoid playPause() {if(player.isPlaying()) {player.pause();}else {player.start();}}publicboolean isPlaying() {returnplayer.isPlaying();}publicint getDuration() {returnplayer.getDuration();}publicint getCurrentPosition() {returnplayer.getCurrentPosition();}publicvoid seekTo(int msec) {player.seekTo(msec);}}@Overridepublicvoid onCreate() {//TODO Auto-generated method stubsuper.onCreate();player= new MediaPlayer();try{player.setDataSource(path);player.prepare();}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}}
编写业务逻辑
public class MainActivity extends Activity {privatestatic final int UPDATE_PROGRESS = 0;privateString path = "mnt/sdcard/gbqq.mp3";privateMyConnection conn;privateIntent service;privateMyBinder playerController;privateImageButton ib_controller;privateSeekBar sb_progress;privateHandler handler = new Handler() {publicvoid handleMessage(android.os.Message msg) {switch(msg.what) {caseUPDATE_PROGRESS:updateProgress();break;default:break;}};};@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ib_controller= (ImageButton) findViewById(R.id.ib_controller);sb_progress= (SeekBar) findViewById(R.id.sb_progress);sb_progress.setOnSeekBarChangeListener(newOnSeekBarChangeListener() {@Overridepublicvoid onStopTrackingTouch(SeekBar seekBar) {//TODO Auto-generated method stub}@Overridepublicvoid onStartTrackingTouch(SeekBar seekBar) {//TODO Auto-generated method stub}@Overridepublicvoid onProgressChanged(SeekBar seekBar, int progress,booleanfromUser) {//TODO Auto-generated method stubif(fromUser) {playerController.seekTo(progress);}}});service= new Intent(this, MusicPlayerService.class);startService(service);conn= new MyConnection();bindService(service,conn, BIND_AUTO_CREATE);}publicvoid play(View v) {//MediaPlayer player = new MediaPlayer();//try {//player.setDataSource(path);//player.prepare();//player.start();//} catch (Exception e) {TODO Auto-generated catch block//e.printStackTrace();//}playerController.playPause();updatePlayIcon();}publicclass MyConnection implements ServiceConnection {@Overridepublicvoid onServiceConnected(ComponentName name, IBinder service) {playerController= (MyBinder) service;updatePlayIcon();sb_progress.setMax(playerController.getDuration());sb_progress.setProgress(playerController.getCurrentPosition());}@Overridepublicvoid onServiceDisconnected(ComponentName name) {//TODO Auto-generated method stub}}@Overrideprotectedvoid onResume() {//TODO Auto-generated method stubsuper.onResume();if(playerController != null) {handler.sendEmptyMessage(UPDATE_PROGRESS);}}@Overrideprotectedvoid onStop() {//TODO Auto-generated method stubsuper.onStop();handler.removeCallbacksAndMessages(null);}@Overrideprotectedvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();unbindService(conn);}publicvoid updatePlayIcon() {if(playerController.isPlaying()) {ib_controller.setImageResource(R.drawable.pause);handler.sendEmptyMessage(UPDATE_PROGRESS);}else {ib_controller.setImageResource(R.drawable.play);handler.removeMessages(UPDATE_PROGRESS);}}publicvoid updateProgress() {System.out.println("updateProgress");intcurrentPosition = playerController.getCurrentPosition();sb_progress.setProgress(currentPosition);handler.sendEmptyMessageDelayed(UPDATE_PROGRESS,500);}}
8. MediaPlayer播放网络音乐
创建一个项目,设计UI,一个按钮。
编写业务逻辑。
public class MainActivity extends Activity {privateMediaPlayer player;privateString path = "http://192.168.0.101:8080/gbqq.mp3";privateButton btn_controller;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn_controller= (Button) findViewById(R.id.btn_controller);}publicvoid play(View v) {if(player == null) {player= new MediaPlayer();btn_controller.setText("暂停");try{player.setDataSource(path);player.prepareAsync();player.setOnPreparedListener(newOnPreparedListener() {@Overridepublicvoid onPrepared(MediaPlayer mp) {//TODO Auto-generated method stubmp.start();}});}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}else {if(player.isPlaying()) {btn_controller.setText("播放");player.pause();}else {btn_controller.setText("暂停");player.start();}}}}
9. MediaPlayer播放网络视频
创建一个项目,设计UI,添加两个按钮和一个SuifaceView。编写业务逻辑。
public class MainActivity extends Activity {privateSurfaceView sv_video;privateString path = "http://192.168.0.101:8080/v1.mp4";privateSurfaceHolder sh;privateMediaPlayer player;privateButton btn_play;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn_play= (Button) findViewById(R.id.btn_play);sv_video= (SurfaceView) findViewById(R.id.sv_video);sh= sv_video.getHolder();}publicvoid prepare(View v) {player= new MediaPlayer();try{player.setDataSource(path);player.prepareAsync();player.setDisplay(sh);player.setOnPreparedListener(newOnPreparedListener() {@Overridepublicvoid onPrepared(MediaPlayer mp) {//TODO Auto-generated method stubmp.start();btn_play.setText("暂停");}});}catch (Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}publicvoid play(View v) {if(player != null) {if(player.isPlaying()) {player.pause();btn_play.setText("播放");}else {player.start();btn_play.setText("暂停");}}}}
SurfaceView是重量级的控件,加载需要一些时间。
10. VideoView
VideoView是对MediaPlayer和SurfaceView的封装。
创建一个项目,设计UI,编写业务逻辑。
public class MainActivity extends Activity {privateVideoView vv_video;privateString path = "http://192.168.0.101:8080/v1.mp4";@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);vv_video= (VideoView) findViewById(R.id.vv_video);}publicvoid prepare(View v) {vv_video.setVideoPath(path);vv_video.setOnPreparedListener(newOnPreparedListener() {@Overridepublicvoid onPrepared(MediaPlayer mp) {//TODO Auto-generated method stubvv_video.start();}});vv_video.setMediaController(newMediaController(this));}publicvoid play(View v) {}}
VidwoView能够支持的视频格式有限的。
11. Vitamio
Vitamio是一个支持android/IOS的多媒体开发框架,支持众多常见的音视频格式。
下载vitamio的源码包,导入到eclipse,导入时将项目拷贝到工作台,右键源码项目>properties>Android>勾选isLibrary。
创建一个项目,右键项目>properties>Android>Add>添加vitamio的库。将vitamio的初始化activity声明到清单文件。
<activity android:name="io.vov.vitamio.activity.InitActivity"></activity>
在UI中,使用vitamio的VideoView,编写业务逻辑
public class MainActivity extends Activity {privateVideoView vv_video;privateString path = "http://192.168.0.101:8080/v1.mp4";@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if(!Vitamio.isInitialized(this)) {return;}setContentView(R.layout.activity_main);vv_video= (VideoView) findViewById(R.id.vv_video);}publicvoid prepare(View v) {vv_video.setVideoPath(path);vv_video.setOnPreparedListener(newOnPreparedListener() {@Overridepublicvoid onPrepared(MediaPlayer mp) {//TODO Auto-generated method stubvv_video.start();}});vv_video.setMediaController(newMediaController(this));}publicvoid play(View v) {}}
类似的框架还有VLC。
十三、 Fragment
1. 简介
Fragment是Android3.0后引入的一个API,起初是为了适应大屏幕的平板电脑,现在也被应用于平板。后来,智能手机也使用了Fragment,字面意思是碎片、片段,可以理解为是Activity片段。使用Fragment可以把屏幕划分为几块,然后进行分组,便于实现模块化的管理。Fragment不能单独使用,需要嵌套在Activity中使用。
2. 静态加载Fragment
创建一个项目,创建fragment的布局文件。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.fragmentindoor.MainActivity" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="第一个Fragment"/></RelativeLayout><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.fragmentindoor.MainActivity" ><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20sp"android:textColor="#00ff00"android:text="第二个Fragment"/></RelativeLayout>
编写两个Fragment类。
public class FirstFragment extends Fragment {@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,BundlesavedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_first, null);returnview;}}public class SecondFragment extends Fragment {@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,BundlesavedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_second, null);returnview;}}
在主布局文件中,声明fragment节点,注意f小写并且声明id。
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:orientation="horizontal"tools:context="com.example.fragmentindoor.MainActivity" ><fragmentandroid:name="com.example.fragmentindoor.FirstFragment"android:id="@+id/first"android:layout_weight="1"android:layout_width="0dp"android:layout_height="match_parent"/><fragmentandroid:name="com.example.fragmentindoor.SecondFragment"android:id="@+id/second"android:layout_weight="2"android:layout_width="0dp"android:layout_height="match_parent"/></LinearLayout>
3. 动态替换fragment
创建一个项目,设计UI,为动态替换的fragment添加一个容器。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.replacefragmentdynamically.MainActivity"><TextViewandroid:id="@+id/text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/hello_world" /><LinearLayoutandroid:id="@+id/fragemnt_container"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@id/text"android:orientation="vertical"></LinearLayout></RelativeLayout>
创建两个fragment布局文件。
创建两个Fragment类。
public class FirstFragment extends Fragment {@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,BundlesavedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_first, null);returnview;}}public class SecondFragment extends Fragment {@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,BundlesavedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_second, null);returnview;}}
动态替换
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);intwidth = getWindowManager().getDefaultDisplay().getWidth();intheight = getWindowManager().getDefaultDisplay().getHeight();FragmentManagermanager = getFragmentManager();FragmentTransactiontransaction = manager.beginTransaction();if(width > height) {transaction.replace(R.id.fragemnt_container,new SecondFragment());}else {transaction.replace(R.id.fragemnt_container,new FirstFragment());}transaction.commit();}}
4. 使用动态fragment制作选项卡的例子
创建一个项目,设计主布局UI,最下4个图片按钮,上面一个fragment容器。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.wechatview.MainActivity"><LinearLayoutandroid:id="@+id/ll_button"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:orientation="horizontal"android:background="@drawable/bg"android:gravity="center_vertical"><ImageButtonandroid:id="@+id/ib_chat"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:background="@null"android:src="@drawable/wechat"/><ImageButtonandroid:id="@+id/ib_contact"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:background="@null"android:src="@drawable/contacts"/><ImageButtonandroid:id="@+id/ib_find"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:background="@null"android:src="@drawable/find"/><ImageButtonandroid:id="@+id/ib_me"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:background="@null"android:src="@drawable/me"/></LinearLayout><LinearLayoutandroid:id="@+id/fragment_container"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_above="@id/ll_button"android:orientation="vertical"></LinearLayout></RelativeLayout>
创建4个fragment布局
创建4个fragment类。
在主布局类中编写选项卡逻辑。
public class MainActivity extends Activityimplementsandroid.view.View.OnClickListener{privateImageButton ib_chat;privateImageButton ib_contact;privateImageButton ib_find;privateImageButton ib_me;privateChatFragment chatFragment;privateContactsFragment contactsFragment;privateFindFragment findFragment;privateMeFragment meFragment;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ib_chat= (ImageButton) findViewById(R.id.ib_chat);ib_contact= (ImageButton) findViewById(R.id.ib_contact);ib_find= (ImageButton) findViewById(R.id.ib_find);ib_me= (ImageButton) findViewById(R.id.ib_me);ib_chat.setOnClickListener(this);ib_contact.setOnClickListener(this);ib_find.setOnClickListener(this);ib_me.setOnClickListener(this);ib_chat.performClick();}@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stubclearIcon();FragmentManagermanager = getFragmentManager();FragmentTransactiontransaction = manager.beginTransaction();switch(v.getId()) {caseR.id.ib_chat:if(chatFragment == null) {chatFragment= new ChatFragment();}transaction.replace(R.id.fragment_container,chatFragment);ib_chat.setImageResource(R.drawable.wechat2);break;caseR.id.ib_contact:if(contactsFragment == null) {contactsFragment= new ContactsFragment();}transaction.replace(R.id.fragment_container,contactsFragment);ib_contact.setImageResource(R.drawable.contacts2);break;caseR.id.ib_find:if(findFragment == null) {findFragment= new FindFragment();}transaction.replace(R.id.fragment_container,findFragment);ib_find.setImageResource(R.drawable.find2);break;caseR.id.ib_me:if(meFragment == null) {meFragment= new MeFragment();}transaction.replace(R.id.fragment_container,meFragment);ib_me.setImageResource(R.drawable.me2);break;}transaction.commit();}privatevoid clearIcon() {ib_chat.setImageResource(R.drawable.wechat);ib_contact.setImageResource(R.drawable.contacts);ib_find.setImageResource(R.drawable.find);ib_me.setImageResource(R.drawable.me);}}
5. 兼容低版本的fragment方法
跟fragment相关的api都要调用android.support.v4.app包的。
创建一个项目,在主UI中添加一个LinearLaytou作为fragment容器。
编写一个fragment布局,创建这个布局的类。
public class LowerFragment extends Fragment {@OverridepublicView onCreateView(LayoutInflater inflater,@NullableViewGroup container, @Nullable Bundle savedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_lower, null);returnnull;}}
动态替换,主activity要继承自FragmentActivity。
public class MainActivity extendsFragmentActivity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//FragmentManager manager = getFragmentManager();FragmentManagermanager = getSupportFragmentManager();FragmentTransactiontransaction = manager.beginTransaction();transaction.replace(R.id.fragment_container,new LowerFragment());transaction.commit();}}
6. 关于fragment中的点击事件
fragment中点击事件一般不采用第四种写法,即在xml中声明onclick属性的写法。而是在对应的Fragment类中使用setOnClickListener()的方法。
7. fragment的生命周期
创建一个项目,在主UI中添加一个fragment容器,创建一个fragment布局,创建fragment类。
public class FirstFragment extends Fragment {@Overridepublicvoid onAttach(Activity activity) {//TODO Auto-generated method stubsuper.onAttach(activity);System.out.println("onAttach");}@Overridepublicvoid onCreate(Bundle savedInstanceState) {//TODO Auto-generated method stubsuper.onCreate(savedInstanceState);System.out.println("onCreate");}@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,BundlesavedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_first, null);System.out.println("onCreateView");returnview;}@Overridepublicvoid onActivityCreated(Bundle savedInstanceState) {//TODO Auto-generated method stubsuper.onActivityCreated(savedInstanceState);System.out.println("onActivityCreated");}@Overridepublicvoid onStart() {//TODO Auto-generated method stubsuper.onStart();System.out.println("onStart");}@Overridepublicvoid onResume() {//TODO Auto-generated method stubsuper.onResume();System.out.println("onResume");}@Overridepublicvoid onPause() {//TODO Auto-generated method stubsuper.onPause();System.out.println("onPause");}@Overridepublicvoid onStop() {//TODO Auto-generated method stubsuper.onStop();System.out.println("onStop");}@Overridepublicvoid onDestroyView() {//TODO Auto-generated method stubsuper.onDestroyView();System.out.println("onDestroyView");}@Overridepublicvoid onDestroy() {//TODO Auto-generated method stubsuper.onDestroy();System.out.println("onDestroy");}@Overridepublicvoid onDetach() {//TODO Auto-generated method stubsuper.onDetach();System.out.println("onDetach");}}
动态替换之
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FragmentManagermanager = getFragmentManager();FragmentTransactiontransaction = manager.beginTransaction();transaction.replace(R.id.fragment_container,new FirstFragment());transaction.commit();}}
8. fragment之间的通信
创建一个项目,设计UI,添加两个fragment的容器,创建两个fragment布局,其中一个添加一个按钮。
创建两个fragment类。
public class FirstFragment extends Fragment {@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,BundlesavedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_first, null);Buttonbtn = (Button) view.findViewById(R.id.btn);btn.setOnClickListener(newOnClickListener() {@Overridepublicvoid onClick(View v) {//TODO Auto-generated method stub//SecondFragment second = new SecondFragment();SecondFragmentsecond = (SecondFragment) getActivity().getFragmentManager().findFragmentByTag("right");second.changeStr("测试修改");}});returnview;}}public class SecondFragment extends Fragment {privateTextView tv_text;@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,BundlesavedInstanceState) {//TODO Auto-generated method stubViewview = inflater.inflate(R.layout.fragment_second, null);tv_text= (TextView) view.findViewById(R.id.tv_text);returnview;}publicvoid changeStr(String str) {tv_text.setText(str);}}
动态替换
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FragmentManagermanager = getFragmentManager();FragmentTransactiontransaction = manager.beginTransaction();transaction.replace(R.id.ll_left,new FirstFragment(), "left");transaction.replace(R.id.ll_right,new SecondFragment(), "right");transaction.commit();}}
十四、 menu菜单
menu菜单在app中也会被使用到。
创建一个项目,在menu目录的main.xml文件中添加两个菜单选项。
<menuxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"tools:context="com.example.menu.MainActivity" ><itemandroid:id="@+id/action_settings"android:orderInCategory="100"android:showAsAction="never"android:title="@string/action_settings"/><itemandroid:id="@+id/action_settings1"android:orderInCategory="80"android:showAsAction="never"android:title="菜单项1"/><itemandroid:id="@+id/action_settings2"android:orderInCategory="60"android:showAsAction="never"android:title="菜单项2"/></menu>
编写业务逻辑。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublicboolean onCreateOptionsMenu(Menu menu) {//Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main,menu);returntrue;}@Overridepublicboolean onOptionsItemSelected(MenuItem item) {//Handle action bar item clicks here. The action bar will//automatically handle clicks on the Home/Up button, so long//as you specify a parent activity in AndroidManifest.xml.intid = item.getItemId();switch(id) {caseR.id.action_settings:Toast.makeText(getApplicationContext(),"主菜单",Toast.LENGTH_SHORT).show();break;caseR.id.action_settings1:Toast.makeText(getApplicationContext(),"菜单1",Toast.LENGTH_SHORT).show();break;caseR.id.action_settings2:Toast.makeText(getApplicationContext(),"菜单2",Toast.LENGTH_SHORT).show();break;}returnsuper.onOptionsItemSelected(item);}@Overridepublicboolean onMenuOpened(int featureId, Menu menu) {//TODO Auto-generated method stubAlertDialog.Builderbuilder = new Builder(this);builder.setTitle("菜单选项卡");builder.setMessage("请选择");builder.setPositiveButton("确定", new OnClickListener() {@Overridepublicvoid onClick(DialogInterface dialog, int which) {//TODO Auto-generated method stubToast.makeText(getApplicationContext(),"菜单确定",Toast.LENGTH_SHORT).show();}});builder.show();returnfalse;}}
十五、 自动补全TextView
在用户输入时,提供补全下拉列表。
创建一个项目,设计UI,添加一个AutoCompleteTextView。
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.autocompletetextview.MainActivity"><AutoCompleteTextViewandroid:id="@+id/actv_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:completionThreshold="1"android:hint="请输入内容"/></RelativeLayout>
创建一个下拉列表item。
编写业务逻辑。
public class MainActivity extends Activity {privateAutoCompleteTextView actv_text;privateString[] strings = { "哈哈","heh", "l了" };@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);actv_text= (AutoCompleteTextView) findViewById(R.id.actv_text);ArrayAdapter<String>adapter = new ArrayAdapter<String>(getApplicationContext(),R.layout.item, strings);actv_text.setAdapter(adapter);}}
十六、 动画
动画会在app中被使用到。
1. 帧动画
创建一个项目,在res目录下创建一个drawable文件夹,放入帧素材图片,并创建一个帧动画的xml文件。
<?xml version="1.0"encoding="utf-8"?><animation-listxmlns:android="http://schemas.android.com/apk/res/android" ><item android:drawable="@drawable/a1" android:duration="200"/><item android:drawable="@drawable/a2"android:duration="200" /><itemandroid:drawable="@drawable/a3" android:duration="200"/><itemandroid:drawable="@drawable/a4" android:duration="200"/><itemandroid:drawable="@drawable/a5" android:duration="200"/><itemandroid:drawable="@drawable/a6" android:duration="200"/><itemandroid:drawable="@drawable/a7" android:duration="200"/><itemandroid:drawable="@drawable/a8" android:duration="200"/></animation-list>
设计UI,添加一个图片view控件,来放帧素材,设置background为帧素材文件夹的animation。
<ImageViewandroid:id="@+id/iv_image"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/animation"android:layout_centerInParent="true"/>
编写帧动画业务逻辑
public class MainActivity extends Activity {privateImageView iv_image;privateAnimationDrawable background;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_image= (ImageView) findViewById(R.id.iv_image);background= (AnimationDrawable) iv_image.getBackground();iv_image.setOnTouchListener(newOnTouchListener() {@Overridepublicboolean onTouch(View v, MotionEvent event) {//TODO Auto-generated method stubif(background.isRunning()) {background.stop();}else {background.start();}returnfalse;}});}}
2. 补间动画
创建一个项目,设计UI,添加几个动画的按钮,添加一个ImageView,操作这个图片。
编写业务逻辑
public class MainActivity extends Activity {privateImageView iv_image;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_image= (ImageView) findViewById(R.id.iv_image);}publicvoid alpha(View view) {AlphaAnimationalpha = new AlphaAnimation(1.0f, 0.5f);alpha.setDuration(5000);alpha.setRepeatMode(Animation.RESTART);alpha.setRepeatCount(2);alpha.setFillAfter(true);iv_image.setAnimation(alpha);alpha.start();}publicvoid rotate(View view) {iv_image.getX();RotateAnimationrotate = new RotateAnimation(0, 360);intpivotXType = Animation.RELATIVE_TO_SELF;floatpivotXValue = 0.5f;intpivotYType = Animation.RELATIVE_TO_SELF;floatpivotYValue = 0.5f;rotate= new RotateAnimation(0, 360, pivotXType, pivotXValue,pivotYType,pivotYValue);rotate.setDuration(2000);rotate.setRepeatMode(Animation.RESTART);rotate.setRepeatCount(2);rotate.setFillAfter(true);iv_image.setAnimation(rotate);rotate.start();}publicvoid scale(View view) {ScaleAnimationanimation = new ScaleAnimation(1, 2, 1, 2);intpivotXType = Animation.RELATIVE_TO_SELF;floatpivotXValue = 0.5f;intpivotYType = Animation.RELATIVE_TO_SELF;floatpivotYValue = 0.5f;animation= new ScaleAnimation(1, 2, 1, 2, pivotXType, pivotXValue,pivotYType= Animation.RELATIVE_TO_SELF, pivotYValue);animation.setDuration(2000);animation.setRepeatMode(Animation.RESTART);animation.setRepeatCount(2);animation.setFillAfter(true);iv_image.setAnimation(animation);}publicvoid translate(View view) {TranslateAnimationanimation = new TranslateAnimation(10, 100, 10, 100);intfromXType = Animation.RELATIVE_TO_PARENT;floatfromXValue = -0.5f;inttoXType = Animation.RELATIVE_TO_PARENT;floattoXValue = 0.5f;intfromYType = Animation.RELATIVE_TO_PARENT;floatfromYValue = -0.5f;inttoYType = Animation.RELATIVE_TO_PARENT;floattoYValue = 0.5f;animation= new TranslateAnimation(fromXType, fromXValue, toXType,toXValue,fromYType, fromYValue, toYType, toYValue);animation.setDuration(1000);animation.setRepeatCount(2);animation.setRepeatMode(Animation.RESTART);animation.setFillAfter(true);animation.setInterpolator(newAccelerateDecelerateInterpolator());iv_image.setAnimation(animation);animation.start();}publicvoid set(View view) {AnimationSetset = new AnimationSet(false);AlphaAnimationalpha = new AlphaAnimation(1.0f, 0.5f);alpha.setDuration(5000);alpha.setRepeatMode(Animation.RESTART);alpha.setRepeatCount(2);alpha.setFillAfter(true);set.addAnimation(alpha);RotateAnimationrotate = new RotateAnimation(0, 360);intpivotXType = Animation.RELATIVE_TO_SELF;floatpivotXValue = 0.5f;intpivotYType = Animation.RELATIVE_TO_SELF;floatpivotYValue = 0.5f;rotate= new RotateAnimation(0, 360, pivotXType, pivotXValue,pivotYType,pivotYValue);rotate.setDuration(2000);rotate.setRepeatMode(Animation.RESTART);rotate.setRepeatCount(2);rotate.setFillAfter(true);set.addAnimation(rotate);ScaleAnimationscale = new ScaleAnimation(1, 2, 1, 2);scale= new ScaleAnimation(1, 2, 1, 2, pivotXType, pivotXValue,pivotYType= Animation.RELATIVE_TO_SELF, pivotYValue);scale.setDuration(2000);scale.setRepeatMode(Animation.RESTART);scale.setRepeatCount(2);scale.setFillAfter(true);set.addAnimation(scale);TranslateAnimationtranslate = new TranslateAnimation(10, 100, 10, 100);intfromXType = Animation.RELATIVE_TO_PARENT;floatfromXValue = -0.5f;inttoXType = Animation.RELATIVE_TO_PARENT;floattoXValue = 0.5f;intfromYType = Animation.RELATIVE_TO_PARENT;floatfromYValue = -0.5f;inttoYType = Animation.RELATIVE_TO_PARENT;floattoYValue = 0.5f;translate= new TranslateAnimation(fromXType, fromXValue, toXType,toXValue,fromYType, fromYValue, toYType, toYValue);translate.setDuration(1000);translate.setRepeatCount(2);translate.setRepeatMode(Animation.RESTART);translate.setFillAfter(true);translate.setInterpolator(newAccelerateDecelerateInterpolator());set.addAnimation(translate);iv_image.setAnimation(set);set.start();}}
3. 使用xml文件实现补间动画
一般使用xml文件实现动画,这样使得代码看起来简洁,也有利于解耦合。
创建一个项目,添加动画按钮和一个imageview。
在res目录下创建anim目录,并且创建每个动画的xml文件。
<?xml version="1.0"encoding="utf-8"?><alphaxmlns:android="http://schemas.android.com/apk/res/android"android:fromAlpha="1.0"android:toAlpha="0.5"android:duration="1000"android:repeatCount="2"android:repeatMode="restart"></alpha><?xml version="1.0"encoding="utf-8"?><rotatexmlns:android="http://schemas.android.com/apk/res/android"android:fromDegrees="0"android:toDegrees="245"android:pivotX="50%"android:pivotY="50%"android:duration="1000"android:repeatCount="2"android:repeatMode="restart"android:fillAfter="true"><?xml version="1.0"encoding="utf-8"?><scalexmlns:android="http://schemas.android.com/apk/res/android"android:fromXScale="1"android:toXScale="2"android:fromYScale="1"android:toYScale="2"android:pivotX="50%"android:pivotY="50%"android:duration="1000"android:repeatCount="2"android:repeatMode="reverse"android:fillAfter="true"></scale><?xml version="1.0"encoding="utf-8"?><translatexmlns:android="http://schemas.android.com/apk/res/android"android:fromXDelta="10"android:toXDelta="100"android:fromYDelta="10"android:toYDelta="100"android:duration="1000"android:repeatCount="2"android:interpolator="@android:anim/cycle_interpolator"android:repeatMode="reverse"android:fillAfter="true"></translate><?xml version="1.0"encoding="utf-8"?><setxmlns:android="http://schemas.android.com/apk/res/android"android:shareInterpolator="false"><translateandroid:fromXDelta="0"android:fromYDelta="0"android:toXDelta="200%"android:toYDelta="0" /><setandroid:interpolator="@android:anim/accelerate_interpolator"><scaleandroid:fromXScale="1.0"android:fromYScale="1.0"android:pivotX="0%"android:pivotY="100%"android:toXScale="1.5"android:toYScale="1.5" /><rotatexmlns:android="http://schemas.android.com/apk/res/android"android:fromDegrees="0"android:toDegrees="245"android:pivotX="50%"android:pivotY="50%"android:duration="1000"android:repeatCount="2"android:repeatMode="restart"android:fillAfter="true"/></set></set>
在主activity类中实现业务逻辑
public class MainActivity extends Activity {privateImageView iv_image;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_image= (ImageView) findViewById(R.id.iv_image);}publicvoid alphaX(View view) {Animationanimation = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.alpha);iv_image.setAnimation(animation);animation.start();}publicvoid rotateX(View view) {Animationanimation = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.rotate);iv_image.setAnimation(animation);animation.start();}publicvoid scaleX(View view) {Animationanimation = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.scale);iv_image.setAnimation(animation);animation.start();}publicvoid translateX(View view) {Animationanimation = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.translate);iv_image.setAnimation(animation);animation.start();}publicvoid setX(View view) {Animationanimation = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.set);iv_image.setAnimation(animation);animation.start();}}
4. 属性动画
使用ObjectAnimator类的ofFloat方法实现动画。
创建一个项目,设计UI,添加动画按钮和一个ImageView。
如果使用xml文件实现,需要在res目录下创建一个animator目录,并创建animator的xml文件。
<?xml version="1.0"encoding="utf-8"?><animator xmlns:android="http://schemas.android.com/apk/res/android"><objectAnimatorandroid:propertyName="rotation"android:duration="2000"android:valueFrom="0"android:valueTo="240"android:repeatCount="2"android:repeatMode="restart"></objectAnimator></animator>
编写业务逻辑
public class MainActivity extends Activity {privateImageView iv_image;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_image= (ImageView) findViewById(R.id.iv_image);}publicvoid alpha(View v) {ObjectAnimatoroa = ObjectAnimator.ofFloat(iv_image, "alpha", 1.0f,0.5f,0.9f, 0.1f, 0.6f);oa.setDuration(2000);oa.start();}publicvoid rotate(View v) {ObjectAnimatoroa = ObjectAnimator.ofFloat(iv_image, "rotation", 0, 20,180,90, 360);oa.setDuration(3000);oa.start();}publicvoid scale(View v) {ObjectAnimatoroa = ObjectAnimator.ofFloat(iv_image, "scaleX", 0.2f, 2,1,3);oa.setDuration(3000);oa.start();}publicvoid translate(View v) {ObjectAnimatoroa = ObjectAnimator.ofFloat(iv_image, "translationX",10,30, 50);oa.setDuration(3000);oa.start();}publicvoid set(View v) {AnimatorSetas = new AnimatorSet();ObjectAnimatoroa1 = ObjectAnimator.ofFloat(iv_image, "alpha", 1.0f,0.5f,0.9f, 0.1f, 0.6f);ObjectAnimatoroa2 = ObjectAnimator.ofFloat(iv_image, "scaleX", 0.2f,2,1, 3);ObjectAnimatoroa3 = ObjectAnimator.ofFloat(iv_image, "rotation", 0,20,180, 90, 360);ObjectAnimatoroa4 = ObjectAnimator.ofFloat(iv_image, "translationX",10,30, 50);//as.playSequentially(oa1, oa2, oa3, oa4);as.playTogether(oa1,oa2, oa3, oa4);as.setDuration(3000);as.setTarget(iv_image);as.start();}publicvoid xmlAnimator(View view) {Animatoranimator = AnimatorInflater.loadAnimator(getApplicationContext(),R.animator.oanimator);animator.setTarget(iv_image);animator.start();}}
属性动画和补间动画的区别,view动画不会改变view的属性,属性动画会改变view的属性。属性动画是3.0之后才有的api。
十七、 通知栏
通知栏在app中使用较多,比如音乐播放器、购物app、短信等。
创建一个项目,在UI中添加一个按钮,编写业务逻辑。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid send(View view) {Notification.Builderbuilder = new Builder(this);builder.setTicker("来通知了");builder.setAutoCancel(true);builder.setContentText("sldjf螺丝钉解放螺丝钉解放看见的路口附近的离开解放扩大");builder.setSmallIcon(R.drawable.ic_launcher);//builder.setOngoing(true);Intentintent2 = new Intent(this, MainActivity.class);PendingIntentintent = PendingIntent.getActivity(getApplicationContext(),1, intent2,PendingIntent.FLAG_UPDATE_CURRENT);builder.setContentIntent(intent);Notificationnotification = builder.build();NotificationManagermanager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);manager.notify(1,notification);}}
十八、 JNI
java native interface,java本地接口。本地表示,本地语言,也就是操作系统的语言,比如windows的本地语言是c/c++,android系统是linux系统,也是c/c++。JNI,就是java和本地语言之间相互调用的接口。使用JNI可以使java间接的操作硬件;c/c++的运行效率更高,使用JNI可以提高运行效率;c语言存在时间久,开源项目丰富,使用JNI可以访问优秀的开源项目;java的反编译相比c的反汇编更容易泄漏源码,使用JNI更具安全性。
c语言快速入门见https://blog.csdn.net/xingzhishen/article/details/79340449。
1. ndk
使用JNI,需要编译本地语言,此时,需要进行交叉编译。交叉编译就是在一个平台上编译出在另外一个平台上可以运行的本地代码。NDK是一个交叉编译工具,native develop kit。下载NDK。
NDK的目录结构。
build,处理命令。
docs,帮助文档。
platforms,平台资源。
samples,样例。
sources,源码。
toolchains,工具链。
ndk-build.cmd,ndk编译命令。
在eclipse的preferences>Android>NDK>选择NDK解压的路径。如果没有NDK选项,需要下载NDK插件。
2. 一个JNI程序
创建一个项目,设计UI,添加一个调用c的按钮。
在主activity中编写调用逻辑。
public class MainActivity extends Activity {@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid jniHello(View view) {System.loadLibrary("hello");Toast.makeText(getApplicationContext(),helloC(), Toast.LENGTH_SHORT).show();}publicnative String helloC();}
在项目路径下创建一个jni目录,
创建c代码文件
#include<stdio.h>#include<stdlib.h>#include<jni.h>jstringJava_com_example_chello_MainActivity_helloC(JNIEnv* env,jobject this){char*str = "helloc";return(*env)->NewStringUTF(env,str);}
创建一个Android.mk文件
LOCAL_PATH := $(call my-dir)
#清除上一次编译的信息
include $(CLEAR_VARS)
#指定最终生成的文件的名字
LOCAL_MODULE := hello
#要编译的c代码的名字
LOCAL_SRC_FILES :=hello.c
#要生成的一个动态连接库
include $(BUILD_SHARED_LIBRARY)
然后,在cmd命令中进入项目目录,使用ndk-build命令编译c文件。
然后,测试。
3. JNI开发中常见错误
1) 本地方法没有找到,比如,在java中创建的本地方法名中使用了_。
public native String hello_C();
那么在,c代码中
jstringJava_com_example_chello_MainActivity_hello_C(JNIEnv* env,jobject this){char*str = "helloc";return(*env)->NewStringUTF(env,str);}
由于包名、类名、方法名都是使用_分割的,那么方法名中_就会造成与类名的混淆,为了区分,使用1将一个整体的名字合起来,比如:
jstringJava_com_example_chello_MainActivity_hello_1C(JNIEnv* env,jobject this){char*str = "helloc";return(*env)->NewStringUTF(env,str);}
对于一些很奇怪的方法名,比如h___e_____l__l__o_C(),可以使用jdk的javah工具生成JNI样式的标头文件。进入cmd,如果是jdk1.7及以上,进入项目的src目录,使用javah 类的全路径,比如javah com.example.chello.MainActivity。如果是jdk1.6及以下,到项目的bin/classes目录下,运行javah。在src目录下会生成一个.h文件。
JNIEXPORT jstring JNICALLJava_com_example_chello_MainActivity_h_1_1_1e_1_1_1_1_1l_1_1l_1_1o_1C
(JNIEnv*, jobject);
拷贝后,删掉生产成的.h文件。
如果没有写System.loadLibrary("hello");也会导致出现找不到方法名的异常。
实际上,加载库的动作是在一类加载时就执行的,可以卸载一个静态代码块中。
2) 找库返回空
在加载库文件时,名字写错会出现这个异常。一般生成的.so文件会自动添加lib前缀,加载的文件名不需要写lib前缀和.so扩展名。
一般,没有特别操作,使用ndk-build生成的是armeabi的.so文件,也就是支持arm的cpu架构的编译文件,如果在x86架构的设备上就不能运行。这就需要生成相关架构的.so文件。
解决方法是在项目的jni目下创建一个Application.mk文件,在这个文件中说明需要生成支持那些平台的编译文件。比如:
APP_ABI := armeabi x86
不同的平台使用空格隔开。如果需要支持所有的平台,可以简写为:APP_ABI := all
4. JNI开发简便流程
创建一个项目,设计UI,添加一个调用c的按钮。
在主activity中编写本地方法,并在按钮方法中调用之。
public class MainActivity extends Activity {static{System.loadLibrary("hello");}@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid callc(View view) {Toast.makeText(this,helloC(), Toast.LENGTH_SHORT).show();}publicnative String helloC();}
添加本地支持,前提是在properties>Android>NDK中添加了NDK文件的路径,右键项目>Android Tools>add native support>填写lib name,然后在项目目录下生成一个jni目录,下面有cpp源码文件和一个Android.mk文件,系统生成的是cpp文件,需要将cpp文件扩展名改为c,同时在Android.mk文件中也做相应的修改。
通过cmd,javah 类的全路径生成标头文件。复制标头文件方法。
给CDT插件,指定编译c文件时include的目录,右键项目>properties>c/c++ general>paths and symbols>includes>add>file system>选择NDK文件中的platforms,选择一个版本,选择include目录>ok>ok。
编写c源码。
#include <jni.h>#include<stdio.h>#include<stdlib.h>JNIEXPORT jstring JNICALLJava_com_example_jnisimpleprocess_MainActivity_helloC(JNIEnv* env, jobject thiz){return(*env)->NewStringUTF(env,"hellocccc");}
点击c/c++视图下的小锤子可以编译c源码,如果对c源码确认无误,可以不编译,当项目部署到设备上时也会编译的。
在主activity类中加载c库文件
static{System.loadLibrary("hello");}
5. java传递数据给c
创建一个项目,设计UI,三个按钮。
创建一个JNI方法的类,顺便在这个类中加载c的lib库。
public class JNI {static{System.loadLibrary("passparam");}publicnative int add(int x, int y);publicnative String hellc(String s);publicnative int[] arrElementsIncrease(int[] intArray);}
编写业务逻辑
public class MainActivity extends Activity {privateJNI jni;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);jni= new JNI();}publicvoid passInt(View view) {Toast.makeText(this,"result:" + jni.add(4, 5), Toast.LENGTH_SHORT).show();}publicvoid passString(View view) {Toast.makeText(this,"result:" + jni.hellc("abc"), Toast.LENGTH_SHORT).show();}publicvoid passArray(View view) {int[]arr = { 1, 2, 3, 4 };int[]result = jni.arrElementsIncrease(arr);for(int i : result) {System.out.println(i);}}}
添加jni支持,右键项目>android tools>add native support>库名添加在JNI类中加载的库名。
为了在c源码文件中调用android的logcat,需要在自动生成的Android.mk文件中添加
#加载liblog.so,LDLIBS表示loadlibs
LOCAL_LDLIBS += -llog
设置C/C++的includes目录路径。
在cmd中使用javah生成标头文件,复制方法名到c源码文件,编写c源码文件。
#include <jni.h>#include<stdlib.h>#include<android/log.h>#define LOG_TAG "System.out"#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)#define LOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)char* jstringTostring(JNIEnv* env, jstringjstr){char*rtn = NULL;jclassclsstring = (*env)->FindClass(env,"java/lang/String");jstringstrencode = (*env)->NewStringUTF(env,"utf-8");jmethodIDmid = (*env)->GetMethodID(env, clsstring, "getBytes","(Ljava/lang/String;)[B");jbyteArraybarr= (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode);jsizealen = (*env)->GetArrayLength(env, barr);jbyte*ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);if(alen > 0){rtn= (char*)malloc(alen + 1);memcpy(rtn,ba, alen);rtn[alen]= 0;}(*env)->ReleaseByteArrayElements(env,barr, ba, 0);returnrtn;}JNIEXPORT jint JNICALLJava_com_example_javatransformdatatoc_JNI_add(JNIEnv* env, jobject thiz, jint a, jint b){returna+b;}JNIEXPORT jstring JNICALLJava_com_example_javatransformdatatoc_JNI_hellc(JNIEnv* env, jobject thiz, jstring str){char*cstr = jstringTostring(env, str);intlength = strlen(cstr);inti;for(i= 0;i < length;i++){*(cstr+ i) += 1;}return(*env)->NewStringUTF(env, cstr);}JNIEXPORT jintArray JNICALLJava_com_example_javatransformdatatoc_JNI_arrElementsIncrease(JNIEnv* env, jobject thiz, jintArray arr){intlength = (*env)->GetArrayLength(env,arr);LOGD("length=%d",length);jbooleanisCopy;int*p = (*env)->GetIntArrayElements(env,arr,NULL);inti;for(i= 0;i < length;i++){*(p+ i) += 10;}returnarr;}
测试。
6. c反射调用java
创建一个项目,设计UI,添加4个按钮。
编写一个JNI类,创建jni方法,顺便将clib加载进去。
public class JNI {static{System.loadLibrary("calljava");}publicvoid hello() {System.out.println("hellojava");}publicint add(int x, int y) {return x + y;}publicvoid printString(String str) {System.out.println(str);}publicnative void callvoid();publicnative int callint();publicnative void callstring();}
在主activity中编写按钮业务逻辑,对于弹吐司的方法由于需要用到上下文,如果在c反射方法中创建4大组件,得到的上下文就是空,由两种解决方法,一种是在组件中创建弹吐司的jni方法,另一种是在JNI类中传递一个上下文。
public class MainActivity extends Activity {privateJNI jni;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);jni= new JNI();}publicvoid callvoid(View view) {jni.callvoid();}publicvoid callint(View view) {jni.callint();}publicvoid callstring(View view) {jni.callstring();}publicvoid calltoast(View view) {callToast();}publicnative void callToast();publicvoid showToast(String s) {Toast.makeText(this,s, Toast.LENGTH_SHORT).show();}}
添加jni支持,右键项目>android tools>add native support>填写JNI类中加载的库名。修改c源码文件名。
顺便设置c/c++的includes目录路径。
编写c源码
#include <jni.h>#include<stdlib.h>#include<android/log.h>#define LOG_TAG "System.out"#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)#define LOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)JNIEXPORT void JNICALL Java_com_example_ccalljava_JNI_callvoid(JNIEnv* env, jobject thiz){jclassclazz = (*env)->FindClass(env,"com/example/ccalljava/JNI");jmethodIDmid = (*env)->GetMethodID(env,clazz,"hello","()V");(*env)->CallVoidMethod(env,thiz,mid);}JNIEXPORT jint JNICALL Java_com_example_ccalljava_JNI_callint(JNIEnv* env, jobject thiz){jclassclazz = (*env)->FindClass(env,"com/example/ccalljava/JNI");jmethodIDmid = (*env)->GetMethodID(env,clazz,"add","(II)I");intresult = (*env)->CallIntMethod(env,thiz,mid,3,5);LOGD("%d",result);}JNIEXPORT void JNICALLJava_com_example_ccalljava_JNI_callstring(JNIEnv* env, jobject thiz){jclassclazz = (*env)->FindClass(env,"com/example/ccalljava/JNI");jmethodIDmid =(*env)->GetMethodID(env,clazz,"printString","(Ljava/lang/String;)V");jstringstr = (*env)->NewStringUTF(env,"printjavajava");(*env)->CallVoidMethod(env,thiz,mid,str);}JNIEXPORT void JNICALLJava_com_example_ccalljava_MainActivity_callToast(JNIEnv* env, jobject thiz){jclassclazz = (*env)->FindClass(env,"com/example/ccalljava/MainActivity");jmethodIDmid =(*env)->GetMethodID(env,clazz,"showToast","(Ljava/lang/String;)V");//jobjectmain = (*env)->AllocObject(env,thiz);jstringstr = (*env)->NewStringUTF(env,"hellohello");(*env)->CallVoidMethod(env,thiz,mid,str);}
测试。
7. 锅炉压力测试app
android的app也会被应用到工业硬件设备上。
创建一个项目,设计UI,两个控制的按钮和一个ProgressBar。
在主activity中编写业务逻辑,顺便加载库文件。
public class MainActivity extends Activity {static{System.loadLibrary("pressure");}privateProgressBar pb_progress;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);pb_progress= (ProgressBar) findViewById(R.id.pb_progress);pb_progress.setMax(100);}publicvoid start(View v) {newThread() {publicvoid run() {startMoniter();};}.start();}publicvoid stop(View v) {stopMoniter();}publicvoid setPressure(int pressure) {pb_progress.setProgress(pressure);}publicnative void startMoniter();publicnative void stopMoniter();}
添加本地支持,修改c源码文件名。给项目设置includes路径。
使用javah生成标头文件名。
编写c源码
#include <jni.h>#include<stdlib.h>int getPressure(){returnrand()%101;}int flag = 1;JNIEXPORT void JNICALLJava_com_example_pressurediagram_MainActivity_startMoniter(JNIEnv* env, jobject thiz){jclassclazz =(*env)->FindClass(env,"com/example/pressurediagram/MainActivity");jmethodIDmid =(*env)->GetMethodID(env,clazz,"setPressure","(I)V");flag= 1;while(flag){(*env)->CallVoidMethod(env,thiz,mid,getPressure());sleep(1);}}JNIEXPORT void JNICALLJava_com_example_pressurediagram_MainActivity_stopMoniter(JNIEnv* env, jobject thiz){flag= 0;}
测试。
加入自定义控件的锅炉压力app
在UI中添加一个按钮。
编写一个自定义的控件
public class MyPressureView extends View {privateint pressure = 50;publicMyPressureView(Context context) {super(context);//TODO Auto-generated constructor stub}publicMyPressureView(Context context, AttributeSet attrs, int defStyle) {super(context,attrs, defStyle);//TODO Auto-generated constructor stub}publicMyPressureView(Context context, AttributeSet attrs) {super(context,attrs);//TODO Auto-generated constructor stub}@Overrideprotectedvoid onDraw(Canvas canvas) {//TODO Auto-generated method stubPaintpaint = new Paint();paint.setTextSize(20);if(pressure > 80) {paint.setColor(Color.RED);}else if (pressure < 40) {paint.setColor(Color.GREEN);}else {paint.setColor(Color.YELLOW);}canvas.drawText("压力值:" + pressure, 30, 30, paint);canvas.drawRect(30,200 - pressure, 50, 200, paint);}publicvoid setPressure(int pressure) {this.pressure= pressure;postInvalidate();}}
在UI中添加自定义的控件。
在主activity中添加使用自定义控件的代码
public class MainActivity extends Activity {static{System.loadLibrary("pressure");}privateProgressBar pb_progress;publicMyPressureView pressure_view;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);pb_progress= (ProgressBar) findViewById(R.id.pb_progress);pb_progress.setMax(100);pressure_view= (MyPressureView) findViewById(R.id.pressure);}publicvoid start(View v) {newThread() {publicvoid run() {startMoniter();};}.start();}publicvoid stop(View v) {stopMoniter();}publicvoid setPressure(int pressure) {pb_progress.setProgress(pressure);pressure_view.setPressure(pressure);}publicnative void startMoniter();publicnative void stopMoniter();}
8. c++开发JNI
在c++下,JNIEnv不是结构体的一级指针,而是JNIEnv的别名。env就是JNIEnv的一级指针,就是结构体。访问结构体成员,不需要(*env)->而是直接env->。c++的结构体中可以有函数,c的结构体中只能有函数指针,不能定义函数。c++的函数在使用时需要声明,可以使用生成的标头文件,放到与cpp源码同一目录下,使用#include “”包含之,作为函数声明。
创建一个项目,设计UI,一个按钮。
编写业务逻辑,顺便加载cpp的库文件。
public class MainActivity extends Activity {static{System.loadLibrary("cpp");}@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid hellocpp(View v) {Toast.makeText(this,hello(), Toast.LENGTH_SHORT).show();}publicnative String hello();}
添加jni支持,填写库名。设置includes目录路径。
使用javah,生成标头文件,并放到与cpp源码同一目录下。
编写cpp源码
#include <jni.h>#include"com_example_cppjni_MainActivity.h"JNIEXPORT jstring JNICALL Java_com_example_cppjni_MainActivity_hello(JNIEnv* env, jobject thiz){returnenv->NewStringUTF("hellocppcpp");}
测试。
9. cfork分叉进程
fork函数可以开启子进程。常用于守护推送进程。
创建一个项目,设计UI,一个开启fork进程的按钮。
编写业务逻辑,顺便加载库文件。
public class MainActivity extends Activity {static{System.loadLibrary("cfork");}@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}publicvoid fork(View v) {cfork();}publicnative void cfork();}
添加jni支持,填写库名。设置includes目录路径。
编写c源码
#include <jni.h>#include<stdlib.h>#include<stdio.h>#include<android/log.h>#define LOG_TAG "System.out"#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)JNIEXPORT void JNICALLJava_com_example_cfork_MainActivity_cfork(JNIEnv* env, jobject thiz){intpid = fork();if(pid> 0){LOGD("pid= %d", pid);}elseif(pid < 0){LOGD("pid< 0");}else{LOGD("pid= %d", pid);intppid = 0;FILE*file = NULL;while(1){ppid= getppid();if(ppid== 1){//ppid等于1只有两种情况,被卸载或被杀死file= fopen("data/data/com.example.cfork","r");if(file== NULL){LOGD("appuninstalled");//弹出浏览器,做调查execlp("am","am","start","--user","0","-a","android.intent.action.VIEW","-d","http://www.baidu.com",(char*)NULL);}else{//偷偷开启进程LOGD("appforce closed");execlp("am","am","start","--user","0","-n","com.example.cfork/com.example.cfork.MainActivity",(char*)NULL);}}sleep(2);LOGD("subprocess is running");}}}
测试。
androidBasic相关推荐
- 自定义View之指南针(反编译别人的代码实现)
一.说明 偶尔点开魅族手机内置的工具箱应用,发现其指南针做的还不错,就想模拟做一个类似的效果,在这里我们不准备自己从头开始编写代码,而是采用一点黑科技,首先,我们从魅族系统中导出工具箱应用的apk,然 ...
- (二)Linux ALSA 音频系统:逻辑设备篇
逻辑设备篇 转自:https://me.csdn.net/zyuanyun Linux ALSA 音频系统:逻辑设备篇 Linux ALSA 音频系统:逻辑设备篇 原创 zyuanyun 最后发布于2 ...
- Android插件化技术
一.Android插件化技术 我们在平时的开发过程中,会经常遇到产品需求的变更或者出现bug,在传统的模式中,我们需要首先需要修改代码,然后重新打包Apk,再交给公司的运营去官网或者应用商店上线,用户 ...
最新文章
- hdu 1003 Max Sum 解题报告
- 我从哆啦A梦的口袋里,掏出一辆充气电动车
- RxJava实现事件总线——RxBus
- DL之FAN:FAN人脸对齐网络(Face Alignment depth Network)的论文简介、案例应用之详细攻略
- scrapy爬取京东
- 鸿蒙开发者社区入口,鸿蒙OS 社区
- 查了一晚上的资料,累啊。
- 东莞城院c语言上机报告,浙大城市学院c语言上机试题
- VMware pro 14
- 云erp系统、进销存软件、仓储管理系统之间有哪些区别
- matlab所有元素求和,数组 – MATLAB对数组值表达式的所有元素求和
- nginx做反向代理网站加载验证码图片不出来
- 港大计算机系教授中科大毕业的吗,女儿在香港大学(港大)
- 数据分析2_使用Tableau进行收视率分析
- sockjs-node报错
- 联想微型计算机B3O5,触摸电视功能 联想B3系列一体电脑评测
- 《必然》 一书中120条书摘
- c语言开发无人机自动驾驶仪,无人机自动驾驶仪.pdf
- SQL练习-sqlzoo
- Date 的GMT、UTC、ISO、CST、timestamp 等格式 及Moment、Dayjs
热门文章
- CRX-VF低温探针台安装实验室安装准备条件
- 微信小程序--超简单贪吃蛇
- [2019 年百度之星·程序设计大赛 - 初赛三]简要题解?
- 50件事爸爸一定要与孩子做(转)
- ZOJ3380_Patchouli's Spell Cards_概率DP
- 爬取豆瓣电影前十页的好评一般差评
- [Practical.Vim(2012.9)].Drew.Neil.Tip04学习摘要
- 10000+门店的蜜雪冰城,帮你找回软件赚钱的初心
- Cannot find module 'xxx' 错误的解决方案
- 2012年建模A题 葡萄酒的评价