移动开发_移动开发主要学什么 - CSDN
  • 本视频教程拥有180节课程,包含iOS开发的方方面面:iOS开发基础理论知识、 视图、视图控制器、多媒体、数据处理、常用插件、信用卡卡号识别、自动化测试、网络访问、多线程、ShareSDK社会化分享、CoreImage、...
  • ... 使用列表系列标签完成常见网页中的列表结构; 熟练掌握表格,表单都系列标签,在项目中熟练使用;...了解哪些标签在项目中经常使用,哪些标签已被废除;...迈出HTML5开发的第一步,为后续课程打下基础;
  • 正在开发app的同志们注意了说到APP开发,大家会想到以IOS、Android的纯原生开发;而在这个app横飞的年代,对于整个产品研发团队或个人来讲,高速的迭代,爆炸式的功能追加已经成为互联网行业的时代标签,常常以小时...

            正在开发app的同志们注意了说到APP开发,大家会想到以IOS、Android的纯原生开发;而在这个app横飞的年代,对于整个产品研发团队或个人来讲,高速的迭代,爆炸式的功能追加已经成为互联网行业的时代标签,常常以小时甚至分钟为单位的进度度量成为了常态。早在2010年的时候,乔布斯就预言HTML5将会成为取代Flash的下一波技术浪潮,那么你还在坚持原生开发么?究竟怎么样才能以H5为核心技术开发APP呢?

            想要使用H5快速的开发APP,平台技术必不可少。正好最近正在调研MDP开发平台,便于大家分享一下调研成果。

            MDP移动端开发平台是一个可以实现快速开发标准化的开发平台,这个平台有个亮点就是有他们自主开发的可视化设计器,通过在设计器中的拖拖拽拽就能配置出实际的界面,通过对组件事件也处理可以进行自定义扩展。一次开发就可以支持Android和IOS两大平台,这也是一大亮点。

            这是MDP设计器的主界面,在线体验了一下还不错,操作模式类似bootstrap的布局设计器都是拖拽栅格进行操作。

            

            照着他们官方提供的教程做了一个DEMO,十分方便,容易上手,比较适合前端新手。

            

            右上角有个预览按钮,可以对我配置的页面直接预览,我是在线体验的没办法用真机测试,据官方描述这个预览与真机测试相差不到5%,给点个赞。

     


            综合下来对于移动开发来说,简单易学很多,而且平台自身也很好为开发人员屏蔽了许多开发中带来的不便,比如兼容性问题,开发成本高等问题。通过这个设计器H5团队可以直接从pc前端团队演变过来,甚至后端开发兼职H5的例子并不是没有。总结起来就是:

            学得快开发快用人少花钱少

     

            产品地址:http://product.mftcc.cn/mdp

     


    展开全文
  • 移动开发基础

    2018-07-04 12:31:19
    原文点击打开链接一个移动端的时代从我工作以来,开发的一直都是移动端的页面,只有偶尔去开发几个PC端的页面,现在是一个移动端的时代,移动先行已经深入骨髓,作为一个web前端开发,如果你还在为如何开发移动端...

    原文点击打开链接

    一个移动端的时代

    从我工作以来,开发的一直都是移动端的页面,只有偶尔去开发几个PC端的页面,现在是一个移动端的时代,移动先行已经深入骨髓,作为一个web前端开发,如果你还在为如何开发移动端页面而迷茫,或者你还在为开发出了一个在你手机上“完美”的移动页面而沾沾自喜却不知移动的世界有多“残酷”的时候,那你应该看看这篇文章了。希望这能给你帮助,同时也能给我帮助,有不合理的地方,欢迎评论支持,必将改正。

    移动端

    这里是基础,你了解否?

    一、像素 - 什么是像素

    像素是web页面布局的基础,那么到底什么才是一个像素呢?
    像素:一个像素就是计算机屏幕所能显示一种特定颜色的最小区域。 这是像素的概念,实际上,在web前端开发领域,像素有以下两层含义:
    1、设备像素:设备屏幕的物理像素,对于任何设备来讲物理像素的数量是固定的。
    2、CSS像素:这是一个抽象的像素概念,它是为web开发者创造的。

    如下图,是在缩放比例为1,即scale = 1的情况下,设备像素和CSS像素示意图

    设备像素和CSS像素

    现在你已经了解了,原来像素对于web前端开发来讲有这样的两层含义,那么你有没有再深入的考虑这样一个问题,当我给一个元素设置了 width: 200px; 这条样式的时候,到底放生了什么事情?
    你可能会说:“废话!元素的宽度是200px呗。”;对,并没有什么问题,但是这个200px指的是什么呢?因为我们知道,对于web前端来讲像素有两层含义,那么到底是设备像素还是CSS像素?实际上我们控制的是CSS像素,因为前面提到了,CSS像素是给我们web前端开发者创造的抽象概念。所以你要记住:当你给元素设置了 width: 200px 时,这个元素的宽度跨越了200个CSS像素。但是它并不一定跨越200个设备像素,至于会跨越多少个设备像素,就取决于手机屏幕的特性用户的缩放了,举个栗子:

    苹果手机的视网膜屏幕,是一个高密度屏幕,它的像素密度是普通屏幕的2倍,所以当我们设置 width: 200px; 时,200个CSS像素跨越了400个设备像素,如下图:

    苹果视网膜屏幕的设备像素与CSS像素的关系

    如果用户缩小页面,那么一个CSS像素会明显小于一个设备像素,这个时候 width: 200px; 这条样式中所设置的200个CSS像素跨越不了200个设备像素,如下图:

    用户缩页面时设备像素与CSS像素的关系

    让我们来做一个总结

    1、web前端领域,像素分为设备像素CSS像素
    3、一个CSS像素的大小是可变的,比如用后缩放页面的时候,实际上就是在缩小或放大CSS像素,而设备像素无论大小还是数量都是不变的。

    二、移动端的三个视口

    一看标题,你是不是蒙了?三个视口?什么三个视口?先别急,让我们慢慢来讲。
    你一定写过这样一条样式:width: 25%; 但是你有想过给一个元素加上这样一条样式之后发生了什么吗?25%是基于谁的25%?明白的同学可能知道了:一个块元素默认的宽度是其父元素的100%,是基于起父元素的,所以25%指的是父元素宽度的25%,所以,body元素的默认宽度是html元素宽度的100%,那么你有没有想过html元素的宽度是基于谁的呢?这个时候,就要引出一个概念:初始包含块和视口了

    记住一句话:视口是html的父元素,所以我们称视口为初始包含块。 这样你就明白了,html元素的百分比是基于视口的。

    第一个视口:布局视口

    首先你需要了解一个原因:浏览器厂商是希望满足用户的要求的,即在手机也能浏览为PC端设计的网站,所以浏览器厂商必须想办法来在满足~
    在PC浏览器中,视口只有一个,并且 视口的宽度 = 浏览器窗口的宽度,但是在移动端也要根据这个来设计的话,那么PC端设计的网站在移动端看起来会很丑,因为PC端的网页宽度在800 ~ 1024个CSS像素,而手机屏幕要窄的多,这个时候再PC端25%的宽度在移动端看起来会很窄。所以,布局视口的概念产生了。

    布局是口:移动端CSS布局的依据视口,即CSS布局会根据布局视口来计算。
    也就是说,在移动端,视口和浏览器窗口将不在关联,实际上,布局视口要比浏览器窗口大的多(在手机和平板中浏览器布局视口的宽度在768~1024像素之间),如下图(布局视口和窗口的关系):

    布局是口和浏览器窗口的关系

    可以通过以下JavaScript代码获取布局视口的宽度和高度:

    document.documentElement.clientWidth
    document.documentElement.clientHeight
    

    第二个视口:视觉视口

    视觉视口可能要更好理解一些,他就是用户正在看到网站的区域,如下图:

    视觉视口

    第三个视口:理想视口

    理想视口,这是我们最需要关注的视口,现在我们来回顾一下我们知道了哪些视口,有两个,分别是:布局视口,视觉视口。
    我们前面提到过,布局视口的宽度一般在 680~1024像素之间,这样可以使得PC网站在手机中不被压扁,但是这并不理想,因为手机更适合窄的网站,换句话说,布局视口并不是最理想的宽度,所以,就引入了理想视口。

    理想视口,定义了理想视口的宽度,比如对于iphone5来讲,理想视口是320*568。但是最终作用的还是布局视口,因为我们的css是依据布局视口计算的,所以你可以这样理解理想视口:理想的布局视口。下面这段代码可以告诉手机浏览器要把布局视口设为理想视口:

    <meta name="viewport" content="width=device-width" />
    

    上面那段代码告诉浏览器:将布局视口的宽度设为理想视口。所以,上面代码中的width指的是布局视口的宽 device-width 实际上就是理想视口的宽度。

    好了,移动端的三个视口介绍完了,让我们总结一下:

    1、在PC端,布局视口就是浏览器窗口
    2、在移动端,视口被分为两个:布局视口、视觉视口。
    3、移动端还有一个理想视口,它是布局视口的理想尺寸,即理想的布局视口。(注:理想视口的尺寸因设备和浏览器的不同而不同,但这对于我们来说无所谓)
    4、可以将布局视口的宽度设为理想视口

    三、设备像素比(DPR)

    下面你还需要了解一个概念,设备像素比(Device Pixel Ratio 简称:DPR)。
    下面是设备像素比的计算公式

    公式成立的大前提:(缩放比例为1)
    设备像素比(DPR) = 设备像素个数 / 理想视口CSS像素个数(device-width)

    与理想视口一样,设备像素比对于不同的设备是不同的,但是他们都是合理的,比如早起iphone的设备像素是320px,理想视口也是320px,所以早起iphone的DPR=1,而后来iphone的设备像素为640px,理想视口还是320px,所以后来iphone的DPR=2。在开发中,打开浏览器的调试工具可以看到设备像素比,如下图:

    chrome浏览器调试工具

    如上图,我们可以获得以下信息:
    iPhone5的理想视口是:320*568 <==> device-width = 320,device-height = 568
    iPhone5的设备像素比:2
    根据公式:设备像素比(DPR) = 设备像素个数 / 理想视口CSS像素个数(device-width)
    可知iPhone5的设备像素为 640*1136

    缩放

    在讲设备像素比公式的时候讲到了:

    公式成立的大前提:(缩放比例为1)
    设备像素比(DPR) = 设备像素个数 / 理想视口CSS像素个数(device-width)

    那么缩放到底是什么呢?也许这个问题让你很疑惑,如果你自己阅读了前面的内容,你会注意到CSS像素的大小是可变得,而缩放从技术实现的角度来讲,就是放大或缩小CSS像素的过程,怎么样?明白了吧,当你用双指缩放页面的时候,实际上是在放大或缩小CSS像素,至于什么是CSS像素,晕,回去从头好好看~

    也学你会觉得缩放没什么,但是你了解这个概念至关重要,因为在《一篇真正教会你开发移动端页面的文章(二)》中,会用到这里的概念。即

    缩放:缩小放大的是 CSS像素。

    meta标签

    meta视口标签存在的主要目的是为了让布局视口和理想视口的宽度匹配,meta视口标签应该放在HTML文档的head标签内,语法如下:

    <meta name="viewport" content="name=value,name=value" />
    

    其中 content 属性是一个字符串值,字符串是由逗号“,”分隔的 名/值 对组成,共有5个:

    1、width:设置布局视口的宽
    2、init-scale:设置页面的初始缩放程度
    3、minimum-scale:设置了页面最小缩放程度
    4、maximum-scale:设置了页面最大缩放程度
    5、user-scalable:是否允许用户对页面进行缩放操作

    下面是一个常用的meta标签实例

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    

    上面代码的意思是,让布局视口的宽度等于理想视口的宽度,页面的初始缩放比例以及最大缩放比例都为1,且不允许用户对页面进行缩放操作。

    媒体查询

    媒体查询是响应式设计的基础,他有以下三点作用:

    1、检测媒体的类型,比如 screen,tv等
    2、检测布局视口的特性,比如视口的宽高分辨率等
    3、特性相关查询,比如检测浏览器是否支持某某特性(这一点不讨论,因为它被目前浏览器支持的功能对于web开发来讲很无用)

    css中使用媒体查询的语法:

    @media 媒体类型 and (视口特性阀值){
        // 满足条件的css样式代码
    
    }
    

    下面是一段在css中使用媒体查询的示例:

    @media all and (min-width: 321px) and (max-width: 400px){
        .box{
            background: red;
        }
    }
    

    上面代码中,媒体类型为all,代表任何设备,并且设备的布局视口宽度大于等于321px且小于等于400px时,让拥有box类的元素背景变红。
    这里只是简单介绍了css3的媒体查询,但是用于本系列文章(我也呵呵了,其实就两篇文章这是第一篇,第二篇会尽快更新)已经足够用了,感兴趣或者希望获得更多css3 媒体查询知识的同学可以在网上查阅相关资料。

    经过6个小时的奋战,好吧,终于整理好了 《一篇真正教会你开发移动端页面的文章》系列文章的第一篇,这篇文章主要是为第二篇文章要将到的内容打基础,同学们如果不了解可以多阅读即便,如果有问题欢迎评论,我会及时回复的,另外,下一篇文章将会带来干货,真真正正的让大家掌握移动端页面的开发。如果你已经阅读到了这里,我对你表示感谢,你的坚持一定会得到回报,共勉

    展开全文
  • 今天给大家介绍10款有关HTML5移动开发APP开发框架,这几款框架都是比较优秀的移动 Web 开发框架,能够帮助开发者更加高效的开发移动Web应用。. 十款移动APP开发框架: 1.jquery mobile框架 2.bootstrap框架 3.ionic...

    今天给大家介绍10款有关HTML5移动开发APP开发框架,这几款框架都是比较优秀的移动 Web 开发框架,能够帮助开发者更加高效的开发移动Web应用。.

      十款移动APP开发框架:

    1.jquery mobile框架

    2.bootstrap框架

    3.ionic框架

    4.Mobile Angular UI框架

    5.Intel XDK框架

    6.Appcelerator Titanium框架

    7.Sencha Touch框架

    8.Kendo UI框架

    9.PhoneGap框架

    10.mui框架

      1.jquery mobile框架

    用于HTML5移动开发的10大移动APP开发框架

      jQuery Mobile是jQuery 在手机上和平板设备上的版本。jQuery Mobile 不仅会给主流移动平台带来jQuery核心库,而且会发布一个完整统一的jQuery移动UI框架。支持全球主流的移动平台。

      2.bootstrap框架

    用于HTML5移动开发的10大移动APP开发框架

      Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的,它简洁灵活,使得 Web 开发更加快捷。它由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架。Bootstrap提供了优雅的HTML和CSS规范,它即是由动态CSS语言Less写成。Bootstrap一经推出后颇受欢迎,一直是GitHub上的热门开源项目,包括NASA的MSNBC(微软全国广播公司)的Breaking News都使用了该项目。 国内一些移动开发者较为熟悉的框架,如WeX5前端开源框架等,也是基于Bootstrap源码进行性能优化而来。

      3.ionic框架

    用于HTML5移动开发的10大移动APP开发框架

      Ionic 是一个强大的 HTML5 应用程序开发框架,可以帮助您使用 Web 技术,比如 HTML、CSS 和 Javascript 构建接近原生体验的移动应用程序。Ionic 主要关注外观和体验,以及和你的应用程序的 UI 交互,特别适合用于基于 Hybird 模式的 HTML5 移动应用程序开发。

      4.Mobile Angular UI框架

    用于HTML5移动开发的10大移动APP开发框架

      Mobile Angular UI是使用 bootstrap 3 和 AngularJS 的响应式移动开发HTML5框架。

      Mobile Angular UI的关键字有:

      1. Bootstrap 3

      2. AngularJS

      Bootstrap 3 Mobile组件,比如switches, overlays和sidebars,这些都是bootstrap中没有的。

      AngularJS modules, 比如 angular-route, angular-touch 和 angular-animate

      响应式媒体查询是将bootstrap作为单独的文件,你只需要包含你所需要的东西。Mobile Angular UIu并不包含任何jQuery依赖,你需要做的只是通过一些AngularJS指令创建友好的用户体验。

      5.Intel XDK框架

    用于HTML5移动开发的10大移动APP开发框架

      Intel发布了其首个版本基于web的编程工具,可帮助开发者为Android和iOS开发移动应用。这款免费的软件名为Intel XDK,实际上这是今年2月份Intel收购的AppMobi软件的重新包装后的版本,所以并非新鲜事物。开发者可用此软件开发基于HTML5的应用,并 用于移动设备中。

      6.Appcelerator Titanium框架

    用于HTML5移动开发的10大移动APP开发框架

      Titanium 是一个跟手机平台无关的开发框架,用来开发具有本地应用效果的Web应用。当前主要支持 iPhone 和 Android 手机。

      主要提供的API包括:

      2D/3D animations

      Geo-location, compass, and maps

      Augmented reality features

      Social app authentication and native client support for email

      SOAP or REST API calls

      Audio, video, and image capture and playback

      Taps into local filesystem and SQL lite databases

      Accesses photo gallery or address data

      7.Sencha Touch框架

    用于HTML5移动开发的10大移动APP开发框架

      Sencha Touch框架是世界上第一个基于HTML5的Mobile App框架。Sencha Touch可以让你的Web App看起来像Native App。美丽的用户界面组件和丰富的数据管理,全部基于最新的HTML5和CSS3的 WEB标准,全面兼容Android和Apple iOS设备。提供了丰富的 WEB UI 组件,可以快速的开发出运行于移动终端的应用程序。

      8.Kendo UI框架

    用于HTML5移动开发的10大移动APP开发框架

      Kendo UI的每个方面都从底层开始构建,以提供强大的JavaScript应用程序性能。Kendo UI不是另一个jQuery UI的克隆,它的每一个决定都是从优化性能出发。从轻量级的、执行明显快于jQurey模板的内置模板库,到利用CSS3硬件加速的(如果可能)优化动画 和先进的虚拟化用户界面,Kendo UI不遗余力地提供高性能的客户端UI。

      9.PhoneGap框架

    用于HTML5移动开发的10大移动APP开发框架

      说到跨平台开发工具,很多人首先会想到PhoneGap。这样一款能够让开发者使用HTML、JS、CSS来开发跨平台移动App的开源免费框架,一直以来都深受开发者喜爱,从iOS、Android、BB10、Windows Phone到Amazon Fire OS、Tizen等,各大主流移动平台一应俱全,还能让开发者充分利用地理位置、加速器、联系人、声音等手机核心功能。

      业界很多主流的移动开发框架均源于PhoneGap。较著名的有Worklight、appMobi、WeX5等。其中WeX5为国内打造,完全Apache开源,在融合Phonegap的基础上,做了深度优化,具备接近Native app的性能,同时开发便捷性也较好。

      10.mui框架

    用于HTML5移动开发的10大移动APP开发框架

      最接近原生APP体验的高性能前端框架,具有以下特点:

      轻量

      追求性能体验,是我们开始启动MUI项目的首要目标,轻量必然是重要特征;

      MUI不依赖任何第三方JS库,压缩后的JS和CSS文件仅有100+K和60+K

      原生UI

      鉴于之前的很多前端框架(特别是响应式布局的框架),UI控件看起来太像网页,没有原生感觉,因此追求原生UI感觉也是我们的重要目标

      MUI以iOS平台UI为基础,补充部分Android平台特有的UI控件

      流畅体验

      •  下拉刷新

      为实现下拉刷新功能,大多H5框架都是通过DIV模拟下拉回弹动画,在低端android手机上,DIV动画经常出现卡顿现象(特别是图文列表的情况); mui通过双webview解决这个DIV的拖动流畅度问题;拖动时,拖动的不是div,而是一个完整的webview(子webview),回弹动画使用原生动画。

      •  侧滑导航

      mui提供了两种侧滑导航实现:webview模式和div模式,两种模式各有优劣,适用于不同的场景。每种侧滑实现模式,有不同的侧滑动画效果,主要分为四类:

      动画1:主界面移动、菜单不动

      动画2:主界面不动、菜单移动

      动画3:主界面和菜单同时移动

      动画4:缩放式侧滑(类手机QQ)

      •  滑动触发操作菜单

      在手机应用中(特别是iOS平台),很多操作菜单都是滑动触发的,比如短信界面,左滑显示“删除”按钮,点击可以删除该短信对话;邮件列表界面,左滑可以删除,右滑可以标注为”已读/未读”状态; mui的列表控件也支持滑动触发操作菜单功能,仅需按照特定格式拼装DOM结构即可;另外,滑动还支持事件触发,开发者可以通过监听滑动事件(slideleft/slideright),完成操作前的确认提醒工作。

    展开全文
  • 一,首先确定布局和基本权限二,当开始在主Activity中开始写代码的时候,首先找所需的控件,然后思考本Activity需不需要开启子线程.三,书写子线程代码示例工程activity_main.xml&lt;?xml version="...

    一,首先确定布局和基本权限

    二,当开始在主Activity中开始写代码的时候,首先找所需的控件,然后思考本Activity需不需要开启子线程.

    三,书写子线程代码






    示例工程


    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.mi.MainActivity">
    
        <com.handmark.pulltorefresh.library.PullToRefreshListView
            android:id="@+id/plv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    
    </LinearLayout>
    

    items.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ImageView
            android:id="@+id/img"
            android:layout_width="100dp"
            android:layout_height="100dp" />
    
        <TextView
            android:id="@+id/txt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </LinearLayout>

    MainActivity.java

    package com.example.mi;
    
    import android.os.Handler;
    import android.os.Message;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.ListView;
    import android.widget.Toast;
    
    import com.example.mi.bean.DateBean;
    import com.example.mi.utils.JsonDao;
    import com.example.mi.utils.NetStateUtil;
    import com.google.gson.Gson;
    import com.handmark.pulltorefresh.library.LoadingLayoutProxy;
    import com.handmark.pulltorefresh.library.PullToRefreshBase;
    import com.handmark.pulltorefresh.library.PullToRefreshListView;
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private PullToRefreshListView plv;
        private int oderType = 1;
        private int pageIndex = 1;
        private List <DateBean.NewslistBean> dataAll=new ArrayList <>();
        private MyAdapter adapter;
        private String url="https://api.tianapi.com/wxnew/?key=48a7d7193e11bd2dd4a683b6e2f90a4f&num=10&page="+pageIndex;
        private JsonDao jsonDao = new JsonDao(this);
        private Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                List <DateBean.NewslistBean> list = (List <DateBean.NewslistBean>)msg.obj;
                showData(list);
            }
        };
    
        private void showData(List <DateBean.NewslistBean> list) {
            if (oderType==1)
            {
                dataAll.clear();
            }
            dataAll.addAll(list);
            setPlvAdapter();
            plv.onRefreshComplete();
        }
    
        private void setPlvAdapter() {
            if (adapter==null)
            {
                adapter=new MyAdapter(MainActivity.this,dataAll);
                plv.setAdapter(adapter);
            }else {
                adapter.notifyDataSetChanged();
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            plv = findViewById(R.id.plv);
    
            initPlv();
    
            requestNetData();
    
        }
    
        private void requestNetData() {
            //判断网络是否连接
            if (NetStateUtil.isConn(this)){
                openThread();
            }else {
                NetStateUtil.showNoNetWorkDlg(this);
                //吐司提示
                Toast.makeText(this,"现在没有网络,请稍后重试!",Toast.LENGTH_SHORT).show();
                //展示本地数据
                String s = jsonDao.select(url);
                Gson gson = new Gson();
                showData(gson.fromJson(s,DateBean.class).getNewslist());
    
            }
        }
    
        public void openThread(){
            new Thread(){
                @Override
                public void run() {
                    try {
                        URL url1=new URL(url);
                        HttpURLConnection urlConnection = (HttpURLConnection)url1.openConnection();
                        urlConnection.setReadTimeout(5000);
                        urlConnection.setConnectTimeout(5000);
                        urlConnection.setRequestMethod("GET");
                        if (urlConnection.getResponseCode()==200)
                        {
                            InputStream inputStream = urlConnection.getInputStream();
                            String s = streamToString(inputStream);
                            jsonDao.insertData(url,s);
                            Gson gson = new Gson();
                            DateBean dateBean = gson.fromJson(s, DateBean.class);
                            List <DateBean.NewslistBean> newslist = dateBean.getNewslist();
                            Message mes = Message.obtain();
                            mes.obj=newslist;
                            handler.sendMessage(mes);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    
        private String streamToString(InputStream inputStream) {
            StringBuilder stringBuilder = new StringBuilder();
            String str;
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            try {
                while ((str=bufferedReader.readLine())!=null)
                {
                    stringBuilder.append(str);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return  stringBuilder.toString();
        }
    
        private void initPlv() {
            plv.setMode(PullToRefreshBase.Mode.BOTH);
            //设置下拉提示文字
            LoadingLayoutProxy layoutProxy = (LoadingLayoutProxy) plv.getLoadingLayoutProxy(true, false);
            //下拉的时候显示的文本
            layoutProxy.setPullLabel("继续拉,对,继续,拉,拉,拉");
            //放开刷新的时候显示的文本
            layoutProxy.setReleaseLabel("松开你就会发现一件神奇的事情");
            //执行刷新的时候显示的文本
            layoutProxy.setRefreshingLabel("哈哈哈哈哈,神奇不?");
    
            //设置上拉提示文字
            LoadingLayoutProxy layoutProxy1 = (LoadingLayoutProxy) plv.getLoadingLayoutProxy(false, true);
            //上拉的时候显示的文本
            layoutProxy1.setPullLabel("继续上拉,加载更多~");
            //放开刷新的时候显示的文本
            layoutProxy1.setReleaseLabel("松开加载");
            //执行刷新的时候显示的文本
            layoutProxy1.setRefreshingLabel("正在加载...");
    
    
            plv.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2 <ListView>() {
                @Override
                public void onPullDownToRefresh(PullToRefreshBase <ListView> pullToRefreshBase) {
                    oderType=1;
                    pageIndex=1;
                    url="https://api.tianapi.com/wxnew/?key=48a7d7193e11bd2dd4a683b6e2f90a4f&num=10&page="+pageIndex;
                    requestNetData();
                }
    
                @Override
                public void onPullUpToRefresh(PullToRefreshBase <ListView> pullToRefreshBase) {
                    oderType=2;
                    pageIndex++;
                    url="https://api.tianapi.com/wxnew/?key=48a7d7193e11bd2dd4a683b6e2f90a4f&num=10&page="+pageIndex;
                    requestNetData();
                }
            });
        }
    
        
    }
    

    package com.example.mi;
    
    import android.os.Handler;
    import android.os.Message;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.Adapter;
    import android.widget.ListView;
    import android.widget.Toast;
    
    import com.example.mi.bean.DateBean;
    import com.example.mi.utils.JsonDao;
    import com.example.mi.utils.NetStateUtil;
    import com.google.gson.Gson;
    import com.handmark.pulltorefresh.library.PullToRefreshBase;
    import com.handmark.pulltorefresh.library.PullToRefreshListView;
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private PullToRefreshListView plv;
        private int oderType = 1;
        private int pageIndex = 1;
        private List <DateBean.NewslistBean> dataAll=new ArrayList <>();
        private MyAdapter adapter;
        private String url="https://api.tianapi.com/wxnew/?key=48a7d7193e11bd2dd4a683b6e2f90a4f&num=10&page="+pageIndex;
        private JsonDao jsonDao = new JsonDao(this);
        private Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                List <DateBean.NewslistBean> list = (List <DateBean.NewslistBean>)msg.obj;
                showData(list);
            }
        };
    
        private void showData(List <DateBean.NewslistBean> list) {
            if (oderType==1)
            {
                dataAll.clear();
            }
            dataAll.addAll(list);
            setPlvAdapter();
            plv.onRefreshComplete();
        }
    
        private void setPlvAdapter() {
            if (adapter==null)
            {
                adapter=new MyAdapter(MainActivity.this,dataAll);
                plv.setAdapter(adapter);
            }else {
                adapter.notifyDataSetChanged();
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            plv = findViewById(R.id.plv);
    
            initPlv();
    
            requestNetData();
    
        }
    
        private void requestNetData() {
            //判断网络是否连接
            if (NetStateUtil.isConn(this)){
                openThread();
            }else {
                NetStateUtil.showNoNetWorkDlg(this);
                //吐司提示
                Toast.makeText(this,"现在没有网络,请稍后重试!",Toast.LENGTH_SHORT).show();
                //展示本地数据
                String s = jsonDao.select(url);
                Gson gson = new Gson();
                showData(gson.fromJson(s,DateBean.class).getNewslist());
    
            }
        }
    
        public void openThread(){
            new Thread(){
                @Override
                public void run() {
                    try {
                        URL url1=new URL(url);
                        HttpURLConnection urlConnection = (HttpURLConnection)url1.openConnection();
                        urlConnection.setReadTimeout(5000);
                        urlConnection.setConnectTimeout(5000);
                        urlConnection.setRequestMethod("GET");
                        if (urlConnection.getResponseCode()==200)
                        {
                            InputStream inputStream = urlConnection.getInputStream();
                            String s = streamToString(inputStream);
                            jsonDao.insertData(url,s);
                            Gson gson = new Gson();
                            DateBean dateBean = gson.fromJson(s, DateBean.class);
                            List <DateBean.NewslistBean> newslist = dateBean.getNewslist();
                            Message mes = Message.obtain();
                            mes.obj=newslist;
                            handler.sendMessage(mes);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    
        private String streamToString(InputStream inputStream) {
            StringBuilder stringBuilder = new StringBuilder();
            String str;
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            try {
                while ((str=bufferedReader.readLine())!=null)
                {
                    stringBuilder.append(str);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return  stringBuilder.toString();
        }
    
        private void initPlv() {
            plv.setMode(PullToRefreshBase.Mode.BOTH);
            plv.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2 <ListView>() {
                @Override
                public void onPullDownToRefresh(PullToRefreshBase <ListView> pullToRefreshBase) {
                    oderType=1;
                    pageIndex=1;
                    url="https://api.tianapi.com/wxnew/?key=48a7d7193e11bd2dd4a683b6e2f90a4f&num=10&page="+pageIndex;
                    requestNetData();
                }
    
                @Override
                public void onPullUpToRefresh(PullToRefreshBase <ListView> pullToRefreshBase) {
                    oderType=2;
                    pageIndex++;
                    url="https://api.tianapi.com/wxnew/?key=48a7d7193e11bd2dd4a683b6e2f90a4f&num=10&page="+pageIndex;
                    requestNetData();
                }
            });
        }
    
        
    }
    

    MyAdapter.java

    package com.example.mi;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.example.mi.bean.DateBean;
    import com.nostra13.universalimageloader.core.DisplayImageOptions;
    import com.nostra13.universalimageloader.core.ImageLoader;
    import com.nostra13.universalimageloader.core.assist.ImageScaleType;
    
    import java.util.List;
    
    /**
     * Created by Administrator on 2018/3/31.
     */
    
    public class MyAdapter extends BaseAdapter {
    
        private Context context;
        private List<DateBean.NewslistBean> list;
        private DisplayImageOptions options;
    
        public MyAdapter(Context context, List <DateBean.NewslistBean> list) {
            this.context = context;
            this.list = list;
            options=new DisplayImageOptions.Builder()
                    .cacheInMemory(true)//使用内存缓存
                    .cacheOnDisk(true)//使用磁盘缓存
                    .showImageOnLoading(R.mipmap.ic_launcher_round)//设置正在下载的图片
                    .showImageForEmptyUri(R.mipmap.ic_launcher_round)//url为空或请求的资源不存在时
                    .showImageOnFail(R.mipmap.ic_launcher_round)//下载失败时显示的图片
                    .bitmapConfig(Bitmap.Config.RGB_565)//设置图片色彩模式
                    .imageScaleType(ImageScaleType.EXACTLY)//设置图片的缩放模式
                    .build();
        }
    
        @Override
        public int getCount() {
            return list.size();
        }
    
        @Override
        public Object getItem(int i) {
            return list.get(i);
        }
    
        @Override
        public long getItemId(int i) {
            return i;
        }
    
        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
    
            ViewHolder viewHolder;
            if (view==null)
            {
                view=View.inflate(context,R.layout.items,null);
                viewHolder=new ViewHolder();
                viewHolder.img = view.findViewById(R.id.img);
                viewHolder.txt = view.findViewById(R.id.txt);
                view.setTag(viewHolder);
            }else {
                viewHolder=(ViewHolder)view.getTag();
            }
    
            ImageLoader.getInstance().displayImage(list.get(i).getPicUrl(),viewHolder.img,options);
            viewHolder.txt.setText(list.get(i).getTitle());
    
            return view;
        }
    
        class ViewHolder{
            ImageView img;
            TextView txt;
        }
    }
    

    package com.example.mi;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    import com.example.mi.bean.DateBean;
    import com.nostra13.universalimageloader.core.DisplayImageOptions;
    import com.nostra13.universalimageloader.core.ImageLoader;
    import com.nostra13.universalimageloader.core.assist.ImageScaleType;
    
    import java.util.List;
    
    /**
     * Created by Administrator on 2018/3/31.
     */
    
    public class MyAdapter extends BaseAdapter {
    
        private Context context;
        private List<DateBean.NewslistBean> list;
        private DisplayImageOptions options;
    
        public MyAdapter(Context context, List <DateBean.NewslistBean> list) {
            this.context = context;
            this.list = list;
            options=new DisplayImageOptions.Builder()
                    .cacheInMemory(true)//使用内存缓存
                    .cacheOnDisk(true)//使用磁盘缓存
                    .showImageOnLoading(R.mipmap.ic_launcher_round)//设置正在下载的图片
                    .showImageForEmptyUri(R.mipmap.ic_launcher_round)//url为空或请求的资源不存在时
                    .showImageOnFail(R.mipmap.ic_launcher_round)//下载失败时显示的图片
                    .bitmapConfig(Bitmap.Config.RGB_565)//设置图片色彩模式
                    .imageScaleType(ImageScaleType.EXACTLY)//设置图片的缩放模式
                    .build();
        }
    
        @Override
        public int getCount() {
            return list.size();
        }
    
        @Override
        public Object getItem(int i) {
            return list.get(i);
        }
    
        @Override
        public long getItemId(int i) {
            return i;
        }
    
        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
    
            ViewHolder viewHolder;
            if (view==null)
            {
                view=View.inflate(context,R.layout.items,null);
                viewHolder=new ViewHolder();
                viewHolder.img = view.findViewById(R.id.img);
                viewHolder.txt = view.findViewById(R.id.txt);
                view.setTag(viewHolder);
            }else {
                viewHolder=(ViewHolder)view.getTag();
            }
    
            ImageLoader.getInstance().displayImage(list.get(i).getPicUrl(),viewHolder.img,options);
            viewHolder.txt.setText(list.get(i).getTitle());
    
            return view;
        }
    
        class ViewHolder{
            ImageView img;
            TextView txt;
        }
    }
    

    MyApplication.java

    package com.example.mi;
    
    import android.app.Application;
    import android.os.Environment;
    
    import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
    import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
    import com.nostra13.universalimageloader.core.ImageLoader;
    import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
    
    import java.io.File;
    
    /**
     * Created by Administrator on 2018/3/31.
     */
    
    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            //程序的入口 函数
    
            //自定义缓存路径  sd卡下的piccache文件夹下
            File cachefile=new File(Environment.getExternalStorageDirectory().getPath()+"/piccache");
            //File cachefile1=getExternalCacheDir();//获取系统默认的sd卡缓存路径
    
            ImageLoaderConfiguration configuration=new ImageLoaderConfiguration.Builder(this)
                    .memoryCacheExtraOptions(480, 800)//缓存图片最大的长和宽
                    .threadPoolSize(2)//线程池的数量
                    .threadPriority(4)
                    .memoryCacheSize(2*1024*1024)//设置内存缓存区大小
                    .diskCacheSize(20*1024*1024)//设置sd卡缓存区大小
                    .diskCache(new UnlimitedDiscCache(cachefile))//自定义缓存目录
                    .writeDebugLogs()//打印日志内容
                    .diskCacheFileNameGenerator(new Md5FileNameGenerator())//给缓存的文件名进行md5加密处理
                    .build();
    
            ImageLoader.getInstance().init(configuration);
    
    
        }
    }
    

    package com.example.mi;
    
    import android.app.Application;
    import android.os.Environment;
    
    import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache;
    import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
    import com.nostra13.universalimageloader.core.ImageLoader;
    import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
    
    import java.io.File;
    
    /**
     * Created by Administrator on 2018/3/31.
     */
    
    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            //程序的入口 函数
    
            //自定义缓存路径  sd卡下的piccache文件夹下
            File cachefile=new File(Environment.getExternalStorageDirectory().getPath()+"/piccache");
            //File cachefile1=getExternalCacheDir();//获取系统默认的sd卡缓存路径
    
            ImageLoaderConfiguration configuration=new ImageLoaderConfiguration.Builder(this)
                    .memoryCacheExtraOptions(480, 800)//缓存图片最大的长和宽
                    .threadPoolSize(2)//线程池的数量
                    .threadPriority(4)
                    .memoryCacheSize(2*1024*1024)//设置内存缓存区大小
                    .diskCacheSize(20*1024*1024)//设置sd卡缓存区大小
                    .diskCache(new UnlimitedDiscCache(cachefile))//自定义缓存目录
                    .writeDebugLogs()//打印日志内容
                    .diskCacheFileNameGenerator(new Md5FileNameGenerator())//给缓存的文件名进行md5加密处理
                    .build();
    
            ImageLoader.getInstance().init(configuration);
    
    
        }
    }
    

    DataBean.java

    package com.example.mi.bean;
    
    import java.util.List;
    
    /**
     * Created by Administrator on 2018/3/31.
     */
    
    public class DateBean {
    
        /**
         * code : 200
         * msg : success
         * newslist : [{"ctime":"2018-03-30","title":"关于红楼梦的N个念头","description":"三联生活周刊","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900671.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MTc5MTU3NTYyMQ==&idx=2&mid=2650651785&sn=75526e17b4b480ea510f65ec3793bf54"},{"ctime":"2018-03-30","title":"广州有座\u201c猪宾馆\u201d,里面的猪会\u201c尬舞\u201d?这是世界首例!","description":"广州日报","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-45262906.static/640","url":"https://mp.weixin.qq.com/s?__biz=MjM5MjA0MDk2MA==&idx=2&mid=2652421905&sn=0ae22141145b30c848ecf0a0ac612153"},{"ctime":"2018-03-30","title":"\u201c你不就是个服务员吗?凭什么不让我换头等舱?\u201d","description":"暴走大事件","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900613.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzI4ODA1MDA2MQ==&idx=1&mid=2652647217&sn=efaa395be06b18c55b3e48a03c4a8677"},{"ctime":"2018-03-30","title":"左眼跳财,右眼跳灾?女子左眼皮连跳8年,终于出事了...","description":"FM93交通之声","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900601.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MTIzNDg3NzY2MA==&idx=3&mid=2653040765&sn=cafb01cf0ebcbd673ee4c4637764f0f2"},{"ctime":"2018-03-30","title":"千万别着急!又一大波重磅新车将上市,买早了吃亏!","description":"有车以后","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900573.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzAxMTA0ODE3Mw==&idx=1&mid=2656904565&sn=5a75df7049ca08181ecaf18476e1f370"},{"ctime":"2018-03-30","title":"2.0T+9AT的大7座SUV,卖多少钱能让你放弃汉兰达/奥迪Q5?","description":"车买买","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62898269.static/640","url":"https://mp.weixin.qq.com/s?__biz=MzA5NTU1MjEwNA==&idx=4&mid=2668673664&sn=197feccde9aad5f1c47772235c541e3e"},{"ctime":"2018-03-30","title":"黑人的头发养活了中国的一座城,这里藏着一段屈辱的民族史","description":"微在Wezeit","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900480.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzA5MjMzNTQyNw==&idx=1&mid=2656030884&sn=9a79b276f2c66c9b2694427d19ee5c72"},{"ctime":"2018-03-30","title":"朋友养了一只猛犬,网友本来是不相信的,直到靠近狗窝后.....","description":"大爱萌狗控","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900520.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MjM5NjQ0MDE1OQ==&idx=2&mid=2650063440&sn=42593507b8b2f992795fb8ed6c675a5a"},{"ctime":"2018-03-30","title":"中国最美的20个旅行目的地,走过10个以上,就算超级旅行达人。","description":"带着相机去旅行","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900363.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzAxNjI1MzUwOA==&idx=3&mid=2247521649&sn=9c9c6d637348d975baa2c4352039f286"},{"ctime":"2018-03-30","title":"【入流】网红食物打造指南","description":"腾讯时尚","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-61887411.static/640","url":"https://mp.weixin.qq.com/s?__biz=MjM5MTIxOTEyMA==&idx=2&mid=2650591720&sn=e7e246e4bdf3c2659fd26f36b88697fa"}]
         */
    
        private int code;
        private String msg;
        private List <NewslistBean> newslist;
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public List <NewslistBean> getNewslist() {
            return newslist;
        }
    
        public void setNewslist(List <NewslistBean> newslist) {
            this.newslist = newslist;
        }
    
        public static class NewslistBean {
            /**
             * ctime : 2018-03-30
             * title : 关于红楼梦的N个念头
             * description : 三联生活周刊
             * picUrl : https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900671.jpg/640
             * url : https://mp.weixin.qq.com/s?__biz=MTc5MTU3NTYyMQ==&idx=2&mid=2650651785&sn=75526e17b4b480ea510f65ec3793bf54
             */
    
            private String ctime;
            private String title;
            private String description;
            private String picUrl;
            private String url;
    
            public String getCtime() {
                return ctime;
            }
    
            public void setCtime(String ctime) {
                this.ctime = ctime;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
    
            public String getDescription() {
                return description;
            }
    
            public void setDescription(String description) {
                this.description = description;
            }
    
            public String getPicUrl() {
                return picUrl;
            }
    
            public void setPicUrl(String picUrl) {
                this.picUrl = picUrl;
            }
    
            public String getUrl() {
                return url;
            }
    
            public void setUrl(String url) {
                this.url = url;
            }
        }
    }
    

    package com.example.mi.bean;
    
    import java.util.List;
    
    /**
     * Created by Administrator on 2018/3/31.
     */
    
    public class DateBean {
    
        /**
         * code : 200
         * msg : success
         * newslist : [{"ctime":"2018-03-30","title":"关于红楼梦的N个念头","description":"三联生活周刊","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900671.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MTc5MTU3NTYyMQ==&idx=2&mid=2650651785&sn=75526e17b4b480ea510f65ec3793bf54"},{"ctime":"2018-03-30","title":"广州有座\u201c猪宾馆\u201d,里面的猪会\u201c尬舞\u201d?这是世界首例!","description":"广州日报","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-45262906.static/640","url":"https://mp.weixin.qq.com/s?__biz=MjM5MjA0MDk2MA==&idx=2&mid=2652421905&sn=0ae22141145b30c848ecf0a0ac612153"},{"ctime":"2018-03-30","title":"\u201c你不就是个服务员吗?凭什么不让我换头等舱?\u201d","description":"暴走大事件","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900613.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzI4ODA1MDA2MQ==&idx=1&mid=2652647217&sn=efaa395be06b18c55b3e48a03c4a8677"},{"ctime":"2018-03-30","title":"左眼跳财,右眼跳灾?女子左眼皮连跳8年,终于出事了...","description":"FM93交通之声","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900601.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MTIzNDg3NzY2MA==&idx=3&mid=2653040765&sn=cafb01cf0ebcbd673ee4c4637764f0f2"},{"ctime":"2018-03-30","title":"千万别着急!又一大波重磅新车将上市,买早了吃亏!","description":"有车以后","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900573.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzAxMTA0ODE3Mw==&idx=1&mid=2656904565&sn=5a75df7049ca08181ecaf18476e1f370"},{"ctime":"2018-03-30","title":"2.0T+9AT的大7座SUV,卖多少钱能让你放弃汉兰达/奥迪Q5?","description":"车买买","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62898269.static/640","url":"https://mp.weixin.qq.com/s?__biz=MzA5NTU1MjEwNA==&idx=4&mid=2668673664&sn=197feccde9aad5f1c47772235c541e3e"},{"ctime":"2018-03-30","title":"黑人的头发养活了中国的一座城,这里藏着一段屈辱的民族史","description":"微在Wezeit","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900480.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzA5MjMzNTQyNw==&idx=1&mid=2656030884&sn=9a79b276f2c66c9b2694427d19ee5c72"},{"ctime":"2018-03-30","title":"朋友养了一只猛犬,网友本来是不相信的,直到靠近狗窝后.....","description":"大爱萌狗控","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900520.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MjM5NjQ0MDE1OQ==&idx=2&mid=2650063440&sn=42593507b8b2f992795fb8ed6c675a5a"},{"ctime":"2018-03-30","title":"中国最美的20个旅行目的地,走过10个以上,就算超级旅行达人。","description":"带着相机去旅行","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900363.jpg/640","url":"https://mp.weixin.qq.com/s?__biz=MzAxNjI1MzUwOA==&idx=3&mid=2247521649&sn=9c9c6d637348d975baa2c4352039f286"},{"ctime":"2018-03-30","title":"【入流】网红食物打造指南","description":"腾讯时尚","picUrl":"https://zxpic.gtimg.com/infonew/0/wechat_pics_-61887411.static/640","url":"https://mp.weixin.qq.com/s?__biz=MjM5MTIxOTEyMA==&idx=2&mid=2650591720&sn=e7e246e4bdf3c2659fd26f36b88697fa"}]
         */
    
        private int code;
        private String msg;
        private List <NewslistBean> newslist;
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public List <NewslistBean> getNewslist() {
            return newslist;
        }
    
        public void setNewslist(List <NewslistBean> newslist) {
            this.newslist = newslist;
        }
    
        public static class NewslistBean {
            /**
             * ctime : 2018-03-30
             * title : 关于红楼梦的N个念头
             * description : 三联生活周刊
             * picUrl : https://zxpic.gtimg.com/infonew/0/wechat_pics_-62900671.jpg/640
             * url : https://mp.weixin.qq.com/s?__biz=MTc5MTU3NTYyMQ==&idx=2&mid=2650651785&sn=75526e17b4b480ea510f65ec3793bf54
             */
    
            private String ctime;
            private String title;
            private String description;
            private String picUrl;
            private String url;
    
            public String getCtime() {
                return ctime;
            }
    
            public void setCtime(String ctime) {
                this.ctime = ctime;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
    
            public String getDescription() {
                return description;
            }
    
            public void setDescription(String description) {
                this.description = description;
            }
    
            public String getPicUrl() {
                return picUrl;
            }
    
            public void setPicUrl(String picUrl) {
                this.picUrl = picUrl;
            }
    
            public String getUrl() {
                return url;
            }
    
            public void setUrl(String url) {
                this.url = url;
            }
        }
    }

    MyDatabaseHelper.java

    package com.example.mi.dateBase;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    /**
     * Created by Administrator on 2018/4/1.
     */
    
    public class MyDatebaseHelper extends SQLiteOpenHelper {
    
        //构造方法,用于确定数据库的名称,版本号等
        public MyDatebaseHelper(Context context) {
            super(context, "mydb",null,1);
        }
    
        //创建数据库时调用一次,初始化表中数据
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
            sqLiteDatabase.execSQL("create table users(id integer primary key autoincrement,urlPath text,jsonData text)");
        }
    
        //数据库版本更新时回调
        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    
        }
    }
    

    package com.example.mi.dateBase;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    /**
     * Created by Administrator on 2018/4/1.
     */
    
    public class MyDatebaseHelper extends SQLiteOpenHelper {
    
        //构造方法,用于确定数据库的名称,版本号等
        public MyDatebaseHelper(Context context) {
            super(context, "mydb",null,1);
        }
    
        //创建数据库时调用一次,初始化表中数据
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
            sqLiteDatabase.execSQL("create table users(id integer primary key autoincrement,urlPath text,jsonData text)");
        }
    
        //数据库版本更新时回调
        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    
        }
    }
    

    JsonDao.java

    package com.example.mi.utils;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    
    import com.example.mi.dateBase.MyDatebaseHelper;
    
    /**
     * Created by Administrator on 2018/4/2.
     */
    
    public class JsonDao {
    
        private final MyDatebaseHelper myDatebaseHelper;
    
        //构造方法,便于别类中调用
        public JsonDao(Context context){
            myDatebaseHelper = new MyDatebaseHelper(context);
        }
    
        //插入方法
        public void insertData(String urlpath,String jsondata){
            //1.得到数据库的可写权限
            SQLiteDatabase writableDatabase =myDatebaseHelper.getWritableDatabase();
            //2.先清空表数据
            writableDatabase.delete("users","urlPath=?",new String[]{urlpath});
    
            //3.新建数据对象,作为插入语句参数
            ContentValues contentValues = new ContentValues();
            //4.将要插入的数据存到对象中
            contentValues.put("urlPath",urlpath);
            contentValues.put("jsonData",jsondata);
    
            //5.插入数据,
            long users = writableDatabase.insert("users", null, contentValues);
        }
    
        public String select(String urlpath){
            //1.得到数据库的可写权限
            SQLiteDatabase writableDatabase =myDatebaseHelper.getWritableDatabase();
            //2.按条件查询
            Cursor userss = writableDatabase.query("users", null, "urlPath=?", new String[]{urlpath}, null, null, null);
            //4.创建一个字符串变量,用来储存结果
            String data="";
            //3.获得数据结果
            while (userss.moveToNext()){
                data=userss.getString(userss.getColumnIndex("jsonData"));
            }
            //返回结果
            return data;
        }
    
    }
    

    package com.example.mi.utils;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    
    import com.example.mi.dateBase.MyDatebaseHelper;
    
    /**
     * Created by Administrator on 2018/4/2.
     */
    
    public class JsonDao {
    
        private final MyDatebaseHelper myDatebaseHelper;
    
        //构造方法,便于别类中调用
        public JsonDao(Context context){
            myDatebaseHelper = new MyDatebaseHelper(context);
        }
    
        //插入方法
        public void insertData(String urlpath,String jsondata){
            //1.得到数据库的可写权限
            SQLiteDatabase writableDatabase =myDatebaseHelper.getWritableDatabase();
            //2.先清空表数据
            writableDatabase.delete("users","urlPath=?",new String[]{urlpath});
    
            //3.新建数据对象,作为插入语句参数
            ContentValues contentValues = new ContentValues();
            //4.将要插入的数据存到对象中
            contentValues.put("urlPath",urlpath);
            contentValues.put("jsonData",jsondata);
    
            //5.插入数据,
            long users = writableDatabase.insert("users", null, contentValues);
        }
    
        public String select(String urlpath){
            //1.得到数据库的可写权限
            SQLiteDatabase writableDatabase =myDatebaseHelper.getWritableDatabase();
            //2.按条件查询
            Cursor userss = writableDatabase.query("users", null, "urlPath=?", new String[]{urlpath}, null, null, null);
            //4.创建一个字符串变量,用来储存结果
            String data="";
            //3.获得数据结果
            while (userss.moveToNext()){
                data=userss.getString(userss.getColumnIndex("jsonData"));
            }
            //返回结果
            return data;
        }
    
    }

    NetStateUtil.java

    package com.example.mi.utils;
    
    import android.app.AlertDialog;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    
    import com.example.mi.R;
    
    /**
     * Created by Administrator on 2018/4/2.
     */
    
    public class NetStateUtil {
    
        //判断网络是否打开
        public static boolean isConn(Context context){
            //1.设置网络状态变量,默认false(false:关闭,true:打开)
            boolean isConnFlag=false;
            //2.获得系统权限
            ConnectivityManager conManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
            //3.获得网络管理权限
            NetworkInfo network = conManager.getActiveNetworkInfo();
            //4.判断是否获得权限
            if(network!=null){
                //5.获得网络连接状态
                isConnFlag=network.isAvailable();
            }
            //返回链接状态结果
            return isConnFlag;
        }
    
        //当没有网络时选择是否打开网络设置
        public static void showNoNetWorkDlg(final Context context){
            //1.创建弹出式对话框对象
            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setIcon(R.mipmap.ic_launcher)//设置提示图片
                    .setTitle(R.string.app_name)//设置提示标题(通常是应用名)
                    .setMessage("当前没有网络连接!")//设置提示信息
                    .setPositiveButton("设置", new DialogInterface.OnClickListener() {//设置确定按钮样式
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            // 跳转到系统的网络设置界面
                            Intent intent = null;
                            // 先判断当前系统版本
                            if(android.os.Build.VERSION.SDK_INT > 10){  // 3.0以上
                                intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
                            }else{
                                intent = new Intent();
                                intent.setClassName("com.android.settings", "com.android.settings.WirelessSettings");
                            }
                            //开启设置页面
                            context.startActivity(intent);
                        }
                    })
                    .setNegativeButton("知道了", null)//设置取消按钮样式
                    .show();
        }
    }
    package com.example.mi.utils;
    
    import android.app.AlertDialog;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    
    import com.example.mi.R;
    
    /**
     * Created by Administrator on 2018/4/2.
     */
    
    public class NetStateUtil {
    
        //判断网络是否打开
        public static boolean isConn(Context context){
            //1.设置网络状态变量,默认false(false:关闭,true:打开)
            boolean isConnFlag=false;
            //2.获得系统权限
            ConnectivityManager conManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
            //3.获得网络管理权限
            NetworkInfo network = conManager.getActiveNetworkInfo();
            //4.判断是否获得权限
            if(network!=null){
                //5.获得网络连接状态
                isConnFlag=network.isAvailable();
            }
            //返回链接状态结果
            return isConnFlag;
        }
    
        //当没有网络时选择是否打开网络设置
        public static void showNoNetWorkDlg(final Context context){
            //1.创建弹出式对话框对象
            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setIcon(R.mipmap.ic_launcher)//设置提示图片
                    .setTitle(R.string.app_name)//设置提示标题(通常是应用名)
                    .setMessage("当前没有网络连接!")//设置提示信息
                    .setPositiveButton("设置", new DialogInterface.OnClickListener() {//设置确定按钮样式
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            // 跳转到系统的网络设置界面
                            Intent intent = null;
                            // 先判断当前系统版本
                            if(android.os.Build.VERSION.SDK_INT > 10){  // 3.0以上
                                intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
                            }else{
                                intent = new Intent();
                                intent.setClassName("com.android.settings", "com.android.settings.WirelessSettings");
                            }
                            //开启设置页面
                            context.startActivity(intent);
                        }
                    })
                    .setNegativeButton("知道了", null)//设置取消按钮样式
                    .show();
        }
    }
    

    AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.mi">
    
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.INTERNET"/>
        <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"
            android:name=".MyApplication">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    展开全文
  • 作者 | 曹立成本文经授权转自InterestDriven(ID:hello123android) 2019年人工智能系统学: ...从2017年下半年开始,就开始听到各种言论,例如 “Android 开发凉凉”、“移动端开发没出路了赶紧转行”、“要被...

    640?wx_fmt=gif

    640?wx_fmt=jpeg

    作者 | 曹立成
    本文经授权转自InterestDriven(ID:hello123android)

    2019年人工智能系统学:

    https://edu.csdn.net/topic/ai30?utm_source=csdn_bw

    我是一名 Android 开发者。从2017年下半年开始,就开始听到各种言论,例如 “Android 开发凉凉”、“移动端开发没出路了赶紧转行”、“要被 XXX 替代了” 等等,充分反映了大家焦虑的心态。

    移动端开发真的要凉凉了吗?我也经常和朋友聊起这个话题,今天我决定写下自己的一些看法,供大家参考。

     

    640?wx_fmt=png

    移动端开发的现状

     

     

    移动端开发的现状是什么?我们可以从自己写的代码中寻找线索。以 Android 为例,很多大公司的移动端开发者写的最多的代码是这样的:

    LinearLayout layout = new LinearLayout();
    layout.addView(xxxx);
    ...
    

    或者也许是这样的:

    public class XXXView extends RelativeLayout {
    
        public XXXView(Context context) {
            this(context, null);
        }
    
        public XXXView(Context context,
                @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public XXXView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView();
        }
    
        private void initView() {
            LayoutInflater.from(getContext()).inflate(R.layout.xxxlayout, thistrue);
            ...
        }
    
        public void setData(XXX xxx) {
          ...
        }
    }
    

    又或者是对着 xml 标签做出各种骚操作——UI 开发。

    没错,如今移动端技术栈已经愈发趋于成熟完善,对业务来说,就连大公司的工程师也是在做 UI 的展示逻辑。大公司产品相对比较完善,后端管控了大部分业务逻辑,客户端做的就是取到后端的数据,然后通过setText(xxx)展示出来,然后通过接口返回的 Boolean 值来判断 View 显示还是隐藏。

    我听到很多人说,工作几年感觉自己没什么提升,天天都在堆代码,随便找个刚毕业的学生也能分分钟替代自己,于是就很焦虑。那对于工作几年的人来说,要想尽可能不让自己过早的被替代、被淘汰,就需要选一个有潜力的有前景的领域深挖。

     

    640?wx_fmt=png

    那么移动端开发的未来在哪里?

     

    动态化

    在聊动态化之前,我们先聊聊 Android 的插件化。

    前两年,插件化火起来了。为什么火?因为可以实现 Android 应用不发版本的同时动态上线需求,同时热修复还可以动态的修复线上出现的 bug。但是插件化存在最大的问题是什么呢?兼容性。Android 机型太多太复杂,插件化框架难免会涉及到系统 API 的 hook,兼容性问题就出现了,开发者会看到莫名其妙的错误上报上来却束手无策。

    随着 Android 版本的迭代更新,插件化这条路越来越不好走,Android P 给了开发者们一个信号:别随便 hook 系统 API 了,Google 要开始收口整顿了。插件化这条路注定无法一直走下去,于是开发者们另辟蹊径。

    最开始是继续用 H5 混合开发的方式,例如老牌 Hybrid 框架 Cordova,但是性能不尽人意。这时候 Facebook 开发出了一个跨时代的框架:React-Native。它提供了一个全新的思路:通过 jscore 进行 js 解析,使用原生的 View 进行渲染,提供桥机制调用原生的能力。

    React-Native 是第一个真正的高性能的动态化框架,它的出现让 Web 前端和客户端的界限迅速变得模糊。随之而来的是各大公司的自研框架,例如阿里巴巴的 Weex 和美团点评的 Picasso 框架。React-Native 只提供了 Android 和 iOS 的双端支持,Weex 扩展了思路,提供的 Web 端的支持,一个是 React 语法糖,一个是 Vue 语法糖。Picasso 又不一样,Picasso 是纯 TS,DSL 语法树,写法实在太简单,又准备在三端的基础上提供小程序端的支持。

    2018年 GMTC 大会上,Flutter 发布了第一个预览版,阿里巴巴闲鱼团队已经在闲鱼 APP 中使用了 Flutter 技术。Flutter 四年前开始开发,直到去年才有第一个 beta 版本。通过 Dart 编写 APP,然后编译成机器码同时运行在 Android 和 iOS 上,做到了原生跨平台,Hot Reload,性能很不错,可定制性也非常强,连封装的系统层 Dart 代码都能改。提供了 Web、iOS、Android、React-Native 开发者学习 Flutter 的入门文档,也是去年移动端少数的几个比较火的热点之一。它的思路不同于 React-Native,是一种全新的思路,目前我对 Flutter 持乐观态度。

    支付宝的动态方式不同于以上所有,它是完全基于自己的 H5 容器进行 Hybrid 开发,不过由于内核也是自研的(UC 内核),所以性能各方面都比原生的 WebView 好,支付宝里非常多的页面都是基于 H5 容器开发的,包括支付宝小程序,这也算是一种动态化的方式吧,只不过研发成本太高:自研 WebView 内核。

    目前来看,动态化的三驾马车已经初步形成:

    • React-Native 为首的 jscore(v8)桥通信动态化方案;

    • 自研 WebView 内核,H5 容器化方案;

    • Flutter(我觉得 Flutter 正在发力争取第三驾马车的位置)。

    动态化改变了移动开发的方式,模糊了 Web 前端和客户端的界限,让越来越多的人认识了什么是 “大前端”。如果有经验的开发者们对这个方向进行深入研究,五年内,应该不会被淘汰了(笑)。

    移动端机器学习

    毫无疑问,移动端机器学习从2017年年底就开始火起来了,TensorFlow 也推出了移动端框架,很多应用都开始在移动端部署机器学习模型,例如相机类应用(FaceU)、电商类应用(唯品会)等,包括离线的机器学习。

    如果深挖这个方向,我觉得十年内不会被淘汰。当然了,难度也不小。

    AR & VR

    从支付宝的 AR 抢红包、QQ 的 AR 踢球等玩法被越来越多的人体验,AR 走进了人们的视野。对于广告引流玩法来说,AR 无疑是能带来巨大收益的。

    此前大众点评亲子上线了 “亲子奇妙日” 活动,就是基于大头儿子小头爸爸的 AR 玩法。那么开发 AR 引擎的移动开发者自然成了香饽饽,目前 AR 主要应用的领域还是广告变现,不过很多公司也找到了 AR 的一些落地场景,比如卖家具的电商公司可以通过 AR 技术让用户在购买之前就能看到实物在自己家里到底能不能放得下,这能极大的提升用户的购买体验。

    如果是研究 OpenGL、计算机图形学的移动开发者,可以往 AR 引擎开发方向发展,非常有前景。

    VR 同理。

    移动端音视频

    这个领域最火的产品当之无愧是抖音了,就连我自己都天天刷抖音。除了抖音,还有各大视频应用,都需要音视频开发领域的专业开发人员,图像处理、滤镜、裁剪等等骚操作都是需要专业的音视频开发知识才能做出来的,研究 OpenGL、计算机图形学的移动开发者在这个领域也有很大的优势。

    据我所知,这方面人才现在缺口依然很大,各大公司求贤若渴,如果在这方面有经验并且愿意深挖的同学,自然是各大公司的香饽饽啦。

    移动端区块链

    区块链,2018年最火的技术没有之一。

    从人人都在谈比特币到人人都在谈区块链,可以看出区块链技术已经被越来越多人熟知。无数区块链公司如雨后春笋般的冒出,连大公司们也按捺不住开始涉足区块链领域。对于移动端来说,区块链应用最多的还是数字钱包。但是未来移动端区块链一定会有更多的落地应用,例如 Status。区块链去年大火,移动端的区块链应用还没开始火,但是也不远了。

    如果有移动开发同学对区块链方向感兴趣的,可以开始研究起来了。如果要学习智能合约开发的同学,我推荐一个非常好的网站:Cryptozombies,绝对是入门 solidity 智能合约开发的精品。

    目前移动端的区块链人才还不多,各大公司也没有在招,但是两三年内,应该就会爆发了,现在还在探索落地场景的阶段,也给了对区块链技术感兴趣的移动开发同学学习的时间。改变世界的技术了解一下?

    移动基础框架

    UI 未来可能都会被动态化技术接管,但是移动基础框架不会。但凡要开发一个 APP,网络请求、日志处理、数据库处理、缓存、Push 等框架都是不可或缺的,这部分和 UI 关系不大,又是 APP 的命脉所在。

    只要 APP 存在,基础框架就存在。有同学可能要说了,我用 okhttp 分分钟写一个网络请求出来,没错,这是 okhttp 已经帮我们做好了很多事。什么是框架?用一句话总结,我的理解就是:

    一种可以让能力差别较大的开发者写出功能、性能差不多的代码的库。

    不知道大家能不能理解这种说法,我举个例子:如果使用 HttpUrlConnection 来写网络请求,可能不同能力的开发者写出来的代码不一样,网络请求的性能和效果也不一样。但是如果使用了 okhttp 库,只要看一看 okhttp 的文档,新手和老手写出来的代码估计差不多,性能和效果也差不多,这就是框架做出的最大的贡献。

    在这些框架的基础上,P5 写的代码可能和 P6、P7 区别不会多大。然而,这些框架总要有人开发,开源的框架往往不能满足公司业务的需求,需要自研基础框架。尤其是 BAT、TMD 六家公司,移动端框架有大部分都是自研的,越来越多的公司也开始自研,所以如果是在这个方向有经验的移动开发者,在 APP 被淘汰之前,应该都不会被淘汰。这方面对移动开发者的知识深度和广度都要求相对较高,需要沉淀和学习。

    Android 工具类应用

    这个就比较特殊了,这类应用我举个例子:360 手机助手。对于 Android 用户来说,清理内存、清理垃圾等已经成为了一种习惯,这方面应用的开发自然是不可少的,而且非常考验对 Android 知识的深度,你们懂得。

     

    640?wx_fmt=png

    结语

     

    上面六个(七个)移动端开发方向,是我总结出来的有潜力有前景的方向,各位移动开发同学可以参考,可以选择一个方向进行深挖,相信一定会有收获。

    嘴上焦虑是没有意义的,最好的办法就是付出行动。

    积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里;不积小流,无以成江海。骐骥一跃,不能十步;驽马十驾,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也。蟹六跪而二螯,非蛇鳝之穴无可寄托者,用心躁也。

    60s测试:你是否适合转型人工智能?

    https://edu.csdn.net/topic/ai30?utm_source=csdn_bw

    640?wx_fmt=jpeg

     热 文 推 荐 

    ☞ 华为“造车”?

    ☞ 苹果的困境源于优质移动应用的垮台吗?

    ☞ Redis Labs 再次更改开源许可证,但 Redis 本身不受影响

    那些简历造假拿 Offer 的程序员,后来都怎么样了?

    被V神点赞, 我是如何用五子棋打败以太坊排名最高的应用的? |人物志

    ☞ 50个最有价值的数据可视化图表(推荐收藏)

    一键免费自动AI抠图,效果连PS大哥也点赞!

    史上最难的一道Java面试题

     

    
     

    print_r('点个好看吧!');
    var_dump('点个好看吧!');
    NSLog(@"点个好看吧!");
    System.out.println("点个好看吧!");
    console.log("点个好看吧!");
    print("点个好看吧!");
    printf("点个好看吧!\n");
    cout << "点个好看吧!" << endl;
    Console.WriteLine("点个好看吧!");
    fmt.Println("点个好看吧!");
    Response.Write("点个好看吧!");
    alert("点个好看吧!")
    echo "点个好看吧!"

    640?wx_fmt=png喜欢就点击“好看”吧!

    展开全文
  • 目前来说主流的App开发方式有三种:Native App 、Web App、Hybird App。下面我们来分析一下这三种App开发方式的优劣对比: 一 :Native App 即 原生App开发 优点: (1)打造完美的用户体验 (2)性能稳定 (3)操作...
  • 十大移动开发平台

    2018-12-28 19:51:35
    基于浏览器的集成开发环境,可视化和智能化的设计,能轻松完成常规应用和面向手机的移动应用开发;高效、稳定和可扩展的特点,适合复杂企业级应用的运行;跨平台、数据库和浏览器的架构,适应复杂的服务器和客户端...
  • 一、移动端开发分为以下几个方向: 1、native app开发(原生app开发)-所有在应用商店中下载安装的程序都是原生app(都是安卓和ios开发的) --安卓(Java-Native); --IOS(Object-C/swift); 优势:直接安装...
  • 选择合适的技术栈是移动开发领域最关键的决策之一。你选择的技术栈将为你开发的移动应用提供基本的功能、可拓展和维护性。选择技术栈时应该注意项目的类型、预期投入市场的时间和创意的可行性。 原则上来说,选择何...
  • 很难说,因为Java、object-C在原生开发界已大行其道这么多年,C#这么多年固步自封,很难说在移动开发领域能重新有所建树。   目前国内有一个基于VB/C#的APP开发平台,叫Smobiler,它主打的就是.Net移动开发,使用...
  • 什么是移动开发

    2014-04-16 10:29:35
    移动开发[1]也称为手机开发,或叫做移动互联网开发[2]。是指以手机、PDA、UMPC等便携终端为基础,进行相应的开发工作,由于这些随身设备基本都采用无线上网的方式,因此,业内也称作为无线开发。 移动应用开发是为...
  • CSDN博客第一期移动开发排行榜圆满结束,恭喜所有上榜用户,为继续展示移动开发方向优秀博主,发掘潜力新星,为想了解移动开发方向的博客用户提供平台,CSDN博客第二期移动开发排行榜开始。我们将邀请CSDN博客频道...
  • 网页开发要面对各种各样的浏览器,让人很头疼,而移动开发中,你不但要面对浏览器,还要面对各种版本的手机,ios好一点,而安卓就五花八门了,你可能在开发中也被它们折磨过,或者正在被它们折磨,我在这里说几个我...
  • 今天给大家介绍10款有关HTML5移动开发APP开发框架,这几款框架都是比较优秀的移动 Web 开发框架,能够帮助开发者更加高效的开发移动Web应用。.  十款移动APP开发框架: 1.jquery mobile框架 2.bootstrap框架 3....
  • Asp.net 移动开发

    2015-06-23 17:09:10
    Asp.net能进行移动开发移动开发是手机运用,而asp.net是网页开发,能合在一起吗?答案是能的,随着科技的发展,现在asp.net也能进行移动开发移动开发也称为手机开发,或叫做移动互联网开发。是指以手机、PDA...
  • 一、2019新版前端与移动开发学习路线图---每阶段可掌握的能力及各阶段覆盖的关键字 二、2019新版前端与移动开发学习路线图---学习大纲及各阶段知识点 三、2019新版前端与移动开发学习路线图--升级后新增...
  • 为充分展示移动开发方向优秀博主,发掘潜力新星,为想更多了解移动开发方向的博客用户提供平台,CSDN博客频道推出“移动开发排行榜”活动,每月一期。我们将邀请CSDN博客频道活跃的资深专家参与评选。同时,获奖者有...
  • 其实在微信小程序出来之前,就有不少同学问我关于 web 前端与移动开发的选择问题,只不过微信小程序出来了,微信的影响力不得不值得关注,这个问题就被放大了,所以,今天单独写篇文章来解答下你们的困惑。...
1 2 3 4 5 ... 20
收藏数 745,839
精华内容 298,335
关键字:

移动开发