scheme唤醒外部APP

1. 跳转原理

使用 scheme 的方式去实现跳转,先整理一下思路,首先如果要外部能唤醒 App ,
那么 App 肯定要先注册一个全局的事件监听吧。然后,应该有一个页面来处理接受事件,
然后解析出具体的参数,根据参数跳转具体的页面,就是这么简单。

2. 目标APP注册

这里需要使用到 Android Activity中的 <intent-filter> ,现在可以创建
一个解析跳转的IntentSkipActivity,然后在清单文件中配置具体的 <intent-filter><data>里面可以配置scheme host path等字段

<intent-filter>
    <data android:scheme="test" />
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>

3. 页面跳转分三种情况

目标App有三个页面,主页A,假设起名MainActivity,点击主页按钮去
第一个测试页面B,起名为TestActivity1,点击测试页面B上的按钮去测试页面C,
起名为TestActivity2,需求是外部App是点击外部App页面的一个按钮跳转到目标App的页面C

1.当前手机中么有启动目标APP,即进程不存在
简单说就是外部APP或者浏览器链接地址点击要直接跳到目标APP的C 页面,然后回退的时候,要回退到
目标APP的A页面,然后点击A按钮可以进入B页面。这里就是需要我们自己去创建一个堆栈,把 
A、C 按顺序都放进去,所以 C 回退到 A,A 然后可以启动 B。知识点就TaskStackBuilder ,配合它的
就是在 Manifest 中可以指定 Parent 的属性。

具体代码

(1)首先是在清单文件中配置B,C页面的Parent属性当然B页面可以不配置 让C页面直接返回A页面 
指定C页面parentActivityName为A页面,<meta-data的value改为A页面

    B页面 
    <activity android:name=".TestActivity1"
           android:parentActivityName=".MainActivity">
           <meta-data
               android:name="android.support.PARENT_ACTIVITY"
               android:value=".MainActivity"
               />
    </activity>

    C页面
    <activity
        android:name=".TestActivity2"
        android:parentActivityName=".TestActivity1">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".TestActivity1"
            />
    </activity>

(2)解析跳转的IntentSkipActivity中写跳转代码
    TaskStackBuilder.create(this)
                    .addParentStack(resultIntent.getComponent())
                    .addNextIntent(resultIntent)
                    .startActivities();

2.目标APP已经启动,在后台运行着,并且指定的 C 页面并没有打开,上面的方式,
不管你App启动没,它都是会重新启动的,这个让人也有点儿不爽,看看启动的方法就知道

public void startActivities(Bundle options) {
   if (mIntents.isEmpty()) {
        throw new IllegalStateException(
                "No intents added to TaskStackBuilder; cannot startActivities");
    }

    Intent[] intents = mIntents.toArray(new Intent[mIntents.size()]);
    intents[0] = new Intent(intents[0]).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
            IntentCompat.FLAG_ACTIVITY_CLEAR_TASK |
            IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME);
    if (!ContextCompat.startActivities(mSourceContext, intents, options)) {
        Intent topIntent = new Intent(intents[intents.length - 1]);
        topIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mSourceContext.startActivity(topIntent);
    }
}

看重点,这个方法每次都会给第一个Intent添加Intent.FLAG_ACTIVITY_NEW_TASK
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK|IntentCompat.FLAG_ACTIVITY_TASK_ON_HOME 这三个Flag,
因为有IntentCompat.FLAG_ACTIVITY_CLEAR_TASK 所以就成这个样子咯,那么怎么解决呢?
其实很简单的,我们在跳转的时候先判断下当前App是否已经开启过了,没有的话,那就直接上面
的代码,有的话,那就不用再去创建堆栈了,直接开启就好了。直接开启的时候记得要加上
Intent.FLAG_ACTIVITY_NEW_TASK的Flag,不然就在浏览器所在的堆栈里面了。

具体代码

1.判断当前APP是否开启过了
public static boolean isLaunchedActivity(Context context, Class<?> clazz) {
     try {
         Intent intent = new Intent(context, clazz);
         ComponentName cmpName = intent.resolveActivity(context.getPackageManager());
         boolean flag = false;
         if (cmpName != null) { // 说明系统中存在这个activity
             ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
             List<ActivityManager.RunningTaskInfo> taskInfoList = am.getRunningTasks(10);
             for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {
                 if (taskInfo.baseActivity.equals(cmpName)) { // 说明它已经启动了
                     flag = true;
                     break;
                 }
             }
         }
         return flag;
     } catch (Exception e) {
         e.printStackTrace();
         return false;
     }
 }

2.解析跳转的IntentSkipActivity中写跳转代码 
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     TAG = this.getClass().getSimpleName();
     Uri data = getIntent().getData();
     try {
         if (data != null) {
             Log.e(TAG, "url: " + data.toString());
             Intent resultIntent = JumpUtils.parseIntent(this, data.toString(), null);
             if (resultIntent == null) {
                 finish();
                 return;
             }

             if (JumpUtils.isLaunchedActivity(this, MainActivity.class)){
                resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(resultIntent);
             } else {
                TaskStackBuilder.create(this)
                         .addParentStack(resultIntent.getComponent())
                         .addNextIntent(resultIntent)
                         .startActivities();
             }
             finish();
         }
     } catch (Exception Exception) {
         Exception.printStackTrace();
         finish();
     }
  }

3.第三种情况,目标App已经启动,在后台运行着,指定的 C 页面打开着的。

这个其实就是启动模式的问题,C 已经打开,又一次打开,如果是正经的启动模式,这里肯定重复出现多个 C 页面的,
所以设置一个 SingleTop 应该是可以解决问题的。当然,如果设置了该模式,你需要去处理 onNewIntent() 的方法了。

4. 参数解析

比如说我的url:test://test1/user?userId=0001
@Nullable
public static Intent parseIntent(Context context, String url, String title) {
     if (!isKnownSchemes(url)) {
         return null;
     }
     try {
        //Uri uri = getIntent().getData();
        Uri data = Uri.parse(url);
        //获取协议名称
        String scheme = data.getScheme();
        //获取主机名称
        String host = data.getHost();
        //获取路径
        String path = data.getPath();
        //获取参数为userId的值
        String userId = data.getQueryParameter("userId");
        //获取所有参数的集合
        Set<String> parameterNames = data.getQueryParameterNames();
         HashMap<String, String> map = null;
         if (!parameterNames.isEmpty()) {
             map = new HashMap<>();
             for (String name : parameterNames) {
                 map.put(name, data.getQueryParameter(name));
             }
         }
         Log.e(TAG, "host: " + host);  //test
         Log.e(TAG, "path: " + path);  //user
         Log.e(TAG, "scheme: " + scheme); //test
    Log.e(TAG, "userId: " + userId); //0001
         if (map != null) {
             Log.e(TAG, "query: " + map.toString());
         }
         return parseSchemes(context, host, path, map);
      } catch (Exception e) {
         e.printStackTrace();
         return null;
      }
  }

5. 具体跳转逻辑代码

@Nullable
private static Intent parseSchemes(Context context, String host, String path, Map<String, String> queryies) {
     Log.e(TAG, "parse: host:" + host);
     Log.e(TAG, "parse: path:" + path);
     Log.e(TAG, "parse: queryies:" + queryies);
     Intent intent;
     switch (host) {
         case "test1":
             intent = new Intent(context, TestActivity1.class);
             try {
                 intent.putExtra(Constants.TITLE, queryies.get("title"));
             } catch (Exception e) {
                 e.printStackTrace();
             }
             return intent;
         case "test2":
             intent = new Intent(context, TestActivity2.class);
             try {
                 intent.putExtra(Constants.TITLE, queryies.get("title"));
             } catch (Exception e) {
                 e.printStackTrace();
             }
             return intent;
         default:
     }
     intent = new Intent(context, MainActivity.class);
     return intent;
 }

解析跳转的IntentSkipActivity中的跳转代码参考上面

6. 总结

scheme方式跳转适用一个APP的一个页面跳转目标APP的目标页面, 

WebView的一个链接地址跳转目标APP的目标页面,

适用浏览器的链接地址跳转目标APP的目标页面

Notification 的方式也是一样的

又get一个新的跳页面的方式startActivities() 

7. 联系方式

qq: 1509815887
email:[email protected]

8.下载地址

点击去下载

猜你喜欢

转载自blog.csdn.net/rjgcszlc/article/details/79593999