第二章
感悟:
每一次学习,都会有新的收获
我相信我看到的奇迹,没有是平白无故产生的,它们是源于环环相扣的逻辑巧妙组合而成。
隐藏标题栏(p35)
在调用setContentView()之前,写上下面代码:
requestWindowFeature(Window.FEATURE_NO_TITLE);
隐式Intent(p44)
隐式里的category和data就像是更精确的过滤器
保存临时数据 (p67)
当启动新的活动时,当前的活动数据如果不加以保存,那么就会丢失掉数据(系统回收)。
这样的用户体验不好,可以通过onSaveInstanceState()
方法来解决这个问题。
相应的,在onCreate(Bundle)
中进行恢复操作。
活动启动模式(p68)
修改方式:在Manifest.xml的对应activity中添加android:launchMode="standard|singleTop|singleTask|singleInstance"
- standard模式:无论返回栈中是否存在该活动,每次启动都会创建一个新的实例(例如,自己启动自己,也会创建多个实例出来)
- singleTop模式:如果返回栈的栈顶是要启动的活动,则直接使用它,而不是再创建一个新的活动实例出来。
- singleTask模式:使该活动在整个应用程序的上下文中只存在一个实例,如果不在栈顶,则将该活动上面的所有活动出栈,如果没有改活动,则创建一个活动实例。
- singleInstance模式:指定为该模式的活动就启用新的返回栈来管理这个活动。(还不是很了解)
总结:现在还不是很会用哦,只是知道有这个东西。
技巧:
- 快速找到对应界面的活动(p77):简单的说在基类BaseActivity中设置打印的日志,其继承的类就会响应的打印日志。清晰的说是在基类的onCreate()方法中,使用代码
Log.d("BaseActivity", getClass().getSimpleName())
- 随时随地退出程序(p78):利用集合类对活动进行管理(自己创建ActivityCollector类,添加和删除活动)。
- 启动活动(p80):为每个活动添加一个封装优美的,静态的,清晰的启动方法(传入参数即可启动活动)。
第三章
感悟:
从整体到局部,从构架到细节。
宽度,高度(p84)
match_parent:表示让当前控件的大小和父控件的大小一样
fill_parent:同match_parent
wrap_content:使当前控件的大小正好包含住控件包含的内容--文字,图片等。
控件可见性(p98)
xml布局中控制:
为控件添加属性(所有控件均支持),android:visibility="visible|invisible|gone"
visible或者不写:可见,占空间
invisible:不可见,占空间(透明状态)
gone:不可见,不占空间
Java代码中控制:
动态控制可见性:setVisibility(View.VISIBLE|View.INVISIBLE|View.GONE)
效果同xml
另外,可以通过getVisibility()方法获取当前的控件状态
LinearLayout重要属性说明(p105)
- android:orientation="vertical|horizontal";控制LinearLayout的排列方向(该属性用在LinearLayout本身上)
- android:layout_gravity="left|right|top|bottom|center_vertical|center_horizontal";控制控件的布局方式。(该属性用在LinearLayout的子控件中)
- android:layout_weight="1";通过比例的方式控制控件的大小。(该属性用在LinearLayout的子控件中,对手机的适配性起到很大作用)
易混淆的属性(p107)
android:layout_gravity
和android:gravity
属性区别:
android:layout_gravity
属性:用于指定该控件在父控件中的对齐方式(可选值:top|bottom|left|right|center|center_vertical|center_horizontal)android:gravity
属性:用于指定文字在控件中的对齐方式(可选值:top|bottom|left|right|center|center_vertical|center_horizontal)- 注意:layout_gravity,当父控件LinearLayout排列方向是horizontal时,只有垂直方向的对齐方式才会生效(bottom, top, center_vertical);当父控件LinearLayout排列方向是vertical时,只有水平方向的对齐方式才会生效(left,right,center_horizontal)
RelaviteLayout布局
RelaviteLayout中的控件,既可以相对于父布局进行定位:
- android:layout_alignParentTop="true"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_centerInParent="true"
RelaviteLayout中的控件,也可以相对于其他控件进行布局:
- android:layout_above="@id/view3"
- android:layout_below="@id/view3"
- android:layout_toRightOf="@id/view3"
- android:layout_toLeftOf="@id/view3"
RelaviteLayout中的控件,也可以相对于其他控件的边缘进行对齐显示:
- android:layout_alignRight="@id/view3"
- android:layout_alignLeft="@id/view3"
- android:layout_alignTop="@id/view3"
- android:layout_alignBottom="@id/view3"
其他:
- android:layout_centerVertical="true"
- android:layout_centerHorizontal="true"
注意1:引用的控件一定要先定义。
注意2:可以在一个控件中使用上面的一个或多个。例如,相对于父布局的“左上”布局,就需要填写两个属性android:layout_alignParentTop="true";android:layout_alignParentLeft="true"
。
TableLayout布局
- TableLayout中的每一个TableRow都表示表格中的一行,每一个控件表示一列。
- TableRow中的控件不可以指定宽度。
- 在TableLayout中添加属性:
android:stretchColumns="1"
,使指定的那一列拉伸,以自动适应屏幕的宽度。(0表示第一列,1表示第二列)
其他布局
- FrameLayout布局(所有控件摆放在左上角,可能会重叠显示)
- AbsoluteLayout布局(不推荐使用)
ListView控件(p127)
需要的元素:
- 数据源
- 单项模板
- 适配器(一般继承自ArrayAdapter,以后会接触到SimpleAdapter等)
优化
- 利用缓存,convertView;已经加载过的,下一次就利用缓存,没有加载过的才inflate进来。
- 使用Tag;每一个View都可以存储一个Tag对象(看源码mTag),对控件实例进行缓存。
其他补充:
- 为listview绑定适配器:lv.setAdapter(mAdapter);
- 数据发生变化,使变化体现在界面上:mAdapter.notifyDataSetChanged();
- 定位到某一项(即在屏幕上显示这一项):lv.setSelection(num);
- 点击事件:setOnItemClickListener(OnItemClickListener ocl);
- 给ListView项的分割线设置为透明色:android:divider=#0000
要解决问题
- Fragment和html中的frame区别(多研究下)。
- Fragment的hide()和show()以及replace()区别。
第四章 Fragment碎片
感悟
第一遍:不知道自己不知道。
第二遍:知道自己不知道,或不知道自己知道。
第三遍:知道自己知道。
碎片(Fragment)是一种可以嵌入在活动当中的UI片段,它能让程序更加合理和充分地利用大屏幕空间。
下面的参考资料讲的都很不错:
http://blog.csdn.net/guolin_blog/article/details/8881711
http://blog.csdn.net/lmj623565791/article/details/37970961
http://blog.csdn.net/lmj623565791/article/details/37992017
ForLou要点难点:
- 通信--数据传递。
- 抽象成一个公用的类。
- 回退栈(remove,add,hide,show等方法的区别)
- 屏幕适配:限定符;
- 双页单页模式(p182)
第五章 广播机制
注册广播:
1. 动态注册(p190)
新建一个类,继承BroadcastReceiver,重写父类的onReceive方法。
自定义的MainActivity中通过Context的registerReceiver(BroadCastReceiver bc,IntentFilter if)方法来注册。
说明:其中参数bc是刚才新建类的实例,参数if是广播发送过来的action值包装成IntentFilter对象。
注意:用完之后需要销毁,在activity的onDestroy()方法中调用unregisterReceiver()方法。
注意:当访问的内容涉及到系统的关键性信息,那么需要在配置文件中配置相应的权限permission(http://developer.android.com/reference/android/Manifest.permission.html )。
注意:应用程序发送的广播是可以被其他应用程序接收到的。跨进程通信。
2. 静态注册
- 创建一个集成自BroadCast的类,并重写onReceive()方法
- 类似于activity的注册,使用标签,使用android:name来指定刚才创建的广播接收器(即实现了BroadCast的类的实例)
- 在标签中添加想要接收的广播。
另外:使用静态注册可以达到开机启动的目的。静态注册在程序未启动的时候也可以接收广播。
注意:监听某些广播也是需要声明权限的。
注意:不要在onReceive()中添加过多的逻辑或者任何的耗时操作,因为在广播接收器中是不允许开启线程的(如果运行时间较长,程序会报错)。
应用场合/扮演的角色:打开程序其他组件,例如创建一条状态栏通知,或启动一个服务等。
发送广播
标准广播
例如在MainActivity中点击按钮时发送广播:
Intent intent = new Intent("com.lyloou.broadcasttest.MY_BROADCAST");//还可以绑定一些数据到Intent中供接收者使用;
sendBroadcast(intent);
有序广播
- 区别于标准广播的
sendBroadcast(Intent intent)
,有序广播是sendOrderedBroadcast(Intent intent, String receiverPermission)
。(可以参考Context的API文档看更详细内容) - 广播接收器的先后顺序,是通过在注册的时候设定的。例如,
<intent-filter android:priority="100">
。 - 有序广播是可以截断的。在onRecevie()中调用
abortBroadcast();
方法将广播截断,后续的接收器就无法接收到该条广播。(only works with broadcasts sent through Context.sendOrderedBroadcast.
)
本地广播(p202)
- 区别于一般的广播,本地广播只允许本应用程序接收本应用程序发送的广播,解决了广播的安全性问题。其他程序的广播无法发送到我们程序的内部,更安全。比全局广播更高效。
- 与一般的广播的动态注册方式类似。(一般广播,这里指的是全局广播)
- 区别于一般广播的接收方式
registerReceiver()
,本地广播使用:LocalBroadcastManager localBroadCastManager = LocalBroadcastManager.getInstance(Context c);
localBroadCastManager.registerReceiver(BroadcastReceiver br, Intent i);
- 区别于一般广播的发送方式
sendBroadcast(intent)
,本地广播使用:localBroadCastManager.sendBroadcast(intent)
- 注意:也需要unregister。
- 注意:本地广播无法通过静态方式注册接收,静态注册在程序未启动的时候也可以接收广播,而本地广播肯定是在启动程序之后发送,所以不需要静态注册。
其他
在广播接收器写一个弹框(p210)。
从广播接收器中启动活动需要注意的内容(p211):给Intent加入FLAG_ACTIVITY_NEW_TASK标识。
第六章 数据存储
以下三种存储方式均属于本地持久化
文件存储:存储简单的文本数据和二进制数据。
SharePreference存储:存储键值对。
数据库--SQLITE:存储复杂关系型的数据。
文件存储(p221)
核心方法:Context提供的openFileInput()和openFileOutput()方法;
注意:不适用于存储复杂类型的数据。
技巧:
TextUtils.isEmpty(String str);
:如果str为""或者为null,都会返回true
。
EditText.setSelection(str.length());
:将光标至于str最后。
SharePreference存储(p228)
- 使用键值对的方式存储。
- 使用xml格式来对数据进行管理。
- 获取SharePreference
- Context类中的getSharedPreference(String filename, MODE_PRIVATE|MODE_MULTI_PROCESS)
- Activity类中的getPreference(MODE_PRIVATE|MODE_MULTI_PROCESS),文件名以当前活动的类名起名。
- PreferenceManager类中的getDefaultSharedPreference(MODE_PRIVATE|MODE_MULTI_PROCESS),文件名以包名起名。
-
保存数据:
- 通过获取到的SharePreference对象实例sp,引用其edit()方法,
SharedPreferences.Editor editor = sp.edit();
- editor.putInt("age",18);
- editor.putString("name","Bob");
- ...
- editor.commit();
- 通过获取到的SharePreference对象实例sp,引用其edit()方法,
-
获取数据:
- 同样首先获取到SharePreference对象实例sp:
- int age = sp.getInt("age");
- String name = sp.getString("name");
- ...
- 获取到数据后的其他操作。
数据库--SQLITE(p238)
- 创建一个继承自SQLiteOpenHelper类的帮助类,重写onCreate()和onUpgrate()方法,这这两个方法中实现创建表和升级数据库的逻辑。
- 通过帮助类获取实例:getReadableDatabase()和getWritableDatabase()方法获取实例。区别是前者在磁盘满的时候以只读方式打开数据库,而后者抛出异常。
- 通过db.execSQL()可以直接执行数据库语句 。
- 事务保证数据安全
-
数据库创建后,将不再重新创建,这意味着onCreate只执行一次。除非卸载掉重新安装,另外下一次更改数据库结构的时候可以通过onUpgrade来控制了。
CRUD -- 添加数据
利用ContentValues组件一组数据,通过insert来添加到数据库中。
ContentValues利用键值的方式添加:,ContentValues contentValues = new ContentValues();
contentValues.put("name", "think in java");
contentValues.put("version", 3);
db.insert("Book", null, contentValues);
CRUD -- 更改数据
利用ContentValues组件一组数据,通过update来添加到数据库中。
ContentValues利用键值的方式更新:,ContentValues contentValues = new ContentValues();
contentValues.put("version", 4);
db.update("Book", contentValues, "name=?" , new String[]{"think in java"});
CRUD -- 删除数据
获取到db实例后,即可删除
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "version < ?", new String[]{"4"});
CRUD -- 查询数据
利用db的query方法来得到一个Cursor实例。对Cursor进行取值操作,即可得到想要的值。
cursor.moveToFirst(); 指针移动到第一行;
cursor.moveToNext(); 指针移动到下一个;Cursor cursor = db.query("Book", null, null, null, null, null, null);
cursor.moveToFirst();
Log.d("LOU",cursor.getString(cursor.getColumnIndex("name")));
Log.d("LOU",cursor.getInt(cursor.getColumnIndex("version")));
cursor.close();
技巧:利用switch来优化数据库升级(p264)。
第七章 内容提供器(p268)
感悟:
目前所有的工作都是对上api,利用好api。
- 内容提供器主要用于在不同的应用程序之间实现数据共享的功能,目前内容提供器是Android实现跨程序共享数据的标准方式。
- ContentProvider比起文件和SharedPreference的MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE两种操作模式要更安全可靠。因为你只提供想要给其他程序访问的数据,其他数据就不会有任何影响。
- ContentResolver实例可以通过Context的getContentResolver()来获取。
- ContentResolver提供的一些方法进行CRUD操作。---- 基本类似于数据库的CRUD,最明显的区别就是:数据库中的CRUD方法传入的参数是【表名】,而ContentResolver中的传入的参数是【Uri】,对应的继承自ContentProvider的自定义内容提供器里面的CRUD方法设置的参数也是Uri。
-
内容提供器的用法有两种:
- 使用现有的提供器来读取和操作某程序的数据。
- 自己创建ContentProvider给自己的程序或其他程序提供外部访问接口,以供其读取和操作数据。
-
Uri:区别于SQLiteDatabase接受的参数“table”,ContentResolver接受的参数是“URI”,要对某程序的数据进行读取和操作,需要制定其Uri。
- URI格式:由两部分组成,权限authority和路径path(p269),,+通配符的使用(p277)
- UriMatcher的使用(p279)
- Uri的getPathSegments()方法可以将字符串按照“/”分割成字符串列表,通过get(0)这种方式访问列表中的字符串。
- 所有内容提供者必须提供的方法(getType()),getType()方法的实现,用于获取Uri对象所对应的MIME类型;(p280)
- 自定义的ContentProvider需要注册到Manifest中(p286)。
注意:在每次创建内容提供者的时候,都要提醒自己,是不是真的需要这样做。因为只有真正需要将数据共享出去的时候我们才应该创建内容提供器,仅仅是用于内部程序访问的数据没必要使用内容提供者。
Lou理解:ContentProvider扮演的是程序和程序之间的桥梁角色。A程序规定好自己的哪些数据内容可以访问,并设置好对应的Uri,B程序借助ContentProvider提供器传入A程序规定好的Uri来读取和操作A程序的数据。
在ContentProvider中操作数据库,好像是ContentProvider提供了一个包装,在其内根据传入的Uri来进行具体的数据库操作,对外只需要提供一个接口即可(接口需要有正确的参数,例如Uri,以及过滤条件)。
第八章 多媒体
通知
使用步骤:
- 得到manager:
NotifictionManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
- 得到notification实例:
Notification notification = new Notification(R.drawable.ic_launcher, "This is ticker text", System.curentTimeMillis());
- 设置通知里的布局:
notification.setLatesEventInfor(this, "This is content title", "This is content text", null);
,其中第四个参数是用来设置点击通知触发的逻辑,用PendingIntent来设定。 - 利用manager发出通知:
manager.notify(1, notification);
,其中1是该通知的唯一标志,可以用于之后的取消。
取消通知栏的通知:
NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
manager.cancel(1); //其中1表示刚才设置的通知唯一标志。
使通知栏的通知可点击
PendingIntent(p302)
- 通过getActivity(),或getBroadCast(),或getService()来获取PendingIntent实例,PendingIntent结合第二个参数Intent来构建出“意图”,当被点击时执行相应的逻辑。(p302)
- 将得到的实例作为
notification.setLatesEvenInfor()
的第四个参数以实现点击通知的逻辑(启动一个Activity,发送一条广播,启动某个服务等等)。
通知的其他内容(p305)
- 添加声音:
Uri soundUri = Uri.fromFile(new File("/system/media/audio/ringtones/Basic_tone.ogg"));
notification.sound = sounUri;
-
添加振动:
long[] vibrates = {0, 1000, 1000, 1000};
notification.vibrate = vibrates;
//需要添加权限<uses-permission android:name="android.permission.VIBRATE"/>
-
添加LED通知
notification.ledARGB = Color.GREEN;
notification.ledOnMS = 1000;
notification.ledOffMS = 1000;
notification.flags = Notification.FLAG_SHOW_LIGHTS;
-
设置默认通知类型:
notification.defaults = Notification.DEFAUT_ALL
-
扩展:
点亮屏幕
第九章 服务
多线程编程
- 子线程中不允许进行UI操作。
- 异步消息处理机制:handler,AsyncTask,Activity.runOnUIThread(Runnable runnable)。
Handler(p348)
- 在主线程中创建Handler实例,并重写handMessage()方法。
- 在子线程中通过handler发送消息(
handler.handleMessage(msg);
) - msg会被添加到MessageQueue等待处理。
- Looper不断的监测MessageQueue中是否有Message,如果有那么就取出来分发到handler的handleMessage()方法中处理。
- 在handler.handleMessage()方法中进行UI操作。
AsyncTask(p349)
- AsyncTask是一个抽象类,需要去继承才能用。
- 三个泛型参数
- Params
- Progress
- Result
-
常用的方法(重写)
- onPreExecute(),界面的初始化操作。
- doInBackground(Params...),耗时操作
- onProgressUpdate(Progress...),当在doInBackground方法里调用publishProgres(Progress...)时会被调用,用于更新当前的UI状态。
- onPostExecute(Result),当执行完doInBackground()方法之后,该方法会被调用。用于善后操作。
-
执行方式:
new MyAsyncTask().execute();
服务
-
间接继承自Context
-
两种创建服务的路径:
- startService(serviceIntent);
- bindService(bindIntent, connection, BIND_AUTO_CREATE);
-
我们完全有可能读一个服务既调用了startService()方法,有调用了bindService()方法。这种情况如何销毁服务?需要同时调用stopService()(或者stopself())和unBindService()方法才行。(p364)
-
stopself()可以在MyService的任何位置调用,以停止服务。
*stopService()跟startService()一样,需要根据传入的参数来确定stop哪个服务。Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
-
需要在Manifest中注册服务。
-
通过bindService的方式创建的服务,可以更方便的控制它。
-
IntentService,一个异步的,可以自动停止的服务。(p367)。在重写的方法onHandleIntent()里的操作,就相当于在Handler.handleMessage()里的操作一样(查看IntentService的源码)。
- 服务的应用:定时任务(Alarm)
学习资料:
http://www.cnblogs.com/mengdd/archive/2013/03/24/2979944.html
http://www.cnblogs.com/mengdd/archive/2013/03/24/2979903.html
http://www.cnblogs.com/yejiurui/p/3429451.html
第十章 网络技术
使用Http协议访问网络(注意要添加网络权限)
HttpURLConnection(p380)
get数据
private void sendRequestWithHttpURLConnection(){
new Thread(
@Override
public void run(){
HttpURLConnection connection = null;
try{
URL url = new URL("http://www.baidu.com");
connection = (HttpURLConnection)url.openConnection();
// Get表示希望从服务器获取数据;Post表示希望提交数据给服务器
connection.setRequestMethod("GET");
// 设置连接超时
connection.setConnectionTimeout(8000);
// 设置读取超时
connection.setReadTimeout(8000);
// 获取输入流
InputStream in = connection.getInputStream();
// 对输入流进行读取操作
BufferReader reader = new BufferReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while((line=reader.readline()) != null){
response.append(line);
}
// 将结果返回
Message msg = new Message();
msg.what = 1;
msg.obj = response.toString();
handler.sendMessage(msg);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(connection != null){
connection.disconnect();
}
}
}
).start();
}
post数据
// 获取输出流并写出数据。
DataOutputStream out = new DataOutputStream (connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
HttpClient(p385)
get数据
private void sendRequestWithHttpClient(){
new Thread(
@Override
public void run(){
HttpClient httpClient = null;
try{
httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://www.baidu.com");
HttpResponse httpResponse = httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8"); // 将entity转换为字符串,并指定字符集编码
// 将结果返回
Message msg = new Message();
msg.what = 1;
msg.obj = response.toString();
handler.sendMessage(msg);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if(client != null){
client.disconnect();
}
}
}
).start();
}
post数据
HttpPost httpPost = new HttpPost("http://www.baidu.com");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", "admin"));
params.add(new BasicNameValuePair("password", "123456"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(param, "utf-8");
httpPost.setEntity(entity);
HttpResponse httpResponse = httpClient.execute(httpPost);
回调机制(p406,真牛)
public interface HttpCallbackListener{
void onFinish(String response);
void onError(Exception e);
}
......
private void sendRequest(final String address, final HttpCallbackListener listener){
new Thread(
@Override
public void run(){
HttpURLConnection connection = null;
try{
URL url = new URL(address);
connection = (HttpURLConnection)url.openConnection();
// Get表示希望从服务器获取数据;Post表示希望提交数据给服务器
connection.setRequestMethod("GET");
// 设置连接超时
connection.setConnectionTimeout(8000);
// 设置读取超时
connection.setReadTimeout(8000);
// 获取输入流
InputStream in = connection.getInputStream();
// 对输入流进行读取操作
BufferReader reader = new BufferReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while((line=reader.readline()) != null){
response.append(line);
}
// 将结果回调到参数中去处理,而不是return的方式返回数据(return的数据可能因为主线程先执行完而不能接受到,而通过参数就一定会执行了)。
if( listener != null){
listener.onFinish(response.toString());
}
} catch(Exception e) {
if( listener != null){
listener.onError(e);
}
} finally {
if(connection != null){
connection.disconnect();
}
}
}
).start();
}
解析XML
<apps>
<app>
<id>1</id>
<name>Google Maps</name>
<version>1.0</version>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>1.0</version>
</app>
<app>
<id>3</id>
<name>Google Play</name>
<version>2.3</version>
</app>
</apps>
PULL(p391)
private void parseXMLWithPull(String xmlData){
try{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();
String id = "";
String name = "";
String version = "";
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodename = xmlPullParser.getName();
switch(eventType) {
case XmlPullParser.START_TAG:{
if("id".equals(nodename)){
id = xmlPullParser.nextText();
} else if("name".equals(nodename)){
name = xmlPullParser.nextText();
} else if("version".equals(nodename)){
version = xmlPullParser.nextText();
}
break;
}
case XmlPullParser.END_TAG:{
if("app".equals(nodename)){
Log.d("MainActivity", "id is " + id);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "version is " + version);
}
break;
}
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
SAX(p394)
private void parseXMLWithSAX(String xmlData){
try{
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler contentHandler = new ContentHandler();
xmlReader.setContentHandler(contentHandler);
xmlReader.parse(new InputSource(new StringReader(xmlData)));
} catch (Exception e) {
e.printStackTrace();
}
}
......
public class ContentHandler extend DefaultHandler{
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
// startDocument会在开始XML解析的时候调用
@Override
public void startDocument() throws SAXException{
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
// startElement会在解析某个节点的时候调用
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException{
nodeName = localName;
}
// characters会在获取节点内容的时候调用,可能会调用多次,需要对澶蠕动char进行控制
@Override
public void characters(char[] ch, int start, int length) throws SAXException{
if("id".equals(nodeName)){
id.append(ch, start, length);
} else if("name".equals(nodeName)){
name.append(ch, start, length);
} else if("version".equals(nodeName)){
version.append(ch, start, length);
}
}
// endElement会在完成解析某个节点的时候调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException{
if("app".equals(localName)){
Log.d("ContentHandler", "id is " + id.toString().trim());
Log.d("ContentHandler", "name is " + name.toString().trim());
Log.d("ContentHandler", "version is " + version.toString().trim());
// 清空还原数据
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
// endDocument会在完成解析XML的时候调用
@Override
public void endDocument() throws SAXException{
Log.d("ContentHandler", "completed!!!"");
}
}
扩展:DOM
解析JSON
[
{"id":"5", "version":"5.5", "name":"Angry Birds"},
{"id":"6", "version":"7.0", "name":"Clash of Clans"},
{"id":"7", "version":"3.5", "name":"Hey Day"},
]
JSONObject(p399)
private void parseJSONWithJSONObject(String jsonData){
try{
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i<jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String version = jsonObject.getString("version");
String name = jsonObject.getString("name");
Log.d("MainActivity", "id is " + id);
Log.d("MainActivity", "version is " + version);
Log.d("MainActivity", "name is " + name);
}
} catch (Exception e) {
e.printStackTrace();
}
}
GSON(p401)
class App{
private int id;
private String name;
private String version;
public void setId(int id){
this.id = id;
}
public void setName(String name){
this.name = name;
}
public void setVersion(String version){
this.version = version;
}
public int getId(){
return this.id;
}
public String getName(){
return this.name;
}
public String getVersion(){
return this.version;
}
}
......
private void parseJSONWithGSON(String jsonData){
Gson gson = new Gson();
List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>(){}.getType());
for (App app : appList) {
Log.d("MainActivity", "id is " + app.getId());
Log.d("MainActivity", "name is " + app.getName());
Log.d("MainActivity", "version is " + app.getVersion());
}
}