简单Android app之 一键签到 开发日记

致谢:

开发此App期间参考了大量文章与帖子,由于数量众多,来不及一 一记录。在此一并感谢!

如果在本文中发现与您的程序十分雷同的部分(主要为Http请求部分,Stream转String部分,以及主线程与子线程的通信部分),不胜感激。如有无意冒犯之处,请与我联系。

 

附件:

包括: java文件、布局文件、资源文件、AndroidManifest文件。需要Gson和另一个库(忘了)。

https://download.csdn.net/download/zxylv/12411877

 

1.Android Studio简单入门

非常详细的安装教程: https://developer.android.google.cn/studio/install?hl=zh-cn 

安装之后顺便按照以上网址创建第一个简单的安卓app

2.需求

需求来源:一方面,一直想尝试一下Android app开发。另一方面,最近因为疫情宅在家,但每天仍需要app签到,操作步骤为:打开app -- 等待加载--找到签到功能--等待加载--填写信息--提交。十分麻烦。抓包之后发现分析了签到数据格式,用Python简单试验之后可行,但python脚本签到需要打开电脑,不如写个手机app。

总体思路:将抓包数据粘贴进app,app对数据进行保存,之后每次打开app自动签到。所以签到步骤变为:打开app--关闭app,十分简洁。

3.UI设计

UI构想:一个粘贴数据的编辑框(以免日后签到数据发生变化,方便修改),一个显示签到是否成功以及错误信息的文本框,一个保存数据的按钮。

UI预览:

定位方式为:

1.所有控件均为居中
2.绿框相对于四周定位,cookie框相对于绿框定位,Json框相对于Cookie框定位,按钮框相对于Json框定位。

3.框左上角的文件相对于框定位。

关于定位方式的详细介绍,可以看这里:https://developer.android.google.cn/guide/topics/ui/declaring-layout?hl=zh-cn

UI实际效果如下:

 

4.程序逻辑

逻辑框图如下:

 

5.读取数据

首先读取数据:数据分为Cookie和Data两部分。Cookie用于身份验证,Data用于存储签到信息例如位置等

    public Map myRead(String name) {
        //根据name读取不同的信息
        SharedPreferences userData = getSharedPreferences(name, Context.MODE_PRIVATE);
        Map Data = userData.getAll();
        return Data;
    }

然后根据读取到的数据是否合理,判断数据是否以及存储


    public void myUpload(Handler message) {
       //http请求在子线程中完成,所以需要通过Handler将消息发送给主线程
        Log log = new Log(); 
        Gson gson = new Gson();
        final NetWork netWork = new NetWork();
        Headers headers = new Headers();
        Map Cookies = myRead("config_cookies");//读取cookie
        Map Data = myRead("config_data");      //读取Data
        final Map headerPost = headers.post(); //读取 post的请求头
        final Map headerGet = headers.get();   //读取 get 的请求头
        headerPost.put("Cookie", Cookies.get("cookies")); //在Header中添加cookie信息
        headerGet.put("Cookie", Cookies.get("cookies"));  //在Header中添加cookie信息
        //根据Data长度,判断数据是否已经存储
        if (Data.toString().length() > 200) {

            ......//此处为签到相关程序部分

        } else {
            //将消息发送给主线程
            log.show(message, "code_6:请填入Cookie和Json并保存,下次打开app自动签到");
        }
    }

6.签到相关工作

请求头设置

public class Headers {
    public Map post(){
        Map header=new HashMap();
        header.put("Host", Your Host);
        header.put("Connection", "keep-alive");
        header.put("User-Agent", "Mozilla/5.0 ...");
        header.put("X-Tag", ...);
        header.put("content-type", "application/json");
        header.put("Referer", "https://...");
        header.put("Accept-Encoding", "gzip, deflate, br");
        return  header;
    }
    public Map get(){
        Map header=new HashMap();
        header.put("Host","Your Host");
        header.put("Connection", "keep-alive");
        header.put("User-Agent", "Mozilla/5.0...");
        header.put("X-Tag", "...");
        header.put("content-type", "application/json");
        header.put("Referer", "https:...");
        header.put("Accept-Encoding", "gzip, deflate, br");
        return  header;
    }
}

发送Get请求,判断签到情况

if (Data.toString().length() > 200) {
//如果读取到的数据合理,则进入签到相关工作
            //显示部分提交信息
            log.show(message, "__" + (Data.get("Item 1").getClass()).toString() + "__" + Data.get("Item 2") + "__" + Data.get("Item"));
            //发送请求
            NetWork check_res = netWork.get("https://...", headerGet);
            if (check_res.statusCode == 200) {
                Map result = gson.fromJson(check_res.result, Map.class);
                //将返回的json数据转换为Map结构,方便提取信息
                //检查签到情况
                if (result.get("item 1").toString().equals("true") && result.get("item 2").toString().equals("true")) {
                    log.show(message, "无需重复签到:" + result.toString());
                } else {
                    log.show(message, "暂未签到:" + result.toString());
                    //签到
                    NetWork upload = netWork.post("https://...", headerPost, Data);
                    if (upload.statusCode == 200) {
                        result = gson.fromJson(upload.result, Map.class);
                        //检查签到情况
                        if (result.get("item 1").toString().equals("true") && result.get("item 2").toString().equals("true")) {
                            //显示部分提交信息
                            log.show(message, "_" + upload.posted);
                            log.show(message, "签到成功:\n" + upload.result);

                        }else{
                            log.show(message, "签到失败:\n" + upload.result);
                        }
                    } else {
                        log.show(message, "code_3:网络连接错误" + upload.result);
                    }
                }
            } else if (check_res.statusCode == 0) {
                log.show(message, "code_8:程序出错" + check_res.result);
            } else {
                log.show(message, "code_9:检查状态失败");
            }
        }

7.Get 和Post的封装

以下为Post的封装,get也是类似的操作,篇幅原因就省略了

public NetWork post(String url_String, Map headers, Map data){
        this.statusCode=0;
        this.result = null;
        try {
            URL url = new URL(url_String);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setReadTimeout(5000);
            connection.setConnectTimeout(5000);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            //遍历请求头的每一项,生成请求头
            for (Object Key : headers.keySet()) {
                connection.setRequestProperty(Key.toString(), headers.get(Key).toString());
            }
            JSONObject jsonObject = new JSONObject(data);
            JSONArray nameList = new JSONArray();
            nameList = jsonObject.names();
            //将字符串形式的数值改为整数形式的数值
            for (int i = nameList.length(); i > 0; i--) {
                try {
                    jsonObject.put(nameList.getString(i - 1), Integer.valueOf(jsonObject.getString(nameList.getString(i - 1))));
                } catch (Throwable e) {
                }
            }
            String Data = jsonObject.toString();
            //截取四项有代表性的信息作为返回,以便判断签到数据是否设置正确
            JSONObject temp = new JSONObject();
            temp.put("item 1",jsonObject.get("item 1"));
            temp.put("item 2",jsonObject.get("item 2"));
            temp.put("item 3",jsonObject.get("item 3"));
            temp.put("item 4",jsonObject.get("item 4"));

            this.posted = temp.toString();
            connection.getOutputStream().write(Data.getBytes());
            this.statusCode = connection.getResponseCode();
            if (this.statusCode == 200) {
                InputStream is = connection.getInputStream();
                connection.disconnect();
                //将请求得到的Stream格式的数据转换为String形式
                this.result = Stream.Stream2String(is);
            } else {
                this.result = "";
            }
        }catch(Throwable e){}
        return this;
    }

Stream转换为String的方法

public class Stream {
    public static String Stream2String(InputStream inStream) throws Exception{
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[2048];
        int len = 0;
        while((len = inStream.read(buffer)) != -1)
        {
            outStream.write(buffer,0,len);
        }
        inStream.close();
        String output= new String(outStream.toByteArray());
        return output;
    }
}

8.数据存储

从输入框读取数据,并保存

    public void save_data(View view) {
        //------------------读取字符串---------------------//
        TextView message_box = (TextView) findViewById(R.id.logView);
        Log log = new Log();
        EditText editData = (EditText) findViewById(R.id.editData);
        String Data = editData.getText().toString();
        EditText editCookies = (EditText) findViewById(R.id.editCookies);
        String Cookies = editCookies.getText().toString();
        //存储数据
        if (mySave("config_data", Data) && mySave("config_cookies", "cookies: " + Cookies)) {
            toast("code_3:" + "数据已保存,请再次打开app填报");
        } else {
            toast("code_2:Cookies或Data有误,数据未保存");
        }
    }

其中,mySave() 按名称存储数据

    public boolean mySave(String name, String Data_Str) {
    //根据名称name存储数据Data_Str
        myData mydata = new myData();
        mydata = mydata.DataFormat(Data_Str);//将数据格式化为Json格式
        if (mydata.status == true) {
            SharedPreferences userData = getSharedPreferences(name, Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = userData.edit();
            for (Object Key : mydata.data_map.keySet()) {
            //遍历每一项数据,依次写入文件
                editor.putString(Key.toString(), mydata.data_map.get(Key).toString());
            }
            if (editor.commit()) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

其中,mydata.DataFormat() 将输入的数据格式化为规范的Json格式

public class myData {
    public boolean status;
    public Map data_map ;
    public myData DataFormat(String Data_Str){
        this.status= false;
        //数据Json化
        Data_Str = Data_Str.replaceAll("^\\{*\"*","");
        Data_Str = Data_Str.replaceAll("\"*\\}*$","");
        Data_Str = Data_Str.replaceAll("\"*\\s*:\\s+\"*","\" : \"");
        Data_Str = Data_Str.replaceAll("\"*\\s*,\\s*\\n\\s*\"*","\" ,\n \"");
        Data_Str = Data_Str.replaceAll(":\\s*\"\\s*,\\s*\\n",": \"\",\n");
        Data_Str =  "{\""+ Data_Str + "\"}";
        Gson gson=new Gson();
        //检查Data长度,以免存储空数据
        if(Data_Str.length()>20){
            try{
                JSONObject json = new JSONObject(Data_Str);
                this.data_map = gson.fromJson(json.toString(), Map.class);
                this.status = true;
            }catch(JSONException err){
                this.status= false;
            }
        }else {
            this.status = false;
        }
        return this;
    }
}

9.公共部分--显示信息

    public void toast(String data) {
        TextView message = (TextView) findViewById(R.id.logView);
        String old_data = message.getText().toString();
        //新现实的信息跟在旧信息之后,并以换行符隔开
        old_data = old_data + "\n" + data;
        int _n_count = 0;
        int i;
        //限制显示的行数为6行
        for (i = old_data.length(); i > 0; i--) {
            if (old_data.substring(i - 1, i).equals("\n")) {
                _n_count++;
                if (_n_count > 6) {
                    break;
                }
            }
        }
        old_data = old_data.substring(i );
        //去除开头的换行符,以免第一行为空行
        if(old_data.startsWith("\n")){
            old_data = old_data.substring(1);
        }
        //限制显示字符数为240
        if (old_data.length() > 240) {
            old_data = old_data.substring(old_data.length() - 240);
        }
        //显示信息
        message.setText(old_data);
    }

-------------完---------------------

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值