标签: Android应用开发

Android应用开发 WebView与服务器端的Js交互

Android应用开发 WebView与服务器端的Js交互

*近公司再添加功能的时候,有一部分功能是用的html,在一个浏览器或webview中展示出html即可。当然在这里我们当然用webview控件喽

 

WebApp的好处:

在应用里嵌套web的好处有这么几点,1,跨平台,不仅可以在Android上运行,也可以在iOS上运行,而且样式什么的*对统一,因为都是加载的html,用的都是同一套html

2,修改灵活,容易更新版本。例如大家常看到的app里面的广告页,大多是嵌套的html,这样只要后台替换一下页面的内容,手机端就会改变展现内容,跟新版本也是如此,因为界面什么得成了在服务器端,所以要是想跟新界面什么得,只需要在后台修改在发布即可,不需要用户再重新下载app。这个好处我觉得对ios是有很大帮助的,哈哈,绕开苹果审核嘛,由于html我们可以随意替换,审核时可以把违规的部分隐藏,上线之后就可以随意改了,哈哈,你们懂得。

 

当然,开发webapp当然也有局限,就是网速什么的,这个咱无法改变,这里也不废话。不过在开发中呢,如果只是页面之间的交互的话,我们只需提供一个webview控件即可,

可是要是涉及到和手机设备或软件交互的话(如打开相册,摄像头等等),这就需要我们和页面经行js交互,js交互可以说是双向的,一种是,我们调用页面的,就是调用服务端的js方法,另一个呢则是服务端调用我们Android里面的代码,调用其实很简单,下面说一下怎样调用。

当然我们先等有一个WebView,先创建一个Activity,然后设置布局,穿件WebView,布局和Activity如下:

activity_webview.xml



[html] view plain copy print?在CODE上查看代码片派生到我的代码片
01.<?xml version="1.0" encoding="utf-8"?>  
02.<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
03.    android:layout_width="match_parent"  
04.    android:layout_height="match_parent"  
05.    android:orientation="vertical" >  
06.    <WebView   
07.        android:id="@+id/webview"  
08.        android:layout_width="match_parent"  
09.        android:layout_height="match_parent"  
10.        />  
11.  
12.</LinearLayout>  




然后是activity,

WebViewActivity.activity



[java] view plain copy print?在CODE上查看代码片派生到我的代码片
01.import android.app.Activity;  
02.import android.os.Bundle;  
03.import android.webkit.WebSettings;  
04.import android.webkit.WebView;  
05.  
06.public class WebViewActivity extends Activity {  
07.    private static final String url = "http://192.168.30.199:8080/song/test.html";  
08.    private WebView mWebView;  
09.    @Override  
10.    protected void onCreate(Bundle savedInstanceState) {  
11.        super.onCreate(savedInstanceState);  
12.        setContentView(R.layout.activity_webviw);  
13.        initView();  
14.    }  
15.  
16.    private void initView() {  
17.        mWebView = (WebView) findViewById(R.id.webview);  
18.        //或的WebView的Setting  
19.        WebSettings settings = mWebView.getSettings();  
20.        //设置支持js,看方法名字就知道啥意思  
21.        settings.setJavaScriptEnabled(true);  
22.        //加载网页路径  
23.        mWebView.loadUrl(url);  
24.    }  
25.}  




上面就是一个简单的webview,然后很常规的设置属性,然后再加载要加载的页面路径,这样一般就可在网页里面自由点击跳转了,但是要和手机交互的话需要我们写js交互的代码了。

首先说怎样调用服务器端的js方法,很简单,和加载网页路径基本上一样如下:



[java] view plain copy print?在CODE上查看代码片派生到我的代码片
01.mWebView.loadUrl("javascript:forSmallPhoto()");  




就这么简单的一句话你就可以调用服务器端的js方法了,其中JavaScript:是固定写法forSmallPhoto()则是服务端的js方法名字,这是一个无参数的方法,当然也可以传参,这需要我们拼凑字符串,mWebView.loadUrl("javascript:forSmallPhoto('" + data + "')");其中data就是一个变量,也就是你要传的参数值,当然也可以支持多参数传送,这得看你服务器端的js方法有几个参数了,其实就是我们调用一个方法一样,只不过这个方法是在服务器端的。我们调用服务器js,是为了,当Android完成某些功能后,需要告诉服务器,则我们可以调用js来告诉他我们完成了。

 

在一种就是,服务端调用我们的Android代码了,这里Android中也是封装好了接口

我们可以通过void android.webkit.WebView.addJavascriptInterface(Object object, String name)的方法来实现服务器端调用我们的代码,其中这个方法有两个参数,一个是object,另一个是String类型的; 只要webview调用了这个方法就可以调用我们的代码了。而要调用的代码我们写在Object里面,首先我们就先实现这个Object,我们创建一个类,JavaScriptInterface。Android中APi Guides中提供的Demo中取得累的名字是JavaScriptInterface,那我们也用这个名字把。然后实现它,然后随便在里面写一个方法,如下面

JavaScriptInterface.Java类



[java] view plain copy print?在CODE上查看代码片派生到我的代码片
01.import java.util.HashMap;  
02.import java.util.Map;  
03.  
04.import android.content.Context;  
05.import android.os.Handler;  
06.import android.os.Message;  
07.import android.text.TextUtils;  
08.import android.util.Log;  
09.import android.webkit.JavascriptInterface;  
10.import android.widget.Toast;  
11.  
12./** 
13. *  
14. * Title: JavaScriptInterface.java Description: 
15. *  
16. * @author Liusong 
17. * @version V1.0 
18. */  
19.public class JavaScriptInterface {    
20.          
21.    /** Instantiate the interface and set the context */  
22.    public JavaScriptInterface() {  
23.    }  
24.  
25.    /** Show a toast from the web page */  
26.    @JavascriptInterface  
27.    public void showToast() {  
28.        Log.i("TAG", "调用成功==================》》》》》");  
29.  
30.    }  
31.  
32.}  





这样就完成了一个简单的JavaScriptInterface类,这个类的方法是自己随便写的,其中,先说一下这里要注意的几点,首先重要的@JavascriptInterface这个注解,你会发现去掉也不会报错,但是这个是很早重要的,如果你想让服务器端调用你的方法,你就要加上这个注解@JavascriptInterface。在4.4api中说道,一定要加这个注解,负责调用不会成功,其实我在开发中,用红米1s,4.3的系统,就没法调用成功了,当时还纳闷,因为当时手里的文档是4.2的,很是郁闷。所以在这里强掉,一定要在自己写的方法前面加上@JavascriptInterface。

还有一个注意的是方法的参数,这里是一个无参方法,当然这里你也可以写一个有参方法,这里先提一下,待会会配合html里面的js说道,我们先说void android.webkit.WebView.addJavascriptInterface(Object object, String name)这个方法里面的第二个参数,第二个参数你可以理解为是标识符,就是服务器端调用你方法时,需要找到你,怎么找到?就是通过这个标识符,标识符是自己随便定的,但是,你要告诉后台开发人员你的标识符是什么,我们这里把这第二个参数设置为“Android”。下面我给出我测试的html代码结合着看你就明白了。

test.html



[html] view plain copy print?在CODE上查看代码片派生到我的代码片
01.<!doctype html>  
02.<html>  
03.<head>  
04.    <meta name="viewport" content="width=device-width, initial-scale=1" charset="GBK">  
05.    <title>测试</title>  
06.</head>  
07.  
08.<body>  
09.  
10.<div data-role="page" >  
11.      
12.<script type="text/javascript">  
13.     function callAndroidAction(action) {  
14.        Android.showToast();        alert("我敢保证,你现在用的是演示一");  
15.    }  
16.     function forSmallPhoto(action) {  
17.        alert("我敢保证,你现在用的是演示一"+action);  
18.    }  
19.</script>  
20.  
21.    <div data-role="header">  
22.        <h1>调用图库</h1>  
23.        <!--   <a href="#" class="ui-btn">返回</a>-->  
24.    </div>  
25.  
26.    <div data-role="main" class="ui-content">  
27.  
28.        <div style="width: 98%;margin: 0 auto; text-align: center">  
29.            <a href="#" class=" ui-btn ui-btn-inline" onclick="callAndroidAction(0)">调用图库  </a>  
30.              
31.            <a href="#" class=" ui-btn ui-btn-inline" onclick="callAndroidAction('2')"> 充值  </a>  
32.          
33.        </div>  
34.  
35.  
36.    </div>  
37.  
38.     
39.</div>  
40.  
41.</body>  
42.  
43.  
44.</html>  





这个代码有点乱,就将就这看吧,我是把这个页面放在自己的tomcat上的,其中这个html中大家发现
 



[html] view plain copy print?在CODE上查看代码片派生到我的代码片
01.<script type="text/javascript">  
02.    function callAndroidAction(action) {  
03.        Android.showToast();  
04.        alert("我敢保证,你现在用的是演示一");  
05.    }  
06.   function forSmallPhoto(action) {  
07.        alert("我敢保证,你现在用的是演示一"+action);  
08.    }  
09.</script>  





  

这个js方法没,看见里面的Android标识符没,没错,后台就是这么调用我们代码的,Android.showToast();就是这么调用的,就是这么简单,不要想的太难,我们就需按我上面说的那样,把Object实现,把标识符写好就ok了。饭后,后台就会通过标识符和你Object的方法名字调用你的方法。这里要说一下,你Object(即JavaScriptInterface,我们上面已经实现)里的方法的参数要和后台调用你的方法的参数个数和类型一直,就像我们平时调用方法是一样的。这一点知道了就好了。

这样就可以了。

所以WebViewActivity里面加上这一句就可以了。



[java] view plain copy print?在CODE上查看代码片派生到我的代码片
01.view.addJavascriptInterface(new JavaScriptInterface(),"Android");  




 

这样js就说完了。

模版

当后台要调用我们的代码,我们就写一个方法,如果调用多次我们就写多个,这样太麻烦,所以我们来写一个通用的方法,就是无论后台调用你代码干不同的事,都调用你这个方法,那怎么区分不同的执行动作呢?用传的参数,我们在JavaScriptInterface里面写一个方法,这里就叫callAndroidAction,我设计的是给这个方法三个参数,

public void callAndroidAction(String action, String url,String json),*个参数action,即用来表示要执行的动作,第二个则是url,不管是服务其给的下载路径还是,访问其他页面的路径,在一个json就是其他一些参数,由于传的参数不固定,我们就用一个参数,一个参数时就传过来,多个参数时可以通过json字符串传过来,就没必要麻烦一个一个的写参数了。

然后我们在设计一个回调,让操作的代码拿出去,怎大体就是这样了



[java] view plain copy print?在CODE上查看代码片派生到我的代码片
01.import java.util.HashMap;  
02.import java.util.Map;  
03.  
04.import android.content.Context;  
05.import android.os.Handler;  
06.import android.os.Message;  
07.import android.text.TextUtils;  
08.import android.util.Log;  
09.import android.webkit.JavascriptInterface;  
10.import android.widget.Toast;  
11.  
12./** 
13. *  
14. * Title: JavaScriptInterface.java Description: 
15. *  
16. * @author Liusong 
17. * @version V1.0 
18. */  
19.public class JavaScriptInterface {  
20.      
21.    private Handler mHandler;  
22.      
23.    /** Instantiate the interface and set the context */  
24.    public JavaScriptInterface(Handler handler) {  
25.        mHandler = handler;  
26.    }  
27.  
28.    /** Show a toast from the web page */  
29.    @JavascriptInterface  
30.    public void showToast(final String toast) {  
31.        Log.i("TAG", "调用成功==================》》》》》");  
32.    }  
33.  
34.    @JavascriptInterface  
35.    public void callAndroidAction(String action, String url,String json) {  
36.        Map<String, String> params = new HashMap<String, String>();  
37.        if(!TextUtils.isEmpty(url)){  
38.            params.put("url", url);  
39.        }  
40.        if(!TextUtils.isEmpty(json)){  
41.            params.put("json", json);  
42.        }  
43.        Message msg = Message.obtain();  
44.        msg.what = Integer.valueOf(action);  
45.        msg.obj = params;  
46.        mHandler.sendMessage(msg);  
47.    }  
48.}  





这样我们就从服务其拿到的参数都给Handler了,则WebViewActivity里面就要这样写了



[java] view plain copy print?在CODE上查看代码片派生到我的代码片
01.import java.util.HashMap;  
02.import java.util.Map;  
03.  
04.import android.app.Activity;  
05.import android.os.Bundle;  
06.import android.os.Handler;  
07.import android.os.Message;  
08.import android.webkit.WebSettings;  
09.import android.webkit.WebView;  
10.  
11.public class WebViewActivity extends Activity {  
12.    private static final String url = "http://192.168.30.199:8080/song/test.html";  
13.      
14.    //执行动作  
15.    public static final int SELECT_IMAGE = 0;// 打开图库  
16.    public static final int OPEN_PAGE = 1;// 跳转其他特定页面  
17.    public static final int CLOSE_OR_BACK = 2;// 关闭或  
18.      
19.    private WebView mWebView;  
20.    @Override  
21.    protected void onCreate(Bundle savedInstanceState) {  
22.        super.onCreate(savedInstanceState);  
23.        setContentView(R.layout.activity_webviw);  
24.        initView();  
25.    }  
26.   <p>  
27.     private void getIntentDatas() {  
28.          // TODO Auto-generated method stub  
29.        url = getIntent().getStringExtra("url");  
30.   }</p><p> </p>    private void initView() {  
31.        mWebView = (WebView) findViewById(R.id.webview);  
32.        //或的WebView的Setting  
33.        WebSettings settings = mWebView.getSettings();  
34.        //设置支持js,看方法名字就知道啥意思  
35.        settings.setJavaScriptEnabled(true);  
36.        mWebView.addJavascriptInterface(new JavaScriptInterface(handler), "Android");  
37.        //加载网页路径  
38.        mWebView.loadUrl(url);  
39.    }  
40.      
41.    private Handler handler = new Handler(){  
42.        public void handleMessage(Message msg) {  
43.            switch (msg.what) {  
44.            case SELECT_IMAGE://执行打开图库,  
45.                  
46.                //如果有参数,取服务端传过来的参数(url,json)  
47.                Map<String, String> params = (HashMap<String, String>)msg.obj;  
48.                break;  
49.                  
50.            //其他功能,可随着自己功能的增加,在这里增加,只需和后台商量好动作的action值即可  
51.            default:  
52.                break;  
53.            }  
54.        };  
55.    };  
56.}  




*后再让WebViewActivity通用,就是通过传url参数



[java] view plain copy print?在CODE上查看代码片派生到我的代码片
01.private void getIntentDatas() {  
02. // TODO Auto-generated method stub  
03. url = getIntent().getStringExtra("url");  
04.}
复制代码
复制代码

哪些问题能试出一个Android应用开发者真正的水平?

一般面试时间短则30分钟,多则1个小时,这么点时间要全面考察一个人难度很大,需要一些技巧,这里我不局限于回答题主的问题,而是分享一下我个人关于如何做好Android技术面试的一些经验:

面试前的准备

简历调查

简历到你手上的时候,你要做好充分的调查分析,不仅仅是对公司负责,也是对自己与候选人时间的尊重,明显不match的简历,就不要抱着“要不喊过来试试看”的想法了,候选人也许很不错,但如果跟你的岗位不match, 也不要浪费大家时间,你要想清楚现在需要的人是有潜力可以培养的,还是亟需帮忙干活的。另外如果简历里附带了博客链接,GitHub地址,相关作品的,可以提前去看看,直接看人家多年积累的文章与代码,比这短短一小时的面试来得靠谱的多。

  1. 准备问题
    了解清楚候选人背景后,要根据简历,有针对性的准备问题,可以是他作品或做过项目里的某个技术细节的实现方式,也可以是他声称精通的某些领域的相关问题。总之不要等到面试过程中现想问题,特别是刚开始面试别人的同学,往往经验不足稍带紧张导致大脑短路,其实也是很尴尬的,把要问的问题提前写下来,准备充分。

考察哪些点?
1. 简历是否真实
这其实是面试*要务,面试的过程其实就是看简历是否属实的过程,因为能到面试环节,说明这个人是符合要求的,不满足要求的早就被剔除了,如果他真的如简历描述的那样,100%会招过来,如果人人都如此,那就不需要有面试这种过程了。

需要注意的是这里的真实有三层含义:

一是他如实描述了自身经历,很多人只在一些大项目里做一个很小的螺丝钉,但简历里往往夸张这段经历。

二是不知道自己不知道,常见于简历里各种“精通”开头的描述,因为知识体系与视野的局限,明明只是了解很浅却夸口精通,很多时候他并不认为自己说的有问题,而是真的以为自己已然精通,有点井底之蛙的感觉。

三是简历里的真实要与你的期望相匹配,一门技术了解到怎样的程度才算精通,很难有定论,所以这里的“真实”只能是候选人与面试官标准之间的契合,这种有主观运气成分,也许面试官水平不够错误判断了你,也不用感到不爽,面试何尝不是种双向选择呢。

  1. 技术的深度
    技术的深度一向是我*看重的部分,当今任何一个技术领域都非常宽广,一个人要同时掌握那么多知识并且都深入几乎不可能,那都需要拼学习效率与工作年限了。而你曾经做过的东西,正在做的东西,是*对可以了解得更深入的,一个对技术有好奇心,有技术热情的人,都不会仅仅停留在这个东西挺好用,而是会忍不住去探究它背后的技术原理,即便不是亲自去看源码,也会花点时间了解别人整理过的经验,所以单凭考察技术上的深度,就可以考察一个人是否对技术有热情,是否有技术好奇心等等这些很多大牛认为的所谓“优秀程序员的特征”。

    之前曾看到过一句话:“一个人对他所做的事情了解得越深,他就能做的越好”。放在这里再合适不过了。

  2. 技术的广度
    深度是有了,还需要广度吗?我个人的理解是:深度是必要条件,广度是加分项。同样的有技术好奇心的优秀程序员,也不会满足于仅仅局限于自己的一亩三分地,工作之余,也会想要尝试一些其它的领域和方向,因为投入问题也许不够深入,但很多领域知识你知道与不知道,对你个人知识体系的形成关系很大。比如你要实现一个功能,在你当前熟悉的技术领域上很困难或者效果不佳,在你就要放弃时你的同事告诉你,这用一个简单sql语句就可以实现啦,为什么要搞得那么麻烦?这个例子虽然举得很蹩脚,但是我想意思大家应该已经明白了。知识越有广度,头脑里的技术体系就越完备,同样的问题,你就可以想到N个解,思考一下就得出*优解了,如果你听都没听过一些东西,就会经常说出“这个好难搞啊”,“这根本就不可能”,其实有的时候真是知识的局限问题,所谓的从0到1难,也是这个意思。
  3. 逻辑思维能力
    这也是我比较看重的一点,这里并不是指那些臭名昭彰的脑经急转弯问题,而是通过交流观察,判断一个人表达观点逻辑是否清晰,回答问题是否有章法,这个很难描述,但如果你细心观察,你会发现很容易通过一些简单的交流,就可以看出一个人是否逻辑清晰。有时候你会觉得某个人表达沟通很不错,其实不是沟通的问题,是他说出去的话,经过了他大脑的条理清晰的整理,让你很容易就能明白。这种习惯不是一朝一夕就能养成的,所以面试过程中这点装不出来。

    另外一个人如果逻辑清晰,而且反应又敏捷,语速很快,那是大大的加分项,恭喜你,碰到一个聪明人了。

具体问哪些问题?
前面提到的是要重点考察的点,那么具体的Android开发,有没有一些通用的问题可以问的呢?我个人一般会从这几个角度考察候选人:

  1. Android经验
    如果不是校招,Android经验是必须的,我比较喜欢问一些基础概念与技术原理,比如Activity、View、Window的理解,各LaunchMode的使用场景,View的绘制流程,Touch事件机制,Android动画的原理,Handler, Looper的理解,Android跨进程通讯的方式,Binder的理解,Android Mashup设计的理解等等。
  2. Java水平
    基本上就是Effective Java那本书里提到的东西,如果你背完那本书里的问题,并且对答如流,没问题,就要你这样的。其实也会考察关于final用法,反射原理,注解原理,java编译过程,GC等一些常见问题。
  3. IT基础知识
    其实就是计算机科班学生学校里学到的一些东西,在校招时这块是重点,社招会放宽,但一些基本的常识是要有的,比如不少人都不知道http的get post有啥区别,https的那个s是什么意思,讲不清进程与线程的概念,不知道二分算法是个啥东西。这些简单问题的筛选,可以过滤一些所谓野路子的程序员,是不是科班出身不重要,搞这行就得对一些基本常识有概念,不然以后怎么愉快的交流呢?
  4. 代码质量的认识
    我们需要的是一个对代码味道有感觉的人,关于这点,看下《Clean Code》就够了,面试中这点其实不好考察,可以让他聊一聊对代码质量的认识,虽然不能排除对方夸夸其谈,至少想法不多,只能提到命名风格这一点的人是不符合要求的,也可以在写Code的环节中观察。
  5. 技术视野
    比如对Android开发新技术的了解与学习,对其它流行技术领域的了解,这其实与我刚才提到的技术广度的考察有关,就我面试过程中,发现很多非互联网行业的从业人员,因为公司各种操蛋规定与公司技术氛围的原因,技术视野相当狭窄。

    我个人对这点深有体会,2011年我还在传统行业从事软件研发,当时的公司因为担心技术信息泄露,不让上网,相当封闭,我个人虽然自认为已在那个行业内做到业内专家的级别,但总感觉哪里不对,有一天我很兴奋的打算跟身边同事聊一聊Android的时候,发现他们居然都不知Android为何物?2011年啊同志们,当时的震惊无法言表,深切感觉到需要作出改变了,毅然放弃多年行业积累,转战移动互联网,直到现在。时至今日,多年前的小伙伴也有很多混出了名党,开始走向人生巅峰,我也从来没有后悔当初做出的选择。

  6. 技术想象力
    一个优秀的技术人,如果知识的深度与广度足够,知识已成体系,那么他对于一些从未接触过的领域,也是可以做出足够合理的想象与判断,面试过程中如果问到一些领域候选人没有涉猎,这时候一般不用过多纠缠,但如果你想借这个问题考察下他的技术想象力,可以深入下去,比如问他:“你觉得这个东西应该是什么原理呢?”,“这个酷炫的控件,如果要你来做,你会怎么实现?”。在这方面表现出色的同学无疑是有深厚基础与足够广度的人。
  7. 技术习惯
    好的程序员都会有好的习惯,比如各种快捷键的熟练应用,各种命令行的掌握,一些提高开发效率的工具与习惯,碰到问题是baidu还是google,有没有做一些小工具帮助减少重复工作,工作之余有没有继续学习?有没有看什么不错的书等等,这些小细节很大程度上决定了程序员的开发效率,这也是为什么很多人说一个优秀程序员抵得上100个普通程序员,这也是重要原因之一。

面试后的反馈:
面试一般不止一轮,你需要给出你的反馈,多轮面试结果一起考量,减少误判的风险,反馈一般怎么写呢?以下是我的建议:

  1. 面试纪录
    面试过程中的完整纪录,尽量客观评价,让其它面试官知道你问了哪些问题,回答的怎么样,也避免了重复问题的尴尬。
  2. 优点与缺点
    你的主观评价,亮点有哪些,你觉得哪些地方不够好?
  3. 综合评价
    你对候选人的综合评价,hire或者no hire的根本原因,如果有些地方感觉没考察清楚,期望其它面试官继续加强考察,也可以写上。
  4. 怎样才给通过?
    通过标准因人而异,每个人都有自己心中的bar, 但还是有些可直观考量的因素的:

一是岗位的要求,不同的岗位标准当然不一样,校招与设招肯定也不一样。

二是岗位的紧急程度,兄弟们天天加班忙死了,赶紧找人过来帮忙吧哈哈。

三是候选人的年龄,大龄程序员莫怪,一把年纪了还跟刚毕业一两年的同事一个水平,说明成长太慢,做技术的潜力有限,这个大家应该能理解。

四是前面提到的做技术的深度,这个是必须的,广度也要有一些,视野不能太窄。

五是要有亮点,大家在面试的过程中要注意发掘亮点,有时候他问题很多但有一个足够的亮点也够了,用心观察也发现不了什么亮点的,就要注意了。

说了这么水平多,其实*重要的就是一句话,问问你自己:你真的原意跟那个家伙一起并肩战斗吗?

Android应用开发中使用deeplink

一、DeepLink的应用场景
DeepLink简单理解就是通过在手机上点击一个链接后能实现如下功能:

如果目标App没有启动,那么就拉起App,并跳转到App内指定页面
如果目标App已经启动,那么就把App拉到前台并跳转到App内指定页面
二、DeepLink的实现思路
在Android开发中,可以通过在清单文件中配置scheme来实现页面跳转,所以可以通过scheme匹配的方式来实现DeepLink的功能。配置方式大概分为三种:

为每一个要跳转的Activity都指定一个对应的匹配条件,如果页面太多的话,这种方式管理起来不太方便,而且没有办法对App全局配置信息、用户状态等进行统一处理
配置闪屏页面为匹配页面,闪屏页一般都是App冷启动时才会出现,而且打开首页后,闪屏页就会关闭,这种方式在App没有启动的情况下可以很好的处理对应的Intent信息,但是如果App已经启动过了,在去拉起闪屏页就不合理了
配置首页为匹配页面,首页在App中一般都是常驻的,一般情况下首页关闭就意味着App的退出,所以可以在首页中统一处理匹配scheme得到的Intent信息,然后进行统一的跳转分发(需要将首页Activity的启动模式设置为singleTask以防止首页创建多个页面)
三、DeepLink的实现案例
下面使用上述的方式3写个Demo进行验证,实现步骤如下:

提前定义好自己的scheme、host等信息配置到清单文件里面,scheme是必须要有的,像host等信息可以配置也可以没有,我这里配置了scheme和host两个条件,其中sheme是“link”,host是“cn.znh.deeplinkdemo”,清单文件配置如下:
<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”cn.znh.deeplinkdemo”>

<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:roundIcon=”@mipmap/ic_launcher_round”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>

<!–闪屏页设置为启动页–>
<activity android:name=”.SplashActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>

<!–配置首页–>
<activity
android:name=”.MainActivity”
android:launchMode=”singleTask”>
<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=”cn.znh.deeplinkdemo”
android:scheme=”link” />
</intent-filter>
</activity>

<!–普通的Activity–>
<activity android:name=”.Link1Activity” />
<activity android:name=”.Link2Activity” />
<activity android:name=”.Link3Activity” />
</application>

</manifest>

在首页Activity的onCreate方法和onNewIntent方法里面,接收Intent参数进行相应的跳转处理,首页代码如下:
package cn.znh.deeplinkdemo;

import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

/**
* Created by znh on 2018/12/23.
* <p>
* 首页
*/
public class MainActivity extends AppCompatActivity {

//定义的scheme
private static final String SCHEME_VALUE = “link”;

//定义的host
private static final String HOST_VAULE = “cn.znh.deeplinkdemo”;

/**
* 首次启动MainActivity调用该方法
*
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
schemeIntent(getIntent());
}

/**
* 启动已经存在的MainActivity调用该方法(singleTask启动模式)
*
* @param intent
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
schemeIntent(intent);
}

/**
* 处理intent事件
*
* @param intent
*/
private void schemeIntent(Intent intent) {
if (intent == null || intent.getData() == null) {
return;
}

//获取Uri
Uri uri = intent.getData();

//打印出uri里取出的Scheme和Host
Log.e(“schemeIntent”, “getScheme:” + uri.getScheme());
Log.e(“schemeIntent”, “getHost:” + uri.getHost());

//判断取出的Scheme和Host是否和自己配置的一样,如果一样进行相应的处理,否则不处理
if (!SCHEME_VALUE.equals(uri.getScheme()) || !HOST_VAULE.equals(uri.getHost())) {
return;
}

//如果Scheme和Host匹配成功,取出uri中的参数并进行相应的业务处理
String type = uri.getQueryParameter(“type”);
String id = uri.getQueryParameter(“id”);

//打印uri里取出的参数
Log.e(“schemeIntent”, “type:” + type);
Log.e(“schemeIntent”, “id:” + id);

//进行统一的跳转分发
if (“1”.equals(type)) {
Intent intent1 = new Intent(this, Link1Activity.class);
startActivity(intent1);
} else if (“2”.equals(type)) {
Intent intent2 = new Intent(this, Link2Activity.class);
startActivity(intent2);
} else if (“3”.equals(type)) {
Intent intent3 = new Intent(this, Link3Activity.class);
startActivity(intent3);
}
}
}

上述两个步骤就可以实现deeplink的效果了,可以在AndroidStudio里输入如下命令进行测试:

adb shell am start -a android.intent.action.VIEW -d “link://cn.znh.deeplinkdemo?’type=2&id=335′”
1
观察结果发现页面跳转到Link2Activity了,log打印结果如下:

E/schemeIntent: getScheme:link
E/schemeIntent: getHost:cn.znh.deeplinkdemo
E/schemeIntent: type:2
E/schemeIntent: id:335

四、闪屏页的问题处理
经过上面两个步骤,确实已经可以实现deeplink的功能了,但是还有个问题,那就是如果App还没有启动的情况下,由于直接拉起的是首页页面,并没有经过闪屏页(如果App已经启动过了,不需要走闪屏页,直接走首页然后进行对应页面跳转是没有问题的),那么怎么解决这个问题呢,这里想到的一个解决方案是记录一个是否是经闪屏页启动的一个标志位,如果是就正常处理,如果不是就重新开启闪屏页。具体实现如下:

在闪屏页面跳转到首页时,在Intent中传递过去一个标志位参数isSplashLanuch,以标识闪屏页已经启动过了
//跳转到首页
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
intent.putExtra(“isSplashLanuch”, true);
startActivity(intent);
finish();

在首页页面中获取isSplashLanuch的值来判断闪屏页面是否已经启动过(需要注意,在onNewIntent方法中要将isSplashLanuch的值永远设置为true),如果为true不进行特殊处理,如果为false就关闭首页并开启闪屏页,还要传递给闪屏页uri数据,方便跳转场景还原,首页处理Intent的方法如下:
/**
* 处理intent事件
*
* @param intent
*/
private void schemeIntent(Intent intent) {
if (intent == null || intent.getData() == null) {
return;
}

//获取Uri
Uri uri = intent.getData();

//打印出uri里取出的Scheme和Host
Log.e(“schemeIntent”, “getScheme:” + uri.getScheme());
Log.e(“schemeIntent”, “getHost:” + uri.getHost());

//判断取出的Scheme和Host是否和自己配置的一样,如果一样进行相应的处理,否则不处理
if (!SCHEME_VALUE.equals(uri.getScheme()) || !HOST_VAULE.equals(uri.getHost())) {
return;
}

//如果闪屏页启动过了,就不处理,否则关闭首页打开闪屏页
if (!isSplashLanuch) {
Intent intentSplash = new Intent(this, SplashActivity.class);
intentSplash.setData(uri);//设置uri数据,方便场景还原
startActivity(intentSplash);
finish();
return;
}

//如果Scheme和Host匹配成功,取出uri中的参数并进行相应的业务处理
String type = uri.getQueryParameter(“type”);
String id = uri.getQueryParameter(“id”);

//打印uri里取出的参数
Log.e(“schemeIntent”, “type:” + type);
Log.e(“schemeIntent”, “id:” + id);

//进行统一的跳转分发
if (“1”.equals(type)) {
Intent intent1 = new Intent(this, Link1Activity.class);
startActivity(intent1);
} else if (“2”.equals(type)) {
Intent intent2 = new Intent(this, Link2Activity.class);
startActivity(intent2);
} else if (“3”.equals(type)) {
Intent intent3 = new Intent(this, Link3Activity.class);
startActivity(intent3);
}
}

在闪屏页执行结束跳转到首页时,将uri数据带到首页进行场景还原,跳转到首页的代码如下:
//跳转到首页,并重新设置拿到的uri数据
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
if (getIntent() != null && getIntent().getData() != null) {
intent.setData(getIntent().getData());
}
intent.putExtra(“isSplashLanuch”, true);
startActivity(intent);
finish();

在次输入命令进行测试,观察结果,冷启动时可以走闪屏页面了,跳转逻辑页正常。

五、闪屏页和首页的完整代码
闪屏页完整代码:
package cn.znh.deeplinkdemo;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

/**
* Created by znh on 2018/12/23.
* <p>
* 闪屏页
*/
public class SplashActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);

//延迟3秒跳转到首页
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//跳转到首页,并重新设置拿到的uri数据
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
if (getIntent() != null && getIntent().getData() != null) {
intent.setData(getIntent().getData());
}
intent.putExtra(“isSplashLanuch”, true);
startActivity(intent);
finish();
}
}, 3000);
}
}

首页的完整代码:
package cn.znh.deeplinkdemo;

import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

/**
* Created by znh on 2018/12/23.
* <p>
* 首页
*/
public class MainActivity extends AppCompatActivity {

//定义的scheme
private static final String SCHEME_VALUE = “link”;

//定义的host
private static final String HOST_VAULE = “cn.znh.deeplinkdemo”;

//闪屏页是否启动过了
private boolean isSplashLanuch = false;

/**
* 首次启动MainActivity调用该方法
*
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
isSplashLanuch = getIntent().getBooleanExtra(“isSplashLanuch”, false);
schemeIntent(getIntent());
}

/**
* 启动已经存在的MainActivity调用该方法(singleTask启动模式)
*
* @param intent
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
isSplashLanuch = true;
schemeIntent(intent);
}

/**
* 处理intent事件
*
* @param intent
*/
private void schemeIntent(Intent intent) {
if (intent == null || intent.getData() == null) {
return;
}

//获取Uri
Uri uri = intent.getData();

//打印出uri里取出的Scheme和Host
Log.e(“schemeIntent”, “getScheme:” + uri.getScheme());
Log.e(“schemeIntent”, “getHost:” + uri.getHost());

//判断取出的Scheme和Host是否和自己配置的一样,如果一样进行相应的处理,否则不处理
if (!SCHEME_VALUE.equals(uri.getScheme()) || !HOST_VAULE.equals(uri.getHost())) {
return;
}

//如果闪屏页启动过了,就不处理,否则关闭首页打开闪屏页
if (!isSplashLanuch) {
Intent intentSplash = new Intent(this, SplashActivity.class);
intentSplash.setData(uri);//设置uri数据,方便场景还原
startActivity(intentSplash);
finish();
return;
}

//如果Scheme和Host匹配成功,取出uri中的参数并进行相应的业务处理
String type = uri.getQueryParameter(“type”);
String id = uri.getQueryParameter(“id”);

//打印uri里取出的参数
Log.e(“schemeIntent”, “type:” + type);
Log.e(“schemeIntent”, “id:” + id);

//进行统一的跳转分发
if (“1”.equals(type)) {
Intent intent1 = new Intent(this, Link1Activity.class);
startActivity(intent1);
} else if (“2”.equals(type)) {
Intent intent2 = new Intent(this, Link2Activity.class);
startActivity(intent2);
} else if (“3”.equals(type)) {
Intent intent3 = new Intent(this, Link3Activity.class);
startActivity(intent3);
}
}
}

 

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速