Android核心基础-6.Android 耗时操作

1.什么是ANR

  • 在应用程序的主线程中执行一段耗时的代码, 就有可能出现ANR异常.
  • 耗时的代码未执行结束时, 界面会卡住, 用户对界面进行了操作, 10秒之后耗时代码如果还未结束, 就会出现ANR异常

我们的布局文件中有个TextView和一个按钮Button

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textSize="30sp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="go"
        android:text="GO" />

当点击Button按钮时触发go()函数,我们看到函数中执行了一个耗时操作,此时将出现界面卡死现象。

public void go(View v) {
    for (int i = 1; ; i++) {
        System.out.println(i);
        SystemClock.sleep(1000);
    }
}

2.怎么避免ANR

  • 主线程中不要执行耗时的代码
  • 如果一定要做耗时的事情, 开启新线程, 在新线程中执行

我们将耗时操作放置在一个线程中,那么界面就不会被卡死。

public void go(View v) {
    new Thread() {
        public void run() {
            for (int i = 1; ; i++) {
                System.out.println(i);
                SystemClock.sleep(1000);
            }
        }
    }.start();
}

3.UI Thread

  • 安卓手机中主线程负责刷新界面, 以及处理用户的操作
  • 应用程序的界面都是由主线程创建的
  • 界面的修改也只能在主线程中执行

4.Handler

有的时候我们需要执行一些耗时的代码, 会开启新线程, 这时又需要更新界面, 必须在主线程中操作, 那么就需要使用Handler来进行线程之间的通信

Android核心基础-6.Android 耗时操作_第1张图片

4.1 sendMessage():

  • 新线程向主线程发送一个包含数据的消息, 主线程获取消息中的数据
  • 在主线程中创建Handler子类对象, 重写handleMessage()方法
  • 新线程中可以使用Handler的引用调用sendMessage()方法, 发送一个Message对象
  • 只要执行了sendMessage()方法, 那么主线程会自动执行handleMessage()方法, 收到Message对象

4.2 post():

  • 新线程向主线程发送一段代码, 主线程直接执行
  • 在主线程中创建Handler对象
  • 新线程中可以使用Handler调用post()方法发送一个Runnable对象
  • 主线程会自动执行Runable的run()

通过Handler发送Message通知主线程更新界面

public class SendMessageActivity extends Activity {

    private TextView tv;
    private Handler handler = new Handler(){
        public void handleMessage(Message msg) {    // 该方法在sendMessage()方法之后执行, 形参就是发送过来的Message对象
            tv.setText(msg.obj + "");               // 主线程更新界面
        }
    };

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

    public void go(View v) {
        new Thread() {
            public void run() {
                for (int i = 1; ; i++) {
                    // Message msg = new Message();         // 创建消息对象
                    Message msg = handler.obtainMessage();  // 从消息池中获取一个Message(比直接new效率要高)
                    msg.obj = i;                            // 把数据放在消息对象中
                    handler.sendMessage(msg);               // 在新线程中发送消息对象, 主线程会自动执行handleMessage()方法
                    System.out.println(i);
                    SystemClock.sleep(1000);
                }
            }
        }.start();
    }
}

课外知识:Message.obtain()和Handler.obtain()原理都一样,都是去消息池获取一个新的message对象。
但是Message的池全局性更高,两个线程同时使用到同一个的概率高;而Handler的范围相对要小点,指定了某一个Handler的Message对象。

直接通过post发送一段可执行代码更新界面

public class PostActivity extends Activity {

    private TextView tv;
    private Handler handler = new Handler();
    private int i;

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

    public void go(View v) {
        System.out.println("go: " + Thread.currentThread().getName());
        new Thread() {
            public void run() {
                System.out.println("run: " + Thread.currentThread().getName());
                for (i = 1; ; i += 2) {
                    handler.post(new Runnable(){    // 在新线程中使用Handler向主线程发送一段代码, 主线程自动执行run()方法
                        public void run() {
                            System.out.println("Runnable: " + Thread.currentThread().getName());
                            tv.setText(i + "");
                        }
                    });

                    System.out.println(i);
                    SystemClock.sleep(1000);
                }
            }
        }.start();
    }
}

通过打印出来的log:
我们看到go: main, run: Thread-143, Runnable: main
表明post中的代码在主线程中执行的。

示例源代码->百度网盘

你可能感兴趣的:(Android核心基础)