01. 项目介绍
02. SVN的使用
03. 代码的包结构
1.按照模块 组织代码的包结构 ;各个模块之间的业务是独立;
办公软件www.itheima.com
----开会 com.itheima.meeting
----发工资 com.itheima.money
----出差 com.itheima.travel
风行网
-----播放器 com.funshion.android.player
-----下载模块 com.funshion.android.download
-----联网 com.funshion.android.network
2.按照类型组织代码的包结构
-----界面 com.itheima.activity
----自定义界面 com.itheima.ui
----业务逻辑 com.itheima.engine- 联网
----持久化 com.itheima.db
com.itheima.db.dao
----后台服务 com.itheima.service
----接收广播 com.itheima.receiver
----公共API接口 com.itheima.utils
04.工程的创建
创建工程时最好不要选择最大版本,防止以后Android新版本无法安装(maxSdkVersion)
05. splash页面UI完成
(1)提交工程到版本库
a.在版本库中新建一个库,拷贝URl
b.找出mobilesafe工程的路径,把工程文件夹右键检出,使库的URL与工程的绝对路径相对应
c.打开工程文件夹,把问号文件(除了自动生成的svn,bin,gen)添加到本地库,然后提交到服务器
(2)splash界面的作用
a.用来展现产品的Logo;
b.应用程序初始化的操作;
c.检查应用程序的版本;
d.检查当前应用程序是否合法注册(3)配置文件中的版本信息
a. android:versionCode="1" :int类型 ,android:versionName="1.0" :string类型
(4)动态的到版本信息(即配置文件中的版本)
a.程序运行会自动生成一个apk,包含配置文件
<span style="font-size:14px;">
/**
* 得到应用程序的版本名称
*/
private String getVersionName() {
// 用来管理手机的APK
PackageManager pm = getPackageManager();
try {
// 得到知道APK的功能清单文件
PackageInfo info = pm.getPackageInfo(getPackageName(), 0);
return info.versionName;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
</span>
06. 升级的原理
1.在服务器配置,下载的apk和版本信息(json-------版本号,版本描述,版本下载链接)2.升级原理
07.连接服务器获取更新信息
1、到webservice里去写json文件:updata.json
2、Anddroid工程添加联网权限:android.permission.INTERNET
3、在子线程中请求服务器代码checkVersion()。
4, 请求网络的代码: 把url封装到value目录下,创建一个xml文件5. 参照一个解析流的类StreamTools.java :把字节流转换成字符串
6. 解析JSON
7. 用Handler更新信息
8、写延迟2秒进入主页面代码
/**
* 检查是否有新版本,如果有就升级
*/
private void checkUpdate() {
new Thread() {
public void run() {
// URLhttp://192.168.1.254:8080/updateinfo.html
Message mes = Message.obtain();
long startTime = System.currentTimeMillis(); //睡两秒,防止一闪而过,记录当前时间
try {
URL url = new URL(getString(R.string.serverurl));//放在values新建一个xml文件,便于改变
// 联网
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setRequestMethod("GET");//请求方法
conn.setConnectTimeout(4000);//链接超时
int code = conn.getResponseCode();//响应吗
if (code == 200) {
// 联网成功
InputStream is = conn.getInputStream();
// 把流转成String
String result = StreamTools.readFromStream(is);
Log.i(TAG, "联网成功了" + result);
// json解析
JSONObject obj = new JSONObject(result);
// 得到服务器的版本信息
String version = (String) obj.get("version");
description = (String) obj.get("description");
apkurl = (String) obj.get("apkurl");
// 校验是否有新版本
if (getVersionName().equals(version)) {//当前的版本与服务器版本
// 版本一致,没有新版本,进入主页面
mes.what = ENTER_HOME;
} else {
// 有新版本,弹出一升级对话框
mes.what = SHOW_UPDATE_DIALOG;
}
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
mes.what = URL_ERROR;
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
mes.what = NETWORK_ERROR;
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
mes.what = JSON_ERROR;
} finally {
long endTime = System.currentTimeMillis(); //结束时间
// 我们花了多少时间
long dTime = endTime - startTime;
// 2000
if (dTime < 2000) {
try {
Thread.sleep(2000 - dTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
handler.sendMessage(mes);
}
};
}.start();
}
9、创建主页面HomeActivity和布局文件
10、添加AlphaAnimation动画效果: activity间切换效果 (oncreat());
AlphaAnimation aa = new AlphaAnimation(0.2f, 1.0f); // 界面切换动画,朦胧转成清晰
aa.setDuration(500);
findViewById(R.id.rl_root_splash).startAnimation(aa);
11. 提示升级对话框--------升级安装
(1)工具包 afinal.jar
/**
* 弹出升级对话框
*/
protected void showUpdateDialog() {
//this = Activity.this
AlertDialog.Builder builder = new Builder(SplashActivity.this);
builder.setTitle("提示升级");
// builder.setCancelable(false);//强制升级
builder.setOnCancelListener(new OnCancelListener() { //监听取消
@Override
public void onCancel(DialogInterface dialog) {
// TODO Auto-generated method stub
//进入主页面
enterHome();
dialog.dismiss();
}
});
builder.setMessage(description);
builder.setPositiveButton("立刻升级", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 下载APK,并且替换安装
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
// sdcard存在
// afnal
FinalHttp finalhttp = new FinalHttp(); //外部包
finalhttp.download(apkurl, Environment
.getExternalStorageDirectory().getAbsolutePath()+"/mobilesafe2.0.apk",
new AjaxCallBack<File>() {
@Override
public void onFailure(Throwable t, int errorNo, //下载失败
String strMsg) {
t.printStackTrace();
Toast.makeText(getApplicationContext(), "下载失败", 1).show();
super.onFailure(t, errorNo, strMsg);
}
@Override
public void onLoading(long count, long current) { //下载中
// TODO Auto-generated method stub
super.onLoading(count, current);
tv_update_info.setVisibility(View.VISIBLE);
//当前下载百分比
int progress = (int) (current * 100 / count);
tv_update_info.setText("下载进度:"+progress+"%");
}
@Override
public void onSuccess(File t) { //下载完成
// TODO Auto-generated method stub
super.onSuccess(t);
installAPK(t); //下载完成后安装
}
/**
* 安装APK
* @param t
*/
private void installAPK(File t) {
//调用系统的安装
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(Uri.fromFile(t), "application/vnd.android.package-archive");
startActivity(intent);
}
});
} else {
Toast.makeText(getApplicationContext(), "没有sdcard,请安装上在试",
0).show();
return;
}
}
});
builder.setNegativeButton("下次再说", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
dialog.dismiss();
enterHome();// 进入主页面
}
});
builder.show();
}
08.应用程序的签名
(1)在Android手机里不允许有两个应用程序有相同的包名;
假设A应用的包名:com.itheima.mobilesafeA;
A应用已经在系统中存在了。
这个时候再去安装一个应用B ,他的报名也叫con.itheima.mobilesafeA
系统就会去检查这两应用的签名是否相同。如果相同,B会把A给覆盖安装掉;
如果不相同 B安装失败;
要想自动安装成功,必须保证应用程序不同版本的签名完成一样。
(2)正在开发的应用会根据电脑开发环境产生默认签名,工程路径-----bin目录-----apk(3)签名方式:导出应--------------------用已存在的密钥or创建新的密钥
09.Splash界面的细节
(1)显示4.0的样式:方式是去掉功能清单里的Activity对应的android:theme;
放到application里面;------------------------改一下系统代码
(2)当splash页面弹出升级提示框,过滤点击返回的是两种方式:
提示框不能取消-------------------------------强制升级// 一般不用
监听返回键和触摸对话框以外地方
10.两种上下文的区别
对话框是Activity的一部分。
对话框是挂载在Activity上面的 。
如果Activity不存在,对话框就不能被创建。
Activity 实际上是应用程序context上下文的一个子集。
子类有的东西父类不一定有
父类有的东西子类一定有
getApplicationContext();生命周期长,只要应用还存活它就存在;
this 生命周期短,只要Activity不存在了,系统就会回收;
其中:getBaseContext(),getApplication(),getApplicationContext();
都不能放在AlertDialog做上下文;
getApplicationContext() 使用场景是比如频繁需要操作的数据库
推荐用法:Activity.this
11. 应用程序主界面(1)滚动的TextView---------------------------------自定义一个TextView
创建一个类class继承TextView
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewDebug.ExportedProperty;
import android.widget.TextView;
/*
* 自定一个TextView 一出生就有焦点
*/
public class FocusedTextView extends TextView {
public FocusedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public FocusedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public FocusedTextView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
/**
* 当前并没有焦点,我只是欺骗了Android系统
*/
@Override
@ExportedProperty(category = "focus")
public boolean isFocused() {
return true;
}
使用:
<com.itheima.mobilesafe.ui.FocusedTextView
android:singleLine="true"
android:ellipsize="marquee"
android:textSize="18sp"
android:text="最新的手机卫士,快来下载啊,下载送好吃的,最新的手机卫士,快来下载啊,下载送好吃的"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
总界面:
public class HomeActivity extends Activity {
private GridView list_home;
private MyAdapter adapter;
private static String [] names = {
"手机防盗","通讯卫士","软件管理",
"进程管理","流量统计","手机杀毒",
"缓存清理","高级工具","设置中心"
};
private static int[] ids = {
R.drawable.safe,R.drawable.callmsgsafe,R.drawable.app,
R.drawable.taskmanager,R.drawable.netmanager,R.drawable.trojan,
R.drawable.sysoptimize,R.drawable.atools,R.drawable.settings
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
list_home = (GridView) findViewById(R.id.list_home);
adapter = new MyAdapter();
list_home.setAdapter(adapter);
list_home.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
switch (position) {
case 8://进入设置中心
Intent intent = new Intent(HomeActivity.this,SettingActivity.class);
startActivity(intent);
break;
default:
break;
}
}
});
}
private class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
// TODO Auto-generated method stub
return names.length;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view = View.inflate(HomeActivity.this, R.layout.list_item_home, null);
ImageView iv_item = (ImageView) view.findViewById(R.id.iv_item);
TextView tv_item = (TextView) view.findViewById(R.id.tv_item);
tv_item.setText(names[position]);
iv_item.setImageResource(ids[position]);
return view;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
}
}
----------------------------------------------------------------------设置中心-------------------------------------------------------------------------------------
12. 自定义组合控件(1)把一组控件封装起来重用
创建一个新的布局文件xml,和一个继承布局的Java类
布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="68dip" >
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginTop="8dip"
android:text="设置是否自动更新"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_title"
android:layout_marginLeft="10dip"
android:text="自动更新已经关闭"
android:textColor="#88000000"
android:textSize="18sp" />
<CheckBox
android:clickable="false"
android:focusable="false"
android:id="@+id/cb_status"
android:layout_marginRight="10dip"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<View
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_alignParentBottom="true"
android:background="#000000"
android:layout_width="fill_parent"
android:layout_height="0.2dip"/>
</RelativeLayout>
继承布局的java类
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.itheima.mobilesafe.R;
/**
* 我们自定义的组合控件,它里面有两个TextView ,还有一个CheckBox,还有一个View
* @author Administrator
*
*/
public class SettingItemView extends RelativeLayout {
private CheckBox cb_status;
private TextView tv_desc;
private TextView tv_title;
/**
* 初始化布局文件
* @param context
*/
private void iniView(Context context) {
//把一个布局文件---》View 并且加载在SettingItemView
View.inflate(context, R.layout.setting_item_view, this);
cb_status = (CheckBox) this.findViewById(R.id.cb_status);
tv_desc = (TextView) this.findViewById(R.id.tv_desc);
tv_title = (TextView) this.findViewById(R.id.tv_title);
}
public SettingItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
iniView(context);
}
public SettingItemView(Context context, AttributeSet attrs) {
super(context, attrs);
iniView(context);
}
public SettingItemView(Context context) {
super(context);
iniView(context);
}
/**
* 校验组合控件是否选中
*/
public boolean isChecked(){
return cb_status.isChecked();
}
/**
* 设置组合控件的状态
*/
public void setChecked(boolean checked){
cb_status.setChecked(checked);
}
/**
* 设置 组合控件的描述信息
*/
public void setDesc(String text){
tv_desc.setText(text);
}
}
布局使用
<com.itheima.mobilesafe.ui.SettingItemView
android:id="@+id/siv_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</com.itheima.mobilesafe.ui.SettingItemView>
代码使用
public class SettingActivity extends Activity {
private SettingItemView siv_update;
private SharedPreferences sp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting);
sp = getSharedPreferences("config", MODE_PRIVATE);
siv_update = (SettingItemView) findViewById(R.id.siv_update);
boolean update = sp.getBoolean("update", false);
if(update){
//自动升级已经开启
siv_update.setChecked(true);
siv_update.setDesc("自动升级已经开启");
}else{
//自动升级已经关闭
siv_update.setChecked(false);
siv_update.setDesc("自动升级已经关闭");
}
siv_update.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Editor editor = sp.edit();
//判断是否有选中
//已经打开自动升级了
if(siv_update.isChecked()){
siv_update.setChecked(false);
siv_update.setDesc("自动升级已经关闭");
editor.putBoolean("update", false);
}else{
//没有打开自动升级
siv_update.setChecked(true);
siv_update.setDesc("自动升级已经开启");
editor.putBoolean("update", true);
}
editor.commit();
}
});
}
}
(2)保存配置数据用 SharedPreferences
(3)演示并处理CheckBox的点击事件---解决方案:禁用点击事件;
Android:clickable=”false”
Android:focusable=”false”
(4)记录选中状态,并在进入的时候读取保存的状态
(5)在SplashActivity根据是否开启升级而相应的是否升级
(6)延迟两秒进入主页面的代码
//停留2秒后进入主页面;
handler.postDelayed(new Runnable() {
@Override
public void run() {
enterHome();
}
}, 2000);