前言

前面一篇主要是打开软件时闪屏页面的处理,以及获取版本信息进行比较,判断是否需要更新,如需更新并弹出更新的对话框
本篇则对两种情况分别作出处理:

  • 不(用)更新:跳到主页面
  • 更新:下载新版本的安装包,安装

更新版本

1、 将新版本的安装包打包成.apk
2、 将安装包放到服务器上
3、 从服务器上下载安装包
4、 安装应用

.apk打包

  1. 选中项目,点击鼠标右键,一次选择Android tools/Export signed Application package…
  2. 出现弹窗,项目名称,一般不需要修改
  3. android 手机卫士开发 安卓手机卫士下载_安装包

  4. 密钥码,可以选择已有的密钥码,也可以生成新的,这里选择后者。
    选择存放密钥码的路径,我就放在了项目路径下,后缀名用.keystore;并输入密码及确认密码
  5. android 手机卫士开发 安卓手机卫士下载_android_02

  6. 别名、有效期等相关信息,其余不用一一填写
  7. android 手机卫士开发 安卓手机卫士下载_安装包_03

  8. 存放安装包.apk文件路径选择,若系统没有自动添加后缀,建议添加.apk后缀,点击finish便可去刚刚存放的路径下找到安装包。
  9. android 手机卫士开发 安卓手机卫士下载_android_04

将资源放置到服务器上

apache tomcat/webapps/ROOL/项目文件夹下------->打开服务器

android 手机卫士开发 安卓手机卫士下载_安装包_05

解决一个问题:json如有中文文字,需要将文件保存为UTF-8的编码格式,否则应用上显示可能乱码

从服务器上下载安装包

从GitHub找下载资源的类

利用GitHub上的开源代码,其他研发人员写的类XUtils

上官网搜索,选择点赞星星多的进入,查看使用说明,下载,并把jar包导入自己项目便可使用

android 手机卫士开发 安卓手机卫士下载_android 手机卫士开发_06


复制jar包到自己项目中的lib中即可

android 手机卫士开发 安卓手机卫士下载_安装包_07

XUtils类使用

读开发者的readme文件即可。

//使用HttpUtils下载文件:
//支持断点续传,随时停止下载任务,开始任务
HttpUtils http = new HttpUtils();
HttpHandler handler = http.download("http://apache.dataguru.cn/httpcomponents/httpclient/source/httpcomponents-client-4.2.5-src.zip",
    "/sdcard/httpcomponents-client-4.2.5-src.zip",
    true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
    true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
    new RequestCallBack<File>() {

        @Override
        public void onStart() {
            testTextView.setText("conn...");
        }

        @Override
        public void onLoading(long total, long current, boolean isUploading) {
            testTextView.setText(current + "/" + total);
        }

        @Override
        public void onSuccess(ResponseInfo<File> responseInfo) {
            testTextView.setText("downloaded:" + responseInfo.result.getPath());
        }


        @Override
        public void onFailure(HttpException error, String msg) {
            testTextView.setText(msg);
        }
});

...
//调用cancel()方法停止下载
handler.cancel();
...

其中download函数的第二个参数时存放路径,选择SD是需要获取SD卡外部写权限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

我刚开始没有添加权限,结果下载失败,但不解的是logcat也没有提示错误,可能是其它提示,我还未学习到,所以还是要记得添加权限,不然卡在那半天。

下载成功可在File Explorer上查看

android 手机卫士开发 安卓手机卫士下载_android_08

下载时细节界面处理

显示进度条

<TextView
        android:id="@+id/tv_download"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="12dp"
        android:text="下载进度:0%"
        android:visibility="invisible" />

先设置不可见 invisible;再在下载时设置为可见,并设置内容

tvDownload.setVisibility(View.VISIBLE);
tvDownload.setText("下载进度:"+current*100/total+"%");

安装应用

安装应用时需要调用已有的项目PackageInstaller ;在清单文件中查看其包名等相关信息进行页面跳转(调用)。

Intent intent = new Intent(Intent.ACTION_VIEW);
     intent.addCategory(Intent.CATEGORY_DEFAULT);
     intent.setDataAndType(Uri.fromFile(arg0.result), "application/vnd.android.package-archive");
     startActivity(intent);

点击更新对话框的立即更新按钮后会跳到安装界面,但是因为签名冲突无法安装。

签名冲突
如果两个应用程序, 包名相同, 但是签名不同, 就无法覆盖安装

  • 正式签名
  1. 有效期比较长,一般大于25年
  2. 需要设置密码
  3. 正式发布应用时,必须用正式签名来打包
  • 测试签名(debug.keystore)
  1. 有效期是1年,很短
  2. 有默认的别名,密码, alias=android, 密码是androiddebugkey
  3. 在eclipse中直接运行项目是,系统默认采用此签名文件
  • 如果正式签名丢失了怎么办?
  1. 修改包名, 发布, 会发现有两个手机卫士, 用户会比较纠结
  2. 请用户先删掉原来的版本,再进行安装, 用户会流失
  3. 作为一名有经验的开发人员,请不要犯这种低级错误

所以先将不同版本的软件的签名密钥统一打包,先在模拟器上安装低版本,再在更新弹窗上选择立即更新时才能正确更新版本。
在模拟器上安装使用签名打包的低版本软件时,使用adb命令

adb工具即Android Debug Bridge(安卓调试桥) tools。它就是一个命令行窗口,用于通过电脑端与模拟器或者真是设备交互。
一般在ADT安装路径下就有:\adt-bundle-windows-x86_64-20140702\adt-bundle-windows-x86_64-20140702\sdk\platform-tools下有adb.exe;将该路径在环境变量中进行配置,通过cmd进入命令行窗口就可以使用adb命令

安装命令:adb install 路径\软件名.apk   (将安装包拖拽到命令行窗口就可以直接显示路径及软件名)
安装前需要卸载掉之前的调试版本

主页面开发准备

初级准备

  • 创建主页面的activity、布局文件
  • 清单文件中配置主页面的activity
  • 闪屏页面到主页面的跳转

在没有更新版本、用户选择不更新、选择更新但是出现网络错误或其他错误时,等情况下,应该跳转到主页面去。
跳转页面函数代码:

private void enterHome() {
  Intent intent = new Intent(this, HomeActivity.class);
  startActivity(intent);
  finish();
 }

注意,同样的需要在activity中关联布局文件
在OnCreat()中添加语句setContentView(R.layout.activity_home);
在没有更新版本时,如果网络太快,闪屏页面可能还没展示就跳转到主页面,所以需要编写代码使之停留一会(2s),使用线程休眠sleep()

布局文件九宫格显示*

在布局文件中添加GridView

<GridView
        android:id="@+id/gv_Home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginBottom="20dp"
        android:gravity="center"
        android:numColumns="3"
        android:verticalSpacing="25dp" >
    </GridView>

其中每个栅格的布局又写在另一个xml文件中

定义了单个栅格内容:TextView和ImageView(图片和文字)

activity中,对GridView的处理:

//在OnCreate()中
gvHome = (GridView) findViewById(R.id.gv_Home);
gvHome.setAdapter(new gvHomeAdapter());
//item相关定义
 String[] items = new String[]{"手机防盗","通讯卫士","软件管理",
   "进程管理","流量统计","手机杀毒","缓存清理","高级工具","设置中心"};
 int[] pics = new int[]{R.drawable.home_safe,
   R.drawable.home_callmsgsafe,R.drawable.home_apps,
   R.drawable.home_taskmanager,R.drawable.home_netmanager,
   R.drawable.home_trojan,R.drawable.home_sysoptimize,
   R.drawable.home_tools,R.drawable.home_settings};
//创建自定义的HomeAdapter类
public class HomeAdapter extends BaseAdapter  {

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return items.length;
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return items[position];
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @SuppressLint("ViewHolder") @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        View inflate = View.inflate(HomeActivity.this, R.layout.home_item_list, null);
        TextView tvItem = (TextView) inflate.findViewById(R.id.tv_item);
        ImageView ivItem = (ImageView) inflate.findViewById(R.id.iv_item);
        tvItem.setText(items[position]);
        ivItem.setImageResource(pics[position]);
        return inflate;
    }
}

用户体检细节处理

(1)对更新对话框弹出后用户选择返回作做应对处理(进入主页面)

  • 使返回键失效(用户体检差,不推荐使用)
builder.setCancelable(false);
  • 在对话框弹出方法中,设置取消的监听, 用户点击返回键时会触发,触发后进入主页面
// 设置取消的监听, 用户点击返回键时会触发
    builder.setOnCancelListener(new OnCancelListener() {
        @Override
        public void onCancel(DialogInterface dialog) {
            enterHome();
        }
    });

(2) 更新安装时选择取消操作做应对处理(安装取消,进入主页面)
在跳转到应用安装界面处修改代码

// startActivity(intent);
startActivityForResult(intent, 0);// 如果用户取消安装的话,会返回结果,回调方法onActivityResult

回调方法onActivityResult()如下:

// 如果用户取消安装的话,回调此方法
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  enterHome();
  super.onActivityResult(requestCode, resultCode, data);
 }