Android入门笔记13

开发手机安全卫士

此篇笔记记录了android闪屏页面的开发和主页面(静态,功能还未实现)的开发。主要记录闪屏界面中的版本检测和更新。

代码组织结构

  • 根据业务逻辑划分

    • 办公软件

      • 出差 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 编译程序时使用的版本

闪屏页面(Splash)

  • 展示logo,公司品牌
  • 项目初始化
  • 检测版本更新
  • 校验程序合法性(比如:判断是否有网络,有的话才运行)

签名冲突

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

正式签名

1. 有效期比较长,一般大于25年
2. 需要设置密码
3. 正式发布应用时,必须用正式签名来打包

测试签名(debug.keystore)

1. 有效期是1年,很短
2. 有默认的别名,密码, alias=android, 密码是androiddebugkey
3. 在eclipse中直接运行项目是,系统默认采用此签名文件

如果正式签名丢失了怎么办?

1. 修改包名, 发布, 会发现有两个手机卫士, 用户会比较纠结
2. 请用户先删掉原来的版本,再进行安装, 用户会流失
3. 作为一名有经验的开发人员,请不要犯这种低级错误

子类和父类

子类拥有父类的所有方法, 而且可以有更多自己的方法

Activity(token), Context(没有token)
平时,要获取context对象的话, 优先选择Activity, 避免bug出现, 尽量不用getApplicationContext()

项目结构

在这里插入图片描述
app构建文件
在这里插入图片描述

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    defaultConfig {
        applicationId "com.example.administrator.myapplication"
        minSdkVersion 15
        targetSdkVersion 16
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
//    implementation 'com.android.support:appcompat-v7:29.+'
    implementation 'com.android.support:appcompat-v7:+'
    implementation 'com.android.support.constraint:constraint-layout:2.0.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

主要文件

在这里插入图片描述
AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.administrator.myapplication" android:versionCode="1" android:versionName="1.0">

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

    <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">
        <uses-library android:name="android.test.runner"/>
        <activity android:name=".activity.SplashActivity" android:label="主界面" android:icon="@mipmap/chrome">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:name=".activity.HomeActivity"/>
    </application>

</manifest>

activity_splash.xml

<RelativeLayout 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"
              android:background="@drawable/launcher_bg"

>


    <TextView
            android:text="TextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:id="@+id/tv_version" android:layout_weight="1"
            android:layout_centerHorizontal="true" android:layout_alignParentBottom="true"
            android:textSize="16sp"
            android:textColor="#000"
            android:shadowColor="#f00"
            android:shadowDx="1"
            android:shadowDy="1"
            android:shadowRadius="1"
            android:layout_marginBottom="116dp" tools:text="版本号1.0"/>
    <ProgressBar
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/progressBar"
            android:layout_centerInParent="true"/>
    <TextView
            android:text="下载进度"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:textColor="#f00"
            android:layout_marginLeft="20dp"
            android:textSize="16sp"
            android:visibility="gone"
            android:id="@+id/tv_progress"/>

</RelativeLayout>

home_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/iv_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/home_apps" />

    <TextView
        android:id="@+id/tv_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:textColor="@color/black"
        android:textSize="18sp"
        android:text="手机防盗" />

</LinearLayout>

StreamUtils.java

package com.example.administrator.myapplication.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 读取流数据
 */
public class StreamUtils {

    /**
     * 读取输入流返回String
     * @param in
     * @return
     */
    public static String readFromStream(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int len = 0;
        byte [] buffer = new byte[1024];
        while ((len=in.read(buffer))!=-1){
            out.write(buffer,0,len);
        }
        String result = out.toString();
        in.close();
        out.close();
        return result;
    }
}

SplashActivity.java

package com.example.administrator.myapplication.activity;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.example.administrator.myapplication.R;
import com.example.administrator.myapplication.util.StreamUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class SplashActivity extends Activity {

    private static final int CODE_UPDATE_DIALOG = 0;
    private static final int CODE_URL_ERROR = 1;
    private static final int CODE_NET_ERROR = 2;
    private static final int CODE_JSON_ERROR = 3;
    private static final int CODE_ENTER_HOME = 4;
    private TextView tvVserion;
    private TextView tvProgress; //下载进度展示
    private String mVersionName; //版本名
    private int mVersionCode; //版本号
    private String mDesc;//版本描述
    private String mDownloadUrl; //下载地址

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage( Message msg) {
           switch (msg.what){
               case CODE_UPDATE_DIALOG:
                   showUpdateDialog();
                   break;
               case CODE_URL_ERROR:
                   Toast.makeText(SplashActivity.this,"下载地址错误",Toast.LENGTH_SHORT).show();
                   enterHome();
                   break;
               case CODE_NET_ERROR:
                   Toast.makeText(SplashActivity.this,"网络错误",Toast.LENGTH_SHORT).show();
                   enterHome();
                   break;
               case CODE_JSON_ERROR:
                   Toast.makeText(SplashActivity.this,"数据解析错误",Toast.LENGTH_SHORT).show();
                   enterHome();
                   break;
               case CODE_ENTER_HOME:
                   enterHome();

           }
        };
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        setContentView(R.layout.activity_splash);
        super.onCreate(savedInstanceState);
        tvVserion = findViewById(R.id.tv_version);
        tvVserion.setText("版本号:"+getVersionName());
        tvProgress = findViewById(R.id.tv_progress); //默认隐藏
        checkVersion();
    }

    private String getVersionName(){
        PackageManager packageManager = getPackageManager();
        try {
            PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
            return packageInfo.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取本地app 的versioncode
     * @return
     */
    private int getVersionCode(){
        PackageManager packageManager = getPackageManager();
        try {
            PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
            return packageInfo.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * 从服务器获取版本信息进行校验
     */
    public void checkVersion(){
        final long startTime = System.currentTimeMillis();
        //启动子线程加载数据
        new Thread(){
            @Override
            public void run(){
                Message msg = Message.obtain();
                HttpURLConnection conn = null;
                try {
                    URL url = new URL("http://192.168.1.210:8080/update.json");
                    conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);//设置连接超时
                    conn.setReadTimeout(5000);//设置响应超时
                    conn.connect();

                    int responseCode = conn.getResponseCode();//获取响应码
                    if(responseCode==200){
                        InputStream inputStream = conn.getInputStream();
                        String result = StreamUtils.readFromStream(inputStream);
                        System.out.println("网络返回:"+result);

                        //解析json
                        JSONObject json = new JSONObject(result);
                        mVersionName = json.getString("versionName");
                        mVersionCode = json.getInt("versionCode");
                        mDesc = json.getString("description");
                        mDownloadUrl = json.getString("downloadUrl");

                        if(mVersionCode>getVersionCode()){
                            //服务器versionCode大于本地versionCode
                            //弹出升级对话框
                            msg.what = CODE_UPDATE_DIALOG;
                        }else {
                            msg.what = CODE_ENTER_HOME;
                        }

                    }
                } catch (MalformedURLException e) {
                    msg.what = CODE_URL_ERROR;
                    //url 错误异常
                    e.printStackTrace();
                }catch (IOException e){
                    msg.what = CODE_NET_ERROR;
                    //网络错误异常
                    e.printStackTrace();
                } catch (JSONException e) {
                    msg.what = CODE_JSON_ERROR;
                    e.printStackTrace();
                }finally {
                    long endTime =System.currentTimeMillis();
                    long timeUsed = startTime - endTime;

                    //这里就不是很人性化了,本来秒进系统的,你要给人家阻塞,就为了让闪屏页面至少展示2秒
                    if(timeUsed<2000){
                        try {
                            Thread.sleep(2000-timeUsed);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    mHandler.sendMessage(msg);
                    if(conn!=null){
                        conn.disconnect();
                    }
                }
            }
        }.start();

    }

    private void showUpdateDialog(){
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("检测到最新版本:"+mVersionName);
        builder.setMessage(mDesc);
        builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("立即更新");
                download();
            }
        });
        builder.setNegativeButton("以后再说", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                enterHome();
            }
        });
        //设置取消监听,点击返回调到主界面
        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                enterHome();
            }
        });
        builder.show();
    }

    /**
     * 进入主页面
     */
    private void enterHome(){
        Intent intent = new Intent(this,HomeActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 下载apk文件
     */
    private void download(){
        //判断SD卡有没有挂在,至少要有这个概念,会不会不要紧可以去网上搜
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            tvProgress.setVisibility(View.VISIBLE);//显示下载进度
            String target = Environment.getExternalStorageDirectory()+"/update.apk";
            HttpUtils utils = new HttpUtils();
            utils.download("http://192.168.1.210:8080/app-release.apk", target, new RequestCallBack<File>() {

                //文件的下载情况
                @Override
                public void onLoading(long total, long current, boolean isUploading) {
                    super.onLoading(total, current, isUploading);
                    System.out.println("下载进度:"+current+"/"+total);
                    tvProgress.setText("下载进度:"+current*100/total+"%");
                }

                //下载成功
                @Override
                public void onSuccess(ResponseInfo<File> responseInfo) {
                    System.out.println("下载成功");
                    //跳转到安装页面
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.addCategory(Intent.CATEGORY_DEFAULT);
                    intent.setDataAndType(Uri.fromFile(responseInfo.result),"application/vnd.android.package-archive");
                    //startActivity(intent);
                    startActivityForResult(intent,0);//如果用户取消安装会返回结果,回调方法onActivityResult
                }

                //下载失败
                @Override
                public void onFailure(HttpException e, String s) {
                    Toast.makeText(SplashActivity.this,"下载失败!",Toast.LENGTH_SHORT).show();
                }
            });
        }else {
            Toast.makeText(SplashActivity.this,"没有找到SD卡",Toast.LENGTH_SHORT);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        enterHome();
        super.onActivityResult(requestCode, resultCode, data);
    }
}

HomeActivity.java

package com.example.administrator.myapplication.activity;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.administrator.myapplication.R;


/**
 * 主页面
 *
 * @author Kevin
 *
 */
public class HomeActivity extends Activity {

    private GridView gvHome;

    private String[] mItems = new String[] { "手机防盗", "通讯卫士", "软件管理", "进程管理",
            "流量统计", "手机杀毒", "缓存清理", "高级工具", "设置中心" };

    private int[] mPics = 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 };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        gvHome = (GridView) findViewById(R.id.gv_home);
        gvHome.setAdapter(new HomeAdapter());
    }

    class HomeAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return mItems.length;
        }

        @Override
        public Object getItem(int position) {
            return mItems[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = View.inflate(HomeActivity.this,
                    R.layout.home_list_item, null);
            ImageView ivItem = (ImageView) view.findViewById(R.id.iv_item);
            TextView tvItem = (TextView) view.findViewById(R.id.tv_item);

            tvItem.setText(mItems[position]);
            ivItem.setImageResource(mPics[position]);
            return view;
        }

    }
}

此外需要在tomcat的webapp/ROOT/下放一个正式的apk文件,用于版本更新时下载。
在这里插入图片描述
和一个app.json,用于得到服务器的版本信息
app.json

{"versionName": "2.0", "versionCode": 2, "description": "新增NB功能,赶紧体验!!!", "downloadUrl":  "http://www.baidu.com"}

功能演示

在这里插入图片描述
在这里插入图片描述
这里超过5s请求不到网络,会有一个网络错误
在这里插入图片描述
启动tomcat,再试试
在这里插入图片描述
在这里插入图片描述
安装的时候产生了一个签名错误,原因是,本机开发用的是测试的签名,需要用正式的签名在app上安装一个低版本的,再用一个相同签名的高版本来覆盖才行。
在这里插入图片描述
说道这里演示一下怎么生成一个正式签名的apk吧

用的开发工具是idea,
在这里插入图片描述
在这里插入图片描述

没有签名文件则需要先生成一个签名文件
在这里插入图片描述
这里要选V1,V2不能安装,安装的时候会报一个Certificate Error
在这里插入图片描述
点击,finish就会在release下生成一个正式的apk文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值