这个问题有点意思,估计大家都会很明确的说在Activity中setTitle()操作,toast显示,AlertDialog的显示肯定都要放置在UI主线程中操作。

其实我也是这么觉得的,但是感觉Activity的setTitle和AlertDialog,Toast还是有区别的。


下面我做了一个测试:


package com.example.demo_handlertest;import java.util.Timer;import java.util.TimerTask;import android.app.Activity;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.content.DialogInterface.OnClickListener;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.os.SystemClock;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;public class MainActivity extends Activity {private Context mContext=this;private final static String TAG="HandlerTest";private Handler myHandler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.d(TAG, "handler out threadid:"+Thread.currentThread().getId());switch(msg.what){case 1:setTitle("1myHandlerMessage");break;case 2:setTitle("2timerTaskMessage");break;case 3:setTitle("3myHandlerMessage");break;}}};Timer timer = new Timer();      TimerTask task = new TimerTask(){           public void run() {                    Message message = new Message();                        message.what = 2;                        myHandler.sendMessage(message);                }                };  @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); //操作A:2秒钟后,将设置title为timerTaskMessage,is oktimer.schedule(task, 1000*2); Log.d(TAG, "oncreate threadid:"+Thread.currentThread().getId());Button but=(Button) this.findViewById(R.id.but);but.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//操作B:直接设置新title,is ok.setTitle("4 but is clicked");new Thread(){public Handler mHandler;public  AlertDialog dlg;@Overridepublic void run(){Log.d(TAG, "onclick threadid:"+Thread.currentThread().getId());//操作E:在非UI主线程直接更新title,is wrong.//setTitle("error");SystemClock.sleep(1000*5);//操作C:通知myHandler去更新titlemyHandler.sendEmptyMessage(3); SystemClock.sleep(1000*5);Looper.prepare();Toast.makeText(getApplicationContext(), "test1", Toast.LENGTH_LONG).show();Log.d(TAG, "handler in looper threadid:"+Thread.currentThread().getId());dlg=new AlertDialog.Builder(mContext).setTitle("提示").setCancelable(false)                        .setMessage("程序崩溃了...").setNeutralButton("我知道了", new OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                           dlg.dismiss();                            }                        })                        .create();dlg.show(); mHandler = new Handler() {  //如果没用使用Looper.getMainLooper(),里面是不可以写setTitle方法的              public void handleMessage(Message msg) {                    // 处理收到的消息                }            };     mHandler.post(new Runnable(){@Overridepublic void run() {//操作F:更新title,is error//setTitle("5mHanlder message");Log.d(TAG, "1handler post threadid:"+Thread.currentThread().getId());Toast.makeText(getApplicationContext(), "test2", Toast.LENGTH_LONG).show();}            });             Looper.loop();}}.start();}});}}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity" >        <Button        android:id="@+id/but"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="update" /></RelativeLayout>





这个测试结果的log如下:





这个测试说明什么呢?


其实说明了2点:

1、从日志中看threadid:1为UI主线程,从oncreate启动的;threadid:8824是在but点击之后启动的一个线程。

2、操作E和操作F被我注释掉了,setTitle会报错的,因为在非主UI线程;操作A,B,C都是在UI主线程中更新的Title这个好理解,

Looper.prepare();

和Looper.loop()之间操作的Toast和AlertDialog为何可以正常显示,然而setTtile却不可以呢?


这个问题大家可以先思考一下,下面我把代码再修改一下。

package com.example.demo_handlertest;import java.util.Timer;import java.util.TimerTask;import android.app.Activity;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.content.DialogInterface.OnClickListener;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.os.SystemClock;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;public class MainActivity extends Activity {private Context mContext=this;private final static String TAG="HandlerTest";private Handler myHandler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.d(TAG, "handler out threadid:"+Thread.currentThread().getId());switch(msg.what){case 1:setTitle("1myHandlerMessage");break;case 2:setTitle("2timerTaskMessage");break;case 3:setTitle("3myHandlerMessage");break;}}};Timer timer = new Timer();      TimerTask task = new TimerTask(){           public void run() {                    Message message = new Message();                        message.what = 2;                        myHandler.sendMessage(message);                }                };  @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); //操作A:2秒钟后,将设置title为timerTaskMessage,is oktimer.schedule(task, 1000*2); Log.d(TAG, "oncreate threadid:"+Thread.currentThread().getId());Button but=(Button) this.findViewById(R.id.but);but.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//操作B:直接设置新title,is ok.setTitle("4 but is clicked");new Thread(){public Handler mHandler;public  AlertDialog dlg;@Overridepublic void run(){Log.d(TAG, "onclick threadid:"+Thread.currentThread().getId());//操作E:在非UI主线程直接更新title,is wrong.//setTitle("error");SystemClock.sleep(1000*5);//操作C:通知myHandler去更新titlemyHandler.sendEmptyMessage(3); SystemClock.sleep(1000*5);Looper.prepare();Toast.makeText(getApplicationContext(), "test1", Toast.LENGTH_LONG).show();Log.d(TAG, "handler in looper threadid:"+Thread.currentThread().getId());dlg=new AlertDialog.Builder(mContext).setTitle("提示").setCancelable(false)                        .setMessage("程序崩溃了...").setNeutralButton("我知道了", new OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                           dlg.dismiss();                            }                        })                        .create();dlg.show(); mHandler = new Handler(Looper.getMainLooper()) {  //如果没用使用Looper.getMainLooper(),里面是不可以写setTitle方法的              public void handleMessage(Message msg) {                    // 处理收到的消息                }            };     mHandler.post(new Runnable(){@Overridepublic void run() {//操作F:更新title,is rightsetTitle("5mHanlder message");Log.d(TAG, "1handler post threadid:"+Thread.currentThread().getId());Toast.makeText(getApplicationContext(), "test2", Toast.LENGTH_LONG).show();}            });             Looper.loop();}}.start();}});}}

logcat的日志如下:



其实这2个代码中只是把mHandler = new Handler(Looper.getMainLooper()),这个是变化之处,我们知道加上Looper.getMainLooper(),handler.post(runnable)其实就是在主线程中运行了。只有这样子 操作F才会正常执行,但是为何无论哪种方式toast和AlertDialog都可以创建呢?


通过这个例子,大家有何想法可以与我交流。


其实,AsyncTask我也做了同样的测试,结果跟上述一样:

  
public class MyAsyncTask extends AsyncTask<Integer, Integer, String> {    AlertDialog dlg;       /**       * 这里的Integer参数对应AsyncTask中的第一个参数        * 这里的String返回值对应AsyncTask的第三个参数       * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改       * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作       */      @Override      protected String doInBackground(Integer... params) {      Looper.prepare();        Handler mHandler = new Handler(Looper.getMainLooper()) {  //如果没用使用Looper.getMainLooper(),里面是不可以写setTitle方法的;而且只能执行onPreExecute方法就卡住了              public void handleMessage(Message msg) {                    // 处理收到的消息                }            };     mHandler.post(new Runnable(){@Overridepublic void run() {//操作G:更新title,is errorsetTitle("doInBackground");Log.d(TAG, "doInBackground threadid:"+Thread.currentThread().getId()+",name:"+Thread.currentThread().getName());Toast.makeText(getApplicationContext(), "doInBackground", Toast.LENGTH_LONG).show();dlg=new AlertDialog.Builder(mContext).setTitle("doInBackground").setCancelable(false)                        .setMessage("我在UI主线程吗").setNeutralButton("我知道了", new OnClickListener() {                            @Override                            public void onClick(DialogInterface dialog, int which) {                                           dlg.dismiss();                            }                        })                        .create();dlg.show();}            });             Looper.loop();    //Toast.makeText(getApplicationContext(), "doInBackground", Toast.LENGTH_LONG).show();    Log.d(TAG, "Thread name:"+Thread.currentThread().getName()+",1doInBackground");    Looper.loop();    Log.d(TAG, "Thread name:"+Thread.currentThread().getName()+",2doInBackground");        return "";      }          /**       * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)       * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置       */      @Override      protected void onPostExecute(String result) {      setTitle("onPostExecute");    Toast.makeText(getApplicationContext(), "onPostExecute", Toast.LENGTH_LONG).show();    Log.d(TAG, "Thread name:"+Thread.currentThread().getName()+",onPostExecute");    }          //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置      @Override      protected void onPreExecute() {      setTitle("onPreExecute");        //textView.setText("开始执行异步线程");      Toast.makeText(getApplicationContext(), "onPreExecute", Toast.LENGTH_LONG).show();    Log.d(TAG, "Thread name:"+Thread.currentThread().getName()+",onPreExecute");    }          /**       * 这里的Intege参数对应AsyncTask中的第二个参数       * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行       * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作       */      @Override      protected void onProgressUpdate(Integer... values) {      setTitle("onProgressUpdate");    Toast.makeText(getApplicationContext(), "onProgressUpdate", Toast.LENGTH_LONG).show();    Log.d(TAG, "Thread name:"+Thread.currentThread().getName()+",onProgressUpdate");    }  }



更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. Android(安卓)操作系统的内存回收机制
  5. Unity3D调用android方法(非插件方式)
  6. Android(安卓)事件分发机制总结
  7. Android学习路线
  8. android 笔记 --- 自定义Android主题风格theme.xml方法
  9. android 数据库sqlite的使用

随机推荐

  1. Android 报错处理:Android resource linki
  2. 关于No resource identifier found for a
  3. 如何以编程方式退出android应用程序
  4. android 安装文件例子
  5. android 拖动图片/拖动浮动按钮
  6. 处理Android应用在后台被杀死
  7. 使用AndroidStudio编译NDK的方法及错误解
  8. android : java.lang.NoClassDefFoundErr
  9. 选项卡使用方法二(Android学习随笔十三)
  10. Android studio instant run导致的classN