欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android跨应用启动实例详解

程序员文章站 2022-07-18 12:20:31
android跨应用启动 前言: 相信大家,很多时候都是在自己的应用中,启动自己写的activity,service、broadcastreceiver、content...

android跨应用启动

前言:

相信大家,很多时候都是在自己的应用中,启动自己写的activity,service、broadcastreceiver、contentprovider 。换句话说,这些都只是 * 单个应用中 组件间 * 的启动。而我们下面要谈论的是 两个应用间 组件 的启动。即——使用 隐式intent方式 启动应用b的某个组件。

一、在开始之前,先来梳理一下跨应用启动的2种方式:

第一种:在activity中,启动另一个app的组件。

Android跨应用启动实例详解

第二种:在service中,启动另一个app的组件。

Android跨应用启动实例详解

从所周知,android中有四大组件,那么为什么小编,只介绍activity和service中启动另一个应用的四大组件?

其实,broadcastreceiver组件也是可以启动 4大组件的。这是因为onreceive()方法中会要求传入context实例,有了context实例,就能使用context的方法,启动其他组件。

至于contentprovider,我想大家还没见过,这娃自动去干过事情吧,都是被动的调用。

所以在写代码的时候,我们经常会在activity或者service中去启动一个组件,broadcastreceiver很少,而contentprovider更是没见过。

另外需要跟大家说一下,context类是一个抽象类,传入的context实例是由其子类来实现的,这种——用父类声明变量,由子类来实现的思维方式,在java中是很常见的。特别是接口和抽象类,经常用到这种方式。对于小编这种由c转java的人来说,真是一大坑啊。

为什么activity和service都可以直接使用图中的四个方法呢,这是因为activity和service都是继承自contextwrapper,所以子类拥有父类的方法。broadcastreceiver和contentprovider则不是,具体大家可以看官方api。

二、跨应用启动的实战

** 下面让我们正式进入今天的主题:跨应用启动实战**

1:appa的activity中,启动appb的activity

android提供了在一个app中启动另一个app中的activity的能力,这使我们的程序很容易就可以调用其他程序的功能,从而就丰富了我们app的功能。比如在微信中发送一个位置信息,对方可以点击这个位置信息启动腾讯地图并导航。这个场景在现实中作用很大,尤其是朋友在陌生的环境找不到对方时,这个功能简直就是救星。

本来想把本文的名字叫启动另一个进程中的activity,觉得这样才有逼格。因为每个app都会运行在自己的虚拟机中,每个虚拟机跑在一个进程中。但仔细一想,能够称为一个进程,前提是这个app必须要运行起来才行。而android提供的能力,是不需要另一个app启动就可以将其特定的activity启动起来的。

也就是说b应用是处理未启动的状态,也就是还没有成为系统的一个进程,那么当使用a启动b应用的某个组件时,请问,b应用是否成为系统的进程?答案是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);

就可以成功在 a应用中 启动b应用的 组件。另外还要跟大家说一点,secondactivity的category一定要在文件清单中添加上,否则启动的时候会报错的。

Android跨应用启动实例详解

不知道大家有没有思考过这三个事情: 
1、当a应用 启动 b应用的secondactivity,那么b应用的mainactivity会不会被启动呢?正常情况下,我们点击应用b,进到的是mainactivity这个活动,那么现在我们是通过跨应用启动,会不会要经过b的mainactivity呢?答案是不会。

2、当我们在secondactivity中点击back回退键时,回到的是a应用的mainactivity界面,这里时候大家有没有想过。 
secondactivity和appa的mainactivity是不是同处于一个栈中呢?这时候就要去打印栈的id了。

3、由上面的两件事,不知道大家想起:android对于activity的管理,也就是framework层的activitymanager。也就是说,你手机上的n多应用,当你打开某一个应用是,这个应用的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应用的服务

应用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应用并不会打开自己的activity。

本次代码参考:

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!