代码组织结构
-
根据业务逻辑划分
-
办公软件
-
出差 com.itheima.travel
-
工资 com.itheima.money
-
会议 com.itheima.meeting
-
-
网盘
-
上传 com.vdisk.upload
-
下载 com.vdisk.download
-
分享 com.vdisk.share
-
-
-
根据功能模块划分(Android开发推荐此方法)
-
Activity com.itheima.mobilesafe.activty
-
后台服务 com.itheima.mobilesafe.service
-
广播接收者 com.itheima.mobilesafe.receiver
-
数据库 com.itheima.mobilesafe.db.dao
-
对象(java bean) com.itheima.mobilesafe.domain/bean
-
自定义控件 com.itheima.mobilesafe.view
-
工具类 com.itheima.mobilesafe.utils
-
业务逻辑 com.itheima.mobilesafe.engine
-
项目创建
应用名称、项目名称、包名
版本选择:
-
minimum SDK 要求最低的安装版本, 安装apk前,系统会判断当前版本是否高于(包含)此版本, 是的话才允许安装
-
maxSdkVersion 要求最高的安装版本(一般不用)
-
Target SDK 目标SDK, 一般设置为开发时使用的手机版本, 这样的话,系统在运行我的apk时,就认为我已经在该做了充分的测试, 系统就不会做过多的兼容性判断, 从而提高运行效率
-
Compile With 编译程序时使用的版本
Activity创建
1、闪屏页面(SplashActivity)
-
展示logo,公司品牌
-
项目初始化
-
检测版本更新
-
校验程序合法性(比如:判断是否有网络,有的话才运行)
1) 根据功能模块创建/修改包名,activity名称
2) 编辑闪屏的布局文件
背景图片
android:background="@drawable/launcher_bg"
显示版本号,居中
android:layout_centerHorizontal="true" android:layout_centerVertical="true"
显示进程图标,版本号正下方,居中
android:layout_below="@+id/tv_Version"
3) 界面内容编程
获取版本号信息
在activity文件中新创建方法getVersionName()–获取版本信息的方法
再在OnCreate()方法中调用该方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
tvVersion = (TextView) findViewById(R.id.tv_Version);
tvVersion.setText("版本号:"+getVersionName());
}
private String getVersionName(){
PackageManager packageManager = getPackageManager();
try {
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
//版本名称&版本号
//android:versionCode="1"
//android:versionName="1.0"
int versionCode = packageInfo.versionCode;
String versionName = packageInfo.versionName;
return versionName;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
4) 细节界面处理
- 使手机最上面的主题条不显示
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
修改为:
<application
android:theme="@android:style/Theme.Black.NoTitleBar"
- 字体加阴影
一般需要给显示字体指定颜色,英文不同版本默认颜色不同;同时还有大小;最终效果见上面的图。
<TextView
android:id="@+id/tv_Version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:shadowColor="#f00"
android:shadowDx="1"
android:shadowDy="1"
android:shadowRadius="1"
android:textColor="#000"
android:text="版本号" />
<!-- 包括阴影颜色、偏移、半径;其中"#f00"代表红色,代表"#000"白色-->
2、网络获取json数据及解析 并弹窗提示更新
流程图:
将会涉及到的内容:
- 获取项目自身版本信息 PackageManager、PackageInfo
- 网络获取数据 URL 、HttpsURLConnection(需要INTERNET权限)
- 解析Json JSONObject
- 子线程使用 Thread
- Handler处理,& Message
- 更新对话框弹出 showUpdateDailog
网络获取数据(网络存在延时,需要引用子线程处理,否则导致主线程等待时间长或不确定)得到最新版本信息,与项目自身版本信息比较,判断是否需要更新,更新则弹出对话框给用户(对话框不能放在子线程中处理,又引用Handler来处理,其中消息用Message传递)
从网络获取到的信息需要转换 InputStream->String,此转换按功能模块分类放在工具类Utils中
服务器:Apache.Tomcat
和服务器进行数据交互的大部分都是json格式
{ “name” : “value”,
“name2” : “value2” }
(1) json数据放入服务器
(2) 开启服务器,利用网址连接可访问服务器
将APP的版本信息编辑成Json格式(update.json),存入安装目录的.\webapps\ROOT下面,通过网址http://localhost:8080/update.json可以访问内容
{
"versionName":"2.0",
"versionCode":2,
"description":"NB功能,赶快体验",
"downloadURL":"http://localhost:8080/update.json"
}
养成良好的编程习惯
- 快捷键:
* Ctrl + 1 弹出相关提示,可以将变量改成全局编程
* Ctrl + 2 ,L 自动创建变量类型及名称
- 命名:(首字母适当大写)
* 布局文件中的控件名称采用形如 **tv_描述** 的格式命名
* 在activity.java文件中,对应控件采用形如 **tv描述** 的格式命名
* 全局变量采用形如 **m描述** 的格式命名
1)从服务器获取最新版本信息(网络获取数据,解析Json)
参考blog:Java通过URL 从web服务端获取数据
(1)问题:未将网络获取数据程序段以子线程运行时,报如下错误:
Android之NetworkOnMainThreadException异常 的理解与处理
主要内容如下:
官宣:从Honeycomb SDK(3.0)开始,google不再允许(禁止)网络请求(HTTP、Socket)等相关操作直接在Main Thread类中,因为直接在UI线程进行网络操作,会阻塞UI、用户体验相当bad!
So 解决方案:
- 和network有关比较耗时的操作放到一个子线程里,然后用Handler消息机制与主线程通信。(本文采用)
- 使用异步机制如:asynctask,这个举个简单的加载网络图片的例子
- 直接在main Thread 进行网络操作的方法:在发起Http请求的Activity里面的onCreate函数里面添加相关代码(可参考blog,但也不推荐使用)
//简单的启动子线程异步加载数据
new Thread(){
public void run() {
...
}
}.start();
(2)网络获取数据,解析Json,版本对比,子线程处理等编写在同一个方法中
代码如下,结合上下文一起更好理解。
private void checkVersion() {
//启动子线程异步加载数据
new Thread(){
public void run() {
Message message = Message.obtain();
HttpURLConnection httpURLConnection = null;
try {
//连接服务端
URL url = new URL("http://10.0.2.2:8080/update.json");
//把HttpURLConnection与HttpsURLConnection搞错了
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.setReadTimeout(5000);
httpURLConnection.connect();
int responseCode = httpURLConnection.getResponseCode();
if (responseCode==200) {
//响应成功,获取输入流
InputStream inputStream = httpURLConnection.getInputStream();
//利用自己编写的工具类将输入流转换成String
String result = StreamUtils.readStream(inputStream);
//利用Json解析
JSONObject jsonObject = new JSONObject(result);
mVersionName = jsonObject.getString("versionName");
mVersionCode = jsonObject.getInt("versionCode");
mDescription = jsonObject.getString("description");
mDownloadURL = jsonObject.getString("downloadURL");
System.out.println("description:"+mDescription);
//判断是否需要更新
if (mVersionCode>getVersionCode()) {
//需要更新,弹出更新对话框
//使用Handler机制,将message.what设置不同属性值,下面else以及各种异常同理
message.what = CODE_UPDATE_DIALOG;
}
else {
//不需要更新,保持原来页面or进入主页面
message.what = CODE_ENTER_HOME;
}
}
} catch (MalformedURLException e) {
// URL错误
message.what = CODE_URL_ERROR;
e.printStackTrace();
} catch (IOException e) {
//连接错误
message.what = CODE_NET_ERROR;
e.printStackTrace();
} catch (JSONException e) {
// json解析错误
message.what = CODE_JSON_ERROR;
e.printStackTrace();
}finally{
//将message发送到Handler
mHandler.sendMessage(message);
//关闭网络
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
};
}.start();
}
2)Handler消息机制处理
软件自动补充的mHandler对象的代码如下:
但注意:这是java.util.logging.Handler; 下的Handler,我们需要用的是android.os.Handler;下的Handler
private Handler mHandler = new Handler() {
@Override
public void publish(LogRecord record) {
// TODO Auto-generated method stub
......
}
@Override
public void flush() {
// TODO Auto-generated method stub
......
}
@Override
public void close() {
// TODO Auto-generated method stub
......
}
};
大致编写代码如下:
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message message) {
// TODO Auto-generated method stub
switch (message.what) {
case CODE_UPDATE_DIALOG:
//弹出升级对话框
break;
case CODE_ENTER_HOME:
//进入主页面
break;
case CODE_URL_ERROR:
//弹出吐司:网址错误
break;
case CODE_NET_ERROR:
//弹出吐司:网络出错
break;
case CODE_JSON_ERROR:
//弹出吐司:数据解析错误
break;
default:
break;
}
};
};
3)对话框处理
private void showUpdateDailog() {
AlertDialog.Builder uBuilder = new AlertDialog.Builder(this);
uBuilder.setTitle("更新版本:"+mVersionName);
uBuilder.setMessage(mDescription);
uBuilder.setPositiveButton("立马更新", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
System.out.println("立马更新");
......
}
});
uBuilder.setNegativeButton("以后再说", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
System.out.println("以后再说");
......
}
});
uBuilder.show();
}