App 唤起场景
游戏开发中,经常存在 App 间相互跳转的场景:
- 从游戏跳转到第三方应用,完成指定功能后,再返回游戏 App。如 QQ/WeChat 登录、分享等。
- 从第三方应用拉起游戏 App。如 QQ/WeChat 游戏中心直接拉起游戏。
- 从 Web 页面拉起游戏 App。
App 唤起方案
指定 Activity 名拉起
1 2 3 4 5 6 7 8 9 10
| Intent intent = new Intent(Intent.ACTION_VIEW); ComponentName componentName = new ComponentName("com.tencent.morris.activity", "com.tencent.morris.activity.MainProxyActivity"); intent.setComponent(componentName); this.startActivity(intent);
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setClassName("com.tencent.morris.activity", "com.tencent.morris.activity.MainProxyActivity"); this.startActivity(intent);
|
指定 Package 名拉起
1 2
| Intent intent = getPackageManager().getLaunchIntentForPackage("com.tencent.morris.activity"); this.startActivity(intent);
|
通过 Scheme 拉起
1 2 3 4
| Intent intent = new Intent(Intent.ACTION_VIEW); Uri data = Uri.parse("mytest://hello.world:1024/test?name=zhangsan&age=27"); intent.setData(data); this.startActivity(intent);
|
1
| <a href="mytest://hello.world:1024/test?name=zhangsan&age=27">打开APP页面</a>
|
被拉起的 App AndroidManifest 中需要新增 intent-filter 配置。
1 2 3 4 5 6 7 8 9 10 11 12
| <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:host="hello.world" android:port="1024", android:path="/test" android:scheme="mytest" /> </intent-filter>
|
参数传递与处理
参数发送
一般将数据封装到 Bundle 中,然后通过 Intent 把 Bundle 对象传递过去。
1 2 3 4 5 6 7
| Bundle data = new Bundle(); data.putInt("age", 18); data.putString("name", "Jack");
intent.putExtra("data", data);
|
参数接收
被拉起的 App 需要在 Activity 激活时,处理传递数据。
- 拉起的Activity 未启动,此时会触发 onCreate,可以在此生命周期函数中处理数据。
- 拉起的Activity 已启动,此时会触发 onNewIntent,需要在此声明周期中接收并更新 intent,然后进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Override protected void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate"); super.onCreate(savedInstanceState); Intent intent = this.getIntent(); }
@Override protected void onNewIntent(Intent intent) { Log.i(TAG, "onNewIntent"); super.onNewIntent(intent);
setIntent(intent); Intent intent = this.getIntent(); }
|
参数解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| Uri uri = getIntent().getData(); if (uri != null) { String url = uri.toString(); Log.e(TAG, "Url : " + url);
String scheme = uri.getScheme(); Log.e(TAG, "Scheme : " + scheme);
String host = uri.getHost(); Log.e(TAG, "Host : " + host);
int port = uri.getPort(); Log.e(TAG, "Port : " + port);
String path = uri.getPath(); Log.e(TAG, "Path : " + path);
String query = uri.getQuery(); Log.e(TAG, "Query : " + query);
String authority = uri.getAuthority(); Log.e(TAG, "Authority : " + authority);
String token = uri.getQueryParameter("token"); Log.e(TAG, "Token : " + token);
List<String> segments = uri.getPathSegments(); Log.e(TAG, "Segments : " + segments);
Set<String> names = uri.getQueryParameterNames(); Log.e(TAG, "QueryParameterNames : " + names);
String schemeSpecificPart = uri.getSchemeSpecificPart(); Log.e(TAG, "SchemeSpecificPart : " + schemeSpecificPart); }
Bundle data = getIntent().getBundleExtra("data"); if (data != null) { for (String key : data.keySet()) { Log.i(TAG, "[Bundle]: " + key + " = " + data.get(key)); } }
|
Android Intent
Intent 是一个将要执行的动作的抽象的描述,一般来说是作为参数来使用,由 Intent 来协助完成 Android 各个组件之间的通讯。
Intent 用途
- 启动Activity,通过Context.startActvity() / Activity.startActivityForResult()启动一个Activity;
- 启动Service,通过Context.startService()启动一个服务,或者通过Context.bindService()和后台服务交互;
- 发送Broadcast,通过广播方法Context.sendBroadcasts() / Context.sendOrderedBroadcast() / Context.sendStickyBroadcast()发给 Broadcast Receivers。
Intent 种类
- 显式Intent,通过组件名指定启动的目标组件。
- 隐式Intent,不指定组件名,而是设置Action、Data、Category,让系统来根据所有的 intent-filter 来筛选满足属性的组件,当不止一个满足时,会弹出一个让我们选择启动哪个的对话框。
Intent 属性
Intent对象大致包括7大属性:Action(动作)、Data(数据)、Category(类别)、Type(数据类型)、Component(组件)、Extra(扩展信息)、Flag(标志位)。其中最常用的是Action、Data、Extra属性。
属性 |
用途 |
备注 |
Action |
字符串,用来指定Intent要执行的动作类别 |
一个 Intent Filter 可以包含多个 Action |
Category |
字符串,表示哪种类型的组件来处理这个Intent |
一个 Intent Filter 可以包含多个 Category |
Data |
URI 格式的数据 |
|
Type |
指定数据类型 |
一般Intent的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。 |
Component |
指定Intent的目标组件名称 |
当指定了这个属性后,系统将跳过匹配其他属性,而直接匹配这个属性来启动对应的组件。 |
Extra |
key-value 扩展数据 |
通过调用putExtra()方法设置数据,也可以通过创建 Bundle 对象来存储所有数据,然后通过调用putExtras()方法来设置数据。 |
Flag |
用来指示系统如何启动一个Activity |
通过setFlags()或者addFlags()设置 Intent 标签。 |
Action
ACTION 类型 |
作用 |
ACTION_MAIN |
表示程序入口 |
ACTION_VIEW |
自动以最合适的方式显示Data |
ACTION_EDIT |
提供可以编辑的 |
ACTION_PICK |
选择一个一条Data,并且返回它 |
ACTION_DAIL |
显示Data指向的号码在拨号界面Dailer上 |
ACTION_CALL |
拨打Data指向的号码 |
ACTION_SEND |
发送Data到指定的地方 |
ACTION_SENDTO |
发送多组Data到指定的地方 |
ACTION_RUN |
运行Data,不管Data是什么 |
ACTION_SEARCH |
执行搜索 |
ACTION_WEB_SEARCH |
执行网上搜索 |
ACRION_SYNC |
执同步一个Data |
ACTION_INSERT |
添加一个空的项到容器中 |
Broadcast Actions
ACTION 类型 |
作用 |
ACTION_TIME_TICK |
当前时间改变,并即时发送时间,只能通过系统发送。 |
ACTION_TIME_CHENGED |
设置时间。 |
Category
ACTION 类型 |
作用 |
CATEGORY_DEFAULT |
把一个组件Component设为可被implicit启动的 |
CATEGORY_LAUNCHER |
把一个action设置为在顶级执行。 |
CATEGORY_BROWSABLE |
当Intent指向网络相关时,必须要添加这个类别 |
CATEGORY_HOME |
使Intent指向Home界面 |
CATEGORY_PREFERENCE |
定义的Activity是一个 Preference Panel |
Flag
FLAG 类型 |
作用 |
FLAG_ACTIVITY_CLEAR_TOP |
相当于SingleTask |
FLAGE_ACTIVITY_SINGLE_TOP |
相当于SingleTop |
FLAG_ACTIVITY_NEW_TASK |
类似于SingleInstance |
FLAG_ACTIVITY_NO_HISTORY |
当离开该Activity后,该Activity将被从任务栈中移除 |
Activity有四种启动模式:standard、singleTop、singleTask、singleInstance。可以在AndroidManifest.xml中activity标签的属性android:launchMode中设置该activity的加载模式。
- standard模式:默认的模式,以这种模式加载时,每当启动一个新的活动,必定会构造一个新的Activity实例放到返回栈(目标task)的栈顶,不管这个Activity是否已经存在于返回栈中;
- singleTop模式:如果一个以singleTop模式启动的activity的实例已经存在于返回桟的桟顶,那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中;
注:如果以singleTop模式启动的activity的一个实例已经存在于返回桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例;
- singleTask模式:这种模式下,每次启动一个activity时,系统首先会在返回栈中检查是否存在该活动的实例,如果存在,则直接使用该实例,并把这个活动之上的所有活动统统清除;如果没有发现就会创建一个新的活动实例;
- singleInstance模式:总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他activity会自动运行于另一个任务中。当再次启动该activity的实例时,会重新调用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask相同,同一时刻在系统中只会存在一个这样的Activity实例。(singleInstance即单实例)
注:前面三种模式中,每个应用程序都有自己的返回栈,同一个活动在不同的返回栈中入栈时,必然是创建了新的实例。而使用singleInstance模式可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪一个应用程序来访问这个活动,都公用同一个返回栈,也就解决了共享活动实例的问题。(此时可以实现任务之间的切换,而不是单独某个栈中的实例切换)
Android URL Scheme
Scheme://Host:Port/Path?Query
- 协议:Scheme
- 授权:Authority(Host:Port)
- 请求:Query(key1=value1&key2=value2)
参考文档
https://www.runoob.com/w3cnote/android-tutorial-intent-pass-data.html