android应用是单线程模式的。

单线程模式需要记住两条:

一、防止UI线程阻塞

二、确保只在UI线程中访问Android UI工具包

在开发Android应用时必须遵守单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

每个Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。

开一个线程或者在后台线程中来执行耗时的操作,如下面的例子:

publicvoidonClick(Viewv){

newThread(newRunnable(){

publicvoidrun(){

Bitmapb=loadImageFromNetwork(); //从网络上下载图片

mImageView.setImageBitmap(b); //把图片设置给ImageView

}

}).start()

}

上面的代码会报错,你可能会说逻辑很正确啊,但是它违背了Android单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.

例如:如果在非UI线程直接对UI进行了操作,则会报错:

CalledFromWrongThreadException:only the original thread that created a view hierarchy can touch its views

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

Andriod提供了几种在其他线程中访问UI线程的方法:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable,long)

Hanlder

对于运算量较大的操作和IO操作,我们需要新开线程来处理这些繁重的工作,以免阻塞ui线程。

例子:下面我们以获取CSDN logo的例子,演示如何使用Thread+Handler的方式实现在非UI线程发送消息通知UI线程更新界面

ThradHandlerActivity.java:
package com.example.thread;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import com.example.test.R;import android.annotation.SuppressLint;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ImageView;import android.widget.Toast;public class ThreadHandlerActivity extends Activity{private static final int MSG_SUCCESS = 0;private static final int MSG_FAILURE = 1;private ImageView mImageView;private Button mButton;private Thread mThread;@SuppressLint("HandlerLeak")private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_SUCCESS:mImageView.setImageBitmap((Bitmap)msg.obj);Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();                  break; case MSG_FAILURE:                  Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();                  break;}super.handleMessage(msg);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.thread_layout);mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView      mButton = (Button) findViewById(R.id.click);    mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if (mThread == null) {mThread = new Thread(runnable);mThread.start();}else {                      Toast.makeText(getApplication(), "线程已经运行", Toast.LENGTH_LONG).show();                  } }});}Runnable runnable = new Runnable() {@Overridepublic void run() {HttpClient hc = new DefaultHttpClient();HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");final Bitmap bm;try {HttpResponse hr = hc.execute(hg);bm = BitmapFactory.decodeStream(hr.getEntity().getContent());} catch (Exception e) {e.printStackTrace();mHandler.obtainMessage(MSG_FAILURE).sendToTarget();return;}mHandler.obtainMessage(MSG_SUCCESS, bm).sendToTarget();//mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素// mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。  //             @Override  //             public void run() {//run()方法会在ui线程执行  //                 mImageView.setImageBitmap(bm);  //             }  //         });  }};}

对于上面的方法,我们使用的是handler+Thread来实现更新UI,在里面也有一条注意的就是

mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素
其实我们上面提到一个方法Activity.runOnUiThread(Runnable),将这个Runnable以UI线程的方式启动
/**     * Runs the specified action on the UI thread. If the current thread is the UI     * thread, then the action is executed immediately. If the current thread is     * not the UI thread, the action is posted to the event queue of the UI thread.     *     * @param action the action to run on the UI thread     */    public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }

上面Activity的runOnUiThread(Runnable)方法实现。
利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 这样Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程。
使用示例:

current_activity.this. runOnUiThread(new Runnable()                     @Override                   public void run() {                           // refresh ui 的操作代码                   }});
这里需要注意的是runOnUiThread是Activity中的方法,在线程中我们需要告诉系统是哪个activity调用,所以前面显示的指明了activity.

所以我们修改一下上面的代码:

package com.example.thread;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import com.example.test.R;import android.annotation.SuppressLint;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ImageView;import android.widget.Toast;public class ThreadHandlerActivity extends Activity{private static final int MSG_SUCCESS = 0;private static final int MSG_FAILURE = 1;private ImageView mImageView;private Button mButton;@SuppressLint("HandlerLeak")private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_SUCCESS:mImageView.setImageBitmap((Bitmap)msg.obj);Toast.makeText(getApplication(), "成功获取图片", Toast.LENGTH_LONG).show();                  break; case MSG_FAILURE:                  Toast.makeText(getApplication(), "获取图片失败", Toast.LENGTH_LONG).show();                  break;}super.handleMessage(msg);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.thread_layout);mImageView= (ImageView) findViewById(R.id.logo);//显示图片的ImageView      mButton = (Button) findViewById(R.id.click);    mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {ThreadHandlerActivity.this.runOnUiThread(runnable);}});}Runnable runnable = new Runnable() {@Overridepublic void run() {HttpClient hc = new DefaultHttpClient();HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");final Bitmap bm;try {HttpResponse hr = hc.execute(hg);bm = BitmapFactory.decodeStream(hr.getEntity().getContent());} catch (Exception e) {e.printStackTrace();mHandler.obtainMessage(MSG_FAILURE).sendToTarget();return;}mImageView.setImageBitmap(bm);}};}
也可以在线程里面直接更新UI。

有人会说我传递一个当前的Activity到一个线程中,然后实现UI更新,那我就是调用的当前的Activity的内容,其实这个也是不对的也会提示

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

更多相关文章

  1. Android(安卓)数据库的简单使用
  2. Android(安卓)中如何调节 TextView 的字间距
  3. android volatile的使用
  4. Android(安卓)彻底关闭WebView,防止WebView造成OOM
  5. 旅行青蛙(旅かえる)逆向笔记
  6. Android: 如何打开assets or raw文件夹下的数据库文件
  7. Android并发修改异常:java.util.ConcurrentModificationException
  8. Android逆向之旅—Hook神器Frida使用详解
  9. Android(安卓)应用安装过程分析

随机推荐

  1. Mac环境下配置python3
  2. 我需要一个django-芹菜守护进程来监听特
  3. 利用python破解sqlserver账号密码
  4. python2.X 画一个正方体
  5. jira python oauth:如何获取身份验证的参
  6. python-整理--连接MSSQL
  7. 重构“击中”游戏的值
  8. GetLastInputInfo等价于Linux,用于检测最
  9. 比位移位快2倍?
  10. Python 面相对象 —— 类的三大成员