Android如何充分利用scheme

现在的互联网App经常遇到以下几种需求:

  1. 推送收到不同的推送,点击推送会往不同页面跳且带有不同参数,当然还有推送是无通知的方式,即:收到推送做特定的事情;
  2. H5项目之间跳转:因为项目里容纳里多个离线H5的资源包,并以加载网页资源的方式加载模块,这类项目之间本来是完全没有关联的,需要建立桥梁帮他们互相唤起,当然也少不了各种参数;
  3. 外部浏览器跳转至App的固定页面:很多时候公司产品会嵌入广告在其他App里做推广,要求点击那些广告要求带指定参数跳转App指定页面;
  4. 扫码方式跳转至App的固定页面:很多地下推广会以二维码方式呈现在海报上,如果用App扫码后跳指定App页面且带参数;
  5. 短信内容有链接,点击链接带参数跳App某个页面;

  就以上这些场景是不可能每个场景都定义解析规则的,毕竟维护工作会太多,所以只要定义一套解析规则即可,scheme的解析是最特殊的,本着求同存异的思想,因此我们所有的工作以scheme开展开来。这里我给它起了一个名字:AppLink,scheme主要组成为:[scheme]://[host][:port]/[path]?[query],这非常类似http url的定义,我们举个例子:cbdbusbutler://Bus/OrderDetail?orderId=43423,显而易见这是带着订单号打开汽车票订单详情页的AppLink定义。
  关于用scheme用来做跳转的文章其实网上已经有很多,原理大家都很懂,但是我们需要的是真实运用,所以有必要对它进行设计,让业务接入更加容易,因此我写了一个Library,名叫AppLink,我的设计思想是将每种跳转都以一个类来描述,简单说每种跳转都单独定义一个appLink类,并且按模块划分,并且appLink的path就是对应appLink类的package,即: appLink所在packageName + moduleName + className = appLink的classpath。总之,是通过路径反射找到对应的appLink并解析的,其实思路很easy,如何组织代码才是关键。

下面来看看我们的Library如何接入的:

1. 定义schema:


    
        
        
        
        
    

schema本是用于在AndroidManifest.xml里定义的,但是苦于暂时没法从代码层面获取AndroidManifest.xml里配置的scheme,因此需要代码中也要初始化。

public class MyApplication extends Application {
    private static final String APP_LINK_PACKAGE = "com.feizhang.applink.sample.applink";
    private static final String SCHEME = "my-scheme";

    /**
     * account id is usually dynamic, it maybe a phone number or a uuid generated by server according to your projects。
     */
    public static String accountId = "account_123";

    @Override
    public void onCreate() {
        super.onCreate();
        AppLinkUtils.setup(APP_LINK_PACKAGE, R.drawable.nofication_small_ic, SCHEME);
        
        // 这是对于想通过推送透传来自己控制显示通知的情况而言的,对于直接采用推送sdk来显示推送通知的朋友们可以不用注册它
        // 关于PushNotificationReceiver的实现,其实利用了order broadcast的Priority特性,这里注册的Priority比较低,
        // 另外一个`PushContentReceiver(下面会讲)`的Priority比较高,意味着`PushContentReceiver`没有收到message就会被
        // `PushNotificationReceiver`收到了,然后它负责弹Notification;
        PushNotificationReceiver.register(this, new PushNotificationReceiver() {

            @Override
            public String getAccount(@NonNull Context context) {
                return accountId;
            }

            @Override
            public int getSmallIcon(@NonNull Context context) {
                return R.drawable.nofication_small_ic;
            }
        });
    }
}

因为暂时无法从代码层面获取AndroidManifest.xml里的scheme定义,所以在代码层面也要定义下scheme(切记要一样),

2. 如何非常方便的定义解析规则:

创建package,为每个AppLink创建单独的类,并通过反射获取此类解析并触发跳转。


Android如何充分利用scheme_第1张图片
appLink.png

由上可见,每个package对应每个module,每个package下放有很多class,这些class是按功能命名的,比如:AppHome(打开app)、OrderDetail(打开订单详情页)、NewMsgAlert(通知有新消息)

下面我们以打开订单详情页的AppLink为例:

public class OrderDetail extends AppLink {

    /**
     * 构建收到appLink打开页面的Intent
     */
    @Override
    public Intent onStartActivity(@NonNull Context context) {
        String orderId = params.get("orderId");
        Intent intent = new Intent(context, OrderDetailActivity.class);
        intent.putExtra("orderId", orderId);
        return intent;

        // 如果参数复杂,也可以取json转对象再取值
        XXXObject object = new Gson().fromJson("json", XXXObject.class);
        Intent intent = new Intent(context, OrderDetailActivity.class);
        intent.putExtra("orderId", object.orderId);
        return intent;
    }

    /**
     * 和{@link #onStartActivity(Context)}一并会执行,当收到appLink时候可以做一些额外的工作
     */
    @Override
    public void onExecute(@NonNull Context context) {
        super.onExecute(context);
        Toast.makeText(context, "您的订单有更新", Toast.LENGTH_SHORT).show();
    }

    /**
     * 是否特定账户的信息,对于特定账户的订单推送是需要账户隔离的
     */
    @Override
    public boolean isPrivate() {
        return true;
    }

    /**
     * 是否需要用DB存储,以DB存储是为了下次启动查询到未读状态
     */
    @Override
    public boolean shouldSave() {
        return false;
    }
}

3. AppLink的目标页面(Activity或Fragment)已经打开如何接收appLink?

这个功能场景我们非常熟悉,类似滴滴打车等待附近车辆接单等待推送,或者打开微信好友的聊天页面收到对方发来消息。

public class OrderDetailActivity extends AppCompatActivity {

    private PushContentReceiver mReceiver = new PushContentReceiver() {
        
        /**
         * 告诉receiver当前可以接收处理哪些appLink,
         * 在微信聊天场景就好比可以接收文字消息、表情消息、定位消息等
         */
        @Override
        public List getAppLinks() {
            return Collections.singletonList("my-scheme://product/OrderDetail");
        }

        @Override
        public String getAccount(@NonNull Context context) {
            // 账户ID按具体项目自己方式获取
            return MyApplication.accountId;
        }

        /**
         * 一般这里我们会再次调用订单接口并刷新当前页面
         * @return true: 终止消息传递,即其他receiver收不到此message
         */
        @SuppressLint("SetTextI18n")
        @Override
        public boolean onReceive(@NonNull Context context, @NonNull AppLink appLink) {
            Toast.makeText(context, "订单已刷新", Toast.LENGTH_SHORT).show();
            return true;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_order_detail);
        
        // 注册页页面级appLink广播接收器(内部onDestroy()会自动解除注册)
        PushContentReceiver.register(this, mReceiver, true);
    }
}

3. 让h5模块之间可以相互跳转:

public class DefaultWebClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView paramWebView, @NonNull final String url) {
        if (url.startsWith(MyApplication.SCHEME)) {
            AppLinkUtils.redirect(paramWebView.getContext, url);
            return true;
        }

        // other case
        return true;
    }
}

拦截跳转url,如果发现是自定义appLink则以AppLinkUtils发起跳转。

4. 推送服务使用appLink解析:

@Override
public void onNotifactionClickedResult(Context context, XGPushClickedResult xgPushClickedResult) {
    String appLink = xgPushClickedResult.getCustomContent();
    AppLinkUtils.redirect(context, pushMsg.appLink);
}

以上以信鸽推送作为案例,其实对其他推送sdk都一样类似,仅仅取出appLink并redirect()而已。

  至此,已经介绍完AppLink的使用方式,关于实现代码可以参考这里, 下一篇文章我在介绍下项目中各种红点提醒是如何实现的,现在想说的是它依赖appLink。

你可能感兴趣的:(Android如何充分利用scheme)