Android跨应用启动

恢弘志士之气,不宜妄自菲薄。这篇文章主要讲述Android跨应用启动相关的知识,希望能为你提供帮助。
Android跨应用启动
序言:

相信大家, 很多时候都是在自己的app中, 启动Activity, Service、BroadcastReceiver、contentProvider
。其实, 这些都只是 一个app中 组件间的启动。本文要讲解的是 两个app间 组件 的启动。即: 跨应用启动——使用隐式Intent 启动appB的某个组件。显示Intent做不到。
一、在开始之前, 先来梳理一下跨应用启动的2种情形:
假设我们现在有两个应用, appA和appB 。
第一种: 在appA的任意一个Activity中, 启动appB的任一组件。
Android跨应用启动

文章图片

一个app项目中, 通常都不会只有一个Activity, 所以任何一个Activity组件都是可以启动appB中的任一组件的。
第二种: 在appA的任何一个Service中, 启动appB的任一组件。
Android跨应用启动

文章图片

众所周知, android中有四大组件, 为什么小编, 只介绍Activity和Service组件启动另一个app的四大组件?
其实, 发送广播也是可以启动另一个app的组件, 只要该app监听了此广播, app接收到广播后, 在onReceive()方法中, 再启动目标组件即可。为什么可以这么做? 这是因为onReceive()方法中会要求传入context实例, 有了context实例, 就能使用context的方法, 启动其他组件。( 当然, BroadcastReceiver本身就是四大组件之一, BroadcastReceiver接收到广播的时候, 已经是第一个成功被启动的组件了) 。广播的用法相对简单和常见, 不打算讲了。
另外需要跟大家说一下, Context类是一个抽象类, 传入的context实例是由其子类来实现的, 这种——用父类声明变量, 由子类来实现的思维方式, 在java中是很常见的。特别是接口和抽象类, 经常用到这种方式。对于小编这种由C转Java的人来说, 真是一大坑啊。
为什么Activity和Service都可以直接使上图中的四个方法呢, 这是因为Activity和Service都是继承自ContextWrapper, 所以子类拥有父类的方法。BroadcastReceiver和contentProvider则不是, 具体大家可以看官方API。
所以在app项目开发中, 我们经常会在Activity或者Service中去启动一个组件, 或发送广播。在实际项目中, 经常会在Activity或者service中发送广播, 进而实现组件间松耦合。
至于contentProvider,我想大家还没见过, 这娃自动去干过事情吧, 都是被动的调用。
二、跨应用启动的实战
下面让我们正式进入今天的主题: 跨应用启动实战
appA的Activity中, 启动appB的Activity
Android提供了在一个App中启动另一个App中的Activity的能力, 这使我们的程序很容易就可以调用其他程序的功能, 从而就丰富了我们App的功能。比如在微信中发送一个位置信息, 对方可以点击这个位置信息启动腾讯地图并导航。这个场景在现实中作用很大, 尤其是朋友在陌生的环境找不到对方时, 这个功能简直就是救星。
【Android跨应用启动】本来想把本文的名字叫启动另一个进程中的Activity, 觉得这样才有逼格。因为每个App都会运行在自己的虚拟机中, 每个虚拟机跑在一个进程中。但仔细一想, 能够称为一个进程, 前提是这个App必须要运行起来才行。而Android提供的能力, 是不需要另一个App启动就可以将其特定的Activity启动起来的。
也就是说, appB是处于未启动的状态, 即——appB还不是系统的一个进程, 那么当使用appA启动appB的某个组件时, 请问, appB是否成为系统的进程? 答案是yes。怎么看呢, 可以从Android Studio 的Android device monito 中结合虚拟机看。
Android跨应用启动

文章图片

那么我们要怎么启动另一个应用的组件呢? 有两种办法可以启动另一个App中的Activity。
第一种———隐式Intent的action方式。
相信这种方式, 大家都不会陌生。这里就不进行过多的解析。这里只贴一下AppB的manifest( 文件清单) :
Android跨应用启动

文章图片

从文件清单中, 我们可以看到, appB中有两个Activity。其中SecondActivity就是要被appA启动的Activity。
那么我们只要在appA的任意一个组件( Activity或Service) , 做如下的调用:
Intent intent= new Intent(" android.intent.action.SecondActivity" ); startActivity(intent);

就可以成功在 appA中 启动 appB 的 组件。另外还要跟大家说一点, SecondActivity的category一定要在文件清单中添加上, 否则启动的时候会报错的。至于调用的时候为什么没添加category, 可以自己百度下。
Android跨应用启动

文章图片

不知道大家有没有思考过这三个事情:
1、当appA 启动 appB的SecondActivity, 请问: appB的MainActivity会不会被启动呢? 正常情况下, 我们点击appB, 进到的是MainActivity这个lunch活动, 那么现在我们是通过跨应用启动的, 会不会要先启动B的MainActivity呢? 答案是不会。这就体现了组件的思想。
2、当我们在SecondActivity中点击Back回退键时, 回到的是appA的mainActivity界面, 这里时候大家有没有想过。
SecondActivity和appA的mainActivity是不是同处于一个栈中呢? 想要了解这个问题, 只好自己动手打印栈的ID了。
3、综上, 不知道大家想起: Android对于Activity的管理, 也就是framework层中有一个ActivityManager。也就是说, 无论你手机上有多少个应用, 他们的Activity都是由ActivityManager这娃来创建和管理的。应用本身并没有创建Activity的能力。当然这其中又涉及到了Ibinder的通讯。这里暂时不讲。
第二种——用intent设置className或component的办法启动。
举例如下。新建两个项目ProjectA和ProjectB, 用B中的MainActivity启动A的MainActivitity。关键代码如下:
ProjectA MainActivity
@ Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_VIEW); String packageName = " com.example.mylife.anotherapp" ; String className = " com.example.mylife.anotherapp.MainActivity" ; intent.setClassName(packageName, className); //second method //intent.setComponent(new ComponentName(" com.example.mylife.anotherapp" ," com.example.mylife.anotherapp.MainActivity" )); Bundle bundle = new Bundle(); bundle.putString(" msg" , " this message is from project B " ); intent.putExtras(bundle); intent.putExtra(" pid" , android.os.Process.myPid()); startActivityForResult(intent, 1); //startActivity(intent); }@ Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case 1: if(resultCode = = RESULT_OK) { textView.setText(data.getStringExtra(" result" )); } break; } }

ProjectB MainActivity
@ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView)findViewById(R.id.text); Intent intent = getIntent(); if(intent != null) { textView.setText(intent.getStringExtra(" msg" )); } }public void OnClick(View view) { Intent intent = new Intent(); intent.putExtra(" result" ," OK! from project a." ); this.setResult(RESULT_OK,intent); this.finish(); //要清楚这里为什么要用finish()。 }

注意: 如果在应用B中, 是通过按下Back键, 回退到应用A的MainActivity活动, 那么A的onActivityResult()方法是不会被回调的, 这是因为ProjectB的MainActivity活动只是出栈而已, 并没有销毁。而只有ProjectB的MainActivity活动被销毁的时候, 才会回调A的onActivityResult()方法。那如果是按了Back键回退的话怎么处理呢? 这时候只要重写appB的onBackPressed()方法就好了。
@ Override public void onBackPressed() { super.onBackPressed(); Intent intent = new Intent(); intent.putExtra(" result" ," OK! from project a." ); this.setResult(RESULT_OK,intent); this.finish(); //要清楚这里为什么要用finish()。 }

三: 进阶———在A应用的Activity中启动( 停止) ——B应用的Service
应用B的manifest
Android跨应用启动

文章图片

应用B的service的代码:
public class MyService extends Service {private static final String TAG = " MyService" ; public MyService() { }@ Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException(" Not yet implemented" ); }@ Override public void onCreate() { super.onCreate(); Log.d(TAG, " onCreate: " ); }@ Override public int onStartCommand(Intent intent,int flags, int startId) { Log.d(TAG, " onStartCommand: " ); if(intent != null) { Log.d(TAG, " onStartCommand: " + intent.getStringExtra(" msg" )); } return super.onStartCommand(intent, flags, startId); }@ Override public void onDestroy() { super.onDestroy(); Log.d(TAG, " onDestroy: " ); } }

应用A的代码:
@ Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_VIEW); String packageName = " com.example.mylife.anotherapp" ; String className = " com.example.mylife.anotherapp.MyService" ; intent.setClassName(packageName, className); switch (v.getId()) { case R.id.btn_start: Bundle bundle = new Bundle(); bundle.putString(" msg" , " this message is from project B " ); intent.putExtras(bundle); intent.putExtra(" pid" , android.os.Process.myPid()); startService(intent); break; case R.id.btn_stop: stopService(intent); break; } }

测试结果: A应用直接启动B应用的服务, 而B应用并不会打开自己的MainActivity。
好了, 相信大家看完, 都能够成功的启动另一个应用的Activity或者Service了。
另外大家还可以手动做一些尝试:
1、在ProjectA的Service中启动另一个应用的四大组件
2、在ProjectA的发送广播, 启动另一个应用的四大组件
3、ContentProvider组件只有被访问的份了。
本次代码参考: http://blog.csdn.net/lincyang/article/details/45503675

    推荐阅读