文档章节

ConnectSTU的制作过程

宋昊
 宋昊
发布于 2016/05/17 08:10
字数 3972
阅读 20
收藏 0
点赞 2
评论 0

首先,软件追求的目的是简洁和自动化,让我用尽量少的步骤来自动重复登录这个无线网络(反复提交登录请求),并且保持长久的连接(Activity和Service要尽量不被后台杀死)。

因为需要解析登录之后返回的页面,所以在网络方面,我选择使用Jsoup这一工具(后来发现不用也行)。

那么,制作过程就开始了。

一开始,一个简单的界面:俩EditText和一个Button附带一些说明的TextView和一个反馈TextView。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:padding="@dimen/activity_margin"
    tools:context="org.out.naruto.connectstu.Activity_Main">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/str_main_introduce" />

    <EditText
        android:id="@+id/id_main_et_stuid"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="学号"
        android:inputType="number" />

    <EditText
        android:id="@+id/id_main_et_pwd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="密码"
        android:inputType="textPassword" />
    <Button
        android:id="@+id/id_main_bt_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/str_main_bt_login"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/str_main_more"/>

    <TextView
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:autoLink="email"
        android:text="@string/str_main_fb"/>

</LinearLayout>

点击button之后,另外启动一个线程来登录。咦,这时候就不对了,app后台之后,线程还跑个**。

 

那还是写个service吧,点击button之后,startService,这Service犹如脱缰野马,不受我控制了,都能独立于Activity之外存在了,用户想要控制的太少,想提前下线换设备登录都不行。

 

那就BindService吧,这样Service生命周期就和启动它的Activity相关了,Activity还可以把用户名和密码放到Intent里传过去,顺手在Service里写个接口,设置一个回调接口。

serviece完整代码:

package org.out.naruto.connectstu;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by Hao_S on 2016/5/5.
 */

public class ConnectService extends Service {
    public static final int LOGIN_SUCCESS = 0x100, PWD_WRONG = 0x101, UN_WRONG = 0x102,
            UNKNOWN_WRONG = 0x103, LOGOUT_SUCCESS = 0x104, LIMITED_WROING = 0x105,
            TIMEOUT_WROING = 0x106, WIFICHANGE = 0x107, DOCNULL_WRONG = 0x108;// 一些状态码


    private final static String TAG = "Connect_ConnectService";
    private NotificationManager notificationManager;
    private Timer connectTimer;

    private Document document;
    private static final String url = "http://192.168.8.10/portal/login.jsp?Flag=0";
    private static final String logoutUrl = "http://192.168.8.10/portal/logout.jsp";

    private Map<String, String> map;
    private User user;

    private OnConnectListener onConnectListener;

    private Notification.Builder notification;

    private boolean logINorOUT = true;// true = login, false = logout;

    private int connectTimes = 0;

    public ConnectService() {
        map = new HashMap<>();// 这些需要提交的固定参数,在一开始就准备好。
        map.put("isSavePass", "on");
        map.put("Submit1", "%C1%AC%BD%D3");
        map.put("Language", "Chinese");
        map.put("sessionID", "3970696304798764551");
        map.put("timeoutvalue", "45");
        map.put("heartbeat", "240");
        map.put("fastwebornot", "false");
        map.put("StartTime", new Date().getTime() + "");
        map.put("shkOvertime", "720");
        map.put("iIPCONFIG", "0");
        map.put("sHttpPrefix", "http://192.168.8.10");
        map.put("title", "CAMS Portal");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        // 动态注册监听,监听WIFI状态的变化。
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(mReceiver, intentFilter);

        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, Activity_Main.class), 0);
        notification = new Notification.Builder(this).setTicker(getString(R.string.app_name))
                .setSmallIcon(R.mipmap.ic_launcher).setOngoing(true).setContentTitle(getString(R.string.app_name))
                .setContentText("建议保持程序后台运行")
                .setContentIntent(pendingIntent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 下面的代码我是纠结于放在哪,既然bindservice,放在activity里保持activity常驻后台,那么这个service也就理应会存在。

       /* notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, Activity_Main.class), 0);
        notification = new Notification.Builder(this).setTicker(getString(R.string.app_name))
                .setSmallIcon(R.mipmap.ic_launcher).setOngoing(true).setContentTitle(getString(R.string.app_name))
                .setContentText("建议保持程序后台运行")
                .setContentIntent(pendingIntent);*/

        return super.onStartCommand(intent, flags, startId);
    }

    public void setOnConnectListener(OnConnectListener onConnectListener) {
        this.onConnectListener = onConnectListener;
    }

    @Override
    public IBinder onBind(Intent intent) {

        Log.i(TAG, "onBind");

//        user = intent.getParcelableExtra("user"); // 获取数据

//        Log.i(TAG, user.getStrUN());

//        startConnect(user);

        return new MyBinder(); // 返回binder对象,方便activity控制。
    }


    /**
     * @param user 开始自动连接,每20s一次
     */

    public void startConnect(User user) {

        logINorOUT = user.isLogIn();

        if (logINorOUT) {
            notificationManager.notify(0, notification.build());
        } else {
            notificationManager.cancel(0);
        }

        map.put("ClientIP", user.getStrIp());
        map.put("username", user.getStrUN());
        map.put("password", user.getStrPwd());
        map.put("strOldPrivateIP", user.getStrIp());
        map.put("strOldPublicIP", user.getStrIp());
        map.put("strPrivateIP", user.getStrIp());
        map.put("PublicIP", user.getStrIp());

        connectTimer = new Timer();
        connectTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                Log.i(TAG, "TimerTask run~");
                if (onConnectListener != null)
                    onConnectListener.onConnectState(connect());
                document = null;
            }
        }, 0, 20000);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        if (connectTimer != null)
            connectTimer.cancel();
        unregisterReceiver(mReceiver);
        if(notificationManager!=null&&notification!=null)
            notificationManager.cancel(0);
        Log.i(TAG, "service destroy");
    }

    /**
     * 具体访问网络的地方
     *
     * @return 连接之后的状态码
     */

    private int connect() {
        try {
            Log.i(TAG, "第" + ++connectTimes + "次连接");
            document = Jsoup.connect(logINorOUT ? url : logoutUrl).data(map).timeout(50000).post();// 开始尝试连接
            if (document.toString() == null || document == null)// 这个貌似没什么用 如果发生错误,Document是空的 直接报错 跳转到catch里,然后继续往下执行。
                return DOCNULL_WRONG;
        } catch (SocketTimeoutException e) {
            // 连接超时返回TIMEOUT
            return TIMEOUT_WROING;
        } catch (IOException e) {
            e.printStackTrace();
            return DOCNULL_WRONG;
        }

        try {
//            Log.i(TAG, document.toString());

            int length = document.toString().length();
//            Log.i(TAG, "length:" + length + "\n" + document.toString());
            if (length == 1942 || length == 2119) {
                // 请叫我机智的少年 ~ ~
                // 这两个长度都是连接成功时返回的html文本长度,其中1942是第一次连接成功的长度,返回一个空的页面,2119是提示已经连接成功后的页面。
                return LOGIN_SUCCESS;
            }

            if (length == 2334) {
                // 2334就是登出成功的长度。
                connectTimer.cancel();
                return LOGOUT_SUCCESS;
            }

            Element select = document.select("td[class]").get(0);
/*
            String stse = select.text().toString().substring(0, 5);


            if (stse.equals("您已经断开")) {
                connectTimer.cancel();
                return LOGOUT_SUCCESS;
                这段代码应该也用不到了
            }*/


//            if (select.text().toString().equals("\"您已经建立了连接。\"")) {
//                return LOGIN_SUCCESS;
//            } 这段代码可以不要了

            // 如果执行到这里还是没有返回,那么应该是遇到以下的一种情况了

            String string = select.getElementsByTag("b").text().toString().substring(0, 5);

            if (string.equals("E2553")) {
                Log.i(TAG, "用户密码错误");
                connectTimer.cancel();
                return PWD_WRONG;
            } else if (string.equals("E2531")) {
                Log.i(TAG, "用户不存在或者用户没有申请该服务");
                connectTimer.cancel();
                return UN_WRONG;
            } else if (string.equals("E2542")) {
                Log.i(TAG, "超出用户数量限制");
                connectTimer.cancel();
                return LIMITED_WROING;
            }

            return UNKNOWN_WRONG;
        } catch (IndexOutOfBoundsException e) {

            return UNKNOWN_WRONG;
        }
    }

    public class MyBinder extends Binder {

        public ConnectService getService() {
            return ConnectService.this;
        }

    }

    /**
     * 监听回调接口
     */
    public interface OnConnectListener {
        void onConnectState(int state);
    }


    /**
     * 一个广播接收,用以在WIFI状态变化时,及时断开连接
     */

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                Log.i(TAG, "network staite had changed!");
                WifiManager wifiManager = (WifiManager) getSystemService(ConnectService.this.WIFI_SERVICE);
                WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                String wifiName = wifiInfo.getSSID();
                if (!wifiName.equals("\"QLSC_STU\"") || !wifiName.equals("QLSC_STU")) {
                    if (connectTimer != null) {
                        connectTimer.cancel();
                        Log.i(TAG, "timer has stoped!");
                        if (onConnectListener != null)
                            onConnectListener.onConnectState(WIFICHANGE);
                    }
                }
            }
        }

    };

}

BingService的一些准备工作,需要三个参数:intent就不必说了,但是要把要传输的数据放到里面

connectIntent.putExtra("user", user);

这里要注意的是,自己写的对象类要implements Serializable或Parcelable。Parcelable是Android特有的,Serializable是Java带的,从效率上来说肯定是Parcelable更好一些,但是就我这点数据无所谓了~~。

实现Serializable还是比较简单的直接声明就好,实现Parcelable要实现两个方法和一个嵌入接口:

public interface Parcelable {
    //内容描述接口
    public int describeContents();
    //写入接口函数,打包
    public void writeToParcel(Parcel dest, int flags);
     //读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入。
    //为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例。
    public interface Creator<T> {
           public T createFromParcel(Parcel source);
           public T[] newArray(int size);
       }
}

//下面是我的实现 getter和setter就先省略了

private String strUN, strPwd, strIp;

private boolean logIn = true; // false 为登出

public static final Parcelable.Creator<User> CREATOR = new Creator<User>() {

        @Override
        public User createFromParcel(Parcel source) {
            User user = new User();
            user.setStrUN(source.readString());
            user.setStrPwd(source.readString());
            user.setStrIp(source.readString());
            user.setLogIn(Boolean.parseBoolean(source.readString()));
            return user;
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(strUN);
        dest.writeString(strPwd);
        dest.writeString(strIp);
        dest.writeString(logIn+"");
    }

    @Override
    public int describeContents() {
        return 0;
    }

 

第二个参数ServiceConncetion,用以和绑定的service来“互动”,其中要实现两个方法,onServiceConnected是连接成功的回调,onServiceDisconnected是连接断开的回调,根据自己的需求来操作。

我的是在连接成功之后,获取service对象,设置监听。

 private ServiceConnection serviceConnection = new ServiceConnection() {

        private boolean flag = true; // 用于标识是不是第一次连接~~ = =

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            connectService = ((ConnectService.MyBinder) service).getService();

            if (connectService == null)
                Log.i(TAG, "connectService is null");

            Log.i(TAG, "onServiceConnected");

            connectService.setOnConnectListener(new ConnectService.OnConnectListener() {
                @Override
                public void onConnectState(int state) {
                    mainHandler.sendEmptyMessage(state);
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

第三个参数 flags 绑定时的选项,0为空。源码里是这么写的

@IntDef(flag = true,
            value = {
                BIND_AUTO_CREATE,
                BIND_DEBUG_UNBIND,
                BIND_NOT_FOREGROUND,
                BIND_ABOVE_CLIENT,
                BIND_ALLOW_OOM_MANAGEMENT,
                BIND_WAIVE_PRIORITY,
                BIND_IMPORTANT,
                BIND_ADJUST_WITH_ACTIVITY
            })

大部分网上的例子都是直接用了 BIND_AUTO_CREATE:只要绑定存在,就自动创建Service。

BIND_DEBUG_UNBIND:debug模式下用的,具体我也没这么设置过。

BIND_NOT_FOREGROUND: 不允许该绑定至目标服务的进程提高到前台调度优先级(PS:前台进程>可见进程>服务进程>后台进程>空进程;销毁方向逆序)。

BIND_ABOVE_CLIENT:表面该服务比该客户端还要重要(貌似是我想要的)。

BIND_ALLOW_OOM_MENEGEMENT: allow the process hosting the bound service to go through its normal memory management. 这是原话,允许进程托管的绑定服务经历正常的内存管理过程??难道还能不正常?

BIND_WAIVE_PRIOPITY:不影响目标服务的宿主进程的调度和内存管理的优先级。允许后台LRU列表就像在后台定期应用过程中对管理的服务的过程。

BIND_IMPORTANT:这项服务是在客户端非常重要的,所以当客户端应提请前台进程的水平。

BIND_ADJUST_WITH_ACTIVITY: If binding from an activity, allow the target service's process importance to be raised based on whether the activity is visible to the user, regardless whether another flag is used to reduce the amount that the client process's overall importance is used to impact it.(微笑脸)

这个问题探究的不是很深,不敢随意误导他人,暂且放置一下。

 

接着问题来了,在点击LoginButton自动连接之后,用户肯定会后台这个应用,一般都会直接按home键,保不保存状态先不说,运气不好的话没几分钟这三个service就被系统杀死了,真的好尴尬,本来学校WIFI是每15分钟断开一次,至少得撑得了15分钟吧。

遂决定去解决serviece常驻后台的事情。

一般都有用过安卓VPN客户端吧,我用lantern,它在连接之后就会发送一个常驻消息栏通知,借此来实现serviece不被后台杀死,那我也这么干吧。

首先创建这个通知、这个通知的点击事件同时获得通知管理对象。

 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, Activity_Main.class), 0);
        notification = new Notification.Builder(this).setTicker(getString(R.string.app_name))
                .setSmallIcon(R.mipmap.ic_launcher).setOngoing(true).setContentTitle(getString(R.string.app_name))
                .setContentText("建议保持程序后台运行")
                .setContentIntent(pendingIntent);

 notificationManager = (NotificationManager) this
                .getSystemService(NOTIFICATION_SERVICE);

这里又出现另外一个point,这里new Intent,启动MainActivity,如果不加以修改,就会启动一个全新的Activity,所以需要在AndroidManifest文件里相应的Activity修改启动模式

android:launchMode="singleTask"

launchMode有四种模式,默认是standard;这里修改为singleTask,始终只存在这一个Activity;singleTop是当所启动的Activity在栈顶时不创建新的,通过onNewIntent返回栈顶目标Activity;singInstance非常类似singleTask,当确定此Activity只有一个时才用,貌似不推荐使用。

notification的创建过程也很简单,不必多说。

创建好之后合适的时机显示

notificationManager.notify(0, notification.build());

第一个参数0是我自己设置的,用来唯一标识这个通知,进行其他操作的时候用。

据说这样可以让应用在后台久一点。

还可以在AndroidMinafest文件的application标签下添加

android:persistent="true"

但我感觉没啥卵用......

 

2016/05/20更新

在前天的测试中,发现如果把notification放在Activity里,后台不出10分钟就会被杀死,真是尴尬啊,好尴尬好尴尬,还是放在serviece里面吧,

在serviece的onCreate方法里:

 notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, Activity_Main.class), 0);
        notification = new Notification.Builder(this).setTicker(getString(R.string.app_name))
                .setSmallIcon(R.mipmap.ic_launcher).setOngoing(true).setContentTitle(getString(R.string.app_name))
                .setContentText("建议保持程序后台运行")
                .setContentIntent(pendingIntent);

现在整理一下整个app的流程:

启动app(读取用户之前是否登陆过,如果有则填充用户名和密码Edittext;bindservice→用户点击登录button,serviece向通知栏发出通知,保持后台;启动timer,重复登录。

同时,在serviece里面动态注册了一个broadcastReciver,检测到WIFI变化时则停止timer,同时取消通知。

serviece里还曾经出过一个问题:在Android 4.1版本中,获取WIFI名称(getSSID)的结果和高版本不一样 =-= 所以就↓这样了

  String wifiName = wifiInfo.getSSID();
                if (!wifiName.equals("\"QLSC_STU\"") || !wifiName.equals("QLSC_STU")) {

在MainActivity下,在Activity被意外销毁时保存下状态,在onCreate里回复一下,这样让用户感觉Activity没有被销毁。

serviece的回调,handler更新UI就不详细写了,很基础的内容。

记得在onDestroy里释放相关资源服务。

经过真机测试,在自习室正常后台情况下可以后台一两个小时,内存大的手机同时聊微信QQ刷淘宝都没问题。CPU和内存占用率也很低。

最后附上MainActivity的代码(serviece代码在前面已经有了):

package org.out.naruto.connectstu;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class Activity_Main extends AppCompatActivity {
    private static final String TAG = "Activity_Main";

    private MainHandler mainHandler;
    private SharedPreferences sharedPreferences;
    private boolean connectSTUFlag = false;// false是准备登录,true是准备登出。
    private boolean breakFlag = false; // 中断是否重连标识。
    private int progremState = -1;

    private User user;

    private ConnectService connectService;
    private Intent connectIntent;
    private ServiceConnection serviceConnection = new ServiceConnection() {

        private boolean flag = true; // 用于标识是不是第一次连接~~ = =

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            connectService = ((ConnectService.MyBinder) service).getService();

            if (connectService == null)
                Log.i(TAG, "connectService is null");

            Log.i(TAG, "onServiceConnected");

            if (breakFlag) {
                connectService.startConnect(user);
            }

            connectService.setOnConnectListener(new ConnectService.OnConnectListener() {
                @Override
                public void onConnectState(int state) {
                    mainHandler.sendEmptyMessage(state);
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

//    private NotificationManager notificationManager;
//    private Notification.Builder notification; // 也是无奈之举  = =

    private EditText stuidET, pwdET;
    private Button loginButton;

    private String str_bt_login, str_bt_logout;

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

        str_bt_login = getString(R.string.str_main_bt_login);
        str_bt_logout = getString(R.string.str_main_bt_logout);

        mainHandler = new MainHandler();

        stuidET = (EditText) findViewById(R.id.id_main_et_stuid);
        pwdET = (EditText) findViewById(R.id.id_main_et_pwd);
        loginButton = (Button) findViewById(R.id.id_main_bt_login);

        connectIntent = new Intent(Activity_Main.this, ConnectService.class);
        sharedPreferences = Activity_Main.this.getSharedPreferences("ConnectSTU", MODE_PRIVATE);

        if (user == null)
            user = new User();

        String stuid = sharedPreferences.getString("stuid", "null");
        String pwd = sharedPreferences.getString("pwd", "null");
        if (!stuid.equals("null") && !pwd.equals("null")) {
            stuidET.setText(stuid);
            pwdET.setText(pwd);
            user.setStrUN(stuid);
            user.setStrPwd(pwd);
        }

        connectIntent.putExtra("user", user);

        bindService(connectIntent, serviceConnection, Context.BIND_AUTO_CREATE);


        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginButton.setClickable(false);
                if (connectSTUFlag) {
                    progremState = 2;
                    loginButton.setText("下线中...");
                    user.setLogIn(false);
                    connectIntent.putExtra("user", user);
                    connectService.startConnect(user);
                    connectSTUFlag = false;
                } else {

                    String ip = judgeWifiState();
                    if (!ip.equals("error")) {

                        if (user == null)
                            user = new User();
                        String stuid = stuidET.getText().toString().trim();
                        String pwd = pwdET.getText().toString().trim();
                        if (!stuid.equals("") && !pwd.equals("")) {
                            progremState = 1;
                            loginButton.setText("登录中...");
                            user.setStrIp(ip);
                            user.setStrUN(stuid);
                            user.setStrPwd(pwd);
                            user.setLogIn(true);

                            connectIntent.putExtra("user", user);

                            bindService(connectIntent, serviceConnection, Context.BIND_ABOVE_CLIENT);// 提升一下重要性??
                            connectService.startConnect(user);

                            connectSTUFlag = true;
                        } else {
                            ShowToast("用户名或密码不能为空");
                        }
                    }
                }
            }
        });

//        notificationManager = (NotificationManager) this
//                .getSystemService(NOTIFICATION_SERVICE);

        if (savedInstanceState != null) {
            Log.i(TAG, "savedInstanceState is not null");
            int tempInt = savedInstanceState.getInt("state");
            user = savedInstanceState.getParcelable("user");
            breakFlag = true;
            switch (tempInt) {
                case ConnectService.LOGIN_SUCCESS:
                    loginButton.setText(str_bt_logout);
                    break;
                case 1:
                    loginButton.setText("登录中...");
                    break;
                case 2:
                    loginButton.setText("下线中...");
                    break;
                default:
                    breakFlag = false;
                    loginButton.setText(str_bt_login);
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        breakFlag = true;
        outState.putInt("state", progremState);
        outState.putParcelable("user", user);
        outState.putBoolean("breakFlag", breakFlag);
    }

    private String judgeWifiState() {
        //获取wifi服务
        WifiManager wifiManager = (WifiManager) getSystemService(Activity_Main.this.WIFI_SERVICE);
        //判断wifi是否开启
        if (!wifiManager.isWifiEnabled()) {
            // 打开wifi = =
            wifiManager.setWifiEnabled(true);
            ShowToast("我给你打开 \t WIFI 了");
        }
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();

        String wifiName = wifiInfo.getSSID();

        if (wifiName.equals("\"QLSC_STU\"") || wifiName.equals("QLSC_STU")) {
            int ipAddress = wifiInfo.getIpAddress();
            String ip = intToIp(ipAddress);
            return ip;
        } else {
            Log.i(TAG, "wifiName:" + wifiName);
            ShowToast("连接的不是QLSC_STU,连好了再试一下。");
            loginButton.setClickable(true);
            return "error";
        }
    }

    private void ShowToast(String message) {
        Toast.makeText(Activity_Main.this, message, Toast.LENGTH_SHORT).show();
    }

    public static String intToIp(int i) {
        // 把获取到的int型ip转化成string
        return (i & 0xFF) + "." +
                ((i >> 8) & 0xFF) + "." +
                ((i >> 16) & 0xFF) + "." +
                (i >> 24 & 0xFF);
    }

    private class MainHandler extends Handler {

        private boolean isFirstLogin = true;

        @Override
        public void handleMessage(Message msg) {
            loginButton.setClickable(true);
            progremState = msg.what;
            connectSTUFlag = false;
            breakFlag = false;
            loginButton.setText(str_bt_login);
            switch (msg.what) {
                case ConnectService.PWD_WRONG:
                    ShowToast("密码错误!登录失败");
                    break;
                case ConnectService.UN_WRONG:
                    ShowToast("用户名错误!登录失败");
                    loginButton.setText(str_bt_login);
                    break;
                case ConnectService.LOGIN_SUCCESS:
                    connectSTUFlag = true;
                    loginButton.setText(str_bt_logout);
                    if (isFirstLogin) {
                        ShowToast("连接成功!");
                        // 存储用户名和密码~
                        SharedPreferences.Editor editor = sharedPreferences.edit();
                        editor.putString("stuid", user.getStrUN());
                        editor.putString("pwd", user.getStrPwd());
                        editor.commit();
                        isFirstLogin = false;
                    }
                    break;
                case ConnectService.UNKNOWN_WRONG:
                    ShowToast("遇到了未知错误");
                    break;
                case ConnectService.LOGOUT_SUCCESS:
                    ShowToast("登出成功");
                    break;
                case ConnectService.LIMITED_WROING:
                    ShowToast("在线用户数量限制!");
                    break;
                case ConnectService.TIMEOUT_WROING:
                    ShowToast("连接超时!\n可能是网路不好,但我还在努力连接中!");
                    break;
                case ConnectService.WIFICHANGE:
                    ShowToast("WIFI状态变化,已停止自动连接!");
                    break;
                case ConnectService.DOCNULL_WRONG:
                    // donothing
                    break;
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

参考博客:

http://www.juwends.com/tech/android/android-service-2.html

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0520/2897.html

2016/05/20 11:13完结

© 著作权归作者所有

共有 人打赏支持
宋昊
粉丝 3
博文 4
码字总数 11731
作品 0
济南
程序员
怎样在几何画板中查看课件制作过程

几何画板主要以点、线、圆为基本元素,通过对这些基本元素的变换、构造、测算、计算、动画、跟踪轨迹等,构造出其它较为复杂的图形。是数学、物理教学中强有力的工具。对于一个几何画板初学者...

学术研究软件 ⋅ 2016/04/11 ⋅ 0

USB启动盘制作软件--iBurnMgr

iBurnMgr是一款基于Direct2D DirectWrite开发的USB启动盘制作软件,采用流行的扁平风格,使用原生C++开发,不依赖任何第三方库,USB启动盘制作过程使用多线程技术,制作过程可以中途终止,界...

Force武装卫队 ⋅ 2014/06/18 ⋅ 4

NextInstaller的网友评论

NextInstaller是一个功能强大的安装盘制作工具 1.可以制作C/S应用软件安装盘。 2.可以制作软件升级包。 3.可以制作数据库安装盘。 4.可以制作数据库升级包。 5.可以制作Web安装盘(asp,.net,j...

nextinstaller ⋅ 2009/12/11 ⋅ 1

安装盘制作工具--NextInstaller

NextInstaller是一个功能强大的安装盘制作工具 1.可以制作C/S应用软件安装盘。 2.可以制作软件升级包。 3.可以制作数据库安装盘。 4.可以制作数据库升级包。 5.可以制作Web安装盘(asp,.net,j...

匿名 ⋅ 2009/03/30 ⋅ 1

Groover

制作各种节奏的鼓声音乐。制作过程如下:一共有三种鼓声(还包括一种类似沙槌的声音)。界面上出现3*16的格子,每一行对应一种乐器的声音,每一格表示击打一次。对这48个格子,随便点击选择(...

匿名 ⋅ 2012/11/05 ⋅ 0

使用UltraISO制作ubuntu安装u盘启动盘图文教程

制作U盘启动1.gif 1、首先打开UltraISO软件,尽量下载最新版的 http://www.cr173.com/soft/15480.html 2、点击工具栏中的第二个打开镜像文件工具,如图红色方框标志按钮,然后在打开的“打开...

胖先森 ⋅ 2017/09/07 ⋅ 0

网易云音乐LRC编辑器--BesLyric

本软件试图提供给用户一个 上手简单、功能实用的 滚动歌词制作软件,为广大网易云音乐爱好者提供一个良好的歌词制作体验。 功能简介: 1、歌词制作: 制作过程非常简单,你只需要按下 “ ↑ ...

BensonLaur ⋅ 2017/06/07 ⋅ 0

几何画板演示正方形拼凑过程的方法

几何画板作为一款专业的绘图工具,不仅仅可以用来画几何图形,还可以通过其动画功能将图形进行拼凑,组成新的图形。比如是否可以将两个小正方形拼凑成大正方形呢?其实只要满足一定的条件,这...

学术研究软件 ⋅ 2016/12/13 ⋅ 0

解救低版本的mac系统升级

macbook一向以安全稳定著称,即便如此,mac os x系统也会出现各种故障,虽然mac系统可以通过在线恢复,不过速度非常慢,而且不是所有机型都支持,所以我们需要制作mac启动U盘,mac系统U盘可以...

mp_peng ⋅ 01/01 ⋅ 0

Windows to go体验

WinPE研究和使用的人很多,那是Windows移动版没有办法的产物,自Win8宣布Win to go功能后,自然让人眼前一亮。这可是真正的移动版Windows,可以通过U盘或移动硬盘随身带着自己的windows系统了...

ray_deng ⋅ 2014/05/11 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JavaScript零基础入门——(十一)JavaScript的DOM操作

JavaScript零基础入门——(十一)JavaScript的DOM操作 大家好,欢迎回到我们的JavaScript零基础入门。最近有些同学问我说,我讲的的比书上的精简不少。其实呢,我主要讲的是我在开发中经常会...

JandenMa ⋅ 18分钟前 ⋅ 0

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 45分钟前 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 53分钟前 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 59分钟前 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 今天 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

容器之查看minikue的environment——minikube的环境信息

执行如下命令 mjduan@mjduandeMacBook-Pro:~/Docker % minikube docker-envexport DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.100:2376"export DOCKER_CERT_PATH="/U......

汉斯-冯-拉特 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部