我们经常看到当点击一个链接的时候,跳转到app,比如当我们在网页端浏览新闻的时候,要想查看更多评论等就会提示你跳转到app内打开查看,那是如何实现网页中打开app的呢?
要想实现浏览器内打开app,其实我们可以看做就是打开一个链接,只是我们普通的链接都是http或者https开头的,如果要想打开app,那我么需要自定义这个schem。
首先我们来学下下uri的组成吧
scheme://host:port/path?qureyParameter=queryString
一个uri是由上面几个部分组成的,分别是:
https://www.baidu.com/images?keyword=花儿
html中代码很简单,就这么一行,是不是html写好了就可以打开app了呢?当然不可以,我们需要在我们的app中注册这个uri。现在来到app中,我们在AndroidManifest.xml中加入,我们在MainActivity中注册。注册代码如下:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
intent-filter>
//注册scheme
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
//这里myapp一定要和html中scheme一致。
<data android:scheme="myapp"/>
intent-filter>
activity>
所以我们可以给我们的app定义一个uri,html中代码如下:
<a href = 'myapp://wms.com/openwith?name=wms1993&age=24'> 打开app a>
当我们通过浏览器打开链接的时候,就会自动打开我们的app了,不同浏览器可能提示不一样,我用uc浏览器打开就会提示如下:
注意:这里不能直接在浏览器地址栏中直接输入我们的url,这样的话浏览器默认会给我们的url加上http,那么就无法打开应用了,最好是嵌在网页中。
经过上面两个步骤,我们就可以简单实现通过浏览器打开app了。现在我们要讲的是怎么给我们的应用传值呢?我们知道,在http传输的时候,有get请求和post请求,当然这种情况下我们只能通过get请求的方式传值,get方式传值就是把值放在url的后面,如上面例子中,我们传递了两个值
对于网页中我们和普通的http没啥区别,现在关键在我们app端,该怎么接收值呢?看下代码:
/**
* create by wms1993
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getIntent().getAction().equals(Intent.ACTION_VIEW)) {
//通过浏览器打开
Uri uri = getIntent().getData();
String name = uri.getQueryParameter("name");
String age = uri.getQueryParameter("age");
Log.e(TAG, "onCreate: name = " + name);
Log.e(TAG, "onCreate: age = " + age);
}
}
}
logcat如下:
06-12 09:57:43.064 12399-12399/bluetooth.wms.com.openappbybrowser E/MainActivity: onCreate: name = wms1993
06-12 09:57:43.065 12399-12399/bluetooth.wms.com.openappbybrowser E/MainActivity: onCreate: age = 24
当我们打开app的时候,会调用onCreate方法,这里我们看看getIntent的Action是不是ACTION_VIEW,如果是那么是从浏览器中打开的。
经过上面的步骤,我们实现了浏览器打开app以及给app传值的操作,那么是不是就这样结束了呢?当然不是,这里面还有一些小问题,加入我们app是一个新闻类的app,当我们通过网页浏览新闻的时候,然后点击了新闻详情,里面有一个提示在app内查看的时候,这种情况会有如下问题:
先来看下默认情况下是Android是如何操作的,我们现在通过浏览器打开app(app之前没打开),这时候我们按下Home键,然后进入,这时候会发生什么事?现在MainActivity.java中onCreate方法加上打印。当我们通过浏览器打开app,日志如下:
06-12 10:17:07.738 16021-16021/bluetooth.wms.com.openappbybrowser E/MainActivity: onCreate...
06-12 10:17:07.738 16021-16021/bluetooth.wms.com.openappbybrowser E/MainActivity: onCreate: name = wms1993
06-12 10:17:07.738 16021-16021/bluetooth.wms.com.openappbybrowser E/MainActivity: onCreate: age = 24
当按下Home键返回桌面的时候,我们在点击桌面图标打开app,这时候日志如下:
06-12 10:17:59.518 16021-16021/bluetooth.wms.com.openappbybrowser E/MainActivity: onCreate...
通过日志可以看出,这时候应用重新创建了。
默认情况下,浏览器唤起的页面按返回键是回不到之前打开的界面的,那么这是为啥呢?
首先大家应该了解下启动模式,我以前博客中有一篇关于启动模式的文章,Activity 启动模式 ,下面我们简单介绍下任务栈的概念。默认情况下,如果没有对 Activity
设置 TaskAffinity
属性,一个应用的所有 Activity
都是运行在同一个任务栈的,任务栈的名称为应用的 PackageName
。如果从应用A启动应用B的某个 Activity
C,则 C 会运行在 A 的任务栈中。说到这里,相信大家应该明白为啥了吧。
当我们从Launcher启动app时,app运行在Launcher的任务栈中,从浏览器中打开app则运行在浏览器任务栈中,那如何解决这个问题呢?
由于从桌面点击应用会创建自己的应用栈,那么如果我们可以把浏览器任务栈中的界面移动到应用本身的任务栈中。那么怎么将 Activity
从其他任务栈中移到自己的任务栈中呢?方法很简单,只需要在相应的 Activity
中配置 allowTaskReparenting
属性 为 true
即可。但是有时候我们可能不光要将一个 Activity 移过来,有时候我们需要将整个应用移动过来,这时候我们可以将allowTaskReparenting
添加到application
上,代码如下:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
//如果想将整个应用都移动到则在这里添加
android:allowTaskReparenting="true"
android:theme="@style/AppTheme">
<activity
//如果只想移动某个Activity到则在这里添加
android:allowTaskReparenting="true"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
intent-filter>
<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:scheme="myapp"/>
intent-filter>
activity>
<activity android:name=".PageDetailActivity"/>
application>
这样这个问题就解决了。