android中进程之间通信
2012-06-30 19:45:00 weixin_33767813 阅读数 28

Activity是android系统的三大组件之一,他可以跨进程去调用别的进程中的activity。

1.前提:

      被调用的activity必须是共享的。在电话拨号器的例子中,

  1. ntent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678"));  
  2. startActivity(callIntent); 

执行完这些代码后,该应用程序就会去调用系统的拨号功能,实现拨号。其实系统的拨号功能也是有一个activity实现的,系统把这个activity共享了,给他注册了一个id号叫做:android.intent.action.CALL

2.怎么实现activity的共享

       就是给activity注册一个唯一标识符action。在功能配置文件中的意图过滤器中配置他,他的元素名称叫做action,我们可以称之为动作,他的作用是告诉android做什么,就是要去调用那个activity实现功能。

3.如何调用别的进程中的activity

       这里就要用到意图了,intent是一种运行时绑定机制,他有效地降低了android中组件的耦合性。意图通过意图,程序可以向android系统表达某种意愿或诉求,让系统执行某些动作,他还可以激活新的activity或者让activity执行新的动作。intent的构造函数有一个是这样的Intent(String action, Uri uri),第一个参数是共享的activity的唯一标识符。第二个参数是uri。它包括协议和数据。activity的Intent-filter是相 对应的,action能够匹配出由那个activity来执行动作,uri来告诉这个activity具体干什么。因为一个activity可以配置多个 Intent-filter,例如记事本程序的NoteEditor activity有两个filter -- 一个启动并显示一个特定的记录给用户查看或编辑, 另一个启动一个空的记录给用户编辑.

4.如何传递数据

       所有的数据都是通过intent来传递的,但是我们可以根据实现方式的不同来划分为两种方式,第一种直接在uri中绑定数据,通常为协议头+数据,例如Uri.parse("tel:12345678")); 另一种方式是通过intent的put**方法来绑定数据或者intent直接绑定bundler对象。

5.当调用一个新的activity之后,如何获得这个activity的返回值

使用startActivityForResult方法来启动其他应用程序的Activity,以便获得Activity的返回值

然后在InvokeActivity中使用下面的代码来调用Main。

  1. intent = new Intent("net.blogjava.mobile.MYACTION", Uri.parse("info://调用其他应用程序的Activity"));  
  2. intent.putExtra("value", "调用成功");  
  3. startActivityForResult(intent, 1);             // 1为请求码

获得返回值:必须要实现onActivityResult方法

  1. protected void onActivityResult(int requestCode, int resultCode, Intent data)  
  2. {  
  3.    Toast.makeText(this, "返回值:" + data.getExtras().getString("result"),  
  4.            Toast.LENGTH_LONG).show();  
  5. }

总结:跨进程访问Activity(访问其他应用程序中的Activity)主要是通过一个Action来完成的,如果要传递数据,还需要指定一个 Uri。当然,传递数据也可以通过Intent来完成。传递数据的过程可以是双向的。如果要想从调用的Activity中返回数据,就需要使用 startActivityForResult方法来启动Activity了。

2016-04-16 20:22:40 LeeHDsniper 阅读数 7260

概述

假设一个情景,在一个应用程序中,要完成一个比较复杂、耗时较长的计算任务,如果将这个任务直接在主线程中开始,那么用户界面就会停止对用户操作的响应,而去解决这个计算任务,直到任务完成。
显然,这不是我们想要的结果。那么就需要用到多线程来解决这个问题。
但是新的问题又出现了,在C#和Android中,子线程是不能直接修改用户界面的数据的。也就是说,子线程计算出的结果,不能直接在子线程中让它显示在用户界面上。那么就需要把数据首先传递给主线程,让主线程去修改用户界面。
Handler就是为了解决这个问题出现的。

什么是Handler

  • 从宏观上来说,Handler是一种消息传递机制
  • 从微观上来说,Handler是一个类

在子线程中向主线程发送数据

下面通过一段伪代码来实现子线程向主线程发送数据
假设我们新建了一个项目,这个项目有下面这个Activity:

public class CounterActivity extends AppCompatActivity {
    public Button pressBtn;
    public TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        {
            @Override
            public void onClick(View v) 
            {
            //启动一个线程进行一个复杂计算
            //当计算完成时,我们不能在线程中这样写:
            //textView.setText("result");
            }
        });
    }
}

很简单,这个Activity包含两个空间,一个TextView用来显示计算结果,一个Button用来开启一个线程进行一个复杂计算。
那么当计算完成时,我们不能在线程中直接修改TextView的内容,而是要把结果通过Handler传递给主线程。注意消息的传递方向:子线程→主线程。
现在就出现了一个问题,在哪里定义Handler,或者说Handler属于谁?主线程还是子线程?答案是主线程。要给谁发消息,就要用谁的Handler。
什么叫“谁的”?要在java中使用线程,一般都是继承Thread类并重写其中的run()方法,不管主线程还是子线程,都是以类的实例的方式存在的,也就是说,要用Handler向主线程发送消息,这个Handler必须是主线程的类成员,不能是子线程的;同理,要用Handler向子线程发送消息,那么这个Handler必须在子线程中声明,而不能在主线程中声明。
在主线程中声明了Handler之后,就要重写这个Handler的
void hanleMessage(Message msg)方法。
至于如何构建Message对象,如何对Message对象进行筛选和数据提取,这里就不做说明了,很简单。
进一步完善上面的伪代码如下:

public class MainActivity extends AppCompatActivity {
    //由上面的讲解,显然我们要在这个MainActivity中定义一个Handler
    public Handler mHandler;
    public Button pressBtn;
    public TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        //在这里重写mHandler的handleMessage方法
        mHandler=new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                //对msg进行处理,获得其中的数据也就是计算结果
                //修改用户界面
                textView.setText("result");
            }
        };
        pressBtn.setOnClickListener(new View.OnClickListener() 
        {
            @Override
            public void onClick(View v) 
            {

                new Thread()
                {
                    @Override
                    public void run()
                    {
                    //在这里进行一段复杂计算
                    //可以用Thread.sleep(1000);代替
                        try 
                        {
                            Thread.sleep(1000);
                        } 
                        catch (InterruptedException e) 
                        {
                            e.printStackTrace();
                        }
                        //启动一个线程进行一个复杂计算
                        //当计算完成时,我们不能这样写:
                        //textView.setText("result");
                        //而是要通过主线程的Handler通知主线程
                        Message msg=new Message();
                        //应当对要传递的Message进行定制,
                        //将计算结果附在Message对象中
                        mHandler.sendMessage(msg);
                    }
                }.start();
            }
        });
    }
}

这样,当计算完成时,子线程就会把结果通过Handler发送给主线程,然后退出,由主线程完成修改UI的任务。

在主线程中向子线程发送数据

由上面的例子,我们就可以想到,应该在子线程中声明一个Handler对象,并且实现它的void handleMessage(Message msg)方法。然后在主线程中调用子线程的Handler的void sendMessage(Message msg)方法向子线程发送数据。
这里就应该把Handler作为一个宏观的消息传递机制来看而不仅仅是一个类了。因为它和Looper,MessageQueue,Message是作为一个整体完成消息传递任务的。关于这部分内容,让我们先做一个实例后,在进行解释。
首先我们需要思考,在哪种情况下需要主线程向子线程发送消息。
在通常情况下,我们启动一个线程的时候,都是因为有一个需要马上进行处理的数据而开启一个线程,也就是说这个线程在开启之后马上就要开始处理数据。在线程启动的时候,可以直接将数据传递给线程,显然这里就用不到Handler了。
但是,有这样一种情况,我们想在界面初始化的时候,就有一个子线程随着主线程一起启动,一直等待我们传递数据给它,然后让它进行处理。显然这里就需要向子线程发送消息,就需要Handler来实现了。

public class CounterActivity extends AppCompatActivity {
    public Button pressBtn;
    public TextView textView;
    //在这里定义一个属于主界面的线程类,这个线程类必须包含一个Handler成员
    //而且这个Handler不能是私有的
    //而且重写了handleMessage方法
    //关于类的嵌套可以查看其它资料,这里就不解释了
    public class mThread extends Thread
    {
        public Handler cHandler;
        @Override
        public void run()
        {
            //初始化Looper
            Looper.prepare();
            cHanler=new Handler()
            {
                @Override
                public void handleMessage(msg)
                {
                    //对msg进行处理,获得主线程传来的数据
                    //对数据进行处理
                }
            };
            Looper.loop();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        //实例化一个上面定义的线程类,并启动它
        final mThread mthread=new mThread();
        mthread.start();
        pressBtn.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v) 
            {
                //当按钮被单击的时候,需要向已经启动的线程mthread
                //传递数据,这里就要使用Handler
                //将数据封装到Message对象中
                Message msg=new Message();
                //调用子线程的Handler,将msg发送给子线程
                mthread.cHandler.sendMessage(msg);
            }
        });
    }
}

这样,主线程就可以向一个已经启动的子线程发送消息了。
但是上面的这段代码也许会让我们有以下疑惑:
1. Looper.prepare()和Looper.loop()是什么作用?
2. 第一个例子在主线程中使用Handler为什么没有出现Looper?
3. 显然如果线程被启动,也就是mthread.start();这行代码执行后,run()函数并没有陷入循环,那么子线程马上就会执行完退出,怎么还可以向子线程发送消息?
其实所有的原因都在Looper这里。

Handler、Looper、MessageQueue、Message

在Handler这个消息传递机制中,Handler、Looper、MessageQueue、Message是作为一个整体同时出现的,也就是说,缺少这四个要素中的任意一个,这个消息传递机制都无法实现。

  • MessageQueue:顾名思义,是一个消息队列,由Looper负责管理,它采用先进先出的方式来管理Message;
  • Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断从MessageQueue中取出消息,并将消息分给对应的Handler处理;
  • Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

简单的说,每个线程(不论主线程还是子线程)如果要使用Handler,必须要有Looper和MessageQueue。当调用某个线程的Handler的void sendMessage(Message msg)方法时,msg就会被存入这个线程的MessageQueue,而Looper会不断地读取MessageQueue,将msg发给这个线程的Handler,这个Handler的void handleMessage(msg)就会处理这个msg。

我们来回答上面提出的三个问题:
1. Looper.prepare()和Looper.loop()是什么作用?
答: Looper.prepare()为当前线程定义一个Looper实例
Looper.loop()方法是一个死循环,不断读取MessageQueue里面的消息
2. 第一个例子在主线程中使用Handler为什么没有出现Looper?
答: 在第一个例子中,主线程中并没有出现Looper,是因为父类已经定义了Looper,所以在这里就没有必要显示声明了。
3. 显然如果线程被启动,也就是mthread.start();这行代码执行后,run()函数并没有陷入循环,那么子线程马上就会执行完退出,怎么还可以向子线程发送消息?
答: 解决了第一个问题,这个问题也就显而易见了,Looper.loop()本身就是一个死循环。

在主线程和子线程直接互相通信

这个程序就是《疯狂安卓讲义》3.5.2这一节的实例——计算所有质数的改写版。
首先启动一个子线程,等待主线程将一个数(作为上限)通过Handler传递给它,然后进行计算,计算完成后再使用Handler将结果发送给主线程,主线程修改用户UI。
写累了,直接上代码:
主活动java代码:

package com.example.marine.calprimary3;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    public EditText inputText;
    public TextView resultText;
    public Button startBtn;
    public Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final CalThread calThread=new CalThread();
        calThread.start();
        inputText=(EditText)findViewById(R.id.input_text);
        startBtn=(Button)findViewById(R.id.start_btn);
        resultText=(TextView)findViewById(R.id.result_text);
        mHandler=new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                if(msg.what==0x124)
                {
                    String result=msg.getData().getIntegerArrayList("result").toString();
                    resultText.setText(result);
                }

            }
        };
        startBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg=new Message();
                Bundle bundle=new Bundle();
                bundle.putInt("upper",Integer.parseInt(inputText.getText().toString()));
                msg.setData(bundle);
                msg.what=0x123;
                calThread.cHandler.sendMessage(msg);
            }
        });

    }
    class CalThread extends Thread
    {
        public Handler cHandler;
        @Override
        public void run()
        {
            Looper.prepare();
            cHandler=new Handler()
            {
                @Override
                public void handleMessage(Message msg)
                {
                    if(msg.what==0x123) {
                        int upper = msg.getData().getInt("upper");
                        ArrayList<Integer> nums = new ArrayList<Integer>();
                        outer:
                        for (int i = 2; i <= upper; i++) {
                            for (int j = 2; j <= Math.sqrt(i); j++) {
                                if (i != 2 && i % j == 0) {
                                    continue outer;
                                }
                            }
                            nums.add(i);
                        }
                        Message msg_send = new Message();
                        msg_send.what = 0x124;
                        Bundle bundle_send = new Bundle();
                        bundle_send.putIntegerArrayList("result", nums);
                        msg_send.setData(bundle_send);
                        mHandler.sendMessage(msg_send);
                    }
                    Toast.makeText(MainActivity.this, "计算完成", Toast.LENGTH_LONG).show();
                }
            };
            Looper.loop();
        }
    }
}

xml布局文件代码:

<?xml version="1.0" encoding="utf-8"?>
<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="com.example.marine.calprimary3.MainActivity">

    <EditText
        android:id="@+id/input_text"
        android:layout_width="800px"
        android:layout_height="150px"
        android:text=""
        android:textSize="70px"/>
    <Button
        android:id="@+id/start_btn"
        android:layout_width="800px"
        android:layout_height="150px"
        android:layout_below="@id/input_text"
        android:textSize="70px"
        android:text="Start"/>
    <TextView
        android:id="@+id/result_text"
        android:layout_width="800px"
        android:layout_height="wrap_content"
        android:text="计算结果"
        android:textSize="50px"
        android:layout_below="@id/start_btn"/>
</RelativeLayout>

界面效果
这里写图片描述

结语

为了搞明白Handler的原理,在网上找了很多文章,但是写的都不是让我很满意和清楚,所以只能自己探索了。
再次说明,这系列文章的最终目的并不是开发app,只是为了快速了解安卓程序的运行和编写过程,所以如果有理解错误的地方,还请多多指正。

2018-06-05 10:36:33 xuefu_78 阅读数 91

概述

假设一个情景,在一个应用程序中,要完成一个比较复杂、耗时较长的计算任务,如果将这个任务直接在主线程中开始,那么用户界面就会停止对用户操作的响应,而去解决这个计算任务,直到任务完成。 
显然,这不是我们想要的结果。那么就需要用到多线程来解决这个问题。 
但是新的问题又出现了,在C#和Android中,子线程是不能直接修改用户界面的数据的。也就是说,子线程计算出的结果,不能直接在子线程中让它显示在用户界面上。那么就需要把数据首先传递给主线程,让主线程去修改用户界面。 
Handler就是为了解决这个问题出现的。

什么是Handler

  • 从宏观上来说,Handler是一种消息传递机制
  • 从微观上来说,Handler是一个类

在子线程中向主线程发送数据

下面通过一段伪代码来实现子线程向主线程发送数据 
假设我们新建了一个项目,这个项目有下面这个Activity:

public class CounterActivity extends AppCompatActivity {
    public Button pressBtn;
    public TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        {
            @Override
            public void onClick(View v) 
            {
            //启动一个线程进行一个复杂计算
            //当计算完成时,我们不能在线程中这样写:
            //textView.setText("result");
            }
        });
    }
}

很简单,这个Activity包含两个空间,一个TextView用来显示计算结果,一个Button用来开启一个线程进行一个复杂计算。 
那么当计算完成时,我们不能在线程中直接修改TextView的内容,而是要把结果通过Handler传递给主线程。注意消息的传递方向:子线程→主线程。 
现在就出现了一个问题,在哪里定义Handler,或者说Handler属于谁?主线程还是子线程?答案是主线程。要给谁发消息,就要用谁的Handler。 
什么叫“谁的”?要在java中使用线程,一般都是继承Thread类并重写其中的run()方法,不管主线程还是子线程,都是以类的实例的方式存在的,也就是说,要用Handler向主线程发送消息,这个Handler必须是主线程的类成员,不能是子线程的;同理,要用Handler向子线程发送消息,那么这个Handler必须在子线程中声明,而不能在主线程中声明。 
在主线程中声明了Handler之后,就要重写这个Handler的 
void hanleMessage(Message msg)方法。 
至于如何构建Message对象,如何对Message对象进行筛选和数据提取,这里就不做说明了,很简单。 
进一步完善上面的伪代码如下:

public class MainActivity extends AppCompatActivity {
    //由上面的讲解,显然我们要在这个MainActivity中定义一个Handler
    public Handler mHandler;
    public Button pressBtn;
    public TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        //在这里重写mHandler的handleMessage方法
        mHandler=new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                //对msg进行处理,获得其中的数据也就是计算结果
                //修改用户界面
                textView.setText("result");
            }
        };
        pressBtn.setOnClickListener(new View.OnClickListener() 
        {
            @Override
            public void onClick(View v) 
            {

                new Thread()
                {
                    @Override
                    public void run()
                    {
                    //在这里进行一段复杂计算
                    //可以用Thread.sleep(1000);代替
                        try 
                        {
                            Thread.sleep(1000);
                        } 
                        catch (InterruptedException e) 
                        {
                            e.printStackTrace();
                        }
                        //启动一个线程进行一个复杂计算
                        //当计算完成时,我们不能这样写:
                        //textView.setText("result");
                        //而是要通过主线程的Handler通知主线程
                        Message msg=new Message();
                        //应当对要传递的Message进行定制,
                        //将计算结果附在Message对象中
                        mHandler.sendMessage(msg);
                    }
                }.start();
            }
        });
    }
}

这样,当计算完成时,子线程就会把结果通过Handler发送给主线程,然后退出,由主线程完成修改UI的任务。

在主线程中向子线程发送数据

由上面的例子,我们就可以想到,应该在子线程中声明一个Handler对象,并且实现它的void handleMessage(Message msg)方法。然后在主线程中调用子线程的Handler的void sendMessage(Message msg)方法向子线程发送数据。 
这里就应该把Handler作为一个宏观的消息传递机制来看而不仅仅是一个类了。因为它和Looper,MessageQueue,Message是作为一个整体完成消息传递任务的。关于这部分内容,让我们先做一个实例后,在进行解释。 
首先我们需要思考,在哪种情况下需要主线程向子线程发送消息。 
在通常情况下,我们启动一个线程的时候,都是因为有一个需要马上进行处理的数据而开启一个线程,也就是说这个线程在开启之后马上就要开始处理数据。在线程启动的时候,可以直接将数据传递给线程,显然这里就用不到Handler了。 
但是,有这样一种情况,我们想在界面初始化的时候,就有一个子线程随着主线程一起启动,一直等待我们传递数据给它,然后让它进行处理。显然这里就需要向子线程发送消息,就需要Handler来实现了。

public class CounterActivity extends AppCompatActivity {
    public Button pressBtn;
    public TextView textView;
    //在这里定义一个属于主界面的线程类,这个线程类必须包含一个Handler成员
    //而且这个Handler不能是私有的
    //而且重写了handleMessage方法
    //关于类的嵌套可以查看其它资料,这里就不解释了
    public class mThread extends Thread
    {
        public Handler cHandler;
        @Override
        public void run()
        {
            //初始化Looper
            Looper.prepare();
            cHanler=new Handler()
            {
                @Override
                public void handleMessage(msg)
                {
                    //对msg进行处理,获得主线程传来的数据
                    //对数据进行处理
                }
            };
            Looper.loop();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_counter);
        //实例化一个上面定义的线程类,并启动它
        final mThread mthread=new mThread();
        mthread.start();
        pressBtn.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v) 
            {
                //当按钮被单击的时候,需要向已经启动的线程mthread
                //传递数据,这里就要使用Handler
                //将数据封装到Message对象中
                Message msg=new Message();
                //调用子线程的Handler,将msg发送给子线程
                mthread.cHandler.sendMessage(msg);
            }
        });
    }
}

这样,主线程就可以向一个已经启动的子线程发送消息了。 
但是上面的这段代码也许会让我们有以下疑惑: 
1. Looper.prepare()和Looper.loop()是什么作用? 
2. 第一个例子在主线程中使用Handler为什么没有出现Looper? 
3. 显然如果线程被启动,也就是mthread.start();这行代码执行后,run()函数并没有陷入循环,那么子线程马上就会执行完退出,怎么还可以向子线程发送消息? 
其实所有的原因都在Looper这里。

Handler、Looper、MessageQueue、Message

在Handler这个消息传递机制中,Handler、Looper、MessageQueue、Message是作为一个整体同时出现的,也就是说,缺少这四个要素中的任意一个,这个消息传递机制都无法实现。

  • MessageQueue:顾名思义,是一个消息队列,由Looper负责管理,它采用先进先出的方式来管理Message;
  • Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断从MessageQueue中取出消息,并将消息分给对应的Handler处理;
  • Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

简单的说,每个线程(不论主线程还是子线程)如果要使用Handler,必须要有Looper和MessageQueue。当调用某个线程的Handler的void sendMessage(Message msg)方法时,msg就会被存入这个线程的MessageQueue,而Looper会不断地读取MessageQueue,将msg发给这个线程的Handler,这个Handler的void handleMessage(msg)就会处理这个msg。

我们来回答上面提出的三个问题: 
1. Looper.prepare()和Looper.loop()是什么作用? 
答: Looper.prepare()为当前线程定义一个Looper实例 
Looper.loop()方法是一个死循环,不断读取MessageQueue里面的消息 
2. 第一个例子在主线程中使用Handler为什么没有出现Looper? 
答: 在第一个例子中,主线程中并没有出现Looper,是因为父类已经定义了Looper,所以在这里就没有必要显示声明了。 
3. 显然如果线程被启动,也就是mthread.start();这行代码执行后,run()函数并没有陷入循环,那么子线程马上就会执行完退出,怎么还可以向子线程发送消息? 
答: 解决了第一个问题,这个问题也就显而易见了,Looper.loop()本身就是一个死循环。

在主线程和子线程直接互相通信

这个程序就是《疯狂安卓讲义》3.5.2这一节的实例——计算所有质数的改写版。 
首先启动一个子线程,等待主线程将一个数(作为上限)通过Handler传递给它,然后进行计算,计算完成后再使用Handler将结果发送给主线程,主线程修改用户UI。 
写累了,直接上代码: 
主活动java代码:

package com.example.marine.calprimary3;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    public EditText inputText;
    public TextView resultText;
    public Button startBtn;
    public Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final CalThread calThread=new CalThread();
        calThread.start();
        inputText=(EditText)findViewById(R.id.input_text);
        startBtn=(Button)findViewById(R.id.start_btn);
        resultText=(TextView)findViewById(R.id.result_text);
        mHandler=new Handler()
        {
            @Override
            public void handleMessage(Message msg)
            {
                if(msg.what==0x124)
                {
                    String result=msg.getData().getIntegerArrayList("result").toString();
                    resultText.setText(result);
                }

            }
        };
        startBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg=new Message();
                Bundle bundle=new Bundle();
                bundle.putInt("upper",Integer.parseInt(inputText.getText().toString()));
                msg.setData(bundle);
                msg.what=0x123;
                calThread.cHandler.sendMessage(msg);
            }
        });

    }
    class CalThread extends Thread
    {
        public Handler cHandler;
        @Override
        public void run()
        {
            Looper.prepare();
            cHandler=new Handler()
            {
                @Override
                public void handleMessage(Message msg)
                {
                    if(msg.what==0x123) {
                        int upper = msg.getData().getInt("upper");
                        ArrayList<Integer> nums = new ArrayList<Integer>();
                        outer:
                        for (int i = 2; i <= upper; i++) {
                            for (int j = 2; j <= Math.sqrt(i); j++) {
                                if (i != 2 && i % j == 0) {
                                    continue outer;
                                }
                            }
                            nums.add(i);
                        }
                        Message msg_send = new Message();
                        msg_send.what = 0x124;
                        Bundle bundle_send = new Bundle();
                        bundle_send.putIntegerArrayList("result", nums);
                        msg_send.setData(bundle_send);
                        mHandler.sendMessage(msg_send);
                    }
                    Toast.makeText(MainActivity.this, "计算完成", Toast.LENGTH_LONG).show();
                }
            };
            Looper.loop();
        }
    }
}

xml布局文件代码:

<?xml version="1.0" encoding="utf-8"?>
<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="com.example.marine.calprimary3.MainActivity">

    <EditText
        android:id="@+id/input_text"
        android:layout_width="800px"
        android:layout_height="150px"
        android:text=""
        android:textSize="70px"/>
    <Button
        android:id="@+id/start_btn"
        android:layout_width="800px"
        android:layout_height="150px"
        android:layout_below="@id/input_text"
        android:textSize="70px"
        android:text="Start"/>
    <TextView
        android:id="@+id/result_text"
        android:layout_width="800px"
        android:layout_height="wrap_content"
        android:text="计算结果"
        android:textSize="50px"
        android:layout_below="@id/start_btn"/>
</RelativeLayout>

界面效果 
这里写图片描述

结语

为了搞明白Handler的原理,在网上找了很多文章,但是写的都不是让我很满意和清楚,所以只能自己探索了。 
再次说明,这系列文章的最终目的并不是开发app,只是为了快速了解安卓程序的运行和编写过程,所以如果有理解错误的地方,还请多多指正。

2015-11-02 19:08:56 u014022205 阅读数 2117

1. Messenger

Messenger可以理解为信使,顾名思义我们可以利用信使来传递信息(Message),没错利用Messenger来实现跨进程之间的通信就是传递Message对象,我们只需要做的是把数据放进去Message,然后使用Messenger的send方法将数据发送出去,像发送邮件一样。

2. 实现一个Messenger的主要步骤:
既然说到是跨进程之间的通信,那么就是会有多个以上的进程之间进行通信,假设我们利用服务端与客户端两个不同进程的类来模拟进程之间的通信。那么会有以下的步骤:

第一:服务端进程方面
首先,在服务端,我们建立一个Android四大组件之一的Service来处理客户端的请求,在Service里面创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
PS:如果对于Service的使用还不太熟悉的小伙伴,可以去温习一下。

第二:客户端进程方面
客户端进程中,我们需要绑定服务端的Service,绑定成功后客户端的ServiceConnection类中会返回绑定成功后服务端返回的IBinder对象,利用这个IBinder对象创建一个Messenger对象。
如果我们想要服务端给客户端一个响应呢,那么很简单,我们同样的在客户端创建一个Handler来处理服务端的返回,同时服务端需要将响应发送给客户端那么这就需要获得客户端发送过来的Messenger,这个Messenger从哪里来呢?哈哈,我们只需要在客户端创建一个以刚刚创建的客户端Handler来创建一个Messenger,然后再客户端发送数据给服务端的时候利用Message的replyTo将创建的Messenger一起发送给服务端,这样服务端就可以拿到这个用来发送数据给客户端的信使。然后发送数据给客户端。
这个模式简单的理解可以这样,服务端与客户端我们打比方是两个远隔千里的一对情侣,男的代表服务端,因为男方基本上会给女方提供各种抬抬扛扛的服务,女代表客户端。假如这个时候女方想要给男朋友发送数据,那么怎么办呢?当然是请信使过来,那么信使怎么来呢?对于Messenger模式实现两个进程的通信的信使是这个模式的:如果女方想要发送信息给男方,男方需要找到一个他身边的信使亦即new一个Messenger对象,这个Messenger是有要求的,它是由男方的创建的用来处理女方的信件的Handler来构造的,然后将信使通过绑定返回给客户端信使的IBinder:

//男方需要创建一个用来处理(女方)客户端发过来的信息的Handler
private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MyConstants.MSG_FROM_CLIENT:
//这里只是简单的打印出来         

Log.d(MyConstants.TAG_MESSENGER_FOR_IPC, "receive from Client:" + msg.getData().getString("msg")); 
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

//用创建出来的Handler来创建信使
 private final Messenger mMessenger = new Messenger(new MessengerHandler());

//通过onBind方法返回给客户端
 @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

这时候女方就可以在绑定服务的时候在Connection连接中见到男方派过来的信使的IBinder,通过这个信使的IBinder来创建发送的信使,然后发送数据过去:

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg", "hello ,this is client");
            msg.setData(data);
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

//绑定服务
Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

这个时候服务端会受到客户端发送过来信息,打印出来:
这里写图片描述

基本上这个时候我们可以说实现了进程之间的通信,当然我们可能并不满足于此,我们可能需要服务端需要给客户端一个回应,亦即男方能够在受到女方的信息回复女方说我收到了信息了。这个如何去做呢???
模仿服务端的样子,我们只需要在女方(客户端)创建一个用来处理男朋友回馈的信息的Handler并且利用这个Handler来创建一个信使,发送给男朋友,让男朋友将信息传给这个女方创建的闺蜜(信使)带信息过来。好了问题是怎么在女方将自己的闺蜜亦即信使发送给男朋友呢??哈哈很简单在女方刚刚发送信息过去的时候我们就可以顺便将信使捎带过去,怎么捎带呢?将信使存在Message的replyTo中就可以将闺蜜发送过去了,男朋友见到女方发送过去的闺蜜后就可以将信息给这个闺蜜然后由闺蜜带回来给女方。问题是男朋友怎么见到这个由女方发过来的信使呢?我们只需要在男方刚刚处理信件的Handler大叔那里告诉他,将信息里面的msg.replyTo也拿出来,那么拿出来的就是我们的客户端派过来的信使,然后就可以利用这个信使回复女方了。
废话这么多代码贴上:

//在客户端创建一个Handler大叔来处理服务端发过来的信息
 private class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MyConstants.MSG_FROM_SERVICE:
   //将服务端的信息打印出来                 

Log.d(MyConstants.TAG_MESSENGER_FOR_IPC, "receive from Service:" + msg.getData().get("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    //通过这个处理信件的Handler大叔来创建信使
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

//在发送信息的时候将这个信使也捎带过去
 msg.replyTo = mGetReplyMessenger;


//服务端在处理信件的Handler大叔中处理一下客户端传过来的信使
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply", "嗯嗯,我已经收到你的信息,稍后回复你。");
                    replyMessage.setData(bundle);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }

最后注意在Manifest声明,让客户端与服务端不在同一个进程中:

<service
            android:name=".messenger.MessengerService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote" >
</service>

<activity 
            android:name=".messenger.MessengerActivity">
 </activity>

这里写图片描述

主要的类的代码:
http://download.csdn.net/detail/u014022205/9234773

2017-01-15 14:13:57 shaoenxiao 阅读数 15711

今天这篇文章只讲一下怎么使用Handler实现子线程与子线程之间、子线程与主线程之间如何进行通信,关于具体的内部实现因为我也没研究过,所以这篇文章讲不了。

一、子线程向主线程传值:

这个实现比较简单,因为主线程自带Looper机制,所有我们不用创建Looper了,看一下代码吧:

首选在主线程里创建一个Handler

1.Handler mHandler = new Handler(){  
2.  
3.    @Override  
4.    public void handleMessage(Message msg) {  
5.        super.handleMessage(msg);  
6.        switch (msg.what) {  
7.        case 0:  
8.            //do something,refresh UI;  
9.            break;  
10.        default:  
11.            break;  
12.        }  
13.    }  
14.      
15.};  

然后开启一个子线程,在子线程里直接使用Handler发送消息即可

new Thread() {
            public void run() {
                Message message = new Message();
                message.obj = "子线程发送的消息Hi~Hi";
                mHandler .sendMessage(message);
            };
        }.start();

二、主线程向子线程里发送消息:

主线程向子线程发送消息的话,我们需要在子线程里初始化Looper,并在主线程里创建的Handler引用子线程的Looper(Handler中引用的是哪个线程的Looper,就在哪个线程里处理消息),下面看代码:

 public class ThreadHandlerActivity extends Activity{     
     //创建子线程
     class MyThread extends Thread{
         private Looper looper;//取出该子线程的Looper
         public void run() {

             Looper.prepare();//创建该子线程的Looper
             looper = Looper.myLooper();//取出该子线程的Looper
             Looper.loop();//只要调用了该方法才能不断循环取出消息
         }
     }

     private Handler mHandler;//将mHandler指定轮询的Looper

     protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.main);
             thread = new MyThread();
             thread.start();//千万别忘记开启这个线程
             //下面是主线程发送消息
          mHandler = new Handler(thread.looper){
              public void handleMessage(android.os.Message msg) {
           Log.d("当前子线程是----->",Thread.currentThread()+"");
                 };
             };
             mHandler.sendEmptyMessage(1);
     }

 }

其实这样就可以达到主线程向子线程发送消息了,然而当我们运行后发现程序会Crash掉,报了一个控制针,这是因为在Handler初始化的时候,thread.looper还没有初始化,所以会报控制针,这时我们可以让主线程等待一下子线程,也可以来一个while循环来判断thread.looper是否初始化完成。不过Android本身还提供了一个方法,那就是HandlerThread

     protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             tv = new TextView(this);
             tv.setText("Handler实验");
             setContentView(tv);

             //实例化一个特殊的线程HandlerThread,必须给其指定一个名字
            HandlerThread thread = new HandlerThread("handler thread");
             thread.start();//千万不要忘记开启这个线程
             //将mHandler与thread相关联
             mHandler = new Handler(thread.getLooper()){
                 public void handleMessage(android.os.Message msg) {
                     Log.d("当前子线程是----->", Thread.currentThread()+"");
                 };
             };
             mHandler.sendEmptyMessage(1);//发送消息
     }

这时HandlerMessage所在的线程就是HandlerThread 的子线程。
然而HandlerThread 所创建处理的子线程里是不能重写Run()方法的,你写了以后,会发现,HandlerMessage不执行了,这时因为HandlerMessage本身实现了Run()方法,我们看一下内部实现:

@Override
     public void run() {
         mTid = Process.myTid();
         Looper.prepare();
         synchronized (this) {
             mLooper = Looper.myLooper();
             notifyAll();
         }
         Process.setThreadPriority(mPriority);
         onLooperPrepared();
         Looper.loop();
         mTid = -1;
     }

在源代码的第4行,进行了实例化自己的Looper,如果继续追踪源代码翻看其getLooper方法你会发现,如果一个Handler在与HandlerThread进行绑定时,发现Looper为空,Handler则会一直等待直到Looper被创建出来为止,然后才继续执行后续的代码。所以我们重写了HandlerThread的run方法,肯定就不会去创建Looper对象,那么绑定的Handler就会永远处于等待状态,自然而然就不会执行到HandlerMessage信息了。这也是为什么我们要使用HandlerThread这个特殊的线程,因为使用这个,我们不必关心多线程会混乱,Looper会为空等一系列问题,只要去关心我们要实现的逻辑就行了。

三、子线程和子线程之间通信:

其实子线程向子线程之间通信,其实就是在一个子线程中创建一个Handler,它的回调自然就在此子线程中,然后在另一个子线程中调用此handler来发送消息就可以了,不过记得写上Looper哦,下面看代码:

new Thread(new Runnable() {

                      @Override
                      public void run() {
                          String msg;
                          Looper.prepare();

                          childHandler = new Handler() {
                              @Override
                    public void handleMessage(Message msg) {
                                 super.handleMessage(msg);

                                 System.out.println("这个消息是从-->>" + msg.obj+ "过来的,在" + "btn的子线程当中" + "中执行的");

                             }

                         };  
                      Looper.loop();//开始轮循

                     }
                 }).start();

其中 Looper.prepare()和Looper.loop()一定不要忘了写。

然后我们创建第二个子线程

new Thread(new Runnable() {

                      @Override
                      public void run() {
                         Looper loop = Looper.myLooper();
               Message msg = childHandler.obtainMessage();
                         msg.obj = "btn2当中子线程";
                         childHandler.sendMessage(msg);
                     }
                 }).start();
没有更多推荐了,返回首页