精华内容
下载资源
问答
  • 如果要了解云计算和大数据的意思和关系,那我们就要先对这两个进行了解,分别了解两者是什么意思。 云计算是什么? 云计算狭义上的意思:“云”实质上就是个网络,狭义上讲,云计算就是种提供资源的网络,使用...

    云计算是什么?大数据是什么?他们有什么区别?关联又是什么?估计很多人都不是很清楚这两者到底代表什么。如果要了解云计算和大数据的意思和关系,那我们就要先对这两个词进行了解,分别了解两者是什么意思。

    云计算是什么?

    云计算狭义上的意思:“云”实质上就是一个网络,狭义上讲,云计算就是一种提供资源的网络,使用者可以随时获取“云”上的资源,按需求量使用,并且可以看成是无限扩展的,只要按使用量付费就可以,“云”就像自来水厂一样,我们可以随时接水,并且不限量,按照自己家的用水量,付费给自来水厂就可以。
    云计算广义上的意思:云计算是与信息技术、软件、互联网相关的一种服务,这种计算资源共享池叫做“云”,云计算把许多计算资源集合起来,通过软件实现自动化管理,只需要很少的人参与,就能让资源被快速提供。也就是说,计算能力作为一种商品,可以在互联网上流通,就像水、电、煤气一样,可以方便地取用,且价格较为低廉。
    云计算通俗理解:通过互联网提供全球用户计算力、存储服务,为互联网信息处理提供硬件基础。云计算,简单说就是把你自己电脑里的或者公司服务器上的硬盘、CPU都放到网上,统一动态调用。

    大数据是什么?

    大数据的定义(麦肯锡全球研究所给出):一种规模大到在获取、存储、管理、分析方面大大超出了传统数据库软件工具能力范围的数据集合,具有海量的数据规模、快速的数据流转、多样的数据类型和价值密度低四大特征。
    大数据的定义(研究机构Gartner给出):“大数据”是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力来适应海量、高增长率和多样化的信息资产。
    大数据通俗理解:运用日趋成熟的云计算技术从浩瀚的互联网信息海洋中获得有价值的信息进行信息归纳、检索、整合,为互联网信息处理提供软件基础。大数据,简单说,就是把所有的数据放到一起分析,找到关联,实现预测。这里的所有数据对应的是之前的抽样调研取得的部分数据。

    云计算和大数据的区别与关系

    云计算和大数据的区别:云计算注重资源分配,是硬件资源的虚拟化;而大数据是海量数据的高效处理。大数据与云计算之间并非独立概念,而是关系非比寻常,无论在资源的需求上还是在资源的再处理上,都需要二者共同运用。
    云计算和大数据的关系:云计算是基础,没有云计算,无法实现大数据存储与计算。大数据是应用,没有大数据,云计算就缺少了目标与价值。两者都需要人工智能的参与,人工智能是互联网信息系统有序化后的一种商业智能。
    而商业智能(即BI,国内典型代表BI厂商为亿信华辰)中的智能从何而来?方法之一就是通过大数据这个工具来对大量数据进行处理,从而得出一些关联性的结论,从这些关联性中来获得答案,因此,大数据是商业智能的一种工具。 而大数据要分析大量的数据,这对于系统的计算能力和处理能力要求是非常高的,传统的方式是需要一个超级计算机来进行处理,但这样就导致了计算能力空的时候闲着、忙的时候又不够的问题, 而云计算的弹性扩展和水平扩展的模式很适合计算能力按需调用,因此,云计算为大数据提供了计算能力和资源等物质基础。

    展开全文
  • 什么是NoSQL数据库?

    2014-07-21 23:24:20
    NoSQL数据库是什么 NoSQL说起来简单,但实际上到底有多少种呢?我在提笔的时候,到NoSQL的官方网站上确认了一下,竟然已经有122种了。另外官方网站上也介绍了本书没有涉及到的图形数据库和对象数据库等各个类别。...
  • SWT跟有道词典有什么关系?这两个东西有什么关联么? 如果你已经知道它们的关联就不需要看本文了。 下面用windowbuilder生成的个简单的java代码,我在代码中用addFilter增加了对Ctrl-C热键的侦听。如果检测到...

    SWT跟有道词典有什么关系?这两个东西有什么关联么?
    如果你已经知道它们的关联就不需要看本文了。
    下面是用windowbuilder生成的一个简单的java代码,我在代码中用addFilter增加了对Ctrl-C热键的侦听。如果检测到Ctrl-C按下,就输出消息。
    实现的情况是:
    Ctrl-C键的确可以被检测到
    但是在程序的界面中按下鼠标左键拖动一段距离再松开,也能检测出Ctrl-C。
    如果改成Ctr-M,Alt-C这些组合,就正常。

    package testwb;
    
    import org.eclipse.jface.bindings.keys.SWTKeySupport;
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Event;
    import org.eclipse.swt.widgets.Listener;
    import org.eclipse.swt.widgets.Shell;
    import org.eclipse.swt.events.DisposeListener;
    import org.eclipse.swt.events.DisposeEvent;
    
    public class TestCtrlC {
    
        protected Shell shell;
        private final Listener ctrlcListener=new Listener(){
            @Override
            public void handleEvent(Event e) {
                // Ctrl-C检查             
                if(e.type==SWT.KeyDown&&SWTKeySupport.convertEventToModifiedAccelerator(e)==(SWT.CTRL+'C')
                        ){
                    System.out.println("Ctrl-C");
                }               
            }};
        /**
         * Launch the application.
         * @param args
         */
        public static void main(String[] args) {
            try {
                TestCtrlC window = new TestCtrlC();
                window.open();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Open the window.
         */
        public void open() {
            Display display = Display.getDefault();
            createContents();
            shell.open();
            shell.layout();
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch()) {
                    display.sleep();
                }
            }
        }
    
        /**
         * Create contents of the window.
         */
        protected void createContents() {
            shell = new Shell();
            shell.addDisposeListener(new DisposeListener() {
                public void widgetDisposed(DisposeEvent e) {
                    // 对象销毁时删除过滤器
                    shell.getDisplay().removeFilter(SWT.KeyDown, ctrlcListener);
                }
            });
            shell.setSize(450, 300);
            shell.setText("SWT Application");
            // 加入Ctrl-C热键侦听器 
            shell.getDisplay().addFilter(SWT.KeyDown, ctrlcListener);
        }
    
    }
    

    这是为什么?
    为什么明明是发生了鼠标事件,键盘事件却响应了?
    是不是操作系统对Ctrl-C做了特别处理?
    这个问题反反复复折腾了我一个多星期,偶尔却是正常的,一直没找到原因,后来干脆将热键定义成了别的组合避免这个问题。

    今天这个问题又出现了,反反复复被折腾已经忍无可忍了,根据时好时坏的特点感觉不像是代码的问题,倒像是系统中其他软件的干扰,于是下决心找出这个”内鬼”。
    重启电脑后,不打开别的应用,直接开eclipse运行上面的测试程序,发现是好的,
    然后登录qq,也是正常的,
    按照我正常的工作习惯打开了chrom浏览器,360浏览器都是正常的,
    直到打开了有道词典,神迹复现啦!尼玛终于逮到你了。

    在系统托盘中找到有道词典的图标,鼠标右键点击”软件设置”,
    如下图是有道词典的设置界面,默认状态下,红框中的选项”启用划词释义”是勾选的,就是这个开关造成了系统消息混乱,只要不勾选它,就是正常的。

    这里写图片描述

    在这里对有道词典的这种流氓行径表示谴责!。。。

    展开全文
  • 循环神经网络

    万次阅读 2017-07-28 19:38:56
    就是前面的输入和后面的输入关联的,比如句话,前后的关系的,“我肚子饿了,准备去xx”,根据前面的输入判断“xx”很大可能就是“吃饭”。这个就是序列数据。循环神经网络有很多变种,比如LSTM、GRU...

    RNN是什么

    循环神经网络即recurrent neural network,它的提出主要是为了处理序列数据,序列数据是什么?就是前面的输入和后面的输入是有关联的,比如一句话,前后的词都是有关系的,“我肚子饿了,准备去xx”,根据前面的输入判断“xx”很大可能就是“吃饭”。这个就是序列数据。

    循环神经网络有很多变种,比如LSTM、GRU等,这里搞清楚基础的循环神经网络的思想,对于理解其他变种就比较容易了。

    与传统神经网络区别

    下图是我们经典的全连接网络,从输入层到两个隐含层再到输出层,四层之间都是全连接的,而且层内之间的节点不相连。这种网络模型对于序列数据的预测就基本无能为力,比如某句话的下一个单词是什么就很难处理。

    循环神经网络则擅长处理序列数据,它会对前面的信息进行记忆并且参与当前输出的计算,理论上循环神经网络能处理任意长度的序列数据。

    RNN模型

    RNN模型最抽象的画法就是下面这种了,但它不太好理解,因为它将时间维度挤压了。其中x是输入,U是输出层到隐含层的权重,s是隐含层值,W则是上个时刻隐含层作为这个时刻输入的权重,V是隐含层到输出层的权重,o是输出。

    为方便理解,将上图展开,现在可以清楚看到输入x、隐层值s和输出o都有了下标t,这个t表示时刻,t-1是上一时刻,t+1则是下一时刻。不同时刻输入对应不同的输出,而且上一时刻的隐含层会影响当前时刻的输出。

    那么反应到神经元是怎样的呢?如下图,这下就更清晰了,输入的3个神经元连接4个隐含层神经元,然后保留隐含层状态用于下一刻参与计算。

    RNN的正向传播

    还是使用这张图进行说明,设输出层的输入为nett,则很容易可以得到输出,

    nett=Vst

    ot=σ(nett)

    其中σ是激活函数,再设隐含层的输入为ht

    ht=Uxt+Wst1

    st=σ(ht)

    可以继续往前一个时刻推,有

    st=σ(Uxt+Wσ(Uxt1+Wst2))

    而t-2时刻又可以用t-3时刻来表示,从这就可以看出循环神经网络具有记忆,可以往前看任意个输入。

    RNN的训练

    假设损失函数为

    E

    在t时刻,根据误差逆传播,有

    Etnett=Etototnett=E(ot)σ(nett)

    首先,我们来看看对V的求导,每个时刻t的误差至于当前时刻的误差相关,则

    EtV=EtnettnettV=Etnettst

    其次,对W求导,对于一个训练样本,所有时刻的误差加起来才是这个样本的误差,某时刻t对W求偏导为,

    EtW=EtnettnettststW

    其中st=σ(Uxt+Wσ(Uxt1+Wst2))一直依赖上个时刻,某个样本的总误差是需要所有时刻加起来,不断对某个时刻进行求偏导,误差一直反向传播到t为0时刻,则

    EW=tEtW=tk=0Etnettnettst(tj=k+1stsk)skW

    其中stsk根据链式法则是会一直乘到k
    时刻,k可以是0、1、2…,那么上式可以表示成,

    EW=tk=0EtnettnettststskskW

    最后,对U求导,

    EU=EhthtU=Ehtxt

    通过上面实现梯度下降训练。

    梯度消失或梯度爆炸

    对于tanh和sigmoid激活函数的RNN,我们说它不能很好的处理较长的序列,这个是为什么呢?简单说就是因为RNN很容易会存在梯度消失或梯度爆炸问题,发生这种情况时RNN就捕捉不了很早之前的序列的影响。

    为什么会这样?接着往下看,tanh和sigmoid的梯度大致如下图所示,两端的梯度值都基本接近0了,而从上面的求导公式可以看到

    EW=tEtW=tk=0Etnettnettst(tj=k+1stsk)skW

    其中有个连乘操作,而向量函数对向量求导结果为一个Jacobian矩阵,元素为每个点的导数,离当前时刻越远则会乘越多激活函数的导数,指数型,本来就接近0的梯度再经过指数就更加小,基本忽略不计了,于是便接收不到远距离的影响,这就是RNN处理不了较长序列的原因。

    而当矩阵中的值太大时,经过指数放大,则会产生梯度爆炸。

    梯度爆炸会导致程序NaN,可以设置一个梯度阈值来处理。

    梯度消失则可以用ReLU来替代tanh和sigmoid激活函数,或者用LSTM或GRU结构。

    RNN简单应用例子

    比如可以做字符级别的预测,如下图,假如这里只有四种字符,样本为”hello”单词,则输入h预测下个字符为e,e接着则输出l,l则输出l,最后输入l则输出o。

    ========广告时间========

    公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

    鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以购买。感谢各位朋友。

    为什么写《Tomcat内核设计剖析》

    =========================

    欢迎关注:

    这里写图片描述

    展开全文
  • 但通过收件地址、下单IP地址、联系电话、收货人等判断实际购买人为人或与您有紧密关联的,且多笔订单合计购买数量超过该商品限购数量的,销售商有权取消相关订单、解除合同,小米有权基于该情形...
  • 前端开发经常遇到:路由,在Android APP开发中,路由还经常和组件化开发强关联在一起,那么到底什么是路由,个路由框架到底应该具备什么功能,实现原理是什么样的?路由是否是APP的强需求呢?与组件化到底...

    前端开发经常遇到一个词:路由,在Android APP开发中,路由还经常和组件化开发强关联在一起,那么到底什么是路由,一个路由框架到底应该具备什么功能,实现原理是什么样的?路由是否是APP的强需求呢?与组件化到底什么关系,本文就简单分析下如上几个问题。

    路由的概念

    路由这个词本身应该是互联网协议中的一个词,维基百科对此的解释如下:

    路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动。路由发生在OSI网络参考模型中的第三层即网络层。
    

    个人理解,在前端开发中,路由就是通过一串字符串映射到对应业务的能力。APP的路由框首先能够搜集各组件的路由scheme,并生成路由表,然后,能够根据外部输入字符串在路由表中匹配到对应的页面或者服务,进行跳转或者调用,并提供会获取返回值等,示意如下

    image.png

    所以一个基本路由框架要具备如下能力:

      1. APP路由的扫描及注册逻辑
      1. 路由跳转target页面能力
      1. 路由调用target服务能力

    APP中,在进行页面路由的时候,经常需要判断是否登录等一些额外鉴权逻辑所以,还需要提供拦截逻辑等,比如:登陆。

    三方路由框架是否是APP强需求

    答案:不是,系统原生提供路由能力,但功能较少,稍微大规模的APP都采用三方路由框架。

    Android系统本身提供页面跳转能力:如startActivity,对于工具类APP,或单机类APP,这种方式已经完全够用,完全不需要专门的路由框架,那为什么很多APP还是采用路由框架呢?这跟APP性质及路由框架的优点都有关。比如淘宝、京东、美团等这些大型APP,无论是从APP功能还是从其研发团队的规模上来说都很庞大,不同的业务之间也经常是不同的团队在维护,采用组件化的开发方式,最终集成到一个APK中。多团队之间经常会涉及业务间的交互,比如从电影票业务跳转到美食业务,但是两个业务是两个独立的研发团队,代码实现上是完全隔离的,那如何进行通信呢?首先想到的是代码上引入,但是这样会打破了低耦合的初衷,可能还会引入各种问题。例如,部分业务是外包团队来做,这就牵扯到代码安全问题,所以还是希望通过一种类似黑盒的方式,调用目标业务,这就需要中转路由支持,所以国内很多APP都是用了路由框架的。其次我们各种跳转的规则并不想跟具体的实现类扯上关系,比如跳转商详的时候,不希望知道是哪个Activity来实现,只需要一个字符串映射过去即可,这对于H5、或者后端开发来处理跳转的时候,就非常标准。

    原生路由的限制:功能单一,扩展灵活性差,不易协同

    传统的路由基本上就限定在startActivity、或者startService来路由跳转或者启动服务。拿startActivity来说,传统的路由有什么缺点:startActivity有两种用法,一种是显示的,一种是隐式的,显示调用如下:

    <!--1 导入依赖-->
    import com.snail.activityforresultexample.test.SecondActivity;
    
    public class MainActivity extends AppCompatActivity {
    
        void jumpSecondActivityUseClassName(){
        <!--显示的引用Activity类-->
            Intent intent =new Intent(MainActivity.this, SecondActivity.class);
            startActivity(intent);
        }
    

    显示调用的缺点很明显,那就是必须要强依赖目标Activity的类实现,有些场景,尤其是大型APP组件化开发时候,有些业务逻辑出于安全考虑,并不想被源码或aar依赖,这时显式依赖的方式就无法走通。再来看看隐式调用方法。

    第一步:manifest中配置activity的intent-filter,至少要配置一个action

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.snail.activityforresultexample">
        <application
           ...
        <activity android:name=".test.SecondActivity">
                <intent-filter>
                <!--隐式调用必须配置android.intent.category.DEFAULT-->
                       <category android:name="android.intent.category.DEFAULT"/>
                <!--至少配置一个action才能通过隐式调用-->
                    <action android:name="com.snail.activityforresultexample.SecondActivity" />
                    <!--可选-->
      <!--              <data android:mimeType="video/mpeg" android:scheme="http" ... />-->
                </intent-filter>
            </activity>
        </application>
    </manifest>
    

    第二步:调用

    void jumpSecondActivityUseFilter() {
        Intent intent = new Intent();
        intent.setAction("com.snail.activityforresultexample.SecondActivity");
        startActivity(intent);
    }
    

    如果牵扯到数据传递写法上会更复杂一些,隐式调用的缺点有如下几点:

    • 首先manifest中定义复杂,相对应的会导致暴露的协议变的复杂,不易维护扩展。
    • 其次,不同Activity都要不同的action配置,每次增减修改Activity都会很麻烦,对比开发者非常不友好,增加了协作难度。
    • 最后,Activity的export属性并不建议都设置成True,这是降低风险的一种方式,一般都是收归到一个Activity,DeeplinkActivitiy统一处理跳转,这种场景下,DeeplinkActivitiy就兼具路由功能,隐式调用的场景下,新Activitiy的增减势必每次都要调整路由表,这会导致开发效率降低,风险增加。

    可以看到系统原生的路由框架,并没太多考虑团队协同的开发模式,多限定在一个模块内部多个业务间直接相互引用,基本都要代码级依赖,对于代码及业务隔离很不友好。如不考虑之前Dex方法树超限制,可以认为三方路由框架完全是为了团队协同而创建的

    APP三方路由框架需具备的能力

    目前市面上大部分的路由框架都能搞定上述问题,简单整理下现在三方路由的能力,可归纳如下:

    • 路由表生成能力:业务组件**[UI业务及服务]**自动扫描及注册逻辑,需要扩展性好,无需入侵原有代码逻辑
    • scheme与业务映射逻辑 :无需依赖具体实现,做到代码隔离
    • 基础路由跳转能力 :页面跳转能力的支持
    • 服务类组件的支持 :如去某个服务组件获取一些配置等
    • [扩展]路由拦截逻辑:比如登陆,统一鉴权
    • 可定制的降级逻辑:找不到组件时的兜底

    可以看下一个典型的Arouter用法,第一步:对新增页面添加Router Scheme 声明,

    	@Route(path = "/test/activity2")
    	public class Test2Activity extends AppCompatActivity {
    		 ...
    	}
    

    build阶段会根据注解搜集路由scheme,生成路由表。第二步使用

            ARouter.getInstance()
                    .build("/test/activity2")
                    .navigation(this);
    

    如上,在ARouter框架下,仅需要字符串scheme,无需依赖任何Test2Activity就可实现路由跳转。

    APP路由框架的实现

    路由框架实现的核心是建立scheme和组件**[Activity或者其他服务]**的映射关系,也就是路由表,并能根据路由表路由到对应组件的能力。其实分两部分,第一部分路由表的生成,第二部分,路由表的查询

    路由表的自动生成

    生成路由表的方式有很多,最简单的就是维护一个公共文件或者类,里面映射好每个实现组件跟scheme,

    image.png

    不过,这种做法缺点很明显:每次增删修改都要都要修改这个表,对于协同非常不友好,不符合解决协同问题的初衷。不过,最终的路由表倒是都是这条路,就是将所有的Scheme搜集到一个对象中,只是实现方式的差别,目前几乎所有的三方路由框架都是借助注解+APT[Annotation Processing Tool]工具+AOP(Aspect-Oriented Programming,面向切面编程)来实现的,基本流程如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0I0qd9UB-1623739129822)(https://upload-images.jianshu.io/upload_images/1460468-b1e7d0e52a4349ac.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

    其中牵扯的技术有注解、APT(Annotation Processing Tool)、AOP(Aspect-Oriented Programming,面向切面编程)。APT常用的有JavaPoet,主要是遍历所有类,找到被注解的Java类,然后聚合生成路由表,由于组件可能有很多,路由表可能也有也有多个,之后,这些生成的辅助类会跟源码一并被编译成class文件,之后利用AOP技术【如ASM或者JavaAssist】,扫描这些生成的class,聚合路由表,并填充到之前的占位方法中,完成自动注册的逻辑。

    JavaPoet如何搜集并生成路由表集合?

    以ARouter框架为例,先定义Router框架需要的注解如:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.CLASS)
    public @interface Route {
    
        /**
         * Path of route
         */
        String path();
    

    该注解用于标注需要路由的组件,用法如下:

    @Route(path = "/test/activity1", name = "测试用 Activity")
    public class Test1Activity extends BaseActivity {
        @Autowired
        int age = 10;
    

    之后利用APT扫描所有被注解的类,生成路由表,实现参考如下:

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (CollectionUtils.isNotEmpty(annotations)) {
        <!--获取所有被Route.class注解标注的集合-->
            Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
            <!--解析并生成表-->
                this.parseRoutes(routeElements);
           ...
        return false;
    }
     
     <!--生成中间路由表Java类-->
    private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
    						...
                         // Generate groups
                String groupFileName = NAME_OF_GROUP + groupName;
                JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                        TypeSpec.classBuilder(groupFileName)
                                .addJavadoc(WARNING_TIPS)
                                .addSuperinterface(ClassName.get(type_IRouteGroup))
                                .addModifiers(PUBLIC)
                                .addMethod(loadIntoMethodOfGroupBuilder.build())
                                .build()
                ).build().writeTo(mFiler);
    

    产物如下:包含路由表,及局部注册入口。

    image.png

    自动注册:ASM搜集上述路由表并聚合插入Init代码区

    为了能够插入到Init代码区,首先需要预留一个位置,一般定义一个空函数,以待后续填充:

    	public class RouterInitializer {
    	
    	    public static void init(boolean debug, Class webActivityClass, IRouterInterceptor... interceptors) {
    	        ...
    	        loadRouterTables();
    	    }
    		//自动注册代码    
    	    public static void loadRouterTables() {
    	    
    	    }
    }
    

    首先利用AOP工具,遍历上述APT中间产物,聚合路由表,并注册到预留初始化位置,遍历的过程牵扯是gradle transform的过程,

    • 搜集目标,聚合路由表

        /**扫描jar*/
        fun scanJar(jarFile: File, dest: File?) {
      
            val file = JarFile(jarFile)
            var enumeration = file.entries()
            while (enumeration.hasMoreElements()) {
                val jarEntry = enumeration.nextElement()
                if (jarEntry.name.endsWith("XXRouterTable.class")) {
                    val inputStream = file.getInputStream(jarEntry)
                    val classReader = ClassReader(inputStream)
                    if (Arrays.toString(classReader.interfaces)
                            .contains("IHTRouterTBCollect")
                    ) {
                        tableList.add(
                            Pair(
                                classReader.className,
                                dest?.absolutePath
                            )
                        )
                    }
                    inputStream.close()
                } else if (jarEntry.name.endsWith("HTRouterInitializer.class")) {
                    registerInitClass = dest
                }
            }
            file.close()
        }
      
    • 对目标Class注入路由表初始化代码

        fun asmInsertMethod(originFile: File?) {
      
            val optJar = File(originFile?.parent, originFile?.name + ".opt")
            if (optJar.exists())
                optJar.delete()
            val jarFile = JarFile(originFile)
            val enumeration = jarFile.entries()
            val jarOutputStream = JarOutputStream(FileOutputStream(optJar))
      
            while (enumeration.hasMoreElements()) {
                val jarEntry = enumeration.nextElement()
                val entryName = jarEntry.getName()
                val zipEntry = ZipEntry(entryName)
                val inputStream = jarFile.getInputStream(jarEntry)
                //插桩class
                if (entryName.endsWith("RouterInitializer.class")) {
                    //class文件处理
                    jarOutputStream.putNextEntry(zipEntry)
                    val classReader = ClassReader(IOUtils.toByteArray(inputStream))
                    val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
                    val cv = RegisterClassVisitor(Opcodes.ASM5, classWriter,tableList)
                    classReader.accept(cv, EXPAND_FRAMES)
                    val code = classWriter.toByteArray()
                    jarOutputStream.write(code)
                } else {
                    jarOutputStream.putNextEntry(zipEntry)
                    jarOutputStream.write(IOUtils.toByteArray(inputStream))
                }
                jarOutputStream.closeEntry()
            }
            //结束
            jarOutputStream.close()
            jarFile.close()
            if (originFile?.exists() == true) {
                Files.delete(originFile.toPath())
            }
            optJar.renameTo(originFile)
        }
      

    最终RouterInitializer.class的 loadRouterTables会被修改成如下填充好的代码:

     public static void loadRouterTables() {
     
        <!---->
        register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");
        register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin");
        register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");
        register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");
        ...
    }
    

    如此就完成了路由表的搜集与注册,大概的流程就是如此。当然对于支持服务、Fragment等略有不同,但大体类似。

    Router框架对服务类组件的支持

    通过路由的方式获取服务属于APP路由比较独特的能力,比如有个用户中心的组件,我们可以通过路由的方式去查询用户是否处于登陆状态,这种就不是狭义上的页面路由的概念,通过一串字符串如何查到对应的组件并调用其方法呢?这种的实现方式也有多种,每种实现方式都有自己的优劣。

    • 一种是可以将服务抽象成接口,沉到底层,上层实现通过路由方式映射对象
    • 一种是将实现方法直接通过路由方式映射

    先看第一种,这种事Arouter的实现方式,它的优点是所有对外暴露的服务都暴露接口类【沉到底层】,这对于外部的调用方,也就是服务使用方非常友好,示例如下:

    先定义抽象服务,并沉到底层

    image.png

    public interface HelloService extends IProvider {
        void sayHello(String name);
    }
    

    实现服务,并通过Router注解标记

    @Route(path = "/yourservicegroupname/hello")
    public class HelloServiceImpl implements HelloService {
        Context mContext;
    
        @Override
        public void sayHello(String name) {
            Toast.makeText(mContext, "Hello " + name, Toast.LENGTH_SHORT).show();
        }
    

    使用:利用Router加scheme获取服务实例,并映射成抽象类,然后直接调用方法。

      ((HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation()).sayHello("mike");
    

    这种实现方式对于使用方其实是很方便的,尤其是一个服务有多个可操作方法的时候,但是缺点是扩展性,如果想要扩展方法,就要改动底层库。

    再看第二种:将实现方法直接通过路由方式映射

    服务的调用都要落到方法上,参考页面路由,也可以支持方法路由,两者并列关系,所以组要增加一个方法路由表,实现原理与Page路由类似,跟上面的Arouter对比,不用定义抽象层,直接定义实现即可:

    定义Method的Router

    	public class HelloService {
    		
    		<!--参数 name-->
        	@MethodRouter(url = {"arouter://sayhello"})
     	    public void sayHello(String name) {
    	        Toast.makeText(mContext, "Hello " + name, Toast.LENGTH_SHORT).show();
    	    }
    

    使用即可

     RouterCall.callMethod("arouter://sayhello?name=hello");
    

    上述的缺点就是对于外部调用有些复杂,尤其是处理参数的时候,需要严格按照协议来处理,优点是,没有抽象层,如果需要扩展服务方法,不需要改动底层。

    上述两种方式各有优劣,不过,如果从左服务组件的初衷出发,第一种比较好:对于调用方比较友好。另外对于CallBack的支持,Arouter的处理方式可能也会更方便一些,可以比较方便的交给服务方定义。如果是第二种,服务直接通过路由映射的方式,处理起来就比较麻烦,尤其是Callback中的参数,可能要统一封装成JSON并维护解析的协议,这样处理起来,可能不是很好。

    路由表的匹配

    路由表的匹配比较简单,就是在全局Map中根据String输入,匹配到目标组件,然后依赖反射等常用操作,定位到目标。

    组件化与路由的关系

    组件化是一种开发集成模式,更像一种开发规范,更多是为团队协同开发带来方便。组件化最终落地是一个个独立的业务及功能组件,这些组件之间可能是不同的团队,处于不同的目的在各自维护,甚至是需要代码隔离,如果牵扯到组件间的调用与通信,就不可避免的借助路由,因为实现隔离的,只能采用通用字符串scheme进行通信,这就是路由的功能范畴。

    组件化需要路由支撑的根本原因:组件间代码实现的隔离

    总结

    • 路由不是一个APP的必备功能,但是大型跨团队的APP基本都需要
    • 路由框架的基本能力:路由自动注册、路由表搜集、服务及UI界面路由及拦截等核心功能
    • 组件化与路由的关系:组件化的代码隔离导致路由框架成为必须

    作者:看书的小蜗牛
    原文链接: APP路由框架与组件化简析

    展开全文
  • 建立自己的DTD也很简单的件事,一般只需要定义4-5个元素可以了。 调用DTD文件的方法有两种: 1.直接包含在XML文档内的DTD 你只要在DOCTYPE声明中插入一些特别的说明可以了,象这样: 我们有个XML文档...
  • 实验楼课程第二个实验的讲解部分出现了三个,我不知道它们三个是什么关系。查阅了度娘,归纳如下: - 终端: 在UNIX/LINUX系统中,用户通过终端登录系统后得到个Shell进程,这个终端成为Shell进程的控制终端...
  • C++ STL中封装的关联式容器,里面存储的键值对类型,在数据查询中效率较高。 什么是键值对 键值对是一种一一对应的关系,<key,value>结构,第个为关键字,第二个为关键字的值,例如:在词典中我们可以...
  • UML的组合,依赖

    2009-04-22 19:13:43
    说我把个引用(这是他用的,我从来不知道uml里引用到底是什么东西)画成了组合。我后来想想可能他说的引用就是依赖的意思。我先说说我的理解,组合就是说某个类作为另个类的成员变量,比如现在有类B,类A定义...
  • 软件工程教程

    热门讨论 2012-07-06 23:10:29
    问:开发这个软件目标是什么? 答: 提高用户对音乐的学习和娱乐 参与创作音乐 项目背景--钢琴练奏师 问:为什么传统音乐程序不好? 答: 传统音乐程序功能单一,容易令人感到枯燥无味,没有吸引力; 传统音乐...
  • 跑的多快,跳的多高,拳能够打出什么反应,这都能引起视觉反应,简单的说,就是攻击究竟有没有力道感,看你怎么打,用什么姿势打,然后被打的人是什么反映,由于被攻击部位的不同,所作的反应自然要不同,如果都用拳皇那...
  • 实际上我想说,对于个DBA来讲,当你拿到个数据库的时候,你首先需要做的用最短的时间来了解一下跑在这个库上的是一什么系统,比如在线事务(OLTP)系统还是在线分析(OLAP)系统,这对于你做出性能上的...
  • GenoPro 是一个家谱软件,包含有关关系和个人的其他信息。 医学家,家庭治疗师,系谱学家,社会学家,社会工作者,研究人员以及任何有兴趣发现家庭模式和问题的人都可以使用这些基因图。GenoPro 可以轻松构建简单的...
  • 1.7 这不仅仅是一系列图 16 1.8 小结 17 1.9 常见问题解答 17 1.10 小测验和习题 18 1.10.1 小测验 18 1.10.2 习题 18 第2章 理解面向对象 20 2.1 无处不在的对象 20 2.2 一些面向对象的概念 22 2.2.1 抽象 22 ...
  • 这个需求关系方程式着重指明了个事实:需求关系是消费数量和决定消费数量的因素之间种多维的关系。 □ 需求曲线 需求曲线对需求函数的直观描述。于是,我们现在面临经济学中经常遇到的个难题:如何...
  • 黑客(hacker)实际褒义,维基百科的解释喜欢用智力通过创造性方法来挑战脑力极限的人,特别他们所感兴趣的领域,例如软件编程或电气工程。个人电脑、软件和互联网等划时代的产品都黑客创造出来的,如...
  • 黑客(hacker)实际褒义,维基百科的解释喜欢用智力通过创造性方法来挑战脑力极限的人,特别他们所感兴趣的领域,例如软件编程或电气工程。个人电脑、软件和互联网等划时代的产品都黑客创造出来的,如...
  • 需求分析阶段的基本任务是什么? 答: 需求分析是当前软件工程中的关键问题,需求分析阶段的任务是:在可行性分析的基础上,进一步了解、确定用户需求。准确地回答 “系统必须做什么?” 的问题。获得需求规格说 ...

空空如也

空空如也

1 2 3
收藏数 54
精华内容 21
关键字:

一就关联词是什么关系