android 通信
2016-07-12 16:23
357 查看
什么是通信?
通信 ,顾名思义,指的就是信息的传递或者交换
看完本文能收获什么?
按目录索引,你可以学习到1. 组件间的通信,Activity,fragment,Service, Provider,Receiver
2. 进程间的通信,AIDL
3. 线程间的通信,Handler,AnsycTask,IntentService
4. 多个App间的通信
5. 使用大型开源框架完成组件通信,EventBus,otto
6. 网络通信基础篇:Google 课程–AnsycTask+HttpClient
7. 网络通信提高篇:开源框架Ansyc-Httpclient,okttp,Retrofit
建议阅读本文时遵循以下学习思路
1. 研究对象:Activity,fragment等组件2. 信息存在形式:Intent,Bundle,静态变量,全局变量,还是点击事件,触摸事件的回调监听,或者文件形式(Sharepreference,SQLite,File , NetStream) ,本质就是信息源
3. 信息传递的形式:网路,回调监听,线程,Intent,全局Application
4. 相同形式的思路,不会出现第二次,请读者举一反三
5. 最后强调研究对象是单一的
Activity通信
Activity 和 Activity
1. 常规方式:Intent Bundle通过Intent 启动另一个Activity时,有两种重载方式:
startActivity(new Intent(),new Bundle());
startActivityForResult(new Intent(),FLAG,new Bundle());
从参数列表就可以总结出来,有Intent,和Bundle,可以传递8种基本数据类型和可序列化的数据类型,比如字符串和字节数组。提到可序列化,就引发 Intent和Bundle 的局限性了:
Intent Bundle 无法传递“不可序列化”的数据,比如Bitmap,InputStream,解决办法有很多种,最简单的就是将“不可序列化”的对象,转换成字节数组,这里因为主要是讲解通信,所以不展开讲了。
Intent Bundle 能传递的数据大小在40K以内 。
PS : 很多人不理解为什么把Intent和Bundle放在一起谈,因为Intent 底层存储信息的原理也是通过Bundle存储!
2. 公有静态变量
比如 public static String flag=“中国”;
使用方式 比如 在其他Activity当中 FirstActivity.flag=“china”; 修改 静态变量的值
3. 基于物理形式:
比如 File,SQLite,Sharepreference 物理形式
4. 全局变量:
比如Application:Application是与Activity,Service齐名的组件,非常强大,它的特点是全局组件共用,单例形式存在,在其他组件中,我们只需要Context.getApplication()获得该对象的引用即可
Activity 和Fragment,Service,BrodcastReceiver
,首先都遵循,如何启动它们,就如何传递信息的原则:
1. Activity与Fragment
1. 通过构造函数传递 2.获取Fragment的实例对象
//CustFragment 是自定义的fragment,参数列表也可以自己定义咯, getSupportFrag 1af49 mentManager().beginTransaction() .add(new CustFragment(自定义的的参数列表),new String("参数")) //------------------method two----------------------- getSupportFragmentManager().findFragmentById(R.id.headlines_fragment); //------------------method three---------------------- getSupportFragmentManager().findFragmentByTag("HeadLines");
聪明的读者可能会问Fragment如何与Activity通信类似的问题,这是个好问题,请注意我们的研究的原则是单一目标原则,在这节我研究的是Activity,你的疑惑在后面都会一一解答
2. Activity与Service
Activity启动Service的两种方式:
//CustomService 是自定义Service,完成一些后台操作 startService(new Intent(FirstActivity.this,CustomService.class)); bindService(new Intent(FirstActivity.this,CustomService.class)), new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //当前启动的service 一些数据就会回调回这里,我们在Activity中操作这些数据即可 get } @Override public void onServiceDisconnected(ComponentName name) { } },flags);
从启动方式就可以看出,通过Bundle对象的形式存储,通过Intent传输,来完成Activity向Service传递数据的操作
3. Activity与BroadcastReceiver
启动广播的形式也有两种:
//method one !!!----------------------------------------------- registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { } },new IntentFilter(),"",new Handler()); //method two !!!----------------------------------------------- registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { } },new IntentFilter());
关于method one 的第三个参数Handler很多人会很费解
参照registerReceiver中源码关于该Handler参数的解释:
Handler identifying the thread that will receive the Intent. If null, the main thread of the process will be used.
定义了一个用于接收Intent的子线程,如果不填或者默认为null,那么就会在主线程中完成接收Intent的操作
很明显,Activity与BroadcastReceiver通信时,用的也是Intent传递,Bundle存储
4. 通讯时的同步问题
这里的同步通讯问题,为下文Fragment通讯作铺垫,不是这个问题不重要,不值得引起你注意,只是我想把问题放在它最应该出现的位置。
以上只是基础的传递数据的形式,大部分都是静态的,现在有一种需求,用户操作Activity,发出了某些指令,比如按下,滑动,触摸等操作,如何完成这些信息传递呢?这就要求同步了。
同步传递消息也很简单,就是调用系统写好的回调接口
首先我们要知道,用户 点击,触摸 这些行为 也属于 通信的范畴—点击和触摸属于 信息源;
比如用户行为进行点击,那就实现 :
new Button(mCotext).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new ImageView(mCotext).invalidate(); } });
通过此招提示指定的ImageView:嘿!老兄,你该刷新了
又或者 当用户 进行触摸操作,我们需要实现放大缩小平移指定的区域:
new RelativeLayout(mCotext).setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //缩放 v.setScaleX(1f); v.setScaleY(1f); //平移 v.setTranslationX(1f); v.setTranslationY(1f); v.setTranslationY(1f); //旋转 v.setRotation(2f); v.setRotationX(2f); v.setRotationY(2f); v.invalidate(); return true; } });
嘿,你看,当用户进行触摸操作,我们可以通过回调onTouchListenter来完成“触摸”这一操作
关于View重绘机制以及优化刷新UI的细节,不属于本文讨论范围。
Fragment
1. Fragment 与Activity通信通过实例对象传递
同样的,在Fragment中 getActivity()可以获取到它相关联的 Activity实例,就可以轻松获取并且修改Activity的数据
2. Fragment 与 多个Fragment通信
首先,两个Fragment之间不可能直接通信(非正规因素除外),Google官方提出的解决办法是 通过相关联的Activity来完成两个Fragment的通信
只需要记住三步:
1. 定义一个接口:
在让Fragment关联Activity之前,可以在Fragment中定义一个接口,然后让宿主Activity来实现这个接口。接着,在Fragment中捕获这个接口,并且在onAttach()中 捕获Activity实例
//只需关注接口是如何定义的,以及onAttack中的实现 public class HeadlinesFragment extends ListFragment { //定义的接口引用 OnHeadlineSelectedListener mCallback; // 自定义回调接口,宿主Activity必须要实现它 public interface OnHeadlineSelectedListener { public void onArticleSelected(int position); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // 在这里只是为了确保Activity实现了我们定义的接口,如果没有实现,则抛出异常 try { mCallback = (OnHeadlineSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener"); } } ... }
一旦Activity通过OnHeadlineSelectedListener 的实例mCallBack回调 onArticleSelected(),Fragment就可以传递信息 给Activity了
例如 下面是 ListFragment的一个回调方法,当用户点击了list 中的item,这个Fragment就会通过回调接口向宿主Activity传递事件
@Override public void onListItemClick(ListView l, View v, int position, long id) { // 向Activity传递事件信息 mCallback.onArticleSelected(position); }
2. 在宿主Activity实现这个接口
怎么实现?很简单,参考下面代码:
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(int position) { // 用户从从 HeadlinesFragment选中了一个标题 //响应用户的操作,做一些业务逻辑 } }
3. 向其他Fragment传递信息 (完成通信)
宿主Activity可以通过findFragmentById()向指定的Fragment传递信息,宿主Activity可以直接获取Fragment实例,回调Fragment的公有方法
例如:
宿主Activity 包含了一个Listfragment用来展示条目信息,当每个条目被点击的时候,我们希望ListFragment向另外一个DetailsFragment传递一个信息用来 展示不同的细节
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(int position) { // 用户在 HeadlinesFragment中选中了一个item //在activity中添加新的fragment ArticleFragment articleFrag = (ArticleFragment) getSupportFragmentManager().findFragmentById(R.id.article_fragment); if (articleFrag != null) { // If article 对象 可以复用, 我们就不需要创建两遍了 // 回调articleFrag 更新 articleFrag.updateArticleView(position); } else { // 创建 Fragment 并为其添加一个参数,用来指定应显示的文章 ArticleFragment newFragment = new ArticleFragment(); Bundle args = new Bundle(); args.putInt(ArticleFragment.ARG_POSITION, position); newFragment.setArguments(args); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); // 将 fragment_container View 时中的内容替换为此 Fragment , // 然后将该事务添加到返回堆栈,以便用户可以向后回滚 transaction.replace(R.id.fragment_container, newFragment); int setTransition=TRANSIT_FRAGMENT_OPEN; transaction.setTransition(setTransition); transaction.addToBackStack(null); // 执行事务 transaction.commit(); } } }
下面我写了一个实例来供大家理解:
各个类的联系图:
效果如下:
Fragment通信demo实例
Service
Service 与Activity通信主要是如何获得Service实例的问题
总结来说两步:
在Service定义内部类,继承Binder,封装Service作为内部类的属性,并且在onBind方法中返回内部类的实例对象
在Activity中实现ServiceConnection ,获取到Binder对象,再通过Binder获取Service
public class LocalService extends Service { // 传递给客户端的Binder private final IBinder mBinder = new LocalBinder(); //构造Random对象 private final Random mGenerator = new Random(); /** * 这个类提供给客户端 ,因为Service总是运行在同一个进程中的 */ public class LocalBinder extends Binder { LocalService getService() { // 当客户端回调的时候,返回LoacalService实例 return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } /**交给客户端回调的方法 */ public int getRandomNumber() { return mGenerator.nextInt(100); } } public class BindingActivity extends Activity { LocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // 绑定 LocalService Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // 解绑 service if (mBound) { unbindService(mConnection); mBound = false; } } /**button已经通过 android:onClick (attribute) 设置此方法响应用户click*/ public void onButtonClick(View v) { if (mBound) { // 回调 LocalService的方法. //因为在主线程中刷新UI,可能会造成线程阻塞,这里只是为了测试 int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } /**定义通过bindService 回调的Binder */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { //先通过Binder获得Service的内部类 LoacalBinder LocalBinder binder = (LocalBinder) service; // 现在可以获得service对象了 mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; }
除了这种回调的方式外
还有一种方式 是在Service中 发送广播,
比如 在Service中 开启了一个子线程执行任务,就在子线程的run()方法中去sendBroadcast(intent);
数据用Intent封装,传递形式用广播
AIDL完成进程间通信
关于进程和线程的细节改天详细说明,我们首先了解一下进程和线程的概念:当某个应用组件启动且该应用没有运行其他任何组件时,Android 系统会使用单个执行线程为应用启动新的 Linux
进程。默认情况下,同一应用的所有组件在相同的进程和线程(称为“主”线程)中运行。
如果某个应用组件启动且该应用已存在进程(因为存在该应用的其他组件),则该组件会在此进程内启动并使用相同的执行线程。
但是,我们也可以安排应用中的其他组件在单独的进程中运行,并为任何进程创建额外的线程。
各类组件元素的清单文件条目—:activity,servicer,eceiver 和 provider均支持 android:process 属性,此属性可以指定该组件应在哪个进程运行。我们可以设置此属性,使每个组件均在各自的进程中运行,或者使一些组件共享一个进程,而其他组件则不共享。 此外,我们还可以设置 android:process,使不同应用的组件在相同的进程中运行
以及了解一下 进程间通信的概念
Android 利用远程过程调用 (RPC) 提供了一种进程间通信 (IPC) 机制,通过这种机制,由 Activity
或其他应用组件调用的方法将(在其他进程中)远程执行,而所有结果将返回给调用方。这就要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。
然后,返回值将沿相反方向传输回来。 Android 提供了执行这些 IPC 事务所需的全部代码,因此我们只需集中精力定义和实现 RPC
编程接口即可。
要执行 IPC,必须使用 bindService() 将应用绑定到服务上。
具体实现 可以 参考这个实例 和文末给出的官方文档
线程间通信
Handler 和AsyncTask都是用来完成子线程和主线程即UI线程通信的都可以解决主线程 处理耗时操作,造成界面卡顿或者程序无响应ANR异常 这一类问题
Handler 是 一种机制【Handler+Message+Looper】,所有的数据通过Message携带,,所有的执行顺序按照队列的形式执行,Looper用来轮询判断消息队列,Handler用来接收和发送Message
AsyncTask 是一个单独的类,设计之初的目的只是为了 异步方式完成耗时操作的,顺便可以通知主线程刷新Ui,AsyncTask的内部机制则是维护了一个线程池,提升性能。
在这里提供另一种优雅的做法完成线程间的通信:
扩展 IntentService 类
由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用 IntentService 类实现服务值得一试。
但如需同时处理多个启动请求,则更适合使用该基类Service。
IntentService 执行以下操作:
创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
创建工作队列,用于将一个 Intent 逐一传递给 onHandleIntent() 实现,这样我们就永远不必担心多线程问题。
在处理完所有启动请求后停止服务,因此我们不必调用 stopSelf()。
提供 onBind() 的默认实现(返回 null)。
提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。
综上所述,您只需实现 onHandleIntent() 来完成客户端提供的工作即可。(不过,我们还需要为服务提供小型构造函数。)
以下是 IntentService 的实现示例:
public class HelloIntentService extends IntentService { /** * 必须有构造函数 必须调用父 IntentService(String)带有name的构造函数来执行工作线程 */ public HelloIntentService() { super("HelloIntentService"); } /** * IntentService 调用默认的工作线程启动服务 * 当此方法结束,, IntentService 服务结束 */ @Override protected void onHandleIntent(Intent intent) { // 通常在这里会执行一些操作,比如下载文件 //在这里只是sleep 5 s long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } } }
看吧,我们只需要一个构造函数和一个 onHandleIntent() 实现即可。
对于Service 当然也有基础一点的做法,来完成多线程的操作,只不过代码量更多了:
public class HelloService extends Service { private Looper mServiceLooper; private ServiceHandler mServiceHandler; // Handler 接收来自主线程的Message private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { //执行任务,比如下载什么的,这里只是 让线程sleep long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } // 手动停止服务,来处理下一个线程 stopSelf(msg.arg1); } } @Override public void onCreate() { //启动线程. 注意我们在主线程中创建了一些子线程, 这些线程都没有加锁同步. 这些现场都是后台线程,所以不会阻塞UI线程 HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Handler开始轮询遍历了 mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // 每一次请求,都会通过handler发送Message // startID只是为了让我们知道正在进行的是哪一个线程,以便于我们停止服务 Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // 不提供 binding, 所以返回空 return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); } }
多个App间的通信
首先我们要知道以下两点:Android 应用一般具有若干个Activity。每个Activity显示一个用户界面,用户可通过该界面执行特定任务(比如,查看地图或拍照)。要将用户从一个Activity转至另一Activity,应用必须使用 Intent 定义做某事的“意向”。 当我们使用诸如 startActivity() 的方法将 Intent 传递至系统时,系统会使用 Intent 识别和启动相应的应用组件。使用意向甚至可以让我们的应用开始另一个应用中包含的Activity。
Intent 可以为 显式 以便启动特定组件(特定的 Activity 实例)或隐式 以便启动处理意向操作(比如“拍摄照片”)的任何组件。
1.向另一个应用发送用户
Android最重要的功能之一,是可以操作其他应用,比如在我们的应用中,需要使用地图显示公司地址,我们无序在地图应用程序中构建Activity,而是直接创建Intent查看 地址的请求,Android系统之后启动 可以在地图上显示 地址的应用。
1) 构建隐式的意图
隐式意图不用声明要启动的组件类名称,而是声明操作,比如查看,编辑,发送,或者获取某项。
如果您我们的数据是Uri,可以这样构建Intent:
//当我们的应用通过startActivity()调用此Intent时,电话应用会发起向指定电话号码呼叫 Uri number = Uri.parse("tel:5551234"); Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
这里还有一些其他Intent的操作和Uri数据对:
查看地图:
// 基于地址的地图位置 Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California"); // 基于经纬度的地图位置 // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
查看网页:
Uri webpage = Uri.parse("http://www.android.com"); Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
有的同学会问了,我从哪里可以知道,Intent可以传递的 Uri的类型,或者其他数据类型呢?
答:可以查阅Google Intent的Api
2) 确认是否存在 接收意向的应用
注意:如果调用了意向,但设备上没有可用于处理意向的应用,我们的应用将崩溃。
要确认是否存在可响应意向的可用Activity,请调用 queryIntentActivities() 来获取能够处理ntent 的Activity列表。 如果返回的 List 不为空,则可以安全地使用该意向。例如:
PackageManager packageManager = getPackageManager(); List activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); boolean isIntentSafe = activities.size() > 0;
如果 isIntentSafe 是 true,则至少有一个应用将响应该意向。 如果它是 false,则没有任何应用处理该意向。
3) 启动指定Activity
当我指定意图后,通过startActivity(intent);就可以启动指定Activity
此处有一个Google官方的示例:
// 构建Intent Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California"); Intent mapIntent = new Intent(Intent.ACTION_VIEW, location); // 确定意图可以被接收 PackageManager packageManager = getPackageManager(); List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0); boolean isIntentSafe = activities.size() > 0; //启动指定应用 if (isIntentSafe) { startActivity(mapIntent); }
4) 显示应用选择器
比如我们要完成”分享操作“,用户可以使用多个App完成分享,我们应明确显示 选择器对话框,如图这里写链接内容
要显示选择器,需要使用Intent的createChooser()方法 创建Intent,并将其传递至startActivity()
Intent intent = new Intent(Intent.ACTION_SEND); ... String title = getResources().getString(R.string.chooser_title); // Create intent to show chooser Intent chooser = Intent.createChooser(intent, title); // Verify the intent will resolve to at least one activity if (intent.resolveActivity(getPackageManager()) != null) { startActivity(chooser); }
这将显示一个对话框,其中有响应传递给 createChooser() 方法的意向的应用列表,并且将提供的文本用作 对话框标题
2. 接收其他Activity返回的结果
通过Intent.startActivityForResult()来完成。
首先在启动另一个Activity时,我们需要指定request code以便返回结果时,我们可以正常处理它。
static final int PICK_CONTACT_REQUEST = 1; // The request code ... private void pickContact() { Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts")); pickContactIntent.setType(Phone.CONTENT_TYPE); startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST); }
当用户完成操作后,返回数据,系统会调用Activity的 onActivityResult()方法,
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 检查requestCode是否真确 if (requestCode == PICK_CONTACT_REQUEST) { // 确保请求时成功的 if (resultCode == RESULT_OK) { //完成我们的业务逻辑 } } }
为了成功处理结果,我们必须了解Intent的格式,比如联系人返回的是带内容的URI,照相机返回的是Bitmap
如何根据返回的URI来读取数据,我们需要对ContentResolver 和 ContentProvider 有了解
下面就是一个三者结合的获取联系人的实例:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 检查requestCode if (requestCode == PICK_CONTACT_REQUEST) { // 确保请求成功 if (resultCode == RESULT_OK) { //获得选择的联系人的URI Uri contactUri = data.getData(); // 我们只需要NUMBER这一列的信息, String[] projection = {Phone.NUMBER}; // 显示根据NUMBER查询的结果 // We don't need a selection or sort order (there's only one result for the given URI) // 在这里我们并没有对查询的结果进行排序,因为在主线程中进行这种数据库操作,有可能阻塞线程 //优化方案是异步完成排序的操作,这里只是展示多个App间的通信 Cursor cursor = getContentResolver() .query(contactUri, projection, null, null, null); cursor.moveToFirst(); //从NUMBER那一列当中取回phone NUMBER int column = cursor.getColumnIndex(Phone.NUMBER); String number = cursor.getString(column); //接下来就是要操作这些phone number了 } } }
3. 接收其他Activity返回的结果
要允许其他应用开始您的Activity,需要 在相应元素的宣示说明文件中添加一个 元素。
例如,此处有一个在数据类型为文本或图像时处理 ACTION_SEND 意向的意向过滤器:
<activity android:name="ShareActivity"> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> <data android:mimeType="image/*"/> </intent-filter> </activity>
定义操作,通常是系统定义的值之一,比如ACTION_SEND 或 ACTION_VIEW。
定义与Intent关联的数据,只需通过 android:mimeType 指定我们接收的数据类型,比如text/plain 或 image/jpeg。
所有的隐式Intent,都使用 CATEGORY_DEFAULT 进行定义
4. 处理Activity中的Intent
当Activity开始时,调用getIntent检索开始Activity的Intent,
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Intent intent = getIntent(); Uri data = intent.getData(); // 指出接收的数据类型 if (intent.getType().indexOf("image/") != -1) { // 处理带有图片的Intent } else if (intent.getType().equals("text/plain")) { // 处理带有文本的Intent } }
5. 向指定Activity中返回数据
只需调用setResult指定结果代码和Intent
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"); setResult(Activity.RESULT_OK, result); finish();
记住必须为结果指定结果码,通常为 RESULT_OK 或 RESULT_CANCELED。
我们也可以在Intent中 用Bundle存储额外的信息
细心的同学可能发现一个问题:
启动Activity 有startActivity() 和startActivityForResult() 两种启动方式,返回结果的形式id偶有setResult()吗?
如果开启当前Activity的Intent可能需要结果,只需调用 setResult()。 如果原始Activity已调用 startActivityForResult(),则系统将向其传递您提供给 setResult() 的结果;否则,会忽略结果。
使用大型开源框架完成组件间的通信
Github上非常火的两大通信组件EventBus和otto:1. EventBus
EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。
传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。
1)概念:
事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属的 Class。
事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件。
订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 onEvent 函数,这个函数叫事件响应函数。订阅者通过 register 接口订阅某个事件类型,unregister 接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为 0。
发布者(Publisher):发布某事件的对象,通过 post 接口发布事件。
本项目较为简单,总体设计和流程图:
使用方式:
build.gradle 中加入依赖
compile 'org.greenrobot:eventbus:3.0.0'
代码中指需三步
定义事件:只需要是一个Java类
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
完成订阅者
//MessageEvent被Eventbus post提交的时候 将会回调这个方法 //这种方式 提示我们可以直接定义自己的事件 @Subscribe public void onMessageEvent(MessageEvent event){ Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show(); } // 当一些其他事件post提交的时候,回调这个方法 @Subscribe public void handleSomethingElse(SomeOtherEvent event){ doSomethingWith(event);
在Activity或者Fragment中绑定订阅者
@Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { EventBus.getDefault().unregister(this); super.onStop(); }
发布事件:
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
很遗憾未完成的部分
网络通信基础篇:Google 课程–AnsycTask+HttpClient网络通信提高篇:开源框架Ansyc-Httpclient,okttp,Retrofit
相关文章推荐
- Android 自定义属性
- Android 设置相关
- Android 软键盘弹出与关闭
- 三款Android炫酷Loading动画组件推荐
- Android解决ADB not responding问题
- Android 自定义组合控件
- Android系统Choreographer机制实现过程
- UPX对Android上ELF加壳使用过程中的若干问题总结
- Android权限弹窗影响录音动画解决办法
- Android Binder机制(四) defaultServiceManager()的实现
- Android Studio配置SVN
- Android6.0将某个分区作为内置sdcard
- Android上定义播放器控件UniversalVideoView
- Android自学指导
- Android样式的开发(转)
- Android与串口设备通信的方案四种
- android 事件分发机制(图文详解)
- Android 组件动画
- android位置参数left、translationX、x、mScrollX
- Android垂直ProgressBar的实现