10 UI线程阻塞及其优化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10 UI线程阻塞及其优化相关的知识,希望对你有一定的参考价值。

1button1移动30次的小动画:

Ui_thread01Activity.java:

public class Ui_thread01Activity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //为button1添加一个动画
        Button button1=(Button)findViewById(R.id.button1);
        //类似jQuery中的animate方法
        TranslateAnimation animation=new  TranslateAnimation(0,150,0,0);//在x轴上移动150
        animation.setRepeatCount(30);//重复30次
        animation.setDuration(2000);//设置动画经历时间
        button1.setAnimation(animation);
    }

}

运行结果:button1不停在移动,移动30

 


 

2、button1移动30次时,button2也有相应事件的小动画:

Ui_thread01Activity.java:

public class Ui_thread01Activity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //为button1添加一个动画
        Button button1=(Button)findViewById(R.id.button1);
        //类似jQuery中的animate方法
        TranslateAnimation animation=new  TranslateAnimation(0,150,0,0);//在x轴上移动150
        animation.setRepeatCount(30);//重复30次
        animation.setDuration(2000);//设置动画经历时间
        button1.setAnimation(animation);
        
        //为button2添加相应事件
        Button button2=(Button)findViewById(R.id.button2);
        button2.setOnClickListener(new OnClickListener(){
            public void onClick(View v) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
        });
    }

}

运行结果:

一开始button一直在做平移运动,当点击button2时,button1就停止运动,过一段时间之后,button1继续运动直到30次结束;

 


 

Main threadUI thread

当一个应用程序启动之后,android系统会为这个应用程序创建一个主线程。这个线程非常重要,它负责渲染视图,分发事件到响应监听器并执行,对界面进行轮询的监听。因此,一般也叫做“UI线程”。

Android系统不会给应用程序的多个元素组件,建立多个线程来执行。一个视图(activity)中的多个view组件运行在同一个UI线程当中。因此,多个view组件的监听器的执行可能会相互影响。

例如:当在ui线程中执行耗时操作,比如访问网络,访问数据库等。则会导致UI线程阻塞。当UI线程阻塞,则屏幕会出现卡死情况。这样用户体验非常差。当线程阻塞超过5秒以后,Android系统有可能进行干预,弹出对话框询问是否关闭应用程序。

 

 


 

例如,当我们把button2的相应事件中时间改为50秒;那么此时系统会自行进行干预:

代码:Thread.sleep(50000);

运行结果:


 

如何让程序不引起阻塞;解决方案:

1、创建一个新的线程;

代码:

public class Ui_thread01Activity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //为button1添加一个动画
        Button button1=(Button)findViewById(R.id.button1);
        //类似jQuery中的animate方法
        TranslateAnimation animation=new  TranslateAnimation(0,150,0,0);//在x轴上移动150
        animation.setRepeatCount(30);//重复30次
        animation.setDuration(2000);//设置动画经历时间
        button1.setAnimation(animation);
        
        //为button2添加相应事件
        Button button2=(Button)findViewById(R.id.button2);
        button2.setOnClickListener(new OnClickListener(){
            public void onClick(View v) {
                //创建一个新的线程
                new Thread(new Runnable(){
                    public void run() {
                        try {
                            Thread.sleep(50000);
                        }catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();;
                
            }
            
        });
    }

}

运行效果:(当button2点击的时候,不会停止button1的移动)

2、然而会有新的问题发生;

官方规则:

把上面setOnClickListener的代码改成:

会产生问题的代码:

button2.setOnClickListener(new OnClickListener(){
            public void onClick(final View v) {
                //创建一个新的线程
                new Thread(new Runnable(){
                    public void run() {
                        try {
                            Thread.sleep(50000);
                        }catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //执行之后把button2上面的字改成10
                        int sum=10;//通过耗时操作计算处理一个值
                        TextView view = (TextView) v;
                        view.setText(""+10);
                        
                    }
                }).start();;
                
            }
            
        });

运行结果:

对此问题的解决措施:

1、用解决方案1view.post:

原理图:

public class Ui_thread01Activity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //为button1添加一个动画
        Button button1=(Button)findViewById(R.id.button1);
        //类似jQuery中的animate方法
        TranslateAnimation animation=new  TranslateAnimation(0,150,0,0);//在x轴上移动150
        animation.setRepeatCount(30);//重复30次
        animation.setDuration(2000);//设置动画经历时间
        button1.setAnimation(animation);
        
        //为button2添加相应事件
        Button button2=(Button)findViewById(R.id.button2);
        button2.setOnClickListener(new OnClickListener(){
            public void onClick(final View v) {
                //创建一个新的线程
                new Thread(new Runnable(){
                    public void run() {
                        Log.i("myinfo", "线程开始执行!!!");
                        try {
                            Thread.sleep(5000);
                        }catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //执行之后把button2上面的字改成10
                        int sum=10;//通过耗时操作计算处理一个值
                        
                        v.post(new Runnable(){
                            public void run() {
                                Log.i("myinfo", "post开始执行!!!");
                                TextView view = (TextView) v;
                                view.setText(""+10);
                            }
                        });
                        Log.i("myinfo", "线程执行结束!!!");
                    }
                }).start();;
                
            }
            
        });
    }

}

运行结果:点击button2之后,过一段时间,button2会变成10,不会产生阻塞。

LogCat显示情况:

次解决方法的缺点:

    可读性差,维护性差;

2、用解决方案2AsyncTask:(最佳解决方案)

异步:此方法写法与view.post相同,只是将方法进行了封装。

分成后台执行的耗时任务和与UI组件进行交互的任务;

代码:

public class Ui_thread01Activity extends Activity {
    Button button2=null;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //为button1添加一个动画
        Button button1=(Button)findViewById(R.id.button1);
        //类似jQuery中的animate方法
        TranslateAnimation animation=new  TranslateAnimation(0,150,0,0);//在x轴上移动150
        animation.setRepeatCount(30);//重复30次
        animation.setDuration(2000);//设置动画经历时间
        button1.setAnimation(animation);
        
        //为button2添加相应事件
        Button button2=(Button)findViewById(R.id.button2);
        button2.setOnClickListener(new OnClickListener(){
            public void onClick(final View v) {
                //执行,创建Task对象
                new DownloadImageTask().execute();
                
            }
        });
    }
    
    private class DownloadImageTask extends AsyncTask<String, Void, Integer>{  
        @Override  
        protected Integer doInBackground(String...urls ) {  
           try{
               Thread.sleep(5000);
           }catch(InterruptedException e){
               e.printStackTrace();
           }
           int sum=10;
           return sum;
        }  
        @Override
        protected void onPostExecute(Integer sum) {
            button2.setText(""+sum);
        } 
    }
    

}

运行结果:

 

以上是关于10 UI线程阻塞及其优化的主要内容,如果未能解决你的问题,请参考以下文章

IO模型之BIO代码详解及其优化演进

Java多线程之synchronized及其优化

异步代码是不是在 UI 线程或新/不同线程中运行以不阻塞 UI?

如何追踪阻塞主(UI)线程的调用?

为啥 File.ReadAllLinesAsync() 会阻塞 UI 线程?

UIImage initWithData:从异步调度中阻塞 UI 线程?