Android handler

来源:互联网 发布:ubuntu删除文件夹 编辑:程序博客网 时间:2024/06/16 16:11
1:不要在主线程中执行耗时操作,
因为主线程是用来画界面的,每15ms一个。然后接收用户的触摸。
如果我们在主线程执行一个耗时操作的话,它就会卡在那里了。




图片下载的->线程——handler
如果是联网的API接口-》异步任务


谁有handler 谁就能接受消息。谁有handler,谁就可以接受消息,


+++++++++++++++++++++++++++++++++++++++++++++++++++++++
主线程就是用来画UI的。
子线程不允许更新UI界面上面的内容的,请看第5个实例
handler 创建的时候就是被绑定到了创建它的线程
+++++++++++++++++++++++++++++++++++++++++++++++++++++++


2:“应用程序无响应”是否等待或删除?就是在主线程中执行了太多的


3:handle


//按钮的点击事件的处理,是在主线程执行的
        //当主线程被耗时操作占用了之后,将不能处理其他的输入了。
        //也就是点击其他的按钮没有反应了




执行两个button:
<Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="处理操作"
        android:onClick="btnForWork"
        />
    <Button
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="点击试试"
            android:onClick="btnClickMe"
            />
点击的方法:
    /**
     * 点击事件执行耗时操作
     * @param view
     */
    public void btnForWork(View view) {
  //按钮的点击事件的处理,是在主线程执行的
        //当主线程被耗时操作占用了之后,将不能处理其他的输入了。
        //也就是点击其他的按钮没有反应了
        for(int i=0;i<10000;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }


    public void btnClickMe(View view) {
        Toast.makeText(this,"点到我了",Toast.LENGTH_LONG).show();
    }




4:更新:
public class MyActivity extends Activity implements Runnable{




    public void btnForWork(View view) {
        //按钮的点击事件的处理,是在主线程执行的
        //当主线程被耗时操作占用了之后,将不能处理其他的输入了。
        //也就是点击其他的按钮没有反应了
//        for(int i=0;i<10000;i++){
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
        Thread thread = new Thread(this);
        thread.start();


    }


    public void run(){
        for(int i=0;i<10000;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }




5:报错的实例:添加一行txtState.setText("计数完成");会报错,因为子线程不能功能心UI




    public void run(){
        for(int i=0;i<10000;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
txtState.setText("计数完成");
    }


android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
            at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6740)
            at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:962)
            at android.view.View.requestLayout(View.java:16784)
            at android.view.View.requestLayout(View.java:16784)
            at android.view.View.requestLayout(View.java:16784)
            at android.view.View.requestLayout(View.java:16784)
            at android.view.View.requestLayout(View.java:16784)
            at android.widget.TextView.checkForRelayout(TextView.java:7707)
            at android.widget.TextView.setText(TextView.java:4447)
            at android.widget.TextView.setText(TextView.java:4284)
            at android.widget.TextView.setText(TextView.java:4259)
            at com.zqxue.HandlerDemoLesson.MyActivity.run(MyActivity.java:52)
            at java.lang.Thread.run(Thread.java:841)


6:完成的时候,需要告诉主线程更新UI。
Socket。
ServerSocket 就是不停的等待接受消息
Socket 就是发消息的。


引入Handler
主线中有一个Hnalder对象
子线程




7:
Handler 用于线程之间的消息传递,一个线程A创建了Handler之后
将这个Handler交给另一个线程线程B,当线程B通过handler 发送消息的时候,
线程A,能够自动的获取到这个消息。当线程A 收到消息,就可以利用Handler的方法来处理消息。
+++++++++++++++++++++++++++++++++++++
反过来是不行的,
谁有Handler,谁就能收消息,
上面的B 可以给A 发消息,但是A不能给B发消息。


        //TODO:更新UI的操作只能够在子线程完成
        //如果需要从子线程,告诉子线程更新UI,子线程需要给主线程发消息。
        //主线程需要Handler,才可以从其它线程收到消息。


/**
 * 关于主线程
 * 1.所有的Activity 都是在主线程创建的
 * 2:Activity的所有的生命周期方法,都是在主线程调用的
 * 3:Activity 所有成员变量的声明初始化都是在主线程调用的
 */
++++++++++++++++++++++++++++++++++++++++++
    //用于接收从子线程发送过来的消息
    //根据官方的说明,Hanlder创建的时候,在那个线程创建,就被绑定到那个线程
    //当亲啊的这个handler,被绑定到了主线程上
    //其他线程,就可以通过这个Handler 来给主线程发送消息。


    private Handler handler = new Handler(){
        /**
         * 当子线程使用Hanlder发送消息之后,handler所属的线程就会收到这个消息
         * 收到这个消息之后,就会交给handler自身来处理,
         * 使用handlerMessage 来执行这个消息的操作
         * !!!这个方法在绑定的线程中--自动--执行,当前绑定在主线程,所以就会在主线程中自动执行
         * @param msg  子线程发送的消息
         */
        @Override
        public void handleMessage(Message msg) {
            //删除掉super
            //super.handleMessage(msg);
        }
    };
++++++++++++++++++++++++++++++++++++++++++++
        public void handleMessage(Message msg) {
            //删除掉super
            //super.handleMessage(msg);
            //msg.what  消息的识别码,可以任意设置,根据数值进行不同处理,用户定义的消息码,是自己要定义的,可以是998 199,
            // msg.arg1 相对于setData()消耗的小,就像邮递,一个小的组件,没有必要弄一个大的包裹,setData()需要用到bundler,所以直接使用arg1,arg2
            // msg.arg2 简单的数字表示的数值,需要更具自己的业务场景来定义,就像当前网络,
            // msg.obj 可以是任意对象,是从另一个线程发送过来的。可以代表是数据


//msg.getData();Bundler对象,用于传递Android 标准的数据对象,是和Messager配合更好
            
        }


++++++++++++++++++++++++++++++++++++++++++++
private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //1.定义what,代表不同的额消息,更具what 来进行不同的处理;
            int what = msg.what;
            switch(what){
                case WHAT_UPDATE_STATE:
                    //对于本例,定义msg.obj 代表状态字符串,
                    String str =(String) msg.obj;
                    txtState.setText(str);
                    break;
            }
        }


++++++++++++++++++++++++++++++++++++++++++++
run 方法里面添加:
Message msg = new Message();
        msg.what=WHAT_UPDATE_STATE;
        msg.obj="计数完成";
        handler.sendMessage(msg);
++++++++++++++++++++++++++++++++++++++++++++


现在在run 方法的循环里面将当前的i传handler



    public static final int WHAT_UPDATE_NUMBER = 2;


然后修改


    private Handler handler = new Handler(){
        public void handleMessage(Message msg) {
          int what = msg.what;
            switch(what){
                case WHAT_UPDATE_STATE:
                    //对于本例,定义msg.obj 代表状态字符串,
                    String str =(String) msg.obj;
                    txtState.setText(str);
                    break;
                case WHAT_UPDATE_NUMBER:
                    txtState.setText(String.valueOf(msg.arg1));
                    break;
            }
        }
    };


跟新run:
 public void run(){
        for(int i=0;i<10;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message msg = new Message();
            msg.what=WHAT_UPDATE_NUMBER;
            msg.arg1=i;
            handler.sendMessage(msg);
        }
        Message msg = new Message();
        msg.what=WHAT_UPDATE_STATE;
        msg.obj="计数完成";
        handler.sendMessage(msg);
    }
++++++++++++++++++++++++++++++++++++++++++++++++++++




Message 的创建都和设置
1:Message 构造(不要再循环中使用)
2:Message.obtainMessage()方法,利用已经有的Message进行复用;


++++++++++++++++++++++++++++++++++++++++++++++++++++
更新run方法里面的
Message msg = new Message();
            msg.what=WHAT_UPDATE_NUMBER;
            msg.arg1=i;
            handler.sendMessage(msg);
更新为:
//重用之前创建过的消息,如果没有自动创建新的。
            Message msg = Message.obtain();
            msg.what=WHAT_UPDATE_NUMBER;
            msg.arg1=i;
            handler.sendMessage(msg);


++++++++++++++++++++++++++++++
obtain()方法的策略,当一个Message 被处理完之后,才嫩够复用,如果obtain 执行的时候,有可以被复用的Message
那么直接使用,
如果所有Message没有执行完,这个时候obtain()调用,那么不会找到复用的消息,因此就会创建新的Message;


++++++++++++++++++++++++++++++
    public void run(){
        for(int i=0;i<10;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Message msg = new Message();
            //重用之前创建过的消息,如果没有自动创建新的。
            Message msg = Message.obtain();
            msg.what=WHAT_UPDATE_NUMBER;
            msg.arg1=i;
            handler.sendMessage(msg);
        }


        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Message msg = new Message();
        Message msg =Message.obtain();
        msg.what=WHAT_UPDATE_STATE;
        msg.obj="计数完成";
        handler.sendMessage(msg);
    }
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Handler 发送消息的步骤
Hadler sendMessage(Message)将消息发送到内部的消息队列中
MessageQuere mQueue handler 的成员变量mQueue 实际上就是构造时候Looper内部的消息队列。


+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Looper 传送带的形式  水车,,,
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
android 的main 方法在activity-》activityThread 里面的main


关系图:
可以把MessageQueue 看作是adapter,Message 是里面的data
MessageQueue 使用跟着adapter,handler 去更新这个adapter。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
说白了就是对象间的引用
+++++++++++++++++++++++++++++++++++++++++++++++++++++++




关系描述:
1:现在成在启动的时候,准备一个Looper,这个Looper就会自动和线程链接在一起;
2:Handler在创建的时候,会自动的查找当前线程的Looper对象,进行消息队列的获取
3:Looper通过Loop()方法,在当前线程进行一个死循环,一直来检测消息队列,获取消息;有点像ServerSocket 一个while true的循环
4:在子线程中,获取handler 对象;进行消息的发送,发送的时候就会将这个消息,发送给Looper
所包含的消息队列中;
5:第三点Looper.loop()内部因为一直等待消息,当发送的消息添加到队列的时候,loop的方法就可以检测到
就可以进行处理,调用message中对应的handler的方法来进行处理;因为处理的步骤是在loop方法中执行的
也就代表handler的handlerMessage执行是在Looper 所在的线程中执行的。


如果Looper在主线程,Handler 也在主次按成创建,代表,handlerMessage就在主线程执行的。








+++++++++++++++++++++++++++++++++++++++++++
自己做一个聊天的
++++++++++++++++++++++++++++++++++++++++++++
新建一个activity 继承Runnable


//子线程的Handler
    private Handler subHandler;
public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.char_activity);
        Thread thread = new Thread(this);
        thread.start();
    }


     public void run() {
        //创建Looper 对象,绑定到当前的线程
        Looper.prepare();
        subHandler =new Handler();
        Looper.loop();
    }


如果,下面会报错,因为handler 必须要绑定Looper


     public void run() {
        //创建Looper 对象,绑定到当前的线程
        //Looper.prepare();
        subHandler =new Handler();
        //Looper.loop();
    }




子线程Looper的退出,可以调用Looper的成员方法叫做quit()或者quitSafely()
Looper.myLooper().quit 代表当前线程中的Looper 就退出了,looper的loop方法,结束;


Looper looper = Looper.myLooper();//获取当亲啊的Looper
                looper.quitSafely();




注意:主线程不允许退出;


<ListView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        ></ListView>
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    >
    <TextView
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="1"
        />
    <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="btnSendMessage"
            android:text="创建消息"
            />
    <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="btnEndLooper"
            android:text="退出子线程"
            />
</LinearLayout>






public class CharActivity extends Activity implements Runnable {
    //子线程的Handler
    private Handler subHandler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.char_activity);
        Thread thread = new Thread(this);
        thread.start();
    }


    public void run() {
        //创建Looper 对象,绑定到当前的线程
        Looper.prepare();
        subHandler =new Handler(){
            @Override
            public void handleMessage(Message msg) {
                int what = msg.what;
                switch (what) {
                    case 9:
                        Log.d("Chat", "收到消息");
                        break;
                    case 10:
                        Looper looper = Looper.myLooper();//获取当亲啊的Looper
                        looper.quitSafely();
                        break;
                }
            }
        };
        Looper.loop();//一直等待消息
    }


    /**
     * 点击按钮,发送消息,调用子线程的Handler发送消息
     */
    public void btnSendMessage(View view){
        Message message = subHandler.obtainMessage(9);
        subHandler.sendMessage(message);
    }


    /**
     * 点击按钮,退出子线程的Looper
     * @param view
     */
    public void btnEndLooper(View view) {
        Message message = subHandler.obtainMessage(10);
        subHandler.sendMessage(message);
    }
}




1:关于主线程Looper的创建,在所有的Activity 启动之前 实际上主线程的Looper已经由系统创建好了,并且也执行了Looper的loop方法
2:在主线程中,不需要再进行Looper.prepare();
3:所有的Acitivity 都是。。。。。


Looper的方法:
1:Looper.prepare() 给当前线程创建并且绑定一个Looper对象;
2:Looper.myLooper():获取当前线程的Looper对象,
3:Looper.loop()当前线程阻塞,一直等待消息;
4:ThreadLocal




点击发送:把输入宽内容发送给子线程,子线程使用handler处理消息,内部调用服务器API,发送网络请求



0 0
原创粉丝点击