当前位置: 首页 > news >正文

和客户谈建网站怎么说成品网站 免费试用

和客户谈建网站怎么说,成品网站 免费试用,湖北建设银行网站首页,河南省新闻奖RxJava是什么 一个词#xff1a;异步。 RxJava 在 GitHub 主页上的自我介绍是 “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”#xff08;一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序…RxJava是什么 一个词异步。 RxJava 在 GitHub 主页上的自我介绍是 “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库。这就是 RxJava 概括得非常精准。 然而对于初学者来说这太难看懂了。因为它是一个『总结』而初学者更需要一个『引言』。 其实 RxJava 的本质可以压缩为异步这一个词。说到根上它就是一个实现异步操作的库而别的定语都是基于这之上的。 引入RxJava 在build.gradle在dependencies加入 dependencies {compile io.reactivex:rxandroid:1.1.0compile io.reactivex:rxjava:1.1.0 } 异步网络请求 场景异步网络请求一个User数据并在TextView展示。 平常代码 TextView textView ...;MapString, String params new HashMap(); params.put(user_id, userid);// 请求参数UserHttp client new UserHttp(); client.post(http://server.com/user, params, new CallBackString() { // 异步请求Overrideprotected void onSuccess(String result) { // 在UI线程回调// 返回的字符串(通常是一个json)解析成User对象User user parse(result); textView.setText(user.getName());} }); 大概就是这样子了吧当然一般都会再封装一下。 用RxJava大概是这样子 TextView textView ...;ObservableString observable Observable.create(new Observable.OnSubscribeString() {Overridepublic void call(final Subscriber? super String subscriber) {// 下面subscribeOn(Schedulers.newThread()) 把这方法设定在新线程回调MapString, String params new HashMap();params.put(user_id, userid);// 请求参数UserHttp client new UserHttp();Response response client.post(http://kkmike999.com/user, params);// 同步请求if (response.status 200) { // 请求成功String result response.getResult();subscriber.onNext(result);subscriber.onCompleted();} else {// 请求失败subscriber.onError(new Throwable(response.getMessage()));}} }) .subscribeOn(Schedulers.newThread()) // 设置call(...)方法在新线程回调;// 可封装得更美观 ObservableString observable UserHttp.create(userid); observable.observeOn(AndroidSchedulers.mainThread())// 让下面onNext()、onError()、onComplete()在UI线程(主线程)回调.subscribe(new SubscriberString() {Overridepublic void onNext(String result) { // 上面 subscriber.onNext(result)在这里回调// 返回的字符串(通常是一个json)解析成User对象User user parse(result);textView.setText(user.getName());}Overridepublic void onError(Throwable e) {} // 上面subscriber.onError(new Throwable(msg))在这里回调Overridepublic void onCompleted() {} }); 虽然代码增多了RxJava 好在哪就好在简洁好在那把什么复杂逻辑都能穿成一条线的简洁。 API 介绍和原理简析 RxJava 的异步实现是通过一种扩展的观察者模式来实现的 RxJava 的观察者模式 RxJava 有四个基本概念Observable (可观察者即被观察者)、 Observer (观察者)、 subscribe (订阅)、事件。Observable 和 Observer 通过 subscribe() 方法实现订阅关系从而 Observable 可以在需要的时候发出事件来通知 Observer。 与传统观察者模式不同 RxJava 的事件回调方法除了普通事件 onNext() 相当于 onClick() / onEvent()之外还定义了两个特殊的事件onCompleted() 和 onError()。 onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理还会把它们看做一个队列。RxJava 规定当不会再有新的 onNext() 发出时需要触发 onCompleted() 方法作为标志。 onError(): 事件队列异常。在事件处理过程中出异常时onError() 会被触发同时队列自动终止不允许再有事件发出。 在一个正确运行的事件序列中, onCompleted() 和 onError() 有且只有一个并且是事件序列中的最后一个。需要注意的是onCompleted() 和 onError() 二者也是互斥的即在队列中调用了其中一个就不应该再调用另一个。 RxJava 的观察者模式大致如下图 基本实现 基于以上的概念 RxJava 的基本实现主要有三点 1) 创建 Observer Observer 即观察者它决定事件触发的时候将有怎样的行为。 RxJava 中的 Observer 接口的实现方式 ObserverString observer new ObserverString() {Overridepublic void onNext(String s) {Log.d(tag, Item: s);}Overridepublic void onCompleted() {Log.d(tag, Completed!);}Overridepublic void onError(Throwable e) {Log.d(tag, Error!);} }; 除了 Observer 接口之外RxJava 还内置了一个实现了 Observer 的抽象类Subscriber。 Subscriber 对 Observer 接口进行了一些扩展但他们的基本使用方式是完全一样的 SubscriberString subscriber new SubscriberString() {Overridepublic void onNext(String s) {Log.d(tag, Item: s);}Overridepublic void onCompleted() {Log.d(tag, Completed!);}Overridepublic void onError(Throwable e) {Log.d(tag, Error!);} }; 不仅基本使用方式一样实质上在 RxJava 的 subscribe 过程中Observer 也总是会先被转换成一个 Subscriber 再使用。所以如果你只想使用基本功能选择 Observer 和 Subscriber 是完全一样的。它们的区别对于使用者来说主要有两点 onStart(): 这是 Subscriber 增加的方法。它会在 subscribe 刚开始而事件还未发送之前被调用可以用于做一些准备工作例如数据的清零或重置。这是一个可选方法默认情况下它的实现为空。需要注意的是如果对准备工作的线程有要求例如弹出一个显示进度的对话框这必须在主线程执行 onStart() 就不适用了因为它总是在 subscribe 所发生的线程被调用而不能指定线程。要在指定的线程来做准备工作可以使用 doOnSubscribe() 方法具体可以在后面的文中看到。 unsubscribe(): 这是 Subscriber 所实现的另一个接口 Subscription 的方法用于取消订阅。在这个方法被调用后Subscriber 将不再接收事件。一般在这个方法调用前可以使用 isUnsubscribed() 先判断一下状态。 unsubscribe() 这个方法很重要因为在 subscribe() 之后 Observable 会持有 Subscriber 的引用这个引用如果不能及时被释放将有内存泄露的风险。所以最好保持一个原则要在不再使用的时候尽快在合适的地方例如 onPause() onStop() 等方法中调用 unsubscribe() 来解除引用关系以避免内存泄露的发生。 2) 创建 Observable Observable 即被观察者它决定什么时候触发事件以及触发怎样的事件。 RxJava 使用 create() 方法来创建一个 Observable 并为它定义事件触发规则 Observable observable Observable.create(new Observable.OnSubscribeString() {Overridepublic void call(Subscriber? super String subscriber) {subscriber.onNext(Hello);subscriber.onNext(Hi);subscriber.onNext(Aloha);subscriber.onCompleted();} }); 可以看到这里传入了一个 OnSubscribe 对象作为参数。OnSubscribe 会被存储在返回的 Observable 对象中它的作用相当于一个计划表当 Observable 被订阅的时候OnSubscribe 的 call() 方法会自动被调用事件序列就会依照设定依次触发对于上面的代码就是观察者Subscriber 将会被调用三次 onNext() 和一次 onCompleted()。这样由被观察者调用了观察者的回调方法就实现了由被观察者向观察者的事件传递即观察者模式。 这个例子很简单事件的内容是字符串而不是一些复杂的对象事件的内容是已经定好了的而不像有的观察者模式一样是待确定的例如网络请求的结果在请求返回之前是未知的所有事件在一瞬间被全部发送出去而不是夹杂一些确定或不确定的时间间隔或者经过某种触发器来触发的。总之这个例子看起来毫无实用价值。但这是为了便于说明实质上只要你想各种各样的事件发送规则你都可以自己来写。至于具体怎么做后面都会讲到但现在不行。只有把基础原理先说明白了上层的运用才能更容易说清楚。 create() 方法是 RxJava 最基本的创造事件序列的方法。基于这个方法 RxJava 还提供了一些方法用来快捷创建事件队列例如 just(T…): 将传入的参数依次发送出来。 Observable observable Observable.just(Hello, Hi, Aloha); // 将会依次调用 // onNext(Hello); // onNext(Hi); // onNext(Aloha); // onCompleted(); from(T[]) / from(Iterable String[] words {Hello, Hi, Aloha}; Observable observable Observable.from(words); // 将会依次调用 // onNext(Hello); // onNext(Hi); // onNext(Aloha); // onCompleted(); 上面 just(T…) 的例子和 from(T[]) 的例子都和之前的 create(OnSubscribe) 的例子是等价的。 3) Subscribe (订阅) 创建了 Observable 和 Observer 之后再用 subscribe() 方法将它们联结起来整条链子就可以工作了。代码形式很简单 observable.subscribe(observer); // 或者 observable.subscribe(subscriber); 有人可能会注意到 subscribe() 这个方法有点怪它看起来是『observalbe 订阅了 observer / subscriber』而不是『observer / subscriber 订阅了 observalbe』这看起来就像『杂志订阅了读者』一样颠倒了对象关系。这让人读起来有点别扭不过如果把 API 设计成 observer.subscribe(observable) / subscriber.subscribe(observable) 虽然更加符合思维逻辑但对流式 API 的设计就造成影响了比较起来明显是得不偿失的。 Observable.subscribe(Subscriber) 的内部实现是这样的仅核心代码 // 注意这不是 subscribe() 的源码而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。 // 如果需要看源码可以去 RxJava 的 GitHub 仓库下载。 public Subscription subscribe(Subscriber subscriber) {subscriber.onStart();onSubscribe.call(subscriber);return subscriber; } 可以看到subscriber() 做了3件事 调用 Subscriber.onStart() 。这个方法在前面已经介绍过是一个可选的准备方法。 调用 Observable 中的 OnSubscribe.call(Subscriber) 。在这里事件发送的逻辑开始运行。从这也可以看出在 RxJava 中 Observable 并不是在创建的时候就立即开始发送事件而是在它被订阅的时候即当 subscribe() 方法执行的时候。 将传入的 Subscriber 作为 Subscription 返回。这是为了方便 unsubscribe(). 整个过程中对象间的关系如下图 除了 subscribe(Observer) 和 subscribe(Subscriber) subscribe() 还支持不完整定义的回调RxJava 会自动根据定义创建出 Subscriber 。形式如下 Action1String onNextAction new Action1String() {// onNext()Overridepublic void call(String s) {Log.d(tag, s);} }; Action1Throwable onErrorAction new Action1Throwable() {// onError()Overridepublic void call(Throwable throwable) {// Error handling} }; Action0 onCompletedAction new Action0() {// onCompleted()Overridepublic void call() {Log.d(tag, completed);} };// 自动创建 Subscriber 并使用 onNextAction 来定义 onNext() observable.subscribe(onNextAction); // 自动创建 Subscriber 并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError() observable.subscribe(onNextAction, onErrorAction); // 自动创建 Subscriber 并使用 onNextAction、 onErrorAction 和 onCompletedAction 来定义 onNext()、 onError() 和 onCompleted() observable.subscribe(onNextAction, onErrorAction, onCompletedAction); 简单解释一下这段代码中出现的 Action1 和 Action0。 Action0 是 RxJava 的一个接口它只有一个方法 call()这个方法是无参无返回值的由于 onCompleted() 方法也是无参无返回值的因此 Action0 可以被当成一个包装对象将 onCompleted() 的内容打包起来将自己作为一个参数传入 subscribe() 以实现不完整定义的回调。这样其实也可以看做将 onCompleted() 方法作为参数传进了 subscribe()相当于其他某些语言中的『闭包』。 Action1 也是一个接口它同样只有一个方法 call(T param)这个方法也无返回值但有一个参数与 Action0 同理由于 onNext(T obj) 和 onError(Throwable error) 也是单参数无返回值的因此 Action1 可以将 onNext(obj) 和 onError(error) 打包起来传入 subscribe() 以实现不完整定义的回调。事实上虽然 Action0 和 Action1 在 API 中使用最广泛但 RxJava 是提供了多个 ActionX 形式的接口 (例如 Action2, Action3) 的它们可以被用以包装不同的无返回值的方法。 注正如前面所提到的Observer 和 Subscriber 具有相同的角色而且 Observer 在 subscribe() 过程中最终会被转换成 Subscriber 对象因此从这里开始后面的描述我将用 Subscriber 来代替 Observer 这样更加严谨。 4) 场景示例 下面举两个例子 为了把原理用更清晰的方式表述出来本文中挑选的都是功能尽可能简单的例子以至于有些示例代码看起来会有『画蛇添足』『明明不用 RxJava 可以更简便地解决问题』的感觉。当你看到这种情况不要觉得是因为 RxJava 太啰嗦而是因为在过早的时候举出真实场景的例子并不利于原理的解析因此我刻意挑选了简单的情景。 a. 打印字符串数组 将字符串数组 names 中的所有字符串依次打印出来 String[] names ...; Observable.from(names).subscribe(new Action1String() {Overridepublic void call(String name) {Log.d(tag, name);}}); b. 由 id 取得图片并显示 由指定的一个 drawable 文件 id drawableRes 取得图片并显示在 ImageView 中并在出现异常的时候打印 Toast 报错 int drawableRes ...; ImageView imageView ...; Observable.create(new OnSubscribeDrawable() {Overridepublic void call(Subscriber? super Drawable subscriber) {Drawable drawable getTheme().getDrawable(drawableRes));subscriber.onNext(drawable);subscriber.onCompleted();} }).subscribe(new ObserverDrawable() {Overridepublic void onNext(Drawable drawable) {imageView.setImageDrawable(drawable);}Overridepublic void onCompleted() {}Overridepublic void onError(Throwable e) {Toast.makeText(activity, Error!, Toast.LENGTH_SHORT).show();} }); 正如上面两个例子这样创建出 Observable 和 Subscriber 再用 subscribe() 将它们串起来一次 RxJava 的基本使用就完成了。非常简单。 线程控制 —— Scheduler (一) 在不指定线程的情况下 RxJava 遵循的是线程不变的原则即在哪个线程调用 subscribe()就在哪个线程生产事件在哪个线程生产事件就在哪个线程消费事件。如果需要切换线程就需要用到 Scheduler 调度器。 1) Scheduler 的 API (一) 在RxJava 中Scheduler ——调度器相当于线程控制器RxJava 通过它来指定每一段代码应该运行在什么样的线程。RxJava 已经内置了几个 Scheduler 它们已经适合大多数的使用场景 Schedulers.immediate(): 直接在当前线程运行相当于不指定线程。这是默认的 Scheduler。 Schedulers.newThread(): 总是启用新线程并在新线程执行操作。 Schedulers.io(): I/O 操作读写文件、读写数据库、网络信息交互等所使用的 Scheduler。行为模式和 newThread() 差不多区别在于 io() 的内部实现是是用一个无数量上限的线程池可以重用空闲的线程因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中可以避免创建不必要的线程。Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算即不会被 I/O 等操作限制性能的操作例如图形的计算。这个 Scheduler 使用的固定的线程池大小为 CPU 核数。不要把 I/O 操作放在 computation() 中否则 I/O 操作的等待时间会浪费 CPU。 另外 Android 还有一个专用的 AndroidSchedulers.mainThread()它指定的操作将在 Android 主线程运行。 有了这几个 Scheduler 就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。 * subscribeOn(): 指定 subscribe() 所发生的线程即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。 * observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。 文字叙述总归难理解上代码 Observable.just(1, 2, 3, 4).subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程.subscribe(new Action1Integer() {Overridepublic void call(Integer number) {Log.d(tag, number: number);}}); 上面这段代码中由于 subscribeOn(Schedulers.io()) 的指定被创建的事件的内容 1、2、3、4 将会在 IO 线程发出而由于 observeOn(AndroidScheculers.mainThread()) 的指定因此 subscriber 数字的打印将发生在主线程 。事实上这种在 subscribe() 之前写上两句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式非常常见它适用于多数的 『后台线程取数据主线程显示』的程序策略。 而前面提到的由图片 id 取得图片并显示的例子如果也加上这两句 int drawableRes ...; ImageView imageView ...; Observable.create(new OnSubscribeDrawable() {Overridepublic void call(Subscriber? super Drawable subscriber) {Drawable drawable getTheme().getDrawable(drawableRes));subscriber.onNext(drawable);subscriber.onCompleted();} }) .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程 .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程 .subscribe(new ObserverDrawable() {Overridepublic void onNext(Drawable drawable) {imageView.setImageDrawable(drawable);}Overridepublic void onCompleted() {}Overridepublic void onError(Throwable e) {Toast.makeText(activity, Error!, Toast.LENGTH_SHORT).show();} }); 那么加载图片将会发生在 IO 线程而设置图片则被设定在了主线程。这就意味着即使加载图片耗费了几十甚至几百毫秒的时间也不会造成丝毫界面的卡顿。 2) Scheduler 的原理 (一) RxJava 的 Scheduler API 很方便也很神奇加了一句话就把线程切换了怎么做到的而且 subscribe() 不是最外层直接调用的方法吗它竟然也能被指定线程。然而 Scheduler 的原理需要放在后面讲因为它的原理是以下一节《变换》的原理作为基础的。 好吧这一节其实我屁也没说只是为了让你安心让你知道我不是忘了讲原理而是把它放在了更合适的地方。 变换 终于要到牛逼的地方了不管你激动不激动反正我是激动了。 RxJava 提供了对事件序列进行变换的支持这是它的核心功能之一也是大多数人说『RxJava 真是太好用了』的最大原因。所谓变换就是将事件序列中的对象或整个序列进行加工处理转换成不同的事件或事件序列。概念说着总是模糊难懂的来看 API。 1) API 首先看一个 map() 的例子 Observable.just(images/logo.png) // 输入类型 String.map(new Func1String, Bitmap() {Overridepublic Bitmap call(String filePath) { // 参数类型 Stringreturn getBitmapFromPath(filePath); // 返回类型 Bitmap}}).subscribe(new Action1Bitmap() {Overridepublic void call(Bitmap bitmap) { // 参数类型 BitmapshowBitmap(bitmap);}}); 这里出现了一个叫做 Func1 的类。它和 Action1 非常相似也是 RxJava 的一个接口用于包装含有一个参数的方法。 Func1 和 Action 的区别在于 Func1 包装的是有返回值的方法。另外和 ActionX 一样 FuncX 也有多个用于不同参数个数的方法。FuncX 和 ActionX 的区别在 FuncX 包装的是有返回值的方法。 可以看到map() 方法将参数中的 String 对象转换成一个 Bitmap 对象后返回而在经过 map() 方法后事件的参数类型也由 String 转为了 Bitmap。这种直接变换对象并返回的是最常见的也最容易理解的变换。不过 RxJava 的变换远不止这样它不仅可以针对事件对象还可以针对整个事件队列这使得 RxJava 变得非常灵活。我列举几个常用的变换 map(): 事件对象的直接变换具体功能上面已经介绍过。它是 RxJava 最常用的变换。 map() 的示意图 flatmap 这是一个很有用但非常难理解的变换因此我决定花多些篇幅来介绍它。 首先假设这么一种需求假设有一个数据结构『学生』现在需要打印出一组学生的名字。实现方式很简单 Student[] students ...; SubscriberString subscriber new SubscriberString() {Overridepublic void onNext(String name) {Log.d(tag, name);}... }; Observable.from(students).map(new Func1Student, String() {Overridepublic String call(Student student) {return student.getName();}}).subscribe(subscriber); 很简单。那么再假设如果要打印出每个学生所需要修的所有课程的名称呢需求的区别在于每个学生只有一个名字但却有多个课程。首先可以这样实现 Student[] students ...; SubscriberStudent subscriber new SubscriberStudent() {Overridepublic void onNext(Student student) {ListCourse courses student.getCourses();for (int i 0; i courses.size(); i) {Course course courses.get(i);Log.d(tag, course.getName());}}... }; Observable.from(students).subscribe(subscriber); 依然很简单。那么如果我不想在 Subscriber 中使用 for 循环而是希望 Subscriber 中直接传入单个的 Course 对象呢这对于代码复用很重要用 map() 显然是不行的因为 map() 是一对一的转化而我现在的要求是一对多的转化。那怎么才能把一个 Student 转化成多个 Course 呢 这个时候就需要用 flatMap() 了 Student[] students ...; SubscriberCourse subscriber new SubscriberCourse() {Overridepublic void onNext(Course course) {Log.d(tag, course.getName());}... }; Observable.from(students).flatMap(new Func1Student, ObservableCourse() {Overridepublic ObservableCourse call(Student student) {return Observable.from(student.getCourses());}}).subscribe(subscriber); 从上面的代码可以看出 flatMap() 和 map() 有一个相同点它也是把传入的参数转化之后返回另一个对象。但需要注意和 map() 不同的是 flatMap() 中返回的是个 Observable 对象并且这个 Observable 对象并不是被直接发送到了 Subscriber 的回调方法中。 flatMap() 的原理是这样的1. 使用传入的事件对象创建一个 Observable 对象2. 并不发送这个 Observable, 而是将它激活于是它开始发送事件3. 每一个创建出来的 Observable 发送的事件都被汇入同一个 Observable 而这个 Observable 负责将这些事件统一交给 Subscriber 的回调方法。这三个步骤把事件拆成了两级通过一组新创建的 Observable 将初始的对象『铺平』之后通过统一路径分发了下去。而这个『铺平』就是 flatMap() 所谓的 flat。 flatMap() 示意图 3) 延伸doOnSubscribe() 然而虽然超过一个的 subscribeOn() 对事件处理的流程没有影响但在流程之前却是可以利用的。 在前面讲 Subscriber 的时候提到过 Subscriber 的 onStart() 可以用作流程开始前的初始化。然而 onStart() 由于在 subscribe() 发生时就被调用了因此不能指定线程而是只能执行在 subscribe() 被调用时的线程。这就导致如果 onStart() 中含有对线程有要求的代码例如在界面上显示一个 ProgressBar这必须在主线程执行将会有线程非法的风险因为有时你无法预测 subscribe() 将会在什么线程执行。 而与 Subscriber.onStart() 相对应的有一个方法 Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行但区别在于它可以指定线程。默认情况下 doOnSubscribe() 执行在 subscribe() 发生的线程而如果在 doOnSubscribe() 之后有 subscribeOn() 的话它将执行在离它最近的 subscribeOn() 所指定的线程。 进度条示例代码 ProgressDialog progress ...;....observeOn(AndroidSchedulers.mainThread()) // 规定Subscriber在主线程回调.doOnSubscribe(new Action0() { // 主线程Overridepublic void call() {progress.show();}}).doOnCompleted(new Action0() { // 主线程Overridepublic void call() {progress.dismiss();}}).subscribe(new SubscriberListString() { // 主线程Overridepublic void onNext(ListString strings) {...}Overridepublic void onComplete() {...}} 参考链接 给 Android 开发者的 RxJava 详解 RxJava快速入门 - 简书
http://www.yutouwan.com/news/11369/

相关文章:

  • 额敏网站建设网络服务提供者知道或者应当知道网络用户
  • 吴江seo网站优化软件医院网站建设与管理ppt
  • dw网站模版pmp培训
  • 农业基本建设项目信息网站产品推广方式
  • 网站建设手机版js网站建设
  • 一键网站建设建筑工程网官网平台
  • 微餐饮网站建设平台用哪个网站做相册视频
  • 迅速提高网站排名网站建成后 再添加小功能麻烦吗
  • 旅游网网站建设的管理财务公司
  • 顺德网站制作案例信息河南网站seo费用
  • 网站建设首选-云端高科建设网企业沟通平台
  • 企业网站设计费做哪个科目织梦网站模板怎么做
  • 杭州电商网站平台开发公司django做的电子商务网站
  • 关键词排名优化网站建设公司哪家好网站建设项目分析
  • 如何编辑网站后台职业做网站游戏的
  • 做网站下载功能crm软件系统的构成包括
  • 个人建立网站要多少钱wordpress怎么入驻写模板
  • 网站建设的目标和需求河南省建设工程信息网推荐中项网
  • 网站开发 大学专业深圳大型网站建设公司
  • 做外贸常用的网站有哪些网页制作软件免费版下载
  • vs音乐网站开发实例廊坊快速排名优化
  • phpcms网站后台模板上海个人网站建
  • 辛集专业网站建设网页设计代码免费
  • 华久做网站网站建设评审会简报
  • 郑州做网站推广的公司网站建设步骤电脑
  • 常州网站设计公司百度搜索下载安装
  • 网站建设费如何会计处理常用的网页设计软件
  • 做旅游网站的方法西安网站快速优化
  • 2003年北京网站建设设计素材图片大全 psd素材
  • 响应式设计网站案例wordpress在线扫描