**

android4.0强行规定

:**
1 访问网络的操作必须写在子线程。
2、子线程(非ui线程,非主线程)不可以直接修改UI界面的。
(google工程师设计的规则,不是子线程不能修改UI,只是安卓系统会进行检查的操作,如果发现了会抛异常,所有如果在一开始显示页面时进行改变,安卓程序还来不及检查,也不会报错)
3、子线程中也不能使用toast

例如,如果直接在UI线程中开启子线程来更新TextView显示的内容,运行程序我们会发现,如下错 误:

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
翻译过来就是:只有创建这个控件的线程才能去更新该控件的内容。

google android系统为了解决线程同步和互斥问题,ui更新混乱.
规则: 只有主线程才能更新ui

安卓规定所有的UI线程要去负责View的创建并且维护它,例如更新冒个TextView的显示,都必须在主线程中去做,我们不能直接在UI线程中去创建子线程。

handler机制的原理

对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。

推荐的方法是通过一个Handler来处理这些,可以在一个线程的run方法中调用handler对象的 postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些消息。
  
**andriod提供了 Handler 和 Looper 来满足线程间的通信。**Handler 先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(Message Exchange)。
  1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。
2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到Message Queue里;或者接收Looper从Message Queue取出)所送来的消息。
  3) Message Queue(消息队列):用来存放线程放入的消息。
  4)线程:UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。
如下就是handler的简单工作原理图:

Looper ,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和 Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在 HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个 Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要 释放资源,使用Looper.release方法。

Message 在Android是什么呢? 对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器。对于Handler类提供了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息。 将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如 下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回 .

示例:

package com.itheima.message;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

// 1.在主线程创建一个handler 消息处理器
private Handler handler = new Handler() {

// 处理消息的方法
public void handleMessage(android.os.Message msg) {
// 3.消息放在消息队列里面之后,会被轮询器给取出来,调用handlemessage的方法处理这个消息
// 这个代码 是在主线程里面运行的.
int i = (Integer) msg.obj;
tv.setText("当前进度:"+i);
};

};
private TextView tv;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

getMainLooper();

tv = (TextView) findViewById(R.id.tv);
new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
// 2.利用主线程的handler发送一条消息,消息会放在消息队列里面
Message msg = new Message();

msg.obj = i;//携带数据
handler.sendMessage(msg);
// tv.setText("当前进度:"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();

}

}



使用一: handler.post()

public class MainActivity extends Activity {

private EditText pathET;
private TextView contentTV;
private NetService service;
private Handler handler = new Handler(); // 线程之间通信

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 解析XML文件, 生成Java对象, 设置界面布局

pathET = (EditText) findViewById(R.id.pathET);
contentTV = (TextView) findViewById(R.id.contentTV);
service = new NetService();
}

public void go(View v) {
new Thread(){
public void run() {
try {
String path = pathET.getText().toString().trim(); // 获取路径
final String text = service.getText(path); // 访问网络, 得到文本
handler.post(new Runnable(){ // 在创建Handler的线程中执行run()方法
public void run() {
contentTV.setText(text); // 设置文本到TextView
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}


使用二: handler.sendMessage(msg);

public class MainActivity extends Activity {
protected static final int ERROR = 1;
protected static final int SHOW_HTML = 2;
private TextView tv_content;
private EditText et_path;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ERROR:
Toast.makeText(MainActivity.this, "获取html源文件失败", 0).show();
break;

case SHOW_HTML:
String body = (String) msg.obj;
tv_content.setText(body);
break;
}
};
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_content = (TextView) findViewById(R.id.tv_content);
et_path = (EditText) findViewById(R.id.et_path);

}

public void click(View view){
final String path = et_path.getText().toString().trim();
if(TextUtils.isEmpty(path)){
Toast.makeText(this, "路径不能为空", 0).show();
return;
}else{
//联网
new Thread(){
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
int code = conn.getResponseCode();
if(code ==200){
InputStream is = conn.getInputStream();
String body = StreamUtils.readStream(is);
Message msg = Message.obtain();
msg.obj = body;
msg.what = SHOW_HTML; //指定msg的ID
handler.sendMessage(msg);

}else{
Message msg = Message.obtain();
msg.what = ERROR;
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
Message msg = Message.obtain();
msg.what = ERROR;
handler.sendMessage(msg);
}



};
}.start();

}

更多相关文章

  1. Android之用Handler实现主线程和子线程互相通信以及子线程和子线
  2. 《Android 创建线程源码与OOM分析》
  3. 重新认识Java线程的概念
  4. JAVA实现 springMVC方式的微信接入、实现消息自动回复
  5. java线程实现与进程(二)
  6. java多线程jdk1.7与jdk1.6结果不一致的问题
  7. Java线程的生命周期和状态控制
  8. 为什么我们应该将XMPP而不是JSON用于简单(仅文本)消息传递应用程序
  9. 使用线程设置后台进程以处理Android中的工作

随机推荐

  1. 解决PHP导出大量数据时设置超链接的问题
  2. 为什么我们坚持选择用php
  3. PHP结合Redis来限制用户或者IP某个时间段
  4. PHP替换标签字符
  5. 单击保存按钮(PHP和MSQL)时如何保存记录列
  6. 根据网址上的国家/地区将用户重定向到正
  7. 今天开始学php,第一次写博客以鼓励自己能
  8. smarty模板引擎变量的传递方法
  9. 获取所有网站的错误,[重复]
  10. jquery ajax在调用之后将无法工作