5, 线程的异步处理Handler
Http网络编程
Json数据解析
ListView和AdapterHandler消息传递机制:
产生背景:出于性能优化的考虑,Android UI操作并不是线程安全,如果有多个线程并发操作UI组件,可能导致线程安全问题。可以设想下,
如果在一个Activity中有多个线程去更新UI,并且都没有加锁机制,可能会导致什么问题? 界面混乱,如果加锁的话可以避免该问题
但又会导致性能下降。因此,Android制定了一条简单的原则:只允许UI线程(亦即主线程)修改Activity中的UI组件
如果程序员自行定义了一个线程,尝试去修改UI界面是不安全和不推荐的
比如:
new Thread( new Runnable() {
public void run() {
myView.invalidate();
}
}).start(); //这种做法是不行的
Handler 的应用:
在Android的应用启动时,会创建一个主线程,主线程会创建一个
消息队列来处理各种消息。当你创建子线程时,你可以在你的子线程中拿到父线程中
创建的Handler 对象,就可以通过该对象向父线程的消息队列发送消息了。由于Android
要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面。
几个类的作用和关系:
Thread:
ui主线程: 通常就是 main thread,Android 启动程序时会替它建立一个Message Queue
如用户的按键事件、用户接触屏幕的事件、屏幕绘图事件,并把相关的事件分发到相应的组件进行处理,
所以主线程通常又叫做UI线程
普通线程:程序员自行定义了一个线程,可能会做一些耗时的事情,获取数据,并会将数据显示在ui上
Message和Message Queue
Message: 多线程中用于传递数据的载体,可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象
可以通过Handler对象的sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器
Message Queue: 是用来存放线程放入的消息
Looper:一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue,比如循环的从消息队列读取消息
Handler: 与Looper沟通,以便push 新消息到 Message Queue里,或者接收Looper从Message Queue 里所送来的消息
并有一个handleMessage的方法能够处理所有的Message, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程)
Handler中涉及到的两个队列(FIFO),都是有Android操作系统提供的
1,消息队列,也即是Message Queue-- 发送、接受、处理消息–消息队列;
Handler对象维护一个消息队列,有新的消息送来(sendMessage())的时候,把它放在队尾,之后排队 到处理该消息的时候,
由主线程的Handler对象处理(handleMessage())。整个过程也是异步的
2,Runnable队列--- 启动、结束、休眠线程–Runnable队列,比如执行一些周期性的工作
Handler对象维护一个线程队列,有新的Runnable送来(post())的时候,把它放在队尾,而处理 Runnable的时候,
从队头取出Runnable执行,当向队列发送一个Runnable后,立即就返回,并不理会Runnable是否被执行
而具体的执行则是当排队排到该Runnable后系统拿来执行的注意post(Runnable )的时候,并未真正新建线程,只是在原线程上执行而已,没有经过start函数调用run(),
那么就不会创建一个新线程,而post(Thread)才会创建新的线程post(Runnable )其实最后还是会将Runnable转换成一个Message,Message中有一个callback变量,会自动
设置成Runnable,然后在队列中调用Message中的callback的run()方法
基本的使用步骤:
1. 创建Handler对象
2,创建Runnable和消息
3,调用post以及sendMessage方法将Runnable和消息添加到队列
4,在handler对象所在的线程中处理消息创建Looper的两种形式:
1,直接使用UI主线程中的looper,并创建Handler对象
1,创建Handler子类的实例,重写HandlerMessage()方法,该方法负责处理来自其它线程的消息
2,在创建其他线程的时候,将主线程中的Handler对象传给其他线程,其他线程通过该handler就可以直接发消息2,在自己的线程创建looper对象,让别的线程去将消息送过来,除主线程外,Android中的线程默认是没有开启Looper的
(1)调用 Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。
(2)有了Looper之后,创建Handler子类的实例,重写HandlerMessage()方法,该方法负责处理来自其它线程的消息。
(3)调用Looper的loop()方法启动Looper。写在Looper.loop()之后的代码不会被执行,这个函数内部是一个循环
别的线程需要做的事情:
(1)创建nessage,并填充内容。
(2)使用被调用类创建的Handler实例,调用sendMessage(Message msg)方法。
Handler类包含如下方法用于发送、处理消息。
1. void handleMessage(Message msg):处理消息方法,该方法通常用于被重写;
2. final boolean hasMessages(int what):检查消息队列中是否包含what属性为指定值的消息;
3. final boolean hasMessage(int what, Object object):检查消息队列中是否包含what属性为
指定值且Object属性为指定对象的消息;
4. Message obtainMessage():获取消息,或者理解为分配一个Message;Handler中分发消息的一些方法:
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新
5. sendEmptyMessage(int what):发送空消息;
6. final boolean sendEmptyMessageDelayed(int what, long delayMillis):指定多少毫秒后发送空消息:
7. final boolean sendMessage(Message msg):立即发送消息;
8. final boolean sendMessageDelayed(Message msg, long delayMillis):指定多少毫秒之后发送消。
9,sendToTarget()post类方法允许你排列一个Runnable对象到主线程队列中
post(Runnable) // 将Runnable直接添加入队列,直接开始运行
postAtTime(Runnable,long) //定时将Runnable添加入队列
postDelayed(Runnable, long) //延迟一定时间后,将Runnable添加入队列
removeCallbacks(thread) //将Runnable从Runnable队列中取出Handler的常见用法:
1,直接实例化一个Handler,实现对象中的抽象方法,这个Handler将自动与当前运行线程相关联
Handler将与当前运行的线程使用同一个消息队列,并且可以处理该队列中的消息
Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case xxx :
//doing something....
break;
}
}
super.handleMessage(msg);
}
2, 继承Handler,实现父类抽象方法
class MyHandler extends Handler {
public MyHandler() {
}
public MyHandler(Looper L) {
super(L);
}// 子类必须重写此方法,接受数据
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case xxx :
//doing something....
break;
}
}}
=============================================================================
1、JSON(JavaScript Object Notation) 定义
一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案
(有点类似于正则表达式,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换。JSON采用兼
容性很高的文本格式,同时也具备类似于C语言体系的行为2、JSON的结构:
(1) Name/Value Pairs(无序的):类似所熟知的Keyed list、 Hash table、Disctionary
和Associative array。在Android平台中同时存在另外一个类 "Bundle",某种程度上具有相似的行为。
(2) Array(有序的):一组有序的数据列表3,基本数据:
对象:是一个无序的Name/Value Pairs集合{ name:value , name:value }
例子:{ "name":"小猪","age":20 }
Array 是值(value)的有序集合。[ value , value , value ...... ]
值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false
null、对象(object)或者数组(array)。这些结构可以嵌套。
例子1: Array里面包含对象(object)
[ {"id":1,"name":"小猪" ,"age”:22} , {"id":2,"name":"小猫","age”:23} , .......]
例子2:同样对象(object)中可以包含Array
(1)一个对象包含1个数组,2个子对象
{"root":[{"id":"001","name":"小猪"},{"id":"002","name":"小猫"}], "total":3, "success":true }
数组 对象1 对象2
(2)也可以对象嵌套子对象,子对象再嵌套数组
{"calendar":
{"calendarlist":
[
{"id":"001","name":"小猪"},
{"id":"002","name":"小猫"}
]
}
}
4,各种类:在Android中包含四个与JSON相关的类和一个Exceptions:
JSONArray
JSONObject
JSONStringer
JSONTokener
JSONException(1)JSONObject: 这是系统中有关JSON定义的基本单元,其包含一对儿(Key/Value)数值。
它对外部(External:应用toString()方法输出的数值)调用的响应体现为一个标准的字符串
(例如:{"JSON": "Hello, World"},最外被大括号包裹,其中的Key和Value被冒号”:”分隔)。
其对于内部(Internal)行为的操作格式略微,
例如:初始化一个JSONObject实例,引用内部的put()方法添加数值:
new JSONObject().put("JSON", "Hello, World!"),
在Key和Value之间是以逗号”,”分隔。
Value的类型包括:Boolean、JSONArray、JSONObject、Number、String或者默认值JSONObject.NULL object。
有两个不同的取值方法:
get(): 在确定数值存在的条件下使用,否则当无法检索到相关Key时,将会抛出一个Exception信息。
opt(): 这个方法相对比较灵活,当无法获取所指定数值时,将会返回一个默认数值,并不会抛出异常。
(2)JSONArray: 它代表一组有序的数值。将其转换为String输出(toString)所表现的形式是用方括号包裹,
数值以逗号”,”分隔(例如:[value1,value2,value3],大家可以亲自利用简短的代码更加直观的了解其格式)。
这个类的内部同样具有查询行为,
get()和opt()两种方法都可以通过index索引返回指定的数值,
put()方法用来添加或者替换数值。
同样这个类的value类型可以包括:Boolean、JSONArray、JSONObject、Number、
String或者默认值JSONObject.NULL object。
(3)JSONStringer: 根据官方的解释,这个类可以帮助快速和便捷的创建JSONtext。
其最大的优点在于可以减少由于格式的错误导致程序异常,引用这个类可以自动严格按照JSON语法规则(syntaxrules)
创建JSON text。每个JSONStringer实体只能对应创建一个JSON text。 根据下边的实例来了解其它相关信息:
Java代码
String myString = new JSONStringer().object().key("name").value("小猪").endObject().toString();
结果是一组标准格式的JSON text:{"name" : "小猪"}
其中的.object()和.endObject()必须同时使用,是为了按照Object标准给数值添加边界。
同样,针对数组也有一组标准的方法来生成边界.array()和.endArray()。
(4)JSONTokener: 这个是系统为JSONObject和JSONArray构造器解析JSON source string的类,
它可以从source string中提取数值信息。
(5)JSONException: 是JSON.org类抛出的异常信息。
{"phone":["12345678","87654321"],"married":false,"address":{"province":"广东","country":"中国"},"age":100,"name":"george"}json数据的构建:
{
"phone" : ["12345678", "87654321"], // 数组
"name" : "george", // 字符串
"age" : 100, // 数值
"address" : { "country" : "china", "province" : "江苏" }, // 对象
"married" : false // 布尔值
}JSONObject person = new JSONObject();
JSONArray phone = new JSONArray();
phone.put("12345678").put("87654321");
try {
person.put("phone", phone);
person.put("name", "george");
person.put("age", 100);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JSONObject address = new JSONObject();
try {
address.put("country", "china");
address.put("province", "江苏");
person.put("address", address);
person.put("married", false);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.i("JSonTest", person.toString());json数据格式解析我自己分为两种: 一种是普通的,一种是带有数组形式的;
普通形式的:
{"userbean":{"Uid":"100196","Showname":"\u75af\u72c2\u7684\u7334\u5b50","Avtar":null,"State":1}}分析代码如下:
String s = "{\"userbean\":{\"Uid\":100196,\"Showname\":\u75af\u72c2\u7684\u7334\u5b50,\"Avtar\":null,\"State\":1}}";
StringBuilder builder = new StringBuilder();
builder.append(s);
//
JSONObject jsonObject = new JSONObject(builder.toString()).getJSONObject("userbean");
String Uid = jsonObject.getString("Uid");
String Showname = jsonObject.getString("Showname");
String Avtar = jsonObject.getString("Avtar");
String State = jsonObject.getString("State");
以上仅仅是解析简单的对象格式
-----------------------------------------------------------
一下为稍微复杂的, 对象中包含数组的
{"calendar":
{"calendarlist":
[
{"calendar_id":"1705","title":"\u4eb2\u5b50)ddssd","category_name":"\u9ed8\u8ba4\u5206\u7c7b","showtime":"1288927800","endshowtime":"1288931400","allDay":false},
{"calendar_id":"1706","title":"(\u65c5\u884c)","category_name":"\u9ed8\u8ba4\u5206\u7c7b","showtime":"1288933200","endshowtime":"1288936800","allDay":false}
]
}
}
代码:
String s = "以上内容的字符串";
StringBuilder builder = new StringBuilder();
builder.append(s);
// 获取对象
JSONObject jsonObject = new JSONObject(builder.toString()).getJSONObject("calendar");
// 获取对象中的数组
JSONArray jsonArray = jsonObject.getJSONArray("calendarlist");
for(int i=0;i<jsonArray.length();i++){
// 获取对象
JSONObject jsonObject2 = (JSONObject)jsonArray.opt(i);
// 设定容器
CalendarInfo calendarInfo = new CalendarInfo();
// 获取所有的值
calendarInfo.setCalendar_id(jsonObject2.getString("calendar_id"));
calendarInfo.setTitle(jsonObject2.getString("title"));
calendarInfo.setCategory_name(jsonObject2.getString("category_name"));
calendarInfo.setShowtime(jsonObject2.getString("showtime"));
calendarInfo.setEndtime(jsonObject2.getString("endshowtime"));
calendarInfo.setAllDay(jsonObject2.getBoolean("allDay"));
calendarInfos.add(calendarInfo);
}
==========================================================================
高级控件:
AdapterView: 继承于ViewGroup,是一个抽象基类,可以理解为是一个视图容器,同时派生了各种子类,子类的用法基本相似,只是显示的效果不一样
java.lang.Object
android.view.View
android.view.ViewGroup
android.widget.AdapterView<T extends android.widget.Adapter>
直接的子类
AbsListView, AbsSpinner, AdapterViewAnimator
间接的子类
AdapterViewFlipper, ExpandableListView, Gallery, GridView, ListView, Spinner, StackView
特性:
1,AdapterView可以包括多个列表项,并将这些列表项以合适的方式显示处理--- 显示方式
2,列表项的内容是由Adapter提供,调用AdapterView中的setAdapter(Adapter)----数据内容
3,很好的实现了MVC框架,数据模型M(Model)存放数据,利用控制器C(Controller)将数据显示在视图V(View)上
将前端显示和后端数据分离:
ListView相当于MVC框架中的V(视图)
Adapter相当于MVC框架中的C(控制器)
数据源相当于MVC框架中的M(模型)
学习的主要对象,就是AdapterView的子类
(1)ListView:列表,其中只能含有一个控件TextView,
事件的处理:
(1)用户单击列表项事件
为列表加载setOnItemClieckListener监听,重写onItemClick(发生单击事件的列表对象ListView,被单击控件对象view,在列表中的位
置position,被单击列表项的行ID)方法。最后一个是当前的item在listView中的相对位置,大部分时候position和id的值是一样的
(2)用户长按事件
为列表加载setOnItemLongClieckListener监听,重写onItemLongClick(发生单击事件的列表对象ListView,被单击控件对象view,
在列表中的位置position,被单击列表项的行ID)方法。
Adapter的介绍:C(控制器),把数据源中数据以某种样式(xml文件)显示在视图中。
分类:都继承于ListAdapter
(1)ArrayAdapter
只能处理列表项内容全是文本(TextView及其子类)的情况
数据源:数组或者List<String>对象或者其他
构造对象方式:
ArrayAdapter<数据类型> adapter = new ArrayAdapter<数据类型>(context:一般指当前Activity对象,layout:每个列表项显示的布局,data:数据源变量);
(2)SimpleAdapter
不仅可以处理列表项全是文本的情况,当列表项中还有其他控件时,同样可以处理
数据源:只能为List<Map<“键”,“值”>>形式的数据
构造对象方式:
SimpleAdapter adapter = new SimpleAdapter(context:一般指当前Activity对象,data:数据源变量,layout:每个列表项显示的布局,new String[]{}:数据源中的“键”,new int[]{}:显示数据源的控件ID);
(3)BaseAdapter,用于自定义Adapter,可以根据xml文件中定义的样式进行列表项的填充,适用性最强。
1)创建类,继承自BaseAdapter
2)重写其中的四个方法
int getCount():返回的是数据源对象的个数,即列表项数
Object getItem(int position):返回指定位置position上的列表
long getItemId(int position):返回指定位置处的行ID
View getView():返回列表项对应的视图:
|
实例化视图填充器
用视图填充器,根据Xml文件,实例化视图
根据布局找到控件,并设置属性
返回View视图
ListView,垂直方向的可滚动的列表,本身是一个控件,可以在xml文件中定义,也可以代码中示例化出来
xml属性:
android:divider: 列表项之间的分割颜色和Drawable对象
android:entries :指定数组资源,用于在ListView中显示的数据
例子:
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:divider="#f00"
android:dividerHeight="2px"
android:entries="@array/countries" >
</ListView>
通过xml的方式,所定义的listview中的格式基本上是固定的,而且数据也是固定,如果希望listview中动态显示一些数据,就需要
通过Adapter的配合:
ListView + ArrayAdapter(有限制,每个列表项只能是TextView):
private ListView listView; // 适配器控件------->V视图
private ArrayAdapter<String> adapter;// 适配器------>C控制器
listView = (ListView) this.findViewById(R.id.listView1);
adapter = new ArrayAdapter<String>(context, R.layout.tv_item, data); //注意第二参数是R.layout.xx,不是R.id.xxx
listView.setAdapter(adapter);
layout.tv_item:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_info_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:padding="30px"
android:textSize="24sp" ></TextView>
ListView + SimpleAdapter:名字叫简单,但是功能不简单,ListView的大部分应用场景都可以通过SimpleAdapter来完成
LisetView +自定义的xml ==> V
SimpleAdapter ===> C
Map组成的List(比如ArrayList)类型的数据,表示所有列表项中某个空间的数据 ==>M
xml文件:提供视图上的容器
1,主界面,需要一个listview
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:divider="#f00"
android:dividerHeight="2px"
>
</ListView>
2,列表项的布局: person_info.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" ><ImageView
android:id="@+id/itemheader"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
android:paddingLeft="10dp" /><LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" ><TextView
android:id="@+id/itemname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:textColor="#00f"
android:textSize="20sp" /><TextView
android:id="@+id/itemdesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="20dp"
android:textColor="#f0f"
android:textSize="15sp" />
</LinearLayout></LinearLayout>
SimpleAdatper的构建:将数据填入到视图中:
所有列表项中需要的所有数据:
String names[] = new String[] { "习近平", "李白", "刘翔", "马云" };
String descs[] = new String[] { "中国反腐主席", "喜欢喝二锅头的诗人", "没事就跨个栏", "我很丑但我很有钱" };
int imgid[] = new int[] { R.drawable.head1, R.drawable.head2,
R.drawable.head3, R.drawable.head4 };
将所有的数据放入list中:
List<Map<String, Object>> listdata;
listdata = new ArrayList<Map<String, Object>>();
for (int i = 0; i < names.length; i++) {
Map<String, Object> mapItem = new HashMap<String, Object>();
mapItem.put("personname", names[i]);
mapItem.put("persondesc", descs[i]);
mapItem.put("personimg", imgid[i]);
listdata.add(mapItem);}
通过Adapter将数据填入视图中:
listView1 = (ListView) this.findViewById(R.id.listView1);
adapter = new SimpleAdapter(this, listdata, R.layout.person_info,
new String[] { "personimg", "personname", "persondesc" },
new int[] { R.id.itemheader, R.id.itemname, R.id.itemdesc });
// "personimg"键的值对应填充到视图控件R.id.itemheader
listView1.setAdapter(adapter);监听事件:
listView1.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view,
int positon, long id) {TextView tv_name = (TextView) view.findViewById(R.id.itemname);
TextView tv_desc = (TextView) view.findViewById(R.id.itemdesc);Toast.makeText(
mContext,
tv_name.getText().toString() + ":"
+ tv_desc.getText().toString(),
Toast.LENGTH_SHORT).show();}
});
总结
以上都是Android应用程序中经常用到的基础数据适配器,除了可以将将一组数据传到像ListView,
也可以传递到Spinner、Gallery及GridView等UI显示组件,所以只要基础基本步骤就可以:
1,根据情况实例化不同的Adapter,比如ArrayAdapter, SimpleAdapter, BaseAdapter...
2, 调用: setAdapter();
Activity学习日记(八)
猜你喜欢
转载自blog.csdn.net/weixin_42471952/article/details/81141834
今日推荐
周排行