Android Activity的setTitle,AlertDialog,Toast操作是否都必须在非主UI线程中操作呢?

这个问题有点意思,估计大家都会很明确的说在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(){

		@Override
		public 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);    
    
        }            
    };  
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		 //操作A:2秒钟后,将设置title为timerTaskMessage,is ok
		timer.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() {
			
			@Override
			public void onClick(View v) {
				//操作B:直接设置新title,is ok.
				setTitle("4 but is clicked");

				new Thread(){
					public Handler mHandler;
					public  AlertDialog dlg;
					@Override
					public void run(){
						Log.d(TAG, "onclick threadid:"+Thread.currentThread().getId());
						
						//操作E:在非UI主线程直接更新title,is wrong.
						//setTitle("error");
						SystemClock.sleep(1000*5);
						
						//操作C:通知myHandler去更新title
						myHandler.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(){

							@Override
							public 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(){

		@Override
		public 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);    
    
        }            
    };  
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		 //操作A:2秒钟后,将设置title为timerTaskMessage,is ok
		timer.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() {
			
			@Override
			public void onClick(View v) {
				//操作B:直接设置新title,is ok.
				setTitle("4 but is clicked");

				new Thread(){
					public Handler mHandler;
					public  AlertDialog dlg;
					@Override
					public void run(){
						Log.d(TAG, "onclick threadid:"+Thread.currentThread().getId());
						
						//操作E:在非UI主线程直接更新title,is wrong.
						//setTitle("error");
						SystemClock.sleep(1000*5);
						
						//操作C:通知myHandler去更新title
						myHandler.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(){

							@Override
							public void run() {
								//操作F:更新title,is right
								setTitle("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(){

				@Override
				public void run() {
					//操作G:更新title,is error
					setTitle("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");
	    }  
	}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风口猪炒股指标

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值