android保持Session会话

android保持Session会话

在服务端完成实现app在线用户列表的功能,无论是HttpSessionListener还是HttpSessionBindingListener都依赖session机制。但app未退出,服务端的session会话就销毁了,导致无法正确监测到app用户的在线情况。

1.session的生命周期

  • 当服务端接收到请求时,服务端会为其分配一个session会话

  • 这个session会话有一个最大保存时间MaxInactiveInterval,当用户的不活动状态超过这个时间,session会销毁

  • 如果是网页端,当用户注销或者退出浏览器时,session会销毁

2.android无法保持会话的原因

  • 网页端的每次请求都是活动,将重置session会话的时间,实现持久访问

  • android端就不同了,它的请求头中是不带sessionId的,所以服务端无法识别它访问的是哪个session,也无法识别请求来自于哪个session

3.解决问题

建议大家安装 FireFox浏览器,它的检查功能是真的好用,比谷歌浏览器好用

  • 写个servlet,运行服务端,网页里加个css外链

  • 用FireFox打开网页并检查[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

  • 注意到高亮的Cookie:JSESSIONID=70943E03B9683A966235579F4D57892F,打印session.getId()验证,发现这个JSESSIONID就是本次session的sessionId

  • 如何查看android访问服务端的请求头呢?在servlet添加以下代码:

    	Cookie[] Cookies = request.getCookies();
    	for(int i =0;i<Cookies.length;i++){
    		Cookie c = Cookies[i];
    		System.out.println(c.getName() + "=" + c.getValue());
    	}
    
  • 使用android发起一个请求,报错java.lang.NullPointerException,说明android默认的请求是不带Cookie的

  • 给android的请求头加上Cookie,我这里使用的是OkHttp3

    Request request = new Request.Builder()
                    .url(url).addHeader("Cookie", "JSESSIONID=" + session_id)
                    .post(body)
                    .build();
    
  • 至于这个session_id,可以这样做:服务端接收到登录请求,获取会话的sessionID,返回给android端,android端存到SharedPreferences里,发送请求的时候取出

  • 使用带Cookie的方法再次访问服务端,可以看到打印出JSESSIONID,这时候android和网页端一样都能实现session持久了

4.验证Session持久

  • 在服务端工程的web.xml里添加

    <session-config>
    	<session-timeout>1</session-timeout>
    </session-config>
    
  • 表示设置session的最大保存时间为1分钟,设置为1分钟是为了方便验证

  • 添加在线用户监听器,这里给出一个方案:

    //登录处理的Servlet添加,user为用户实体对象
    session.setAttribute("onlineUserBindingListener", new OnlineUserBindingListener(user));
    
    //在线用户监听器,使用注解方式
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.ServletContext;
    import javax.servlet.annotation.WebListener;
    import javax.servlet.http.HttpSession;
    import javax.servlet.http.HttpSessionBindingEvent;
    import javax.servlet.http.HttpSessionBindingListener;
    
    import domain.User;
    
    @WebListener
    public class OnlineUserBindingListener implements HttpSessionBindingListener {
    
    	private User user;
    
    	public OnlineUserBindingListener() {
    
    	}
    
    	public OnlineUserBindingListener(User user) {
    		this.user = user;
    	}
    
    	@SuppressWarnings("unchecked")
    	public void valueBound(HttpSessionBindingEvent event) {
    		HttpSession session = event.getSession();
    		ServletContext application = session.getServletContext();
    
    		List<User> onlineUserList = (List<User>) application.getAttribute("onlineUserList");
    
    		if (onlineUserList == null) {
    			onlineUserList = new ArrayList<User>();
    			application.setAttribute("onlineUserList", onlineUserList);
    		}
    		onlineUserList.add(this.user);
    	}
    
    	@SuppressWarnings("unchecked")
    	public void valueUnbound(HttpSessionBindingEvent event) {
    		HttpSession session = event.getSession();
    		ServletContext application = session.getServletContext();
    
    		List<User> onlineUserList = (List<User>) 			           application.getAttribute("onlineUserList");
    
    		onlineUserList.remove(this.user);
    	}
    }
    
  • 网页端显示用户列表,直接application.getAttribute(“onlineUserList”),我不再赘述。网页每30秒自动刷新:

    <meta http-equiv="refresh" content="30"/>
    
  • app登录后不操作,1分钟后用户列表数据消失

  • app登录后连续操作,每次操作间隔在1分钟内,用户列表数据持续存在。验证成功

5.其它

  • session的getAttribute和setAttribute方法是不会影响它的生命周期的,仍视为无活动状态

  • 根据sessionId获取session的方法已经再Servlet 2.5后弃用,仍然想实现该功能的可以参考https://blog.csdn.net/sihai12345/article/details/81098765,基本原理是自己维护一个HashMap<String,HttpSession>

  • Jsp中显示实体型列表尽量结合JSTL和EL表达式,简便清楚,用JSTL别忘了导包。示例:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<c:when test="${requestScope.operat_type eq 'userList'}">
    <table>
        <thead>
            <tr>
                <th>姓名</th>
                <th>密码</th>
                <th>部门</th>
                <th>等级</th>
                <th>注册时间</th>
                <th>最近登录时间</th>
                <th>最近登录IP</th>
                <th>登录次数</th>
            </tr>
        </thead>
        <tbody>
            <c:forEach items="${requestScope.userList}" var="user">
                <tr>
                    <td>${user.name }</td>
                    <td>${user.password }</td>
                    <td>${user.department }</td>
                    <td>${user.grade }</td>
                    <td>${fn:substring(user.reg_time,0,19) }</td>
                    <td>${fn:substring(user.log_time,0,19) }</td>
                    <td>${user.log_ip }</td>
                    <td>${user.log_count }</td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
</c:when>

转载请注明博文来源,有什么问题欢迎在评论栏留言。 ——Kevin_Lu 2020/4/14

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,我之前的回答中提到的 `SessionConfiguration` 是与自然语言处理相关的,与 Android 相机开发无关。如果您要在 Android Camera2 中创建会话Session),可以使用 `CameraCaptureSession` 类。下面是一个示例代码片段,用于创建一个 `CameraCaptureSession` 对象: ```java private CameraCaptureSession mCameraCaptureSession; private void createCameraSession() { try { Surface surface = mPreviewSurface; mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.JPEG, 1); mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); List<Surface> outputSurfaces = new ArrayList<>(); outputSurfaces.add(surface); outputSurfaces.add(mImageReader.getSurface()); mCameraDevice.createCaptureSession(outputSurfaces, mStateCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private CameraCaptureSession.StateCallback mStateCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { mCameraCaptureSession = cameraCaptureSession; try { mCameraCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { Log.e(TAG, "onConfigureFailed: CameraCaptureSession configuration failed"); } }; ``` 在上面的代码中,我们使用 `CameraDevice` 的 `createCaptureSession()` 方法来创建一个会话。我们在 `createCaptureSession()` 方法中指定了要输出的 `Surface` 列表,包括预览 `Surface` 和图像读取器 `Surface`。在 `onConfigured()` 方法中,我们将创建的 `CameraCaptureSession` 对象保存在变量 `mCameraCaptureSession` 中,并将预览请求设置为重复请求,这样就可以实时预览相机输出了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值