Android Handler 异步调用修改界面与主线程

在Android编程的过程中,如果在Activity中某个操作会运行比较长的时间,比如:下载文件。这个时候如果在主线程中直接下载文件,会造成Activity卡死的现象;而且如果时间超过5秒,会有ANR报错。

在这种情况下, 可以使用Handler来处理。

涉及到的类主要有:Handler、Thread、Message、MessageQueue、Looper、HandlerThread

  如果是针对上面的情况,可以只使用Handler、Message和Thread就可以解决。在Thread中处理下载文件的过程,并在结束后,向Handler发送消息(Message)。Handler在接收到消息后对界面进行修改。

  如果只是不想在界面线程中进行下载文件的操作,那只需要创建一个新的线程来处理下载文件的过程就可以,为什么还要使用Handler呢?因为Android的界面线程是不安全的,所以如果直接在Thread中修改界面会报错,所以要使用Handler来处理。当在界面线程中获取Handler时,Handler将和界面线程在同一个线程中,所以通过Handler修改界面将不会报错。

1、Handler要重写handleMessage方法,用来处理修改界面的逻辑。

        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                super.handleMessage(msg);

                switch (msg.what) {
                case MessageType.ProgressType :
                    int arg1 = msg.arg1;
                    if (arg1 >= maxProgress) {
                        Toast.makeText(MainActivity.this, "Progress is OK", Toast.LENGTH_SHORT);
                        return;
                    } else if (arg1 < 0) {
                        arg1 = 0;
                    }
                    progressBar.setProgress(arg1);
                    break;

                default :

                    break;
                }
            }
        };

说明:

  • 此处直接使用没有参数的Handler构造函数,表示获取当前线程的Looper,而主线程在创建的时候默认生成了一个Looper,所以在主线程的Activity中,可以直接使用Handler的无参构造函数。
  • 如果在主线程中想要获取主线程的Looper,可以使用Looper.myLooper()方法获取。
  • 在其他线程中要想获取主线程的Looper,需要使用Looper.getMainLooper()方法获取,在主线程中也可以使用这个方法获取。
  • 在其他线程中,如果直接使用Looper.myLooper()方法,将获取到null。

2、Thread中要在下载完文件后(我使用了TimerTask和Timer来实现100毫秒进度加1),向Handler发送消息。

public class MyProgressTimerTask extends TimerTask {

    private Handler handler;
    private int count;

    public MyProgressTimerTask(Handler handler) {
        this.handler = handler;
        count = 0;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        count++;
        Message message = handler.obtainMessage();
        message.what = MessageType.ProgressType;
        message.arg1 = count;
        handler.sendMessage(message);

        Looper looper = Looper.myLooper();
        if (null == looper) {
            System.out.println(">>>>>>>>>>>>>>>>Looper.myLooper is null<<<<<<<<<<<<<<");
        }
    }

}

3、在点击开始按钮时,启动Timer;点击停止时,关闭Timer。也可以不使用Timer,而是使用Thread,在Thread中sleep一段时间,依然可以达到预期的效果。同时,两种方式都可以对Activity进行操作,不会有死机的感觉。

     progressBar = (ProgressBar) findViewById(R.id.progressBar);
        maxProgress = progressBar.getMax();

        btnStartProgress = (Button) findViewById(R.id.btnStartProgress);
        btnStartProgress.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                task = new MyProgressTimerTask(handler);
                timer = new Timer();
                timer.schedule(task, 1000, 100);
            }
        });

        btnStopProgress = (Button) findViewById(R.id.btnStopProgres);
        btnStopProgress.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if (task != null) {
                    task.cancel();
                }
                if (timer != null) {
                    timer.cancel();
                }
                task = null;
                timer = null;
            }
        });

注意:在生成Message的时候推荐使用Handler.obtainMessage()和Message.obtain()方法,因为这两个方法(其实最终都是调用Message.obtain()方法)不是简单的new一个对象。分析代码可以看出,它会先判断消息池中是否有可用的消息(由sPool指示开始Message的Message链表),如果没有才会去创建新的Message。

主线程:

  Activity的绘制是在主线程中完成的,而主线程在创建的时候,就会创建Looper对象,所以界面线程使用Handler的时候,不需要考虑创建Looper的问题。那是不是Activity等组件的所有操作都在主线程中进行呢?通过实验发现,基本可以这样理解,当然如果在组件中创建新的线程,在新的线程的操作当然不可能在主线程中进行(比如new一个Thread,在Thread中计算1——100000的和)。除了这样的操作外,可以理解为都在主线程中处理,即使这个组件是由new Thread调用startActivity创建的。

  我写了这样一段代码。首先在启动的Activity中,启动一个新的线程,在这个新的线程中分别启动一个Activity、Service、BroadcastReceiver。在Activity的onCreate、onStart、onResume、onPause、onStop、onDestroy、onRestart、onActivityResult方法中打印当前线程,结果打印出的都是主线程。在Service的onCreate中和BroadcastReceiver的onReceive中也是相同的效果。所以基本可以说组件的操作都是在主线程中进行的。

  因此,如果Handler主要用途是修改界面的话,那HandlerThread就不需要使用了。但是如果考虑到这样的场景,在新启动的线程A中,任务还是太多,又必须启动其他线程,而线程A又需要获得其他线程的返回结果。这时,使用HandlerThread可能就能很方便的解决问题了。Looper的使用应该很少了,HandlerThread的作用就是屏蔽掉Looper,直接使用Looper比较复杂,需要先使用Looper.prepare,然后在所有操作结束后调用Looper.loop使其循环读取消息队列中的消息。使用HandlerThread后,程序将极度简化。

  

参考资料 :

深入理解Android消息处理系统——Looper、Handler、Thread

深入剖析Android消息机制

Android HandlerThread 的使用及其Demo

时间: 2024-10-12 04:41:51

Android Handler 异步调用修改界面与主线程的相关文章

Android Handler 异步消息处理机制的妙用 创建强大的图片加载类

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 最近创建了一个群,方便大家交流,群号:55032675 上一篇博客介绍了Android异步消息处理机制,如果你还不了解,可以看:Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 .那篇博客的最后,提出可以把异步消息处理机制不仅仅是在MainActivity中更新UI,可以用到别的地方,

android 在android中教你一行代码判断是不是主线程

我们有些时候需要判断这是不是主线程,特别是在一些使用别人框架的时候,比如想在某个回调中显示textview控件上的文字,但是我不知道他是不是在主线程中,下面一行代码就可以判断是不是在主线程中 android 在android中教你一行代码判断是不是主线程,代码如下 if (Looper.myLooper() == Looper.getMainLooper()){ //是在主线程中 }else{ } 我这是在看imageload源码中看到了,收货了!

tornado 异步调用系统命令和非阻塞线程池

项目中异步调用 ping 和 nmap 实现对目标 ip 和所在网关的探测 Subprocess.STREAM 不用担心进程返回数据过大造成的死锁, Subprocess.PIPE 会有这个问题. import tornado.gen from tornado.process import Subprocess @tornado.gen.coroutine def run_command(command): """run command""" p

Android Handler 异步消息处理机制的妙用 创建强大的图片载入类

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 近期创建了一个群.方便大家交流,群号:55032675 上一篇博客介绍了Android异步消息处理机制.假设你还不了解,能够看:Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 . 那篇博客的最后,提出能够把异步消息处理机制不仅仅是在MainActivity中更新UI.能够用到别的地方

Android ------ handler 异步处理消息

Handler基本概念: Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分逐个的在消息队列中将消息取出,然后对消息进行出来,就是发送消息和接收消息不是同步的处理. 这种机制通常用来处理相对耗时比较长的操作. Handler 常用方法: post(Runnable) postAtTime(Runnable,long) postDelayed(Runnable long) sendEmptyMessage(int) sendMes

WebService 同步调用,异步调用

阅读目录 一:添加WebService服务 二:添加“客户程序”(这里用winform)用于调用WebService服务 三:异步调用过程解释 一:添加WebService服务 1.添加一个空网站项目,2.在项目里面添加一个WebService服务(WebServiceTest.asmx),3.添加“HelloWorld”方法 代码:(WebServiceTest.asmx) using System; using System.Collections.Generic; using System

Android 之异步任务(AsyncTask,Handler,Message,looper)

AsyncTask: 3个类型(Params,Progress和Result),4个步骤(onPreExecute(),doInBackground(Params…),onProgressUpdate(Progress…), onPostExecute(Result) ) Android的AsyncTask比Handler更轻量级一些,适用于简单的异步处理. 首先明确Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异

android 调用系统界面intent

Intent跳转到系统应用中的拨号界面.联系人界面.短信界面及其他 现在开发中的功能需要直接跳转到拨号.联系人.短信界面等等,查找了很多资料,自己整理了一下. 首先,我们先看拨号界面,代码如下: Intent intent =new Intent(); intent.setAction("android.intent.action.CALL_BUTTON"); startActivity(intent); 和 Uri uri = Uri.parse("tel:xxxxxx&q

客户端的异步调用

C#客户端的异步操作 阅读目录 开始 示例项目介绍 同步调用服务 异步接口介绍 1. 委托异步调用 2. 使用IAsyncResult接口实现异步调用 3. 基于事件的异步调用模式 4. 创建新线程的异步方式 5. 使用线程池的异步方式 6. 使用BackgroundWorker实现异步调用 客户端的其它代码 各种异步方式的优缺点 异步文件I/O操作 数据库的异步操作 异步设计的使用总结 在Asp.net中使用异步 上篇博客[用Asp.net写自己的服务框架] 我讲述了如何实现自己的服务框架,但