Activity学习日记(八)

5,    线程的异步处理Handler
      Http网络编程
      Json数据解析
      ListView和Adapter

Handler消息传递机制:
                产生背景:出于性能优化的考虑,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();

猜你喜欢

转载自blog.csdn.net/weixin_42471952/article/details/81141834