摘要:每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操作,则会报错:CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views。

Android为我们提供了消息循环的机制,我们可以利用这个机制来实现线程间的通信。那么,我们就可以在非UI线程发送消息到UI线程,最终让Ui线程来进行ui的操作。

对于运算量较大的操作和IO操作,我们需要新开线程来处理这些繁重的工作,以免阻塞ui线程。在发送http等网络请求时,会经常使用!

下面承接前几篇文章中对天气情况(Weather)的一些详细说明,继续完善,实现在Android客户端的一个天气预报功能的开发:

WeatherActivity主程序代码:

package com.dm.weather;import android.annotation.TargetApi;import android.app.Activity;import android.os.Build;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v4.widget.SwipeRefreshLayout;import android.util.Log;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import android.widget.Toast;import com.dm.CommonData.RetData;import com.dm.CommonData.Weather;import com.google.gson.Gson;import org.apache.http.HttpResponse;import org.apache.http.NameValuePair;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.message.BasicNameValuePair;import org.apache.http.protocol.HTTP;import org.apache.http.util.EntityUtils;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.List;/** * Thread + Handler 实例 */public class WeatherActivity extends Activity {    private SwipeRefreshLayout swipeRefreshLayout;    private TextView refreshTv;    private static final String LOG_TAG = "WeatherActivity";    private static final int MSG_SUCCESS = 1; // 成功标识    private static final int MSG_FAILURE = 0; // 失败标识    private static final String REQUEST_URL = "http://apistore.baidu.com/microservice/weather?cityname=郑州";    private static final String REQUEST_BASE_URL = "http://apistore.baidu.com/microservice/weather";    private ImageView weatherIcon;    private TextView weatherDateTv;    private TextView weatherTimeTv;    private TextView weatherStatusTv;    private TextView windDirectionTv;    private TextView windSpeedTv;    private TextView weatherCurrentTmpTv;    private TextView weatherLowTmpTv;    private TextView weatherHighTmpTv;    private TextView sunRiseTimeTv;    private TextView sunSetTimeTv;    private Thread mThread = null;    private Handler weatherHander = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_SUCCESS:                    // 使用Gson对Json数据的解析,请翻看之前的文章:[Java中使用Gson解析json数据](http://blog.csdn.net/mduanfire/article/details/44703059%20+ "Java中使用Gson解析json数据")                    Gson weatherGson = new Gson();                    Log.i(LOG_TAG, msg.obj.toString());                    Weather weather = weatherGson.fromJson(msg.obj.toString(), Weather.class);                    RetData retData = weather.getRetData();                    weatherDateTv.setText(retData.getDate() + " ");                    weatherTimeTv.setText(retData.getTime());                    windDirectionTv.setText(retData.getWD());                    windSpeedTv.setText(retData.getWS());                    weatherStatusTv.setText(retData.getWeather());                    weatherCurrentTmpTv.setText(retData.getTemp());                    weatherLowTmpTv.setText(retData.getL_tmp());                    weatherHighTmpTv.setText(retData.getH_tmp());                    sunRiseTimeTv.setText(retData.getSunrise());                    sunSetTimeTv.setText(retData.getSunset());                    switch (retData.getWeather()) {                        case "晴":                            weatherIcon.setImageResource(R.drawable.qing);                            break;                        case "多云":                            weatherIcon.setImageResource(R.drawable.duoyun);                            break;                        case "阴":                            weatherIcon.setImageResource(R.drawable.yin);                            break;                        case "阵雨":                            weatherIcon.setImageResource(R.drawable.zhengyu);                            break;                        case "雷阵雨":                            weatherIcon.setImageResource(R.drawable.leizhengyu);                            break;                        case "雷阵雨伴有冰雹":                            weatherIcon.setImageResource(R.drawable.leizhengyubanyoubingbao);                            break;                        case "雨夹雪":                            weatherIcon.setImageResource(R.drawable.yujiaxue);                            break;                        case "小雨":                            weatherIcon.setImageResource(R.drawable.xiaoyu);                            break;                        case "中雨":                            weatherIcon.setImageResource(R.drawable.zhongyu);                            break;                        case "大雨":                            weatherIcon.setImageResource(R.drawable.dayu);                            break;                        case "暴雨":                            weatherIcon.setImageResource(R.drawable.baoyu);                            break;                        case "大暴雨":                            weatherIcon.setImageResource(R.drawable.dabaoyu);                            break;                        case "特大暴雨":                            weatherIcon.setImageResource(R.drawable.tedabaoyu);                            break;                        case "阵雪":                            weatherIcon.setImageResource(R.drawable.zhengxue);                            break;                        case "小雪":                            weatherIcon.setImageResource(R.drawable.xiaoxue);                            break;                        case "中雪":                            weatherIcon.setImageResource(R.drawable.zhongxue);                            break;                        case "大雪":                            weatherIcon.setImageResource(R.drawable.daxue);                            break;                        case "暴雪":                            weatherIcon.setImageResource(R.drawable.baoxue);                            break;                        case "雾":                            weatherIcon.setImageResource(R.drawable.wu);                            break;                        case "冻雨":                            weatherIcon.setImageResource(R.drawable.dongyu);                            break;                        case "沙尘暴":                            weatherIcon.setImageResource(R.drawable.shachenbao);                            break;                        case "浮尘":                            weatherIcon.setImageResource(R.drawable.fuchen);                            break;                        case "扬沙":                            weatherIcon.setImageResource(R.drawable.yangsha);                            break;                        case "强沙尘暴":                            weatherIcon.setImageResource(R.drawable.qiangshachenbao);                            break;                        case "霾":                            weatherIcon.setImageResource(R.drawable.mai);                            break;                        case "小到中雨":                            weatherIcon.setImageResource(R.drawable.zhongyu);                            break;                        case "中到大雨":                            weatherIcon.setImageResource(R.drawable.dayu);                            break;                        case "大到暴雨":                            weatherIcon.setImageResource(R.drawable.baoyu);                            break;                        case "暴雨到大暴雨":                            weatherIcon.setImageResource(R.drawable.dabaoyu);                            break;                        case "大暴雨到特大暴雨":                            weatherIcon.setImageResource(R.drawable.tedabaoyu);                            break;                        case "小到中雪":                            weatherIcon.setImageResource(R.drawable.zhongxue);                            break;                        case "中到大雪":                            weatherIcon.setImageResource(R.drawable.daxue);                            break;                        case "大到暴雪":                            weatherIcon.setImageResource(R.drawable.baoxue);                            break;                        default:                            weatherIcon.setImageResource(R.drawable.qita);                            break;                    }                    refreshTv.setText("刷新成功");                    swipeRefreshLayout.setRefreshing(false);                    break;                case MSG_FAILURE:                    Toast.makeText(getApplicationContext(), "FAILURE, 网络繁忙!", Toast.LENGTH_LONG).show();                    break;            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.weather_activity);        initTitleView();        initWeatherView();        initBaseView();    }    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)    private void initBaseView() {        refreshTv = (TextView) findViewById(R.id.refresh_tv);        swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);        swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                refreshTv.setText("正在刷新");                // 开启新的线程,请求网络数据,返回json数据                mThread = new Thread(runnable);                mThread.start();            }        });    }    Runnable runnable = new Runnable() {        @Override        public void run() {            // Http get//            String requestUrl = REQUEST_URL;////            HttpGet httpGet = new HttpGet(requestUrl);////            try {//                HttpResponse httpResponse = new DefaultHttpClient().execute(httpGet);////                if (httpResponse.getStatusLine().getStatusCode() == 200) {//                    String requestResult = EntityUtils.toString(httpResponse.getEntity());////                    Log.i(LOG_TAG, "requestResult = " + requestResult);//                    // 使用定义的weatherHander的obtainMessage()方法将成功标志和请求成功得到的json字符串传递过去,之后在Hander中进行解析weatherHander.obtainMessage(MSG_SUCCESS, requestResult).sendToTarget();//                } else {//                    Log.i(LOG_TAG, "requestError = " + httpResponse.getStatusLine());//                    Toast.makeText(getApplicationContext(), "Response Error!!" + httpResponse.getStatusLine(), Toast.LENGTH_SHORT).show();//                }//            } catch (IOException e) {//                Log.i(LOG_TAG, e.getMessage());//                e.printStackTrace();//            }            // Http post            String url = REQUEST_URL;            HttpPost httpPost = new HttpPost(url);            try {                HttpResponse httpResponse = new DefaultHttpClient().execute(httpPost);                if (httpResponse.getStatusLine().getStatusCode() == 200) {                    String requestResult = EntityUtils.toString(httpResponse.getEntity());                    Log.i(LOG_TAG, "requestResult = " + requestResult);                    weatherHander.obtainMessage(MSG_SUCCESS, requestResult).sendToTarget();                } else {                    Log.i(LOG_TAG, "requestError = " + httpResponse.getStatusLine());                    Toast.makeText(getApplicationContext(), "Response Error!!" + httpResponse.getStatusLine(), Toast.LENGTH_SHORT).show();                }            } catch (IOException e) {                Log.i(LOG_TAG, "+++++: " + e.getMessage());                e.printStackTrace();            }        }    };    private void initTitleView() {        ImageView titleBackImv = (ImageView) findViewById(R.id.titleBack_iv);        TextView titleTextTv = (TextView) findViewById(R.id.titleText_tv);        titleTextTv.setText("天气状况");        titleBackImv.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                finish();            }        });    }    private void initWeatherView() {        weatherIcon = (ImageView) findViewById(R.id.weather_icon);        weatherDateTv = (TextView) findViewById(R.id.weather_date);        weatherTimeTv = (TextView) findViewById(R.id.weather_time);        windDirectionTv = (TextView) findViewById(R.id.weather_windstatus);        windSpeedTv = (TextView) findViewById(R.id.weather_windspeed);        weatherStatusTv = (TextView) findViewById(R.id.weather_status);        weatherCurrentTmpTv = (TextView) findViewById(R.id.weather_curent_tmp);        weatherLowTmpTv = (TextView) findViewById(R.id.weather_low_tmp);        weatherHighTmpTv = (TextView) findViewById(R.id.weather_high_tmp);        sunRiseTimeTv = (TextView) findViewById(R.id.weather_sunrise_time);        sunSetTimeTv = (TextView) findViewById(R.id.weather_sunset_time);    }}

weather_activity界面布局代码

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/seashell">    <include        android:id="@+id/weather_include"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        layout="@layout/title_layout" />    <android.support.v4.widget.SwipeRefreshLayout        android:id="@+id/swipe_container"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_alignTop="@+id/weather_icon">        <TextView            android:id="@+id/refresh_tv"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Refresh?"            android:padding="5dp"            android:gravity="center_horizontal" />    </android.support.v4.widget.SwipeRefreshLayout>    <ImageView        android:id="@+id/weather_icon"        android:layout_width="120dp"        android:layout_height="120dp"        android:background="@color/powderblue"        android:padding="10dp"        android:layout_marginTop="15dp"        android:layout_marginLeft="15dp"        android:layout_below="@+id/weather_include"        android:src="@drawable/qita" />    <TextView        android:id="@+id/weather_city"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="郑州"        android:textSize="24sp"        android:textColor="@color/black"        android:layout_alignParentRight="true"        android:layout_marginTop="15dp"        android:layout_marginRight="15dp"        android:layout_below="@+id/weather_include"        android:layout_alignRight="@+id/weather_icon" />    <TextView        android:id="@+id/weather_status"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~~~~"        android:textSize="22sp"        android:textColor="@color/black"        android:layout_alignBottom="@+id/weather_icon"        android:layout_centerHorizontal="true" />    <TextView        android:id="@+id/weather_windspeed"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~ "        android:textSize="18sp"        android:textColor="@color/black"        android:layout_below="@+id/weather_windstatus"        android:layout_alignRight="@+id/weather_city"        android:layout_alignEnd="@+id/weather_city" />    <TextView        android:id="@+id/weather_windstatus"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~~~~"        android:textSize="18sp"        android:textColor="@color/black"        android:layout_above="@+id/weather_status"        android:layout_alignRight="@+id/weather_windspeed"        android:layout_alignEnd="@+id/weather_windspeed" />    <TextView        android:id="@+id/weather_date"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~~~~ "        android:textSize="18sp"        android:textColor="@color/black"        android:layout_toLeftOf="@+id/weather_time"        android:layout_below="@+id/weather_city" />    <TextView        android:id="@+id/weather_time"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~ "        android:textSize="18sp"        android:textColor="@color/black"        android:layout_below="@+id/weather_city"        android:layout_alignLeft="@+id/weather_city"        android:layout_alignStart="@+id/weather_city" />    <TextView        android:id="@+id/weather_curent_tmp_hint"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="当前气温: "        android:textSize="20sp"        android:textColor="@color/black"        android:layout_marginTop="15dp"        android:layout_below="@+id/weather_icon"        android:layout_alignLeft="@+id/weather_icon"        android:layout_alignStart="@+id/weather_icon" />    <TextView        android:id="@+id/weather_curent_tmp"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~"        android:textSize="20sp"        android:textColor="@color/black"        android:layout_alignTop="@+id/weather_curent_tmp_hint"        android:layout_toRightOf="@+id/weather_curent_tmp_hint"        android:layout_toEndOf="@+id/weather_curent_tmp_hint" />    <TextView        android:id="@+id/weather_low_tmp_hint"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="最低气温: "        android:textSize="20sp"        android:textColor="@color/black"        android:layout_below="@+id/weather_curent_tmp_hint"        android:layout_toLeftOf="@+id/weather_curent_tmp"        android:layout_toStartOf="@+id/weather_curent_tmp" />    <TextView        android:id="@+id/weather_low_tmp"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~"        android:textSize="20sp"        android:textColor="@color/black"        android:layout_alignTop="@+id/weather_low_tmp_hint"        android:layout_toRightOf="@+id/weather_low_tmp_hint"        android:layout_toEndOf="@+id/weather_low_tmp_hint" />    <TextView        android:id="@+id/weather_high_tmp_hint"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="最高气温: "        android:textSize="20sp"        android:textColor="@color/black"        android:layout_below="@+id/weather_low_tmp_hint"        android:layout_toLeftOf="@+id/weather_high_tmp"        android:layout_toStartOf="@+id/weather_high_tmp" />    <TextView        android:id="@+id/weather_high_tmp"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~"        android:textSize="20sp"        android:textColor="@color/black"        android:layout_below="@+id/weather_low_tmp"        android:layout_toRightOf="@+id/weather_low_tmp_hint"        android:layout_toEndOf="@+id/weather_low_tmp_hint" />    <TextView        android:id="@+id/weather_sunrise_time_hint"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="日出时间: "        android:textSize="20sp"        android:textColor="@color/black"        android:layout_alignTop="@+id/weather_low_tmp"        android:layout_alignRight="@+id/weather_date"        android:layout_alignEnd="@+id/weather_date" />    <TextView        android:id="@+id/weather_sunrise_time"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~"        android:textSize="20sp"        android:textColor="@color/black"        android:layout_alignTop="@+id/weather_sunrise_time_hint"        android:layout_alignLeft="@+id/weather_time"        android:layout_alignStart="@+id/weather_time" />    <TextView        android:id="@+id/weather_sunset_time_hint"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="日落时间: "        android:textSize="20sp"        android:textColor="@color/black"        android:layout_below="@+id/weather_sunrise_time_hint"        android:layout_alignLeft="@+id/weather_sunrise_time_hint"        android:layout_alignStart="@+id/weather_sunrise_time_hint" />    <TextView        android:id="@+id/weather_sunset_time"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="~~~"        android:textSize="20sp"        android:textColor="@color/black"        android:layout_below="@+id/weather_sunrise_time"        android:layout_alignLeft="@+id/weather_sunrise_time"        android:layout_alignStart="@+id/weather_sunrise_time" /></RelativeLayout>

{ 布局代码中使用到的SwipeRefreshLayout Google官方下拉刷新控件将在下篇文章中介绍 }

在AndroidManifest.xml中添加获取网络的权限

    <uses-permission android:name="android.permission.INTERNET"></uses-permission><!--不要忘记设置网络访问权限-->    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

最终效果预览:

Android 使用Thread+Handler实现非UI线程更新UI界面_第1张图片
Android 使用Thread+Handler实现非UI线程更新UI界面_第2张图片

总结:

非UI线程发送消息到UI线程分为两个步骤

  1. 发送消息到UI线程的消息队列:
    通过使用Handler的
Message obtainMessage(int what,Object object)

构造一个Message对象,这个对象存储了是否成功标识what和json字符串,然后通过message.sendToTarget()方法把这条message放到消息队列中去。

  1. 处理发送到UI线程的消息:
    ui线程中,我们覆盖了handler的
public void handleMessage (Message msg)   

这个方法是处理分发给ui线程的消息,判断msg.what的值可以知道mThread是否成功获取数据,如果json字符串数据成功获取,那么可以通过msg.obj获取到这个对象,之后开始解析。

最后在使用布局中相应的TextView将数据填充,实现更新。

更多相关文章

  1. Android异步加载图像小结(含线程池,缓存方法)[转]
  2. Android 应用程序之间数据共享—ContentProvider 保时被访问
  3. 详解Android中的SQLite数据库存储
  4. android基于tcpdump的数据包捕获完整解决方案
  5. android平台下基于MediaRecorder和AudioRecord实现录制AAC、PCM

随机推荐

  1. DexHunter脱壳神器分析
  2. android 中的引用
  3. django返回json的几种方法以及android调
  4. Android——图形系统
  5. [leakcanary]内存泄露检测
  6. Retrofit2.0使用
  7. ramdisk.img文件解析
  8. android 实现汉字排序功能的 整理和思考
  9. Android(安卓)9.0 (P版本) MTK平台原生的
  10. androidのSharedPreferences存储集合对象