[전] 안 드 로 이 드 와 서버 의 상호작용 대 용량 데이터 문 제 를 해결 합 니 다.

현재 상황 에서 모 바 일 단말기 의 네트워크 상황 은 PC 네트워크 상황 만큼 이상 적 이지 않다.안 드 로 이 드 애플 리 케 이 션 에서 서버 로부터 대 용량 데 이 터 를 받 아야 한다 면 고객 의 데이터 문 제 를 고려 해 야 한다.본 고 는 필자 의 한 프로젝트 실전 경험 에 따라 대 용량 데이터 의 상호작용 문 제 를 해결 하고 데이터 크기 를 해결 하면 실제 상황 에 따라 동적 전환 문제 (서버 동적 선택 이 데 이 터 를 압축 할 지, 클 라 이언 트 동적 분석 데이터 가 압축 되 었 는 지) 를 해결 하 며 데이터 상호작용 의 인 코딩 문제 도 있다.
  데이터 가 너무 큰 문 제 를 해결 하 는 가장 직관 적 인 방법 은 바로 데 이 터 를 압축 하 는 것 이다.서버 는 전달 해 야 할 데 이 터 를 먼저 압축 한 다음 에 안 드 로 이 드 클 라 이언 트 에 보 냅 니 다. 안 드 로 이 드 클 라 이언 트 는 압축 된 데 이 터 를 받 아 압축 을 풀 고 압축 전의 데 이 터 를 얻 습 니 다.
  만약 에 안 드 로 이 드 클 라 이언 트 와 서버 의 상호작용 데이터 가 특정한 압축 알고리즘 을 거 친 데이터 여야 한다 고 규정 하면 이런 '규정' 은 구체 적 인 상황 에 따라 정 해진 유연성 을 잃 게 된다.필 자 는 Http 프로 토 콜 을 패 키 징 하여 전송 할 데 이 터 를 동적 으로 선택 하여 압축 해 야 하 는 지, 클 라 이언 트 도 동적 으로 식별 하여 서버 가 보 내 고 싶 은 데 이 터 를 정리 하고 얻 을 수 있 는 지 를 계획 합 니 다.Android 클 라 이언 트 가 서버 에 특정한 데 이 터 를 요청 합 니 다. 이 데 이 터 는 압축 을 거 쳐 전달 하 는 것 이 적당 할 수도 있 고 원생 데 이 터 를 전달 하 는 것 이 적당 할 수도 있 습 니 다.즉, 필 자 는 하나의 협 의 를 설계 하고 자 한다. 이런 협 의 는 데 이 터 를 전송 하 는 데 적용 되 는 데이터 양 이 동태 적 으로 전환 되 고 작은 데이터 일 수도 있 으 며 데이터 양 이 많은 빅 데이터 일 수도 있다 (빅 데 이 터 는 압축 을 거 쳐 야 한다).
  추상 적 으로 말 할 수 있 으 니 실제 상황 으로 설명 하 겠 습 니 다.
  제 프로젝트 의 실제 상황 은 이 렇 습 니 다. 이 프로젝트 는 안 드 로 이 드 펀드 클 라 이언 트 를 만 드 는 것 입 니 다. 안 드 로 이 드 클 라 이언 트 는 서버 에 특정한 펀드 의 역사 동향 정 보 를 요청 합 니 다. 제 안 드 로 이 드 클 라 이언 트 가 로 컬 캐 시 를 실 현 했 기 때문에 데 이 터 를 전달 하 는 크기 가 매우 큽 니 다.만약 에 로 컬 캐 시 의 역사 동향 정보의 최신 날짜 가 5 월 5 일이 고 서버 의 역사 동향 정보의 최신 날짜 가 5 월 7 일이 라면 서버 는 5 월 6 일과 5 월 7 일 이틀 간 의 동향 정 보 를 보 내 는 것 과 같이 이 데 이 터 는 매우 작 아서 압축 할 필요 가 없다.(제 가 사용 하 는 압축 알고리즘 은 데이터 양 이 너무 적은 데이터 압축 이 이상 적 이지 않 고 데이터 양 이 너무 적은 데 이 터 를 압축 한 데 이 터 는 압축 전의 데이터 보다 클 것 입 니 다)그러나 안 드 로 이 드 클 라 이언 트 는 어떤 펀드 에 대해 캐 시 정보 가 없 을 수도 있 습 니 다. 그러면 서버 가 보 낸 데 이 터 는 지난 3, 4 년 간 의 역사적 동향 정보 가 될 것 입 니 다. 이 데 이 터 는 약간 클 라 이언 트 가 압축 해서 전달 해 야 합 니 다. 그러면 클 라 이언 트 는 같은 요청 으로 얻 은 데이터 에 대해 압축 된 데이터 인지 압축 되 지 않 은 데이터 인지 어떻게 판단 합 니까?
  필자 가 사용 하 는 솔 루 션 은 데 이 터 를 전달 하 는 첫 번 째 바이트 를 표지 바이트 로 하고 이 데 이 터 를 압축 했 는 지 여 부 를 표시 하 는 것 입 니 다. 데 이 터 를 전달 하 는 인 코딩 문제 도 표시 할 수 있 습 니 다. Android 는 받 은 데이터 (바이트 배열) 에 대해첫 번 째 바이트 의 데 이 터 를 먼저 판단 하면 대표 적 인 데이터 형식 과 인 코딩 정보 에 따라 해당 하 는 조작 을 할 수 있 습 니 다. 그렇게 많은 말 을 했 으 니 실제 코드 를 보 는 것 보다 빨리 이해 하지 못 할 수도 있 습 니 다. 먼저 압축 알고리즘 입 니 다. 여기 서 필 자 는 jdk 자체 의 zip 압축 알고리즘 을 사용 합 니 다.

package com.chenjun.utils.compress;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class Compress {
    private static final int BUFFER_LENGTH = 400;
    
    
    //        ,                ,      
    public static final int BYTE_MIN_LENGTH = 50;
    
    
    //           
    public static final byte FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY = 0;
    public static final byte FLAG_GBK_STRING_COMPRESSED_BYTEARRAY = 1;
    public static final byte FLAG_UTF8_STRING_COMPRESSED_BYTEARRAY = 2;
    public static final byte FLAG_NO_UPDATE_INFO = 3;
    
    /**  
     *       
     *   
     * @param is  
     * @param os  
     * @throws Exception  
     */  
    public static void compress(InputStream is, OutputStream os)   
            throws Exception {   
  
        GZIPOutputStream gos = new GZIPOutputStream(os);   
  
        int count;   
        byte data[] = new byte[BUFFER_LENGTH];   
        while ((count = is.read(data, 0, BUFFER_LENGTH)) != -1) {   
            gos.write(data, 0, count);   
        }   
  
        gos.finish();   
  
        gos.flush();   
        gos.close();   
    }   
    
    
    /**  
     *        
     *   
     * @param is  
     * @param os  
     * @throws Exception  
     */  
    public static void decompress(InputStream is, OutputStream os)   
            throws Exception {   
  
        GZIPInputStream gis = new GZIPInputStream(is);   
  
        int count;   
        byte data[] = new byte[BUFFER_LENGTH];   
        while ((count = gis.read(data, 0, BUFFER_LENGTH)) != -1) {   
            os.write(data, 0, count);   
        }   
  
        gis.close();   
    } 
    
    /** 
     *      
     *  
     * @param data 
     * @return 
     * @throws Exception 
     */  
    public static byte[] byteCompress(byte[] data) throws Exception {  
        ByteArrayInputStream bais = new ByteArrayInputStream(data);  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  
        //     
        compress(bais, baos);  
  
        byte[] output = baos.toByteArray();  
  
        baos.flush();  
        baos.close();  
  
        bais.close();  
  
        return output;  
    } 
    
    
    /** 
     *       
     *  
     * @param data 
     * @return 
     * @throws Exception 
     */  
    public static byte[] byteDecompress(byte[] data) throws Exception {  
        ByteArrayInputStream bais = new ByteArrayInputStream(data);  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  
        //      
  
        decompress(bais, baos);  
  
        data = baos.toByteArray();  
  
        baos.flush();  
        baos.close();  
  
        bais.close();  
  
        return data;  
    }  
}

외부 호출 방법 은 by teCompress () 와 by teDecompress () 입 니 다.byte 배열 을 수신 합 니 다. byte Compress 는 데이터 압축 방법 입 니 다. 압축 된 배열 의 데 이 터 를 되 돌려 줍 니 다. byte Decompress 는 데이터 압축 해제 방법 으로 압축 해 제 된 byte 배열 의 데 이 터 를 되 돌려 줍 니 다. FLAG GBK STRING COMPRESSED BYTEARRAY 는 서버 가 전달 하 는 데 이 터 는 GBK 인 코딩 문자열 이 압축 된 바이트 배열 임 을 표시 합 니 다. 다른 상수 도 그 이름 에 따라 정리 할 수 있 습 니 다.풀이. (여기 서 한 마디 더 하면 인 코딩 방식 과 압축 여 부 를 가 진 표지 위 치 를 분리 하 는 것 이 좋 습 니 다. 예 를 들 어 표지 바이트 의 앞 네 자 리 를 표지 인 코딩 방식 의 위치 로 정의 하고 뒤에 네 자 리 를 압축 여부 나 다른 정보의 표지 위치 로 표시 하 며 위치 와 또는 방식 으로 표지 위 치 를 판단 하 는 것 이 좋 습 니 다. 필 자 는 여기 서 게 으 름 을 피 워 서 바로 이렇게 썼 습 니 다.)
  다음은 데 이 터 를 전달 하 는 방법 (압축 여 부 를 판단) 입 니 다. 저 는 Struts 1 프레임 워 크 를 사용 하여 Action 에서 데 이 터 를 조직 하고 해당 하 는 처리 (압축 또는 압축 하지 않 음) 를 하여 보 냅 니 다.

public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) {
        JjjzForm jjjzForm = (JjjzForm) form;
        

        //          
        ArrayList<Jjjz> jjjzs = null;
        
        //                
        
        Gson gson = new Gson();
        String jsonStr = gson.toJson(jjjzs, jjjzs.getClass());
        
        byte[] resultOriginalByte = jsonStr.getBytes();
                
        //               
        ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
        OutputStream os = null;
        
        
        try {
            
            os = response.getOutputStream();
            //              50 ,    
            if(resultOriginalByte.length < Compress.BYTE_MIN_LENGTH){
                byte flagByte = Compress.FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY;
                resultBuffer.write(flagByte);
                resultBuffer.write(resultOriginalByte);
            }
            else{
                byte flagByte = Compress.FLAG_GBK_STRING_COMPRESSED_BYTEARRAY;
                resultBuffer.write(flagByte);
                resultBuffer.write(Compress.byteCompress(resultOriginalByte));
            }
            resultBuffer.flush();
            resultBuffer.close();
                       
            //                 
            os.write(resultBuffer.toByteArray());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally{
            try {
                os.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return null;
    }

제 가 미리 보 낸 데 이 터 는 JSon 형식의 문자열 (GBK 인 코딩) 입 니 다. 이 문자열 의 길 이 를 판단 합 니 다. (압축 에 적합 한 지 판단 합 니 다) 압축 에 적합 하면 버퍼 바이트 배열 (ByteArray OutputStream resultBuffer)FLAG GBK STRING COMPRESSED BYTEARRAY 의 첫 번 째 바이트 에 FLAG GBK STRING STRING BYTEARRAY 를 채 우 고, 마지막 으로 출력 흐름 에 버퍼 바이트 배열 을 기록 하고, 흐름 을 닫 습 니 다. 압축 에 적합 하지 않 으 면 보 낸 데이터 의 첫 번 째 바이트 에 FLAG GBK STRING UNCOMPRESSED BYTEARRAY 를 채 우 고, JSon 문자열 의 바이트 배열 을 직접 채 웁 니 다.데이터 버퍼 바이트 배열 을 저장 하고 출력 흐름 을 기록 하 며 흐름 을 닫 습 니 다.
  마지막 으로 Android 클 라 이언 트 의 분석 입 니 다. 위의 Compress 압축 보조 클래스 를 Android 프로젝트 에 복사 하면 됩 니 다. 다음은 Http 요청 후 받 은 바이트 배열 데 이 터 를 분석 합 니 다. (Android 클 라 이언 트 가 Http 를 사용 하여 서버 에 데 이 터 를 요청 하 는 방법 은 이전 블 로 그 를 참고 하 십시오.)
byte[] receivedByte = EntityUtils.toByteArray(httpResponse.getEntity());

        String result = null;
        
        //                 
        if (receivedByte[0] == Compress.FLAG_GBK_STRING_UNCOMPRESSED_BYTEARRAY) {
            result = new String(receivedByte, 1, receivedByte.length - 1, EXCHANGE_ENCODING);
        } 
        
        else if (receivedByte[0] == Compress.FLAG_GBK_STRING_COMPRESSED_BYTEARRAY) {

            byte[] compressedByte = new byte[receivedByte.length - 1];

            for (int i = 0; i < compressedByte.length; i++) {
                compressedByte[i] = receivedByte[i + 1];
            }
            byte[] resultByte = Compress.byteDecompress(compressedByte);
            result = new String(resultByte, EXCHANGE_ENCODING);
        }

여기 서 마지막 으로 얻 은 result 는 서버 가 실제로 보 낼 내용 입 니 다.
   결함 반성: 모든 디자인 에 결함 이 있 습 니 다. 저 는 이렇게 Http 프로 토 콜 을 패키지 로 만 들 었 습 니 다. Http 의 데이터 부분의 첫 번 째 바 이 트 는 실제 데이터 가 아니 라 표지 바이트 입 니 다. 이렇게 하면 이 인터페이스의 재 활용 성 을 낮 출 수 있 습 니 다. JSon 문자열 을 통일 적 으로 보 내 는 Action 은 웹 페이지 (Ajax) 에 보 낼 수 있 습 니 다.또는 다른 클 라 이언 트 가 사용 할 경우, 패 키 징 압축 을 거 친 후, 이 패 키 징 을 식별 할 수 있 는 클 라 이언 트 만 이 인 터 페 이 스 를 사용 할 수 있 습 니 다. 웹 페이지 (Ajax) 는 분석 할 수 없 으 며, 이 Action 은 Ajax 에서 사용 할 수 없습니다.
  구체 적 인 개발 과정 에서 구체 적 인 상황 에 따라 정 해 야 합 니 다. 데이터 양 이 적 으 면 표준 Http 프로 토 콜 을 사용 하 는 것 을 권장 합 니 다. 즉, 문자열 을 직접 보 내 고 압축 과 패 키 징 을 하지 않 는 것 입 니 다. 데이터 양 이 너무 많 으 면 상기 방법 을 사용 하 는 것 을 권장 합 니 다.
  안 드 로 이 드 애플 리 케 이 션 에 있어 서 어떤 데이터 가 빅 데이터 라 고 할 수 있 느 냐 는 블 로 거들 의 질문 이 있 습 니 다. 이 빅 데이터 의 경 계 는 고정 적 인 것 이 아니 라 10k 이상 이나 100 k 이상 이 빅 데이터 라 고 할 수 있 는 것 이 아니 라 여러 가지 장단 점 으로 평가 되 는 것 이 라 고 생각 합 니 다. 우선 제 가 디자인 한 이 협 의 는 빅 데이터 와 작은 데이터 의 동적 전환 에 적용 되 는 상황 입 니 다.크 고 작은 데이터 경계 에 대한 획 정 은 개발 자 에 게 이 로 움 과 해로 움 을 평가 하도록 맡 깁 니 다. 이 평가 기준 은 다음 과 같은 몇 가지 내용 을 포함해 야 한다 고 생각 합 니 다.
  첫째, 압축 알고리즘 의 유효한 임계 점 입 니 다. 압축 할 데이터 가 이 점 보다 커 야 압축 된 데이터 가 더 작 습 니 다. 반대로 압축 된 데 이 터 는 더욱 커 집 니 다. 제 가 사용 하 는 zip 알고리즘 은 이 점 이 50 바이트 정도 일 것 입 니 다. 따라서 제 응용 프로그램 에서 대 수 를 50 바이트 이상 의 데이터 로 정의 합 니 다.
  두 번 째: 압축 과 압축 해제 비용 입 니 다. 서버 는 데 이 터 를 압축 해 야 합 니 다. 클 라 이언 트 는 데 이 터 를 압축 해 야 합 니 다. 이것 은 모두 CPU 비용 이 필요 합 니 다. 특히 서버 는 요 구 량 이 많 으 면 모든 응답 데 이 터 를 압축 해 야 합 니 다. 서버 의 성능 을 떨 어 뜨 려 야 합 니 다. 우 리 는 이러한 상황 을 상상 할 수 있 습 니 다. 원생 데 이 터 는 50 바이트 만 있 고 압축 이 끝나 면 40 바이트 가 있 습 니 다. 그러면우 리 는 CPU 를 소모 하여 우리 지역 의 10 개의 바이트 로 압축 할 필요 가 있 는 지 생각해 야 합 니까?
  종합 적 으로 이 협 의 는 크기 데이터 의 동적 전환 데이터 전송 에 적합 하지만 빅 데이터 와 작은 데이터 의 분할 점 (얼마나 큰 데 이 터 를 압축 해 야 하 는 지, 얼마나 아래 의 데 이 터 를 압축 할 필요 가 없 는 지 정의 하 는 것) 을 합 리 적 으로 선택 하 는 것 은 잘 평가 해 야 한다.

좋은 웹페이지 즐겨찾기