当前位置: 代码迷 >> Android >> Android通讯篇
  详细解决方案

Android通讯篇

热度:56   发布时间:2016-04-28 00:20:55.0
Android通信篇

在Anroid中,通信技术包括多个层面,在UI层设计多种事件(如触控事件、案件事件、轨迹球事件等);在框架层设计Intent、Meaage等;在内核层则涉及Bundle、RPC、共享内存等技术。本章将重点介绍UI层和框架层的通信机制。

1.Intent通信

        Intent通信实际上是对Open-Binder通信机制的封装。在Linux中,存在D-Bus和open-Binder两种进程间通信机制,其中D-Bus应用得更广泛。但在Android中,采用的是open-Binder通信机制,D-Bus只在开源BT协议栈BlueZ中得到应用。

        每个Intent消息可以由component、action、data、category,extras、flags等几个属性构成。

        按是否有返回值Intent通信可以分为有返回值和无返回值通信,按接收对象可以分为群发消息和个体消息。

        但在通常情况下,Intent仅能传递基本的数据类型,对于复杂的数据类型,则要借助Serializable、Parcelable两个接口进行。

        (1)Intent常用用法

                下面针对网络、地图、电话、消息、电子邮件、多媒体、系统等几个方面介绍Intent的常见用法。

                1)网络相关

                         为了使用网络功能,需要拥有android.permission.INTERNET权限。与网络相关的常用Intent通信包括显示网页、Google地图、搜索等。

                         在由HTTP定义的与服务器交互的方法中,发起请求的方式有两种,即GET和POST,其中GET方式通过RUL提交数据,数据在URL中可以看到,无法保证私密性,且提交的数据最多只能为1024字节;而POST方式是将数据放置在HTML HEADER中提交,且在数据长度上没有限制,通常用于传递敏感数据。

                         在Intent中目前尚不支持POST方式提交数据,仅支持GET方式。为了打开网址,其采用的ACTION为ACTION_VIEW,方法如下:

                                 Uri uri=Uri.parse("http://www.163.com:);

                                 Intent it=new Inent(Intent.ACTION_VIEW,uri);

                                 startActivity(it);

                         如果希望传递敏感数据,在WebView中采用的方法如下:

                                 public void postUrl( String url, byte[] postData);

                         在众多的应用中找到自己想要的应用。就需要用到搜索功能。实现搜索的方法如下:

                                 Uri uri=Uri.parse("market://seatch?q=pname:com.miaozl.hello");

                                 Intent intent=new Intent(Intent.ACTION_VIEW, uri);

                                 startActivity(intent);

                         在网络中搜索相关信息,实现的方法如下:

                                 Intent intent=new Intent();

                                 intent.setAction(Intent.ACTION_WEB_SEARCH);

                                 intent.putExtra(SearchManager.QUERY, "android123")

                                 startActivity(intent);

                2)地图相关

                         为了显示Google地图,需要提供地理位置的经纬度信息,注意,在进行定位时,由于定位算法的原因,移动终端至少要能捕获3颗卫星的数据,才能提供基本的定位,根据GPS芯片的性能和场景的不同,实际GPS芯片能捕获到的卫星数量差异较大,下面是显示Google地图的方法:

                                 Uri uri=Uri.parse("geo:38.899533, -77.036476");

                                 Intent it=new Intent(Intent.ACTION_VIEW,uri);

                                 startActivity(it);

                        采用GPS导航的目的通常在于希望能找到两点间的最佳路线,这种查找最佳路线的过程在数学上称为路径规划,下面是GoogleMap路径规划的方法:

                                 Uri uri=Uri.parse(http://maps.google.com/maps?f=d&saddr=srartLat%20startLng&daddr=engLat%20endLng&hl=en);

                                 Intent it=new Intent(Intent.ACTION_VIEW,uri);

                                 startActivity(it);

                3)电话相关

                        为了查询联系人信息,需要拥有android.permission.READ_CONTACTS权限,如果希望修改联系人信息,则需要拥有android.permission.WEITE_CONTACTS权限。为了执行呼叫,需要拥有android.permission.CALL_PHONE权限。

                        作为移动终端最基本的功能,通话自然是必不可少的,与通话相关的还有电话薄等功能,下面是进入电话薄的方法:

                                Intent intent=new Intent();

                                intent.setAction(Intent.ACTION_VIEW);

                                intent.setData(People.CONTENT_URI);

                                startActivity(intent);

                        如果希望查看具体的联系人,需要制定相关联系人的ID,方法如下:

                                Uri personUri=ContextUris.withAppendedId(People.CONTENT_URI,ID);

                                Intent intent=new Intent();

                                intent.setAction(Intent.ACTION_VIRW);

                                intent.setData(persionUri);

                                startActivity(intent);

                        完成联系人查询后,即可进行拨号了,拨号的方法如下:

                                Uri uri=Uri.parse("tel:xxxxxx");

                                Intent it=new Intent(Intent.ACTION_DIAL, uri);

                                startActivity(it);

                        在Android中,拨号和呼叫时两个过程。下面是呼叫的方法:

                                Uri uri=Uri.parse("tel:xxxxxx");

                                Intent it=new Intent(Intent.ACTION_CALL, uri);

                                startActivity(it);

                4)消息相关

                        除了通话外,传统移动终端的另一个基本功能就是短消息及彩信。短消息的成功是一个奇迹,其技术并不先进,但由于其深刻把握住了用户需求,极受用户的亲睐,进而成就了其移动终端第一应用的地位。

                        为了查看短信,需要具有android.permission.READ_SMS权限,其方法如下:

                                 Intent it =new Intent(Intent.ACTION_VIEW);

                                 it.setType("vnd.android-dir/mms-sms");

                                 startActivity(it);

                         为了发送短信,需要具有android.permission.WEITE_SMS权限,其方法如下:

                                 Uri uri=Uri.parse(“smsto:10086");

                                 Intent it=new Intent(Intent.ACTION_SENDTO, uri);

                                 it.putExtra("sms_body","hello");

                                 startActivity(it);

                        彩信的发送则稍微复杂些,除了基本的文本信息外,还涉及附件。下面是一个发送附件图片的示例:

                                Uri uri=Uri.parse("context://media/exernal/images/media/10");

                                Intent it=new Intent(Intent.ACTION_SEND);

                                it.putExtra("sms_body","hello");

                                it.putExtra(Intent.EXTRA_STREAM, uri);

                                it.setType("image/png");

                                startActivity(it);

                5)电子邮件相关

                        自有互联网以来,电子邮件功能就是互联网的一个基本应用,电子邮件的出现要早于万维网。邮件协议是一个比较复杂的协议,其主要的协议包括简单邮件传输协议(Simple Mail Transfer Protocal, SMTP)、邮局协议(Post Office Protocal, POP3)、互联网信息接入协议(Internet Message Access Protocol,IMAP)等。

                        邮件的发送需要拥有android.permission.INTENRNET权限。

                        发送邮件的最简单方法如下:

                                 Uri uri=Uri.parse("maito:[email protected]");

                                 Intent it=new Intent(Intent.ACTION_DENGTO, uri);

                                 startActivity(it);

                        邮件具有多种不同形式,下面是各种不同形式的邮件的发送方法。

                        直接发送文字信息的邮件的方法如下:

                                Intent it=new Intent(Intent.ACTION_SEND);

                                it.putExtra(Intent.EXTRA_EMAIL, [email protected]);

                                it.putExtra(Intent.EXTRA_TEXT,"NEIRONG");

                                it.setType("text/plain");

                                startActivity(Intent.createChooser(it,"选择一个EMAIL客户端”));

                        在发送给接收人的同时抄送给其它人的方法如下:

                                Intent it=new Intent(Intent.ACTION_SENG);

                                String[] [email protected]);

                                String[] [email protected]}

                                it.putExtra(Intent.EXTRA_EMAIL, tos);

                                it.putExtra(Intent.EXTRA_CC, ccs);

                                it.putExtra(Intent.EXTRA_TEXT, "正文“);

                                it.putExtra(Intent.EXTRA_SUBJECT,”标题“);

                                it.setType("message/rfc822");

                                startActivity(Intent.createChooser(it, "选择一个Email客户端"));

                        发送带附件的邮件的方法如下:

                                Intent it=new Intent(Intent.ACTION_SEND);

                                it.putExtra(Intent.EXTRA_SUBJECT,"正文");

                                it.putExtra(Intent.EXTRA_STREAM, file:///sdcard/miaozl.mp3);

                                sendIntent.setType("audio/mp3");

                                startActivity(Intent.createChooser(it,”选择一个Emai客户端“));

                        从文件管理器等应用中选取特定图片进行发送的方法如下:

                                 Intent intent=new Intent(Intent.ACTION_GET_CONTENT);

                                 intent.addCategory(Intent.CATEGORY_OPENABLE);

                                 intent.setType("image/*");

                                 startActivityForResult(intent, 0);

                6)多媒体相关

                        自移动互联网流行以来,移动终端就担负着四大基本功能,即通信、社交、网络、多媒体。作为传统终端不可或缺的多媒体功能,在移动终端上也很重要的应用,本节仅介绍本地音乐的播放及设置拍照存放位置的实现方法。

                        直接播放本地音频文件的方法如下:

                                Intent it=new Intent(Intent.ACTION_VIEW);

                                Uri uri=Uri.parse(file:///sdcard/nobody.mp3);

                                it.setDataAndType(uri, "audio/mp3");

                               startActivity(it);

                        播放系统多媒体数据库中的文件的方法如下:

                               Uri uri=Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");    //从系统内部的MediaPrivider索引中调用播放

                               Intent it=new Intent(Intent.ACTION_VIEW, uri);

                               startActivity(it);

                        设置拍照后存放位置的方法如下:

                                Intent intent=new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                                intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.geExtrnalStorageDirectory().getAbsolutePath()+"/hello”, miaozl+".jpg")));

                                startActivityForResult(intent, 0);

                7)系统相关

                        本节主要介绍卸载、安装应用以及创建、删除快捷方式的方法。其中卸载应用的方法如下:

                                Uri uri=Uri.fromParts("package", strPackageName, null);

                                Intent it=new Intent(Intent.ACTION_DELETE, uri);

                                startActivity(it);

                        安装应用的方法如下:

                                Uri installUri=Uri.fromParts("package", "xxx", null);

                                retuen it=new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);

                        为了使用户可以更方便地启动应用,避免在主菜单的众多应用中查找,可以为应用创建快捷方式,方法如下:

                                Intent shortcut=new Intent("com.android.laucher.action.INSTALL_SHORTCUT");

                                shortcut.putExtra(Inent.EXTRA_SHORTCUT_NAME, "shortcutname");

                                shortcut.putExtra("duplicate", false);   //不允许重复创建

                                ShortcutIconResource iconRes=Intent.ShortcutIconResource.fromContext(this.R.drawable.background);

                               shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes);     //资源图片

                               String action="con.android.action.START";

                               Intent respondIntent=new Intent(this, this.getClass());

                               respondIntent.setAction(action);

                               shortcut.putExtra(Intent,EXTRA_SHORTCUT_INTENT, respondIntent);

                               sendBroadcast(shortcut);

                        删除快捷方式的方法如下:

                                Intent shortcut=new Intent("com.android.launcher.action.UNINSTALL_SHORTCUT");

                                shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "shortcutname");

                                String action="com.android.action.START");

                                String appClass=this.getPackageName()+"."+this.getLoaclClassName();

                                ComponentName comp=new ComponentName(this.getPackageName(), appClass);

                                shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTEXT,new Intent(action).setComponent(comp));

                                sendBroadcast(shortcut);

                        需要注意,创建和删除快捷方式需要如下权限

                                <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUS"/>

                                <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>

        (2)Serializable接口

                Serializable接口是一个标准的Java接口,要实现某个类支持序列化,可以使该类继承Serizlizable接口。Serializable接口通常在Intent中使用。

                Serializable接口本质上是基于ObjectOutputStream和ObjectInputStream对象进行的,具备Serializable序列化的类的实现非常简单,实例如下:

                        public final class CodeSigner implements Serializable{

                                private static final long serialVersionUID=6819288105193937581L;

                        }

                 serialVersionUID用于在运行时判断类版本的一致性,其可由JDK中的serialver工具生成,也可以采用默认方式来定义。如果是在Eclipse中进行开发,那么Eclipse会自动提示用户者两种方式进行选择。默认的serialVersionUID值如下:

                         private static final long serialVersionUID=1L;

                 在Intent中,可以通过Bundle来设置序列化数据,具体方法如下:

                         putSerializable(String key, Serializable value);

        (3)Parcelable接口

                Parcelable接口是在Android特定的序列化接口,它比Serializable接口更有效,还可以避免Serializable接口存在的一些问题。Parcelable接口通常用于Binder和AIDL场景中。
                Parcelable接口序列化的数据可以存储在Parcel中,继承Parcelable接口的类必须有一个CREATOR的静态变量。下面是一个Parcelable接口序列化的示例:

                        public class CatCmdMessage implements Parcelable{

                                public CatCmdMessage(Parcel in){

                                      mCmdDet=in.readParcelable(null);

                                      mTextMsg=in.readParcelable(null);

                                      mMenu=in.readParcelable(null);

                                      mInput=in.readParcelable(null);

                                }

                               public void writeToParcel(Parcel dest. int flags){

                                      dest.writeParcelable(mCmdDet, 0);

                                      dest.writeParcelable(mTextMsg, 0);

                                      dest.writeParcelable(mMenu, 0);

                                      dest.writeParcelable(mInput, 0);

                               }

                               public static final Parcelable.Creator<CatCmdMessage> CREATOR=new Parcelable.Creator<CatCmdMessage>(){

                                       public CatCmdMessage createFromParcel(Parcel in){

                                               return new CatCmdMessage(in);

                                       }

                                       public CatCmdMeaage[] new Array(int size){

                                               return new CatCmdMeaage[size];

                                      }

                               };

                      }

                通过Intent传递Parcelable接口序列化数据的方法如下:

                        public Intent putExtra(String name, Parcelable value)

                另外,Parcelable也可以通过Bundle在Intent中传递。在Bundle中设置Parcelable的方法如下:

                        public void putParcelable(String key, Parcelabel value)    //Parcelable方法

                        public Intent putExtra(String name, Bundle value)      //Intent方法

               当然Bundle还支持对Parcelable接口数组,Parcelabale接口列表和基本数据类型的传递。

2.UI事件处理

        UI事件主要包括事件监听器(Event Listteners)相关事件、事件句柄(Event Handlers)相关事件、焦点相关事件触控事件、按键事件、轨迹球事件等。UI事件均是基于View来实现的。在Anroid中,触控事件为主要UI事件。

        对于普通控件,当发生触控和单击事件时,UI主线程将先接收到相关事件,接着根据事件发生区域,将相关事件加入事件队列,发送给处理事件的控件。

        在Android中,根据执行的事件不同,相应的事件派发机制可以分为多种,输入事件中,比较重要的几个组件为EventHub、InputManager、InputDispatcher、InputReader,组件间的相互关系如下图:

               

        而在框架层,WindowManagerService是事件处理的神经中枢,它控制着事件如何进一步向UI层派发。

        (1)事件监听器、事件句柄及焦点处理

                 1)事件监听器

                         事件监听器本质上是基于接口的回调函数,包括onClick()、onLongClick()、onFocusChange()、onKey()、onTouch()、onCreateContextMenu()等。

                         在执行监听时,一旦收到监听事件,Android会将事件放置在ViewRoot的事件队列中等待派发。

                         以单击事件为例,事件监听器处理事件的过程如下:

                                 public boolean performClick(){

                                         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

                                         if(mOnClickListener != null){

                                                 playSoundEffect(SoundEffectConstants.CLICK);

                                                 mOnClickListtener.onClick(this);

                                                 return true;

                                         }

                                        return false;

                                 }

                2)事件句柄

                        当实现自定义的视图时,可能需要UI事件自定义的响应。

                        当事件处理完成后,如果不希望事件继续在处理对象链上传递,那么应设其返回值为true.

                 3)焦点处理

                         基于某种场景的需要,需要监听控件的变化,其方法如下:

                         public void setOnFocusChangeListtener(OnFocusChangeListener 1);

        (2)触控事件处理

                UI事件的处理通常是通过MotionEvent(如鼠标、触笔、手指、轨迹球等产生的事件)来进行的,其Action包括ACTION_UP、ACTION_MOVE、ACTION_CANCEL、ACTION_OUTSIDDE、ACTION_POINTER_DOWN、ACTION_POINTER_UP、ACTION_POINTER_INDEX_MASK、ACTION_POINTER_INDEX_SHIFT等。

                触控事件根据触控方式的不同,分为单点触控和多点触控两类。部分场景可能需要根据手势(即运行轨迹)进行处理。手势识别是通过GestureDerector实现的。 

                1)单点触控

                         单点触控是自电阻屏以来就有的触控方式,其实现很简单,示例如下:

                                 public boolean onTouchEvent(MotionEvent ev){

                                  }

                2)多点触控

                        Android的多点触控在本质上需要LCD驱动和程序本身设计上的支持。模拟器无法实现多点触控的测试。

                3)手势识别

                        GestureDetector支持多种手势,如按下、快速滚动、长按、常规滚动、ShowPress(用户按下时提示用户某些信息)、单击、双击等。

        (3)按键事件处理

                目前支持多个类型的按键:12-key、QWERTY、五向键、Home等。

                当按键事件发生后,Linux驱动会将捕捉到的输入事件传递给EventHub.cpp。EventHub是Linux驱动和Android框架层的接口。

        (4)轨迹球事件处理

                 轨迹球事件的处理是由Activity执行的。和其他事件一样,轨迹球事件的描述需要通过MotionEvent进行。

3.任务调度

        针对定时调度或者周期性执行的任务,Android引入了Java的java,util.Timer和java.util.timerTask。需要注意的是,TimerTask会阻塞主线程,对资源的消耗比另起一个线程要好。对于比较耗时的计算,不建议放在TimerTask中运行。

        下面是TimerTask实现任务调度的方法:

                 private Timer mTimer=new Timer(true);

                 private TimerTask mTimerTask;

                 mTimerTask=new TimerTask(){

                         public void run(){

                                 Log.d("TimerTask");

                         }

                 }

        如果希望该任务只执行单次,则执行如下调度:mTimer.schedule(mTimerTask, 5000)       //在5秒后执行调度

        如果希望该任务周期执行,则进行如下调度:mTimer.schedule(mTimerTask, 5000, 1000)     //在1秒后每5秒执行一次调度

        为了取消任务调度,需要执行以下调度:mTimerTask.cancel();

  相关解决方案