由于公司的电脑是加密的,上传代码应该是乱码,所以,只好贴在这里了。有点繁琐。。。
通过和服务器的通信(服务器不是我开发的),实现了下拉刷新,异步下载apk,异步下载图片,文件的读写以及复制操作。下载安装更新。主要的内容还是在对数据的处理、逻辑的编写。通过设置支持的语种,实现动态过滤显示列表。
这是目录结构
资源文件的下载:
效果图片:
需求文档
应用信息:
----------
类包名:com.minstore.market
应用名:MinStore
文件名:MinStore.apk
应用图标:制作一个专有的图标
界面语言:应用市场只做汉语界面,不对应其他语种界面。
屏幕尺寸:不仅仅是随ROM,发布而是个独立应用,所以适应所有屏幕尺寸。
概念释疑:
预装:安装ROM后自动安装好了,直接在应用列表里能看见。不能删除,但是能更新升级。
预包含:直接打包在一个预装应用(应用市场)里,用户启动应用市场后不用下载就可以直接安装。
应用市场可以是随ROM发布的内置应用,也可以是通过网络下载的通用应用。
功能概述:
应用市场本身包含一部分预包含应用,可以直接安装。也可以从服务器下载应用清单后下载新应用。
应用市场主界面上显示的应用列表来自两个地方:预包含应用名称和刷新时候从服务器获取的应用名称。
应用各种信息是通过通过三方信息来源来的:
☆预包含应用描述文件
☆刷新应用列表时候从服务器获取的应用描述文件
☆本地已经安装的应用列表(只更新名单里已有应用的状态,不增减应用列表数量)
-----------------
应用工程目录结构:(MinStore为工程名,assets为安卓项目里的固定名称)
/MinStore/res
/MinStore/src
/MinStore/libs
/MinStore/assets/applist/applist.preincluded.description 预装应用列表描述文件,标准JSON格式
/MinStore/assets/applist/com.sample.abc/abc.apk 样例应用1的文件包
/MinStore/assets/applist/com.sample.abc/logo.png 样例应用1的图标图片
/MinStore/assets/applist/com.sample.abc/sc1.png 样例应用1的截屏1
/MinStore/assets/applist/com.sample.abc/sc2.png 样例应用1的截屏2
-------------
内部存储空间:
所谓内部存储空间是指只有应用本身能够访问,别人看不到。删除应用时候自动被删除的存储空间。
被存在在手机的特定目录下,是依据应用包名唯一定位的。
/data/data/com.minstore.market/applist/applist.all.description
/data/data/com.minstore.market/applist/com.sample.abc/
/data/data/com.minstore.market/applist/com.sample.xyz/
=======================================================================================
应用描述文件
------------
一共有三个应用描述文件,说明如下:
☆应用市场编译时包含的预包含应用的应用描述文件:applist.preinstalled.description
随应用市场发布的,不能更新不能删除。描述随应用市场一期发布的,预包含在应用市场里应用的描述文件。这里描述的文件不需要下载,可以直接安装。只是对应用市场目录/assets/applist/下预包含应用的描述。
保存在应用包里:file:///android_asset/applist/applist.preinstalled.description
☆应用市场更新应用列表时下载的服务器应用描述文件:applist.downloaded.description
每次刷新应用时候需要从服务器获取最新应用列表,就是这个应用描述文件。这里描述服务器上已经准备好的所有应用的信息。这里的有些应用预包含在应用市场里,也有些已经安装到手机里。
只有刷新(拖动释放)时候才获取此文件。应用市场启动时候不刷新应用列表。
从服务器获取,每次刷新应用列表时候都从服务器获取一次,不需要本地保存。
为了调试也可以保存,那就保存在[应用内部存储空间]。
内部存储空间:/data/data/youPackageName/
每个应用独立的,别人访问不到。
/data/data/com.minstore.market/applist/applist.downloaded.description
☆应用市场软件运行时候管理的所有应用的应用描述文件:applist.all.description
此文件刚开始时候是不存在的,是由应用市场组合[applist.preinstalled.description、applist.downloaded.description、已经安装的应用列表]等三个信息源组合后生成,并保存在应用私有数据空间的。
存储路径:/data/data/com.minstore.market/applist/applist.all.description
应用每次启动时候,都要判断文件[applist.all.description]是否存在,如果不存,则需要从[applist.preinstalled.description]重新生成。只要存在[applist.all.description],那么后续就不需要使用[applist.preinstalled.description]了。第一次生成[applist.all.description]时,不需要从服务器获取最新应用描述文件。
=======================================================================================
应用版本及其版本号码:
☆判断两个应用是否为同一个应用:
应用的应用包名是唯一的,判断他俩是否一样。
☆正常应用的版本信息
versionCode:整数型,容易比较,给开发人员看的。容易比较大小。
versionName:字符串,给用户看的。不容易比较大小。
※在文件AndroidManifest.xml里写的,但是有些业余开发人员是不写这个的。
☆判断两个应用的的版本大小
一般直接比较versionCode就可以,这个是个简单的整数比较问题。
但是也要考虑比较versionName,这个比较复杂。但是通过简单计算也能判断大小。
1.1.4 和 1.1.21
1.2.1 和 1.1.987
1.2 和 1.1.987
2 和 1.1.987
我们需要写一个特定函数,用于比较两个版本号的大小:
int return=CompareVersionName(string left,string right);
int return=CompareVersionCode(string left,string right);
return=0-参数无效无法比,1-相等,2-左边大,3-右边大
※我们应用市场采用CompareVersionName。
=======================================================================================
应用市场功能:
---------
1.启动界面
--------------------------------------------------------------------------------------
☆第一次启动:只有第一次启动时候显示显示启动界面。启动界面上候选语种里出现的语言是从applist.preinstalled.description的languages读取的。语言默认选中状态也是从selected属性来判断。
☆是不是第一次启动的判断标准:判断文件:applist.all.description是否存在。
☆如果第一次启动,那么需要根据applist.preinstalled.description来生成applist.all.description文件。
生成逻辑如下:
1.1生成applist.all.description文件
基于文件applist.preinstalled.description生成一个文件applist.all.description。
格式有些不同,所以只要将匹配的字段拷贝过去,其他字段需要填写默认数值(比如未安装等)。
其中applist_versioncode为固定0,applist_num需要实时计算。
1.2遍历applist.all.description中的每个应用并更新相应字段
在现已安装的应用里查找(通过系统函数)applist.all.description中的每个应用,读取这些已经安装应用的各种信息,再逆向更新文件applist.all.description个各个字段。
packagename :直接来自applist.preinstalled.description
name :直接来自applist.preinstalled.description
ispreincluded :直接来自applist.preinstalled.description
issystem :直接来自applist.preinstalled.description
isinstalled :通过计算获取:通过系统函数判断系统里是否已经安装了。
ishavebeenupdated :通过计算获取:[是预包含应用]&[已经安装了]&[安装版本比预包含版本高],那么是升级过了,为1,其他时都是0。
isrequireupdate :通过计算获取:固定设置为0,因为这是还没刷新应用列表,所以不知道是否需要升级。
versionname :直接来自applist.preinstalled.description
versioncode :直接来自applist.preinstalled.description
languages :直接来自applist.preinstalled.description
taketime :直接来自applist.preinstalled.description
size :直接来自applist.preinstalled.description
company :直接来自applist.preinstalled.description
description :直接来自applist.preinstalled.description
appurl :固定设置为:/assets/applist/YourPackageName/123.png
appisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
logoimgurl :固定设置为:/assets/applist/YourPackageName/123.png
logoisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
scstimg1url :固定设置为:/assets/applist/YourPackageName/123.png
scstimg1urlisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
scstimg2url :固定设置为:/assets/applist/YourPackageName/123.png
scstimg2urlisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
scstimg3url :固定设置为:/assets/applist/YourPackageName/123.png
scstimg3urlisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
1.3启动主界面
启动主界面时不自动刷新应用列表。只是从applist.preinstalled.description生成应用列表界面。
也就说启动之前已经准备好了applist.preinstalled.description,并更新了各种相关字段的状态值。
--------------------------------------------------------------------------------------
2.刷新应用列表
是指主界面上拖动释放来从服务器刷新最新应用列表的功能。
应用市场本身任何时候都不要主动刷新应用列表,包括第一次启动应用时候,第二次及以后启动时。
只有[拖动释放刷新]动作时候才从服务器获取最新应用列表数据。
------------------
☆关于应用列表版本
从服务器下载的应用列表数据文件,本地保持的数据描述文件都维持这个一个applist_versioncode。
用于保存当前本地最新的应用列表版本号。当从服务器获取最新应用描述文件后,首先对比这个版本号。
如果服务器上的比本地新(整数对比,比较结果为大),那么需要比较。即进入比较每个应用版本的环节。
否则直接退出。
------------------
☆两个关键判断逻辑
[是否本地存在]:
通过字段downloaded.packagename来在本地文件all.packagename中查找,判断是否存在同一个应用。
应为下载之前已经通过计算更新了本地文件all的所有字段,所以直接使用文件即可。不需要再去查找系统里应用是否存在等。
详细可以参考刷新应用列表动作的执行过程。
[是否需要升级]:
通过将downloaded.versionname与本地all.versionname对比来判断是否需要升级。同样道理直接使用文件all,而不需要判断时查找系统应用列表。
这里有个问题是要明确:需要用versionname判断,还是versioncode判断。
------------------------------
☆重新计算本地文件各种状态字段
这是个很关键等概念,意思是通过系统提供的接口函数,来获取在本地文件(applist.all.description)的各个应用的各种状态信息。
这时候需要重新计算的字段有:
isinstalled :是否已经安装了?只判断系统已安装应用列表里是否存在,不判断版本。
ishavebeenupdated :[已经安装了]&[安装版本比预包含版本高]
appisdownloaded :应用市场内部存储空间里是否存在apk包,并且版本是否跟downloaded.versionname一致?
--------------------------------
☆刷新过程
-----------
第一步:用户拖动释放,启动刷新应用列表功能
只是用户操作。有[释放更新][刷新中][刷新成功][隐藏]等三个状态。
-----------
第二步:根据系统运行状态更新本地文件(all)的各种状态字段
isinstalled :是否已经安装了?只判断系统已安装应用列表里是否存在,不判断版本。
ishavebeenupdated :[是预包含应用]&[已经安装了]&[安装版本比预包含版本高]
appisdownloaded :应用市场内部存储空间里是否存在apk包,并且版本是否跟downloaded.versionname一致?
※这时不能更新isrequireupdate字段,因为下一步的通信可能会失败,所以不能破坏上次的状态。
还有就是,这次网络上获取的也许跟上一次的通信结果是一样的(很频繁刷新)。这时候不会有任何更新。
-----------
第三部:从服务器下载最新应用列表数据
从服务器下载最新应用列表数据。
可以保存到本地文件(如:applist.downloaded.description),总是覆盖已有的文件。也可以直接在内存里保存,用完释放。
从服务器下载的应用列表数据和本地应用列表文件整合的时候发生两种情况:服务器上的应用在本地不存在,服务器上的应用本地已存在。
根据不同情况(存在,不存在)有不同的处理方式(更新,插入)。
-----------
第四部:根据从服务器下载的应用数据,更新本地文件的各种字段
※判断数据有效性:
没什么特殊的,是否有效json,是否applist_num一致?
※判断是否跟最近一次有变化:
downloaded.versionname<=all.versionname,如果是[小或等于],那么跟最近一次无变化,不需要做任何动作。
如果服务器应用条目在本地文件上不存在,那么当然是属于有变化的范畴了。
还有,即使服务器上的应用无变化,但是有可能本地运行状态有变化。比如两次刷新中间有些应用通过第三方渠道安装了。
所以推出刷新之前,需要做[根据系统运行状态更新本地文件(all)的各种状态字段],并更新屏幕更新。
※更新相关字段(更新模式,服务器应用本地已经存在):
packagename :不更新
name :更新,来自applist.downloaded.description
ispreincluded :不更新
issystem :不更新
isinstalled :不更新,在上一步的计算状态时更新完毕。
ishavebeenupdated :不更新,在上一步的计算状态时更新完毕。
isrequireupdate :固定设置为1,因为不需要更新,无变化的分支已经被过滤掉了。到这里的都需要更新。
versionname :更新,来自applist.downloaded.description
versioncode :更新,来自applist.downloaded.description
languages :更新,来自applist.downloaded.description
taketime :更新,来自applist.downloaded.description
size :更新,来自applist.downloaded.description
company :更新,来自applist.downloaded.description
description :更新,来自applist.downloaded.description
appurl :更新,来自applist.downloaded.description
appisdownloaded :固定设置为:0,还没下载。
logoimgurl :更新,来自applist.downloaded.description
logoisdownloaded :固定设置为:0,还没下载。
scstimg1url :更新,来自applist.downloaded.description
scstimg1urlisdownloaded :固定设置为:0,还没下载。
scstimg2url :更新,来自applist.downloaded.description
scstimg2urlisdownloaded :固定设置为:0,还没下载。
scstimg3url :更新,来自applist.downloaded.description
scstimg4urlisdownloaded :固定设置为:0,还没下载。
从此可看出本地文件applist.all.description里的信息不是已经安装应用的信息,而是将要安装的,服务器上的最新应用信息。
※更新相关字段(插入模式,服务器应用本地不存在):
packagename :新建,来自applist.downloaded.description
name :新建,来自applist.downloaded.description
ispreincluded :固定设置为0,因为不可能是预包含的。
issystem :固定设置为0,不可能是系统级的。
isinstalled :通过计算获取:判断系统应用列表里是否已经安装了?不考虑版本。
ishavebeenupdated :固定设置为0,因为这个字段只对预包含应用有效。
isrequireupdate :通过计算获取,计算方法参考上边说明。
versionname :新建,来自applist.downloaded.description
versioncode :新建,来自applist.downloaded.description
languages :新建,来自applist.downloaded.description
taketime :新建,来自applist.downloaded.description
size :新建,来自applist.downloaded.description
company :新建,来自applist.downloaded.description
description :新建,来自applist.downloaded.description
appurl :新建,来自applist.downloaded.description
appisdownloaded :固定设置为:0,还没下载。
logoimgurl :更新,来自applist.downloaded.description
logoisdownloaded :固定设置为:0,还没下载。
scstimg1url :更新,来自applist.downloaded.description
scstimg1urlisdownloaded :固定设置为:0,还没下载。
scstimg2url :更新,来自applist.downloaded.description
scstimg2urlisdownloaded :固定设置为:0,还没下载。
scstimg3url :更新,来自applist.downloaded.description
scstimg3urlisdownloaded :固定设置为:0,还没下载。
-----------
第五部:结合本地文件的各种字段从服务器下载有必要的图片资源
在这里不下载应用本身。只下载在主界面应用列表里显示的图片资源,即应用logo图标。
在应用详情界面显示的图片资源,在进入进入应用详情界面时下载。
这里需要下载的图片资源有:logoimgurl,logoisdownloaded
判断logoimgurl是否为空,如果空不下载。如果不空需要下载。
下载图片:图标资源的默认地址是绝对网络地址,如http://www.abc.com/123.png。
本地保存:/data/data/com.minstore.market/applist/com.sample.abc/123.png。
更新字段:将
logoimgurl:/data/data/com.minstore.market/applist/com.sample.abc/123.png
logoisdownloaded:1,已经下载
注意:图片资源下载后不删除,一直到用户通过操作系统的系统设置来清空缓存。
-----------
第六步:根据本地文件的各种字段刷新屏幕上的应用列表信息。
根据[更新&新建]的不同方式更新屏幕上的应用列表信息。
更新:更新相应字段,如果文本,图片,状态等等。
新建:新建一条条目,设置相关字段。
不能全部清空后重新创建,那样会屏幕非常闪烁。用户体验会很差。
--------------------------------------------------------------------------------------
3.主界面(应用列表界面)
应用列表信息:每个应用占据一行,包括:图标,名称,版本,大小,操作(下载,安装,更新,打开)
其中下载时候,下载结束后自动启动安装。
更新也是,下载后自动启动更新。
--------------------------------------------------------------------------------------
4.应用详情界面:
在主界面上点击一个应用行后进入此画面。显示过程如下:
第一步:显示详情界面,并显示正在加载动画。
第二步:判断详情图片是否已经下载
如果已经下载了,那么直接显示详情界面。根据当前应用状态,在界面上显示相应操作按钮(下载,安装,更新,打开)。
如果还没下载,那么开始下载图片,下载结束后,更新相关字段状态。最后再打开详情界面。
如果下载失败了,显示加载失败图片和消息。
如果下载成功了,那么显示图片等信息。
下载图片资源:
判断scstimg1url是否为空,空那么说明无此应用无图片资源。不需要下载,也不需要显示。如果不空那么需要下载。
默认路径为绝对网络地址,如http://www.abc.com/123.png。
下载后保存到:/data/data/com.minstore.market/applist/com.sample.abc/123.png。
并更新字段:logoisdownloaded:1,已经下载
其他字段也类似。scstimg1url,scstimg2url,scstimg3url
注意:图片资源下载后不删除,一直到用户通过操作系统的系统设置来清空缓存。
--------------------------------------------------------------------------------------
4.设置界面:
通过右上角的菜单按钮进入。详细参考设计图。
确定保存后,返回应用列表时候,不需要从服务器重新下载应用列表。而是将本地文件applist.downloaded.description过滤一遍即可。
下次跟服务器通信,获取最新应用列表时候,这个值会被自动传递给服务器的。
5.下载应用:
开始下载之前都要通知一下服务器,要下载哪个应用。编译统计。
方式参考下边[服务器通信]部分的说明。根据服务器响应有不同动作。
status=1 :可以继续下载
status!=1:错误,显示下载失败的错误提示。
异常:异常是指通信终端,网络不通,服务器异常,不正确json格式等等。都要提示下载失败的错误提示即可。
--------------------------------------------------------------------------------------
6.服务器通信:
网址:http://www.minzuos.com/do.xromminstore.php
请求参数:
mktversion:"1.0.0" 应用市场版本
mktlanguage:"hy;mn;tb" 应用市场支持的语种
mktmode:"100" 应用市场安装方式:
100-预装版,通用随ROM预装模式
101-预装版,后续带扩展,为特定场合定制版,可能会有101,102,103等等多种版本。
200-在线下载版,通用网上下载安装模式
201-在线下载版,,后续带扩展,为特定场合定制版,可能会有101,102,103等等多种版本。
mktaction:list or down 请求操作类型:list=获取应用列表,down=通知下载指定应用
mktapplistversioncode:"12" 客户端应用市场当前所维持的applist的版本号,是整数。
osversion:"1.0.0-20150507-bacon" 手机操作系统版本号,或者是ROM版本
ossdkversion:"16" 手机系统兼容安卓SDK号码,不是类似4.4.2的,而是类似16的整数
brand:"品牌" 终端属性
model:"型号" 终端属性
device:"机型" 终端属性
imei:"串号" 终端属性
existsapplist:"已经存在app列表" 这里可以指定客户端现存app的包名及版本号码。这样可以差额传输。
格式为:com.abc.test=1.2.1;com.xyz.test=2.1;com.sample.test=4.0
pkgname:"com.sample.text" 准备下载的应用的包名(只有mktaction=down时有效,其他时候忽略)
响应数据:
响应数据位标准json格式的数据,根据不用操作类型有不同响应格式。
mktaction=list
详细参考设计文档目录下的文件applist.downloaded.description
mktaction=down
只是个下载通知,用于服务器上计数。别无它用。
{
"status":"1",//status=1 正常,status!=错误(错误信息参考message字段),
"message":"操作成功"
}
----------
类包名:com.minstore.market
应用名:MinStore
文件名:MinStore.apk
应用图标:制作一个专有的图标
界面语言:应用市场只做汉语界面,不对应其他语种界面。
屏幕尺寸:不仅仅是随ROM,发布而是个独立应用,所以适应所有屏幕尺寸。
概念释疑:
预装:安装ROM后自动安装好了,直接在应用列表里能看见。不能删除,但是能更新升级。
预包含:直接打包在一个预装应用(应用市场)里,用户启动应用市场后不用下载就可以直接安装。
应用市场可以是随ROM发布的内置应用,也可以是通过网络下载的通用应用。
功能概述:
应用市场本身包含一部分预包含应用,可以直接安装。也可以从服务器下载应用清单后下载新应用。
应用市场主界面上显示的应用列表来自两个地方:预包含应用名称和刷新时候从服务器获取的应用名称。
应用各种信息是通过通过三方信息来源来的:
☆预包含应用描述文件
☆刷新应用列表时候从服务器获取的应用描述文件
☆本地已经安装的应用列表(只更新名单里已有应用的状态,不增减应用列表数量)
-----------------
应用工程目录结构:(MinStore为工程名,assets为安卓项目里的固定名称)
/MinStore/res
/MinStore/src
/MinStore/libs
/MinStore/assets/applist/applist.preincluded.description 预装应用列表描述文件,标准JSON格式
/MinStore/assets/applist/com.sample.abc/abc.apk 样例应用1的文件包
/MinStore/assets/applist/com.sample.abc/logo.png 样例应用1的图标图片
/MinStore/assets/applist/com.sample.abc/sc1.png 样例应用1的截屏1
/MinStore/assets/applist/com.sample.abc/sc2.png 样例应用1的截屏2
-------------
内部存储空间:
所谓内部存储空间是指只有应用本身能够访问,别人看不到。删除应用时候自动被删除的存储空间。
被存在在手机的特定目录下,是依据应用包名唯一定位的。
/data/data/com.minstore.market/applist/applist.all.description
/data/data/com.minstore.market/applist/com.sample.abc/
/data/data/com.minstore.market/applist/com.sample.xyz/
=======================================================================================
应用描述文件
------------
一共有三个应用描述文件,说明如下:
☆应用市场编译时包含的预包含应用的应用描述文件:applist.preinstalled.description
随应用市场发布的,不能更新不能删除。描述随应用市场一期发布的,预包含在应用市场里应用的描述文件。这里描述的文件不需要下载,可以直接安装。只是对应用市场目录/assets/applist/下预包含应用的描述。
保存在应用包里:file:///android_asset/applist/applist.preinstalled.description
☆应用市场更新应用列表时下载的服务器应用描述文件:applist.downloaded.description
每次刷新应用时候需要从服务器获取最新应用列表,就是这个应用描述文件。这里描述服务器上已经准备好的所有应用的信息。这里的有些应用预包含在应用市场里,也有些已经安装到手机里。
只有刷新(拖动释放)时候才获取此文件。应用市场启动时候不刷新应用列表。
从服务器获取,每次刷新应用列表时候都从服务器获取一次,不需要本地保存。
为了调试也可以保存,那就保存在[应用内部存储空间]。
内部存储空间:/data/data/youPackageName/
每个应用独立的,别人访问不到。
/data/data/com.minstore.market/applist/applist.downloaded.description
☆应用市场软件运行时候管理的所有应用的应用描述文件:applist.all.description
此文件刚开始时候是不存在的,是由应用市场组合[applist.preinstalled.description、applist.downloaded.description、已经安装的应用列表]等三个信息源组合后生成,并保存在应用私有数据空间的。
存储路径:/data/data/com.minstore.market/applist/applist.all.description
应用每次启动时候,都要判断文件[applist.all.description]是否存在,如果不存,则需要从[applist.preinstalled.description]重新生成。只要存在[applist.all.description],那么后续就不需要使用[applist.preinstalled.description]了。第一次生成[applist.all.description]时,不需要从服务器获取最新应用描述文件。
=======================================================================================
应用版本及其版本号码:
☆判断两个应用是否为同一个应用:
应用的应用包名是唯一的,判断他俩是否一样。
☆正常应用的版本信息
versionCode:整数型,容易比较,给开发人员看的。容易比较大小。
versionName:字符串,给用户看的。不容易比较大小。
※在文件AndroidManifest.xml里写的,但是有些业余开发人员是不写这个的。
☆判断两个应用的的版本大小
一般直接比较versionCode就可以,这个是个简单的整数比较问题。
但是也要考虑比较versionName,这个比较复杂。但是通过简单计算也能判断大小。
1.1.4 和 1.1.21
1.2.1 和 1.1.987
1.2 和 1.1.987
2 和 1.1.987
我们需要写一个特定函数,用于比较两个版本号的大小:
int return=CompareVersionName(string left,string right);
int return=CompareVersionCode(string left,string right);
return=0-参数无效无法比,1-相等,2-左边大,3-右边大
※我们应用市场采用CompareVersionName。
=======================================================================================
应用市场功能:
---------
1.启动界面
--------------------------------------------------------------------------------------
☆第一次启动:只有第一次启动时候显示显示启动界面。启动界面上候选语种里出现的语言是从applist.preinstalled.description的languages读取的。语言默认选中状态也是从selected属性来判断。
☆是不是第一次启动的判断标准:判断文件:applist.all.description是否存在。
☆如果第一次启动,那么需要根据applist.preinstalled.description来生成applist.all.description文件。
生成逻辑如下:
1.1生成applist.all.description文件
基于文件applist.preinstalled.description生成一个文件applist.all.description。
格式有些不同,所以只要将匹配的字段拷贝过去,其他字段需要填写默认数值(比如未安装等)。
其中applist_versioncode为固定0,applist_num需要实时计算。
1.2遍历applist.all.description中的每个应用并更新相应字段
在现已安装的应用里查找(通过系统函数)applist.all.description中的每个应用,读取这些已经安装应用的各种信息,再逆向更新文件applist.all.description个各个字段。
packagename :直接来自applist.preinstalled.description
name :直接来自applist.preinstalled.description
ispreincluded :直接来自applist.preinstalled.description
issystem :直接来自applist.preinstalled.description
isinstalled :通过计算获取:通过系统函数判断系统里是否已经安装了。
ishavebeenupdated :通过计算获取:[是预包含应用]&[已经安装了]&[安装版本比预包含版本高],那么是升级过了,为1,其他时都是0。
isrequireupdate :通过计算获取:固定设置为0,因为这是还没刷新应用列表,所以不知道是否需要升级。
versionname :直接来自applist.preinstalled.description
versioncode :直接来自applist.preinstalled.description
languages :直接来自applist.preinstalled.description
taketime :直接来自applist.preinstalled.description
size :直接来自applist.preinstalled.description
company :直接来自applist.preinstalled.description
description :直接来自applist.preinstalled.description
appurl :固定设置为:/assets/applist/YourPackageName/123.png
appisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
logoimgurl :固定设置为:/assets/applist/YourPackageName/123.png
logoisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
scstimg1url :固定设置为:/assets/applist/YourPackageName/123.png
scstimg1urlisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
scstimg2url :固定设置为:/assets/applist/YourPackageName/123.png
scstimg2urlisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
scstimg3url :固定设置为:/assets/applist/YourPackageName/123.png
scstimg3urlisdownloaded :固定设置为:1,因为从预包含文件拷贝过去的,所以文件已经存在。等于下载了。
1.3启动主界面
启动主界面时不自动刷新应用列表。只是从applist.preinstalled.description生成应用列表界面。
也就说启动之前已经准备好了applist.preinstalled.description,并更新了各种相关字段的状态值。
--------------------------------------------------------------------------------------
2.刷新应用列表
是指主界面上拖动释放来从服务器刷新最新应用列表的功能。
应用市场本身任何时候都不要主动刷新应用列表,包括第一次启动应用时候,第二次及以后启动时。
只有[拖动释放刷新]动作时候才从服务器获取最新应用列表数据。
------------------
☆关于应用列表版本
从服务器下载的应用列表数据文件,本地保持的数据描述文件都维持这个一个applist_versioncode。
用于保存当前本地最新的应用列表版本号。当从服务器获取最新应用描述文件后,首先对比这个版本号。
如果服务器上的比本地新(整数对比,比较结果为大),那么需要比较。即进入比较每个应用版本的环节。
否则直接退出。
------------------
☆两个关键判断逻辑
[是否本地存在]:
通过字段downloaded.packagename来在本地文件all.packagename中查找,判断是否存在同一个应用。
应为下载之前已经通过计算更新了本地文件all的所有字段,所以直接使用文件即可。不需要再去查找系统里应用是否存在等。
详细可以参考刷新应用列表动作的执行过程。
[是否需要升级]:
通过将downloaded.versionname与本地all.versionname对比来判断是否需要升级。同样道理直接使用文件all,而不需要判断时查找系统应用列表。
这里有个问题是要明确:需要用versionname判断,还是versioncode判断。
------------------------------
☆重新计算本地文件各种状态字段
这是个很关键等概念,意思是通过系统提供的接口函数,来获取在本地文件(applist.all.description)的各个应用的各种状态信息。
这时候需要重新计算的字段有:
isinstalled :是否已经安装了?只判断系统已安装应用列表里是否存在,不判断版本。
ishavebeenupdated :[已经安装了]&[安装版本比预包含版本高]
appisdownloaded :应用市场内部存储空间里是否存在apk包,并且版本是否跟downloaded.versionname一致?
--------------------------------
☆刷新过程
-----------
第一步:用户拖动释放,启动刷新应用列表功能
只是用户操作。有[释放更新][刷新中][刷新成功][隐藏]等三个状态。
-----------
第二步:根据系统运行状态更新本地文件(all)的各种状态字段
isinstalled :是否已经安装了?只判断系统已安装应用列表里是否存在,不判断版本。
ishavebeenupdated :[是预包含应用]&[已经安装了]&[安装版本比预包含版本高]
appisdownloaded :应用市场内部存储空间里是否存在apk包,并且版本是否跟downloaded.versionname一致?
※这时不能更新isrequireupdate字段,因为下一步的通信可能会失败,所以不能破坏上次的状态。
还有就是,这次网络上获取的也许跟上一次的通信结果是一样的(很频繁刷新)。这时候不会有任何更新。
-----------
第三部:从服务器下载最新应用列表数据
从服务器下载最新应用列表数据。
可以保存到本地文件(如:applist.downloaded.description),总是覆盖已有的文件。也可以直接在内存里保存,用完释放。
从服务器下载的应用列表数据和本地应用列表文件整合的时候发生两种情况:服务器上的应用在本地不存在,服务器上的应用本地已存在。
根据不同情况(存在,不存在)有不同的处理方式(更新,插入)。
-----------
第四部:根据从服务器下载的应用数据,更新本地文件的各种字段
※判断数据有效性:
没什么特殊的,是否有效json,是否applist_num一致?
※判断是否跟最近一次有变化:
downloaded.versionname<=all.versionname,如果是[小或等于],那么跟最近一次无变化,不需要做任何动作。
如果服务器应用条目在本地文件上不存在,那么当然是属于有变化的范畴了。
还有,即使服务器上的应用无变化,但是有可能本地运行状态有变化。比如两次刷新中间有些应用通过第三方渠道安装了。
所以推出刷新之前,需要做[根据系统运行状态更新本地文件(all)的各种状态字段],并更新屏幕更新。
※更新相关字段(更新模式,服务器应用本地已经存在):
packagename :不更新
name :更新,来自applist.downloaded.description
ispreincluded :不更新
issystem :不更新
isinstalled :不更新,在上一步的计算状态时更新完毕。
ishavebeenupdated :不更新,在上一步的计算状态时更新完毕。
isrequireupdate :固定设置为1,因为不需要更新,无变化的分支已经被过滤掉了。到这里的都需要更新。
versionname :更新,来自applist.downloaded.description
versioncode :更新,来自applist.downloaded.description
languages :更新,来自applist.downloaded.description
taketime :更新,来自applist.downloaded.description
size :更新,来自applist.downloaded.description
company :更新,来自applist.downloaded.description
description :更新,来自applist.downloaded.description
appurl :更新,来自applist.downloaded.description
appisdownloaded :固定设置为:0,还没下载。
logoimgurl :更新,来自applist.downloaded.description
logoisdownloaded :固定设置为:0,还没下载。
scstimg1url :更新,来自applist.downloaded.description
scstimg1urlisdownloaded :固定设置为:0,还没下载。
scstimg2url :更新,来自applist.downloaded.description
scstimg2urlisdownloaded :固定设置为:0,还没下载。
scstimg3url :更新,来自applist.downloaded.description
scstimg4urlisdownloaded :固定设置为:0,还没下载。
从此可看出本地文件applist.all.description里的信息不是已经安装应用的信息,而是将要安装的,服务器上的最新应用信息。
※更新相关字段(插入模式,服务器应用本地不存在):
packagename :新建,来自applist.downloaded.description
name :新建,来自applist.downloaded.description
ispreincluded :固定设置为0,因为不可能是预包含的。
issystem :固定设置为0,不可能是系统级的。
isinstalled :通过计算获取:判断系统应用列表里是否已经安装了?不考虑版本。
ishavebeenupdated :固定设置为0,因为这个字段只对预包含应用有效。
isrequireupdate :通过计算获取,计算方法参考上边说明。
versionname :新建,来自applist.downloaded.description
versioncode :新建,来自applist.downloaded.description
languages :新建,来自applist.downloaded.description
taketime :新建,来自applist.downloaded.description
size :新建,来自applist.downloaded.description
company :新建,来自applist.downloaded.description
description :新建,来自applist.downloaded.description
appurl :新建,来自applist.downloaded.description
appisdownloaded :固定设置为:0,还没下载。
logoimgurl :更新,来自applist.downloaded.description
logoisdownloaded :固定设置为:0,还没下载。
scstimg1url :更新,来自applist.downloaded.description
scstimg1urlisdownloaded :固定设置为:0,还没下载。
scstimg2url :更新,来自applist.downloaded.description
scstimg2urlisdownloaded :固定设置为:0,还没下载。
scstimg3url :更新,来自applist.downloaded.description
scstimg3urlisdownloaded :固定设置为:0,还没下载。
-----------
第五部:结合本地文件的各种字段从服务器下载有必要的图片资源
在这里不下载应用本身。只下载在主界面应用列表里显示的图片资源,即应用logo图标。
在应用详情界面显示的图片资源,在进入进入应用详情界面时下载。
这里需要下载的图片资源有:logoimgurl,logoisdownloaded
判断logoimgurl是否为空,如果空不下载。如果不空需要下载。
下载图片:图标资源的默认地址是绝对网络地址,如http://www.abc.com/123.png。
本地保存:/data/data/com.minstore.market/applist/com.sample.abc/123.png。
更新字段:将
logoimgurl:/data/data/com.minstore.market/applist/com.sample.abc/123.png
logoisdownloaded:1,已经下载
注意:图片资源下载后不删除,一直到用户通过操作系统的系统设置来清空缓存。
-----------
第六步:根据本地文件的各种字段刷新屏幕上的应用列表信息。
根据[更新&新建]的不同方式更新屏幕上的应用列表信息。
更新:更新相应字段,如果文本,图片,状态等等。
新建:新建一条条目,设置相关字段。
不能全部清空后重新创建,那样会屏幕非常闪烁。用户体验会很差。
--------------------------------------------------------------------------------------
3.主界面(应用列表界面)
应用列表信息:每个应用占据一行,包括:图标,名称,版本,大小,操作(下载,安装,更新,打开)
其中下载时候,下载结束后自动启动安装。
更新也是,下载后自动启动更新。
--------------------------------------------------------------------------------------
4.应用详情界面:
在主界面上点击一个应用行后进入此画面。显示过程如下:
第一步:显示详情界面,并显示正在加载动画。
第二步:判断详情图片是否已经下载
如果已经下载了,那么直接显示详情界面。根据当前应用状态,在界面上显示相应操作按钮(下载,安装,更新,打开)。
如果还没下载,那么开始下载图片,下载结束后,更新相关字段状态。最后再打开详情界面。
如果下载失败了,显示加载失败图片和消息。
如果下载成功了,那么显示图片等信息。
下载图片资源:
判断scstimg1url是否为空,空那么说明无此应用无图片资源。不需要下载,也不需要显示。如果不空那么需要下载。
默认路径为绝对网络地址,如http://www.abc.com/123.png。
下载后保存到:/data/data/com.minstore.market/applist/com.sample.abc/123.png。
并更新字段:logoisdownloaded:1,已经下载
其他字段也类似。scstimg1url,scstimg2url,scstimg3url
注意:图片资源下载后不删除,一直到用户通过操作系统的系统设置来清空缓存。
--------------------------------------------------------------------------------------
4.设置界面:
通过右上角的菜单按钮进入。详细参考设计图。
确定保存后,返回应用列表时候,不需要从服务器重新下载应用列表。而是将本地文件applist.downloaded.description过滤一遍即可。
下次跟服务器通信,获取最新应用列表时候,这个值会被自动传递给服务器的。
5.下载应用:
开始下载之前都要通知一下服务器,要下载哪个应用。编译统计。
方式参考下边[服务器通信]部分的说明。根据服务器响应有不同动作。
status=1 :可以继续下载
status!=1:错误,显示下载失败的错误提示。
异常:异常是指通信终端,网络不通,服务器异常,不正确json格式等等。都要提示下载失败的错误提示即可。
--------------------------------------------------------------------------------------
6.服务器通信:
网址:http://www.minzuos.com/do.xromminstore.php
请求参数:
mktversion:"1.0.0" 应用市场版本
mktlanguage:"hy;mn;tb" 应用市场支持的语种
mktmode:"100" 应用市场安装方式:
100-预装版,通用随ROM预装模式
101-预装版,后续带扩展,为特定场合定制版,可能会有101,102,103等等多种版本。
200-在线下载版,通用网上下载安装模式
201-在线下载版,,后续带扩展,为特定场合定制版,可能会有101,102,103等等多种版本。
mktaction:list or down 请求操作类型:list=获取应用列表,down=通知下载指定应用
mktapplistversioncode:"12" 客户端应用市场当前所维持的applist的版本号,是整数。
osversion:"1.0.0-20150507-bacon" 手机操作系统版本号,或者是ROM版本
ossdkversion:"16" 手机系统兼容安卓SDK号码,不是类似4.4.2的,而是类似16的整数
brand:"品牌" 终端属性
model:"型号" 终端属性
device:"机型" 终端属性
imei:"串号" 终端属性
existsapplist:"已经存在app列表" 这里可以指定客户端现存app的包名及版本号码。这样可以差额传输。
格式为:com.abc.test=1.2.1;com.xyz.test=2.1;com.sample.test=4.0
pkgname:"com.sample.text" 准备下载的应用的包名(只有mktaction=down时有效,其他时候忽略)
响应数据:
响应数据位标准json格式的数据,根据不用操作类型有不同响应格式。
mktaction=list
详细参考设计文档目录下的文件applist.downloaded.description
mktaction=down
只是个下载通知,用于服务器上计数。别无它用。
{
"status":"1",//status=1 正常,status!=错误(错误信息参考message字段),
"message":"操作成功"
}
MainActivity.java
package com.minstore.market.ui;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.SocketException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.minstore.market.Contance;
import com.minstore.market.MyAdapter;
import com.minstore.market.R;
import com.minstore.market.RefreshableView;
import com.minstore.market.R.drawable;
import com.minstore.market.R.id;
import com.minstore.market.R.layout;
import com.minstore.market.RefreshableView.PullToRefreshListener;
import com.minstore.market.domain.ApkInfoBean;
import com.minstore.market.domain.DevicesInfo;
import com.minstore.market.ui.SettingsActivity.myAdapter.listener;
import com.minstore.market.utils.FileAndJsonOperation;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;
import android.R.bool;
import android.R.integer;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
public class MainActivity extends Activity implements OnItemClickListener {
RefreshableView refreshableView;
ListView listView;
MyAdapter myAdapter;
List<ApkInfoBean> mList = new ArrayList<ApkInfoBean>();
private ImageButton ib_menu;
// 存放预安装应用的list
private List<String> preList = null;
private List<String> preList1 = null;
// 存放下载应用的list
private List<String> downloadList = null;
private List<String> downloadList1 = null;
private List<String> saveAdapterList = null;
private Map<String, String> deviceInfoMap = null;
FileAndJsonOperation fajo;
private List<String> imageUrlList = null;
private List<String> packList = null;
private List<String> imageNameList = null;
private List<Integer> number = null;
private List<String> pppname = null;
// private JSONObject jsonObjectAll = null;
private List<String> btnStateList = null;
private int statusValue = 0;
SharedPreferences sharedPreferences = null;
String resStr = null;
Dialog mLoading = null;
String saveStr = "";
Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case Contance.REFRESH_FAILED:
refreshableView.finishRefreshing(0);
break;
case Contance.REFRESH_LISTVIRE:
mList = addData1();
myAdapter.refresh(mList, saveAdapterList, btnStateList,
preList1.size());
Log.i(Contance.TAG_INFO, "服务器相应的状态值为---->" + statusValue);
refreshableView.finishRefreshing(statusValue);
break;
case Contance.SINGLE_REFRESH_LISTVIEW:
mList = addData1();
myAdapter.refresh(mList, saveAdapterList, btnStateList,
preList1.size());
break;
case Contance.BACKKEY_STOP_DOWNLOAD_OPERATOR:
refreshableView.finishRefreshing(statusValue);
break;
case Contance.LOADING_LOGO:
Log.i(Contance.TAG_INFO,
"初始化异步加载imageurl" + imageUrlList.size() + "--->"
+ imageUrlList.toString());
Log.i(Contance.TAG_INFO,
"初始化异步加载package--->" + packList.toString());
// 将list转化成String数组
String[] imageUrlStr = new String[imageUrlList.size()];
for (int i = 0; i < imageUrlStr.length; i++) {
imageUrlStr[i] = imageUrlList.get(i);
}
Log.i(Contance.TAG_INFO, "初始化异步加载");
AsyncImageTask task = new AsyncImageTask();
task.execute(imageUrlStr);
break;
case Contance.JUMP_APPINFO:
try {
JSONObject jsonObject = new JSONObject(msg.obj.toString());
Intent intent = new Intent(MainActivity.this,
AppInfoActivity.class);
// intent.putExtra(Contance.INTENT_KEY, appInfoStr);
intent.putExtra(Contance.APP_DESCRIPTION,
jsonObject.getString(Contance.APP_DESCRIPTION));
intent.putExtra(Contance.APP_NAME,
jsonObject.getString(Contance.APP_NAME));
intent.putExtra(Contance.APP_VERSIONNAME,
jsonObject.getString(Contance.APP_VERSIONNAME));
intent.putExtra(Contance.APP_SIZE,
jsonObject.getString(Contance.APP_SIZE));
intent.putExtra(Contance.APP_TAKETIME,
jsonObject.getString(Contance.APP_TAKETIME));
// 传递包名,用于picture
intent.putExtra(Contance.APP_PACKAGENAME,
jsonObject.getString(Contance.APP_PACKAGENAME));
// 传递标志位,用于区分是预安装还是下载应用
intent.putExtra(Contance.FALG_KEY, "" + msg.arg1);
intent.putExtra(Contance.APP_COMPANY,
jsonObject.getString(Contance.APP_COMPANY));
// 传递appurl,用于下载
intent.putExtra(Contance.APP_APPURL,
jsonObject.getString(Contance.APP_APPURL));
intent.putExtra(Contance.APP_SCSIIMG1URL,
jsonObject.getString(Contance.APP_SCSIIMG1URL));
intent.putExtra(Contance.APP_SCSIIMG2URL,
jsonObject.getString(Contance.APP_SCSIIMG2URL));
intent.putExtra(Contance.APP_VERSIONNAME,
jsonObject.getString(Contance.APP_VERSIONNAME));
intent.putExtra(Contance.APP_NAME,
jsonObject.getString(Contance.APP_NAME));
startActivity(intent);
MainActivity.this.finish();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
fajo = new FileAndJsonOperation(MainActivity.this);
initView();
}
public void initView() {
refreshableView = (RefreshableView) findViewById(R.id.refreshable_view);
listView = (ListView) findViewById(R.id.list_view);
// mList = addData(0);
mList = addData1();
myAdapter = new MyAdapter(MainActivity.this, mList, saveAdapterList,
btnStateList, preList1.size(), mLoading);
listView.setAdapter(myAdapter);
listView.setOnItemClickListener(this);
refreshableView.setOnRefreshListener(new PullToRefreshListener() {
@Override
public void onRefresh() {
// 判断网络是否可用,可用的话再执行刷新操作,否则就1s后提示刷新失败
if (checkNetStatus()) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 与服务器交互,发送表单数据,并且得到服务器的响应json串
sendData1();
Log.i(Contance.TAG_INFO, "服务器一切处理都结束了吗?");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mHandler.sendEmptyMessage(Contance.REFRESH_FAILED);
}
}
});
ib_menu = (ImageButton) findViewById(R.id.ib_menu);
ib_menu.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(MainActivity.this);
saveStr = sharedPreferences.getString(
Contance.SETTING_LANGUAGE_SAVE_KEY, "hy");
Intent intent = new Intent(MainActivity.this,
SettingsActivity.class);
startActivity(intent);
// finish();
}
});
}
/**
* 发送数据到服务器,并且对响应的数据进行处理
* @return
*/
public void sendData1() {
deviceInfoMap = new HashMap<String, String>();
deviceInfoMap = (new DevicesInfo(MainActivity.this, deviceInfoMap))
.getDevicesInfo();
Log.i(Contance.TAG_INFO, "获得的设备信息map为--->" + deviceInfoMap.toString());
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpPost post = new HttpPost(Contance.PATH);// post请求
// 设置添加对象
List<NameValuePair> paramsForm = new ArrayList<NameValuePair>();
paramsForm.add(new BasicNameValuePair(
Contance.REQ_MKTVERSION, "1.0.0"));
paramsForm.add(new BasicNameValuePair(
Contance.REQ_MKTLANGUAGE, sharedPreferences
.getString(Contance.JUMP_INTENT_LANGUAGE,
"")));
paramsForm.add(new BasicNameValuePair(Contance.REQ_MKTMODE,
"100"));
paramsForm.add(new BasicNameValuePair(
Contance.REQ_MKTACTION, "list"));
paramsForm.add(new BasicNameValuePair(
Contance.REQ_MKTAPPLISTVERSIONCODE,
sharedPreferences.getString(
Contance.APPLISTVERSIONCODEKEY, "0")));
paramsForm.add(new BasicNameValuePair(
Contance.REQ_OSVERSION, deviceInfoMap
.get(Contance.REQ_OSVERSION)));
paramsForm.add(new BasicNameValuePair(
Contance.REQ_OSSDKVERSION, deviceInfoMap
.get(Contance.REQ_OSSDKVERSION)));
paramsForm.add(new BasicNameValuePair(Contance.REQ_BRAND,
deviceInfoMap.get(Contance.REQ_BRAND)));
paramsForm.add(new BasicNameValuePair(Contance.REQ_MODEL,
deviceInfoMap.get(Contance.REQ_MODEL)));
paramsForm.add(new BasicNameValuePair(Contance.REQ_DEVICE,
deviceInfoMap.get(Contance.REQ_DEVICE)));
paramsForm.add(new BasicNameValuePair(Contance.REQ_IMEI,
deviceInfoMap.get(Contance.REQ_IMEI)));
paramsForm.add(new BasicNameValuePair(
Contance.REQ_EXISTAPPLIST, ""));
post.setEntity(new UrlEncodedFormEntity(paramsForm,
HTTP.UTF_8));
Log.i(Contance.TAG_INFO, "请求数据结束,开始接受响应数据");
// 发送请求
HttpParams params = new BasicHttpParams();
DefaultHttpClient localDefaultHttpClient = new DefaultHttpClient(
params);
localDefaultHttpClient.getParams().setParameter(
"http.connection.timeout", Integer.valueOf(30000));
localDefaultHttpClient.getParams().setParameter(
"http.socket.timeout", Integer.valueOf(30000));
HttpResponse response = localDefaultHttpClient
.execute(post);
// 服务器状态码只是为了看通讯是否成功
int status_value = response.getStatusLine().getStatusCode();
Log.i(Contance.TAG_INFO, "服务器相应的状态吗为--->" + status_value);
// 得到应答的字符串,这也是一个JSON格式保存的数据
resStr = EntityUtils.toString(response.getEntity());
Log.i(Contance.TAG_INFO, "服务器响应的结果为---->" + resStr);
// 生成JSON对象
JSONObject result = new JSONObject(resStr);
statusValue = Integer.parseInt(result
.getString(Contance.STATUS));
String messageValue = result.getString(Contance.MESSAGE);
// 获得下载list
downloadList = new ArrayList<String>();
downloadList = fajo.resolveJsonArray(resStr, downloadList,
Contance.APPLIST_DOWNLOADED);
/**
* 写入all文件
*/
if (downloadList.size() != 0) { // 如果列表不为空,就处理数据
// 现读取all文件,获取all文件内容,然后再往进写
JSONObject jsonObjectAll = new JSONObject("{"
+ fajo.readContent(fajo
.filePathName(Contance.ALLNAME)));
// 如果down的版本号大于当前的版本号,就更新
if (Integer.parseInt(result
.getString(Contance.APPLIST_VERSIONCODE)) > Integer.parseInt(jsonObjectAll
.getString(Contance.APPLIST_VERSIONCODE))) {
// 把版本号更新成最新的
jsonObjectAll.put(
Contance.APPLIST_VERSIONCODE,
result.getString(Contance.APPLIST_VERSIONCODE));
// 然后存储新的code到sp
sharedPreferences
.edit()
.putString(
Contance.APPLISTVERSIONCODEKEY,
result.getString(Contance.APPLIST_VERSIONCODE))
.commit();
// 进行applist_all的处理
// 先获得原来的JsonArray
JSONArray beforeArray = jsonObjectAll
.getJSONArray(Contance.APPLIST_ALL);
// 获取现在jsonArray
JSONArray nowArray = result
.getJSONArray(Contance.APPLIST_DOWNLOADED);
number = new ArrayList<Integer>();
// 将现在的列表,挨个进行比较,看看原来的列表中有没有
a: for (int i = 0; i < nowArray.length(); i++) {
JSONObject nowJo = (JSONObject) nowArray.opt(i);
// 获得当前down列表的包名,然后进行遍历比较
String getNowPkgNameS = nowJo
.getString(Contance.APP_PACKAGENAME);
boolean judgePkgFlag = false; // 默认false,不存在
b: for (int j = 0; j < beforeArray.length(); j++) {
JSONObject beforeJo = (JSONObject) beforeArray
.opt(j);
// 获得原来列表应用包名,用于比较
String getBeforePkgName = beforeJo
.getString(Contance.APP_PACKAGENAME);
if (getBeforePkgName.equals(getNowPkgNameS)) {
// 如果包名相同,就将标志位设置成true
judgePkgFlag = true; // 已经存在了
System.out.println(getBeforePkgName
+ "之前的版本--》"
+ beforeJo
.getString(Contance.APP_VERSIONNAME));
System.out.println(getBeforePkgName
+ "之后的版本--》"
+ nowJo.getString(Contance.APP_VERSIONNAME));
// 开始进行版本号的比较
// int value1 = CompareVersionName(
// beforeJo.getString(Contance.APP_VERSIONNAME),
// nowJo.getString(Contance.APP_VERSIONNAME));
int value1 = CompareVersionName(
versionName(beforeJo
.getString(Contance.APP_VERSIONNAME)),
versionName(nowJo
.getString(Contance.APP_VERSIONNAME)));
System.out.println(getBeforePkgName
+ "结果--》" + value1);
if (value1 == 3) { // 有新版本进行处理
// 将all文件的字段更新成1,表示可更新,用于更新按钮的显示
// 如果有新版本,就替换整个条目
beforeJo.put(
Contance.APP_ISREQUIREUPDATE,
1);
// 如果需要更新,就更新url
beforeJo.put(
Contance.APP_APPURL,
nowJo.getString(Contance.APP_APPURL));
// 更新版本号
beforeJo.put(
Contance.APP_VERSIONNAME,
nowJo.getString(Contance.APP_VERSIONNAME));
// 更新size
beforeJo.put(
Contance.APP_SIZE,
nowJo.getString(Contance.APP_SIZE));
// 更新taketime
beforeJo.put(
Contance.APP_TAKETIME,
nowJo.getString(Contance.APP_TAKETIME));
// 将object添加到array中
beforeArray.put(j, beforeJo);
// 如果有相同的包名,处理完版本好,就没必要对
break b;
} else { // 就是值不为3,没有新的版本号,就什么也不管,直接判断下一个应用
break b;
}
}
if (j == (beforeArray.length() - 1)
&& judgePkgFlag == false) {
// 如果
// 到了最后一个,并且标志位一直是false,说明没有相同的包名,添加这个新的应用
nowJo.put(Contance.APP_ISPREINCLUDED,
"0"); // 既然是新的,肯定不是预包含
nowJo.put(Contance.APP_ISINSTALLED, "0"); // 是否已经安装,给个0
nowJo.put(
Contance.APP_ISHAVEBEENUPDATED,
"0"); // 既然是新的,肯定不是预包含
nowJo.put(Contance.APP_ISREQUIREUPDATE,
"0"); // 既然是新的,肯定不是预包含
// 然后将添加过字段的nowJsonObject添加到now的array中
// 先更新这个array
nowArray.put(i, nowJo);
// 先保存这个值
number.add(i);
}
}
}
// 获得原来的长度
int size = beforeArray.length();
// 遍历完了之后,开始将保存的nowarray中的值添加到before中
for (int x = 0; x < number.size(); x++) {
beforeArray.put(size + x,
nowArray.opt(number.get(x)));
}
// 再将arry添加回去
jsonObjectAll
.put(Contance.APPLIST_ALL, beforeArray);
jsonObjectAll.put(Contance.APPLIST_NUM,
beforeArray.length());
// 将文件内容全部写入到applist.all.description
fajo.writeContent(jsonObjectAll.toString(), false,
Contance.ALLNAME);
/**
* 将down的内容写到down文件下
*/
String versioncode = result
.getString(Contance.APPLIST_VERSIONCODE);
Log.i(Contance.TAG_INFO, versioncode);
String applitnum = result
.getString(Contance.APPLIST_NUM);
JSONArray jsonArray = result
.getJSONArray("applist_downloaded");
// 自定义拼写的json串
String writePreStr = "{" + "\""
+ Contance.APPLIST_VERSIONCODE + "\":\""
+ versioncode + "\",\""
+ Contance.APPLIST_NUM + "\":\""
+ applitnum + "\",\""
+ Contance.APPLIST_DOWNLOADED + "\":"
+ jsonArray.toString() + "}";
// 写入到down文件中
fajo.writeContent(writePreStr, false,
Contance.DOWNLOADEDNAME);
/**
* 解析downlist,获得包名,图片url,以及图片名字,用于存储
*/
imageNameList = new ArrayList<String>();
imageUrlList = new ArrayList<String>();
packList = new ArrayList<String>();
for (int i = 0; i < downloadList.size(); i++) {
JSONObject jsonObject = new JSONObject(
downloadList.get(i));
imageUrlList.add(jsonObject
.getString(Contance.APP_LOGOIMGURL));
packList.add(jsonObject
.getString(Contance.APP_PACKAGENAME));
}
Log.i(Contance.TAG_INFO, "下载的图片的所有的连接----->"
+ imageUrlList.toString());
// 去下载所有的图片,存在就算了
mHandler.sendEmptyMessage(Contance.LOADING_LOGO);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
mHandler.sendEmptyMessage(Contance.REFRESH_LISTVIRE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
// 刷新listview
} else { // 如果版本号一样,就不管,说明没有更新
mHandler.sendEmptyMessage(45);
}
} else { // 如果大小为0,就什么也不做,显示刷新成功就行了
mHandler.sendEmptyMessage(45);
}
} catch (UnsupportedEncodingException e) {
mHandler.sendEmptyMessage(Contance.REFRESH_FAILED);
e.printStackTrace();
} catch (ClientProtocolException e) {
mHandler.sendEmptyMessage(Contance.REFRESH_FAILED);
e.printStackTrace();
} catch (SocketException e) {
mHandler.sendEmptyMessage(Contance.REFRESH_FAILED);
e.printStackTrace();
} catch (IOException e) {
mHandler.sendEmptyMessage(Contance.REFRESH_FAILED);
e.printStackTrace();
} catch (JSONException e) {
mHandler.sendEmptyMessage(Contance.REFRESH_FAILED);
e.printStackTrace();
}
}
}).start();
}
/**
* 正是给listview填充数据
*/
public List<ApkInfoBean> addData1() {
List<ApkInfoBean> list = new ArrayList<ApkInfoBean>();
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
String filterStr = (String) sharedPreferences.getString(
Contance.SETTING_LANGUAGE_SAVE_KEY, "hy").subSequence(
1,
sharedPreferences.getString(Contance.SETTING_LANGUAGE_SAVE_KEY,
"hy").length() - 1); // 目前测试专用过滤listview的条目显示
String[] str = filterStr.split(",");
saveAdapterList = new ArrayList<String>();
btnStateList = new ArrayList<String>();
try {
/**
* 读取pre文件,然后进行listview的填充
*/
preList = new ArrayList<String>();
preList1 = new ArrayList<String>();
preList = fajo.resolveJsonArray(
"{"
+ fajo.readContent(fajo
.filePathName(Contance.PREINCLUDEDNAME)),
preList, Contance.APPLIST_PREINCLUDED);
Log.i(Contance.TAG_INFO, "preList111----->" + preList.toString());
// 用于设置item的个数
int preSize = preList.size();
Log.i(Contance.TAG_INFO, "preSize----->" + preSize);
pppname = new ArrayList<String>();
for (int i = 0; i < preSize; i++) {
ApkInfoBean info = new ApkInfoBean();
JSONObject jsonObject = new JSONObject(preList.get(i));
// 获得此应用支持的语言类型
String langStr = jsonObject.getString("languages");
pppname.add(jsonObject.getString(Contance.APP_PACKAGENAME));
for (int j = 0; j < str.length; j++) {
if (langStr.contains(str[j].trim())) { // 只有包含支持语言的才显示
Log.i(Contance.TAG_INFO, "preList第" + i + "个--->"
+ preList.get(i));
InputStream is;
try {
is = this
.getAssets()
.open("applist/"
+ jsonObject
.getString(Contance.APP_PACKAGENAME)
+ "/"
+ jsonObject
.getString(Contance.APP_LOGOIMGURL));
Bitmap bmp = BitmapFactory.decodeStream(is);
info.setApk_logo(bmp);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
info.setApk_name(jsonObject