.net (C #) 서버를 사용한 Android 도킹 (2 개) : HttpTransportSE를 사용하여 SOAP 요청을 보내고 WCF 서비스를 호출하여 WebService 데이터 네트워크 프레임 워크 패키지를 가져옵니다.

〇 서문

현재 Android 개발을위한 가장 일반적인 주류 네트워크 액세스 방법은 Http 프로토콜에서 네트워크 통신에 OkHttp / Retrofit을 사용하는 것이지만 제목에서 알 수 있듯이이 기사에서는 일반적인 주류 네트워크 액세스 방법을 설명하지 않고 WCF 서비스를 호출하여 get WebService 데이터 방식은 일부 프로젝트에서 사용되므로 제목에 여러 속성을 추가하여 이러한 방식을 제한했습니다.

하나, ksoap2-android

이미 Android 시스템에있는 HttpURLConnection과 달리 SOAP 요청을 전송하려면 타사 jar 패키지 (ksoap2-android-assembly-3.3.0-jar-with-dependencies.jar)가 필요합니다.

二 、 SoapServer

SoapServer는 서버를 직접 호출하는 캡슐화 된 네트워크 액세스 클래스입니다. 주요 설명은 주석에 설명되어 있습니다.

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import jdlf_scgl_zp_android.ui.m990_system.BuildConfig;


/**
 * Description: SoapServer 直接调用服务端的网络访问类
 * 依赖ksoap2-android-assembly-3.3.0-jar-with-dependencies.jar包,底层使用HttpTransportSE访问服务端
 * 与原类(SoapService)相比,本类去除了冗余的变量和方法,增加每一步的说明,使逻辑更加清晰,并针对可能的异常直接进行捕获
 * Copyright  : Copyright (c) 2021
 * Author     : mliuxb
 * Date       : 2021-03-12
 */

class SoapServer {
    //private static final String TAG = "SoapServer";

    /**
     * InvokeWCF类使用严格的单例设计模式,因此访问服务端时只会有一个InvokeWCF对象,
     * 所以SoapServer的对象也是唯一的,从而transport的对象也是唯一的。
     */
    private final HttpTransportSE transport;

    SoapServer() {
        final String url = "http://" + BuildConfig.ipConfig + ":7090/JDLF_SCGL_ZP_WCFServices_ForAndroid.svc";
        //final String url = "http://172.18.20.89:7090/JDLF_SCGL_ZP_WCFServices_ForAndroid.svc";
        //设置服务器地址和超时时间(底层默认20秒)
        transport = new HttpTransportSE(url);
        //打开HttpTransportSE的调试
        //transport.debug = true;
    }

    /**
     * 访问服务端的核心方法
     * @param businessName
     *         业务名称(WCF服务中的方法名)
     * @param funName
     *         指定业务中的获取数据的方法,与CDP层方法对应
     * @param message
     *         传入的信息(参数值)
     * @return 服务端响应的信息(String)
     */
    String requestServer(final String businessName, final String funName, final String message) {
        try {
            //soap的核心对象。参一:服务器命名空间;参二:方法名
            final SoapObject request = new SoapObject("http://tempuri.org/", businessName);
            //请求参数
            request.addProperty("funName", funName);
            request.addProperty("message", message);

            //创建一个请求参数对象(传参为协议版本号,根据导入的jar包选择)(2020-9-1:参数对象每次进行单独创建,避免多线程同时发起请求时多个请求混淆)
            final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER10);
            //如果设置为false,服务端将无法拿到请求参数
            envelope.dotNet = true;
            //以下两行作用一样
            //envelope.bodyOut = request;
            envelope.setOutputSoapObject(request);

            final String soapAction = "http://tempuri.org/IJDLF_SCGL_ZP_WCFServices_ForAndroid/" + businessName;
            //发送请求(此方法可能抛出异常)
            transport.call(soapAction, envelope);
            //当transport.debug = true时可用transport.responseDump直接查看接收到的xml,否则transport.responseDump为null
            //Log.w(TAG, "InvokeWcfService: Response Dump >> " + transport.responseDump);

            //返回报文是String,所以以下两种解析方法均可
            //SoapObject response = (SoapObject) envelope.bodyIn;
            //return response.getProperty(0).toString();

            final SoapPrimitive primitive = (SoapPrimitive) envelope.getResponse();
            return primitive.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

三 、 InvokeWCF

InvokeWCF는 WCF를 호출하는 비동기 캡슐화 클래스로, 다음과 같이 개체 관리를위한 싱글 톤 디자인 패턴, 스레드 전환을위한 핸들러, 스레드 관리를위한 스레드 풀을 사용합니다.

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import jdlf_scgl_zp_android.s002_cem.IEnumCem;
import jdlf_scgl_zp_android.ui.m990_system.BuildConfig;


/**
 * Description: InvokeWCF 调用WCF的(异步)封装类
 * 本类采用严格的单例设计模式,保证访问服务端时只有一个InvokeWCF对象,方便管理并节约资源
 * 本类主要作用是进行异步封装(即:在子线程访问网络,并将返回的数据切换到主线程)
 * 其中子线程采用线程池进行管理,避免频繁进行线程的创建和销毁而浪费系统资源
 * 本类中实现了将DataTable数据在子线程进行解析,给业务层直接返回一个限定类型的ArrayList
 * Copyright  : Copyright (c) 2021
 * Author     : mliuxb
 * Date       : 2021-03-12
 */

public class InvokeWCF {
    private static final String TAG = "InvokeWCF";

    // 服务访问对象
    private final SoapServer mSoapServer = new SoapServer();

    // 主线程的Handler对象
    private final Handler mMainHandler = new Handler(Looper.getMainLooper());

    // 线程池
    private final ExecutorService mThreadPool = Executors.newFixedThreadPool(4);

    //单例对象(选择懒汉模式)
    private static volatile InvokeWCF mObject;

    /**
     * 私有构造函数
     */
    private InvokeWCF() {
    }

    /**
     * 公开方法,获取单例对象
     */
    public static InvokeWCF getObject() {
        //懒汉: 考虑线程安全问题,给创建对象的代码块加同步锁
        if (mObject == null) {
            synchronized (InvokeWCF.class) {
                if (mObject == null) {
                    mObject = new InvokeWCF();
                }
            }
        }
        return mObject;
    }

    /**
     * 服务端响应信息包装类
     */
    private static class ResponseInfo {
        //返回结果是否正常
        private boolean Result;
        //返回的结果内容
        private String Message;
    }

    /**
     * 从WCF服务获取数据 此方法在子线程执行
     * @param businessName
     *         业务名称(WCF服务中的方法名)
     * @param funName
     *         指定业务中的获取数据的方法,与CDP层方法对应
     * @param message
     *         传入的信息(参数值)
     * @return 响应结果
     */
    @Nullable
    @WorkerThread
    private ResponseInfo InvokeServer(final String businessName, final String funName, final String message) {
        try {
            final String response = mSoapServer.requestServer(businessName, funName, message);
            if (BuildConfig.DEBUG) {
                Log.i(TAG, "InvokeServer: business = " + businessName);
                Log.i(TAG, "InvokeServer: funName  = " + funName);
                Log.i(TAG, "InvokeServer: message  = " + message);
                Log.i(TAG, "InvokeServer: response = " + response);
            }
            //2020-9-2:response为null、""(空字符串)、" "(空格或tab)时Gson解析都会返回null(不报异常),所以此处可不进行判空。
            return new Gson().fromJson(response, ResponseInfo.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /*-------------------------------------------无回调结果start-------------------------------------------*/

    /**
     * 从WCF服务获取数据:两参,适合只需往服务端发送请求不需要响应的情况。
     * @param enumCem
     *         业务中对应方法的枚举值
     * @param message
     *         传入的信息(参数值)
     */
    @MainThread
    public void GetResultInfo(@NonNull final IEnumCem enumCem, @NonNull final String message) {
        mThreadPool.execute(() -> InvokeServer(enumCem.BusinessName(), enumCem.toString(), message));
    }

    /*-------------------------------------------无回调结果end-------------------------------------------*/

    /*---------------------------------------message为回调结果start---------------------------------------*/

    /**
     * Message数据回调的接口
     */
    public interface OnMessageListener {
        void onResult(boolean result, @NonNull String message);
    }

    /**
     * 切换到主线程进行回调:message不可能为空
     */
    @WorkerThread
    private void callbackResult(@Nullable final OnMessageListener listener, final boolean result, @NonNull final String message) {
        if (listener == null)
            return;
        //主线程回调函数
        mMainHandler.post(() -> listener.onResult(result, message));
    }

    /**
     * 从WCF服务获取数据:三参,第三参为OnMessageListener,适合不需要在底层进行解析的情况。
     * @param enumCem
     *         业务中对应方法的枚举值
     * @param message
     *         传入的信息(参数值)
     * @param listener
     *         数据回调的接口
     */
    @MainThread
    public void GetResultInfo(@NonNull final IEnumCem enumCem, @NonNull final String message, @Nullable final OnMessageListener listener) {
        //线程池中执行,在子线程访问网络
        mThreadPool.execute(() -> {
            final ResponseInfo resInfo = InvokeServer(enumCem.BusinessName(), enumCem.toString(), message);
            if (resInfo == null) {//异常情况
                callbackResult(listener, false, "");

            } else if (resInfo.Message == null) {
                callbackResult(listener, resInfo.Result, "");

            } else {//正常情况
                callbackResult(listener, resInfo.Result, resInfo.Message);
            }
        });
    }

    /*---------------------------------------message为回调结果end---------------------------------------*/

    /*-------------------------------------DataTable为回调结果start-------------------------------------*/

    /**
     * DataTable数据回调的接口
     */
    public interface OnDataTableListener<T> {
        void onResult(boolean result, @NonNull ArrayList<T> list);
    }

    @WorkerThread
    private <T> void callbackResult(@Nullable final OnDataTableListener<T> listener, final boolean result, @NonNull final ArrayList<T> list) {
        if (listener == null)
            return;
        //主线程回调函数
        mMainHandler.post(() -> listener.onResult(result, list));
    }

    /**
     * 从WCF服务获取数据
     * @param enumCem
     *         业务中对应方法的枚举值
     * @param message
     *         传入的信息(参数值)
     * @param cls
     *         Model类
     * @param listener
     *         数据回调的接口
     *         回调直接
     */
    @MainThread
    public <T> void GetResultInfo(@NonNull final IEnumCem enumCem, @NonNull final String message, @NonNull Class<T> cls, @Nullable final OnDataTableListener<T> listener) {
        //线程池中执行,在子线程访问网络
        mThreadPool.execute(() -> {
            final ResponseInfo resInfo = InvokeServer(enumCem.BusinessName(), enumCem.toString(), message);

            final ArrayList<T> list = new ArrayList<>();

            if (resInfo == null) {//异常情况
                callbackResult(listener, false, list);

            } else if (!resInfo.Result) {//返回结果为false
                callbackResult(listener, resInfo.Result, list);

            } else if (resInfo.Message == null || resInfo.Message.isEmpty() || resInfo.Message.contains("NoneRow")) {//返回的Message为空,或DataTable是空行
                callbackResult(listener, resInfo.Result, list);

            } else {
                if (BuildConfig.DEBUG) {
                    Log.w(TAG, "GetResultInfo:  Result = " + resInfo.Result);
                    Log.w(TAG, "GetResultInfo: Message = " + resInfo.Message);
                }
                final ArrayList<T> jsonToList = DataTable.fromJsonToList(resInfo.Message, cls);
                callbackResult(listener, resInfo.Result, jsonToList);

                /*try {
                    //对返回的Message在子线程进行解析
                    //final Gson gson = new Gson();
                    //JsonObject jo = new JsonParser().parse(resInfo.Message).getAsJsonObject();

                    //JsonArray array = jo.getAsJsonArray("Table");
                    //2020-10-13:获取到JsonArray后,也可生成对应的Type直接解析
                    //for (final JsonElement jsonElement : array) {
                    //    list.add(gson.fromJson(jsonElement, cls));
                    //}
                    DataTable<T> dataTable = DataTable.fromJson(resInfo.Message, cls);
                    callbackResult(listener, resInfo.Result, dataTable.Table);
                } catch (Exception e) {
                    e.printStackTrace();
                    //防止解析异常
                    callbackResult(listener, resInfo.Result, list);
                }*/
            }
        });
    }

    /*-------------------------------------DataTable为回调结果end-------------------------------------*/

    /*-------------------------------------DataSet为回调结果start-------------------------------------*/

    /**
     * DataSet 数据回调的接口
     */
    public interface OnDataSetListener {
        void onResult(boolean result, HashMap<String, ArrayList<?>> hashMap);
    }


    /**
     * 从WCF服务获取数据
     * @param //enumCem
     *         业务中对应方法的枚举值
     * @param //message
     *         传入的信息(参数值)
     * @param //map
     *         Model类
     * @param //listener
     *         数据回调的接口
     *         回调直接
     */
    /*@MainThread
    public <T> void GetResultInfo2(@NonNull final IEnumCem enumCem, @NonNull final String message, @NonNull HashMap<String, Class<?>> map, @Nullable final OnDataSetListener listener) {
        //线程池中执行,在子线程访问网络
        mThreadPool.execute(() -> {
            final ResponseInfo resInfo = InvokeServer(enumCem.BusinessName(), enumCem.toString(), message);
            HashMap<String, ArrayList<?>> hashMap = new HashMap<>();


            if (resInfo == null) {//异常情况
                //callbackResult(listener, false, list);

            } else if (!resInfo.Result) {//返回结果为false
                //callbackResult(listener, resInfo.Result, list);

            } else if (TextUtils.isEmpty(resInfo.Message) || resInfo.Message.contains("NoneRow")) {//返回的Message为空,或DataTable是空行
                //callbackResult(listener, resInfo.Result, list);

            } else {

                Log.w(TAG, "GetResultInfo2: resInfo.Message = " + resInfo.Message);
                final Gson gson = new Gson();

                //对返回的Message(DataSet)在子线程进行解析
                //对返回的进行解析
                JsonArray jsonSet = new JsonParser().parse(resInfo.Message).getAsJsonArray();
                for (final JsonElement tableElement : jsonSet) {

                    //Log.w(TAG, "GetResultInfo2: tableElement = " + tableElement.toString());
                    JsonObject jsonTable = tableElement.getAsJsonObject();
                    String tableName = jsonTable.getAsJsonPrimitive("TableName").getAsString();
                    //Log.e(TAG, "GetResultInfo2: tableName = " + tableName);
                    //JsonArray table = jsonTable.getAsJsonArray("Table");
                    //Log.w(TAG, "GetResultInfo2: table = " + table);
                    Class<?> clazz = map.get(tableName);
                    //Log.w(TAG, "GetResultInfo2: clazz = " + clazz);
                    DataTable<?> dataTable = DataTable.fromJson(tableElement, clazz);

                    hashMap.put(dataTable.TableName, dataTable.Table);
                    Log.w(TAG, "GetResultInfo2: ");

                    //for (final JsonElement jsonElement : array) {
                    //    list.add(gson.fromJson(jsonElement, cls));
                    //}
                }
                Log.w(TAG, "GetResultInfo2: hashMap = " + hashMap);
                if (listener != null) {
                    listener.onResult(resInfo.Result, hashMap);
                }
                //callbackResult(listener, resInfo.Result, list);
            }
        });
    }*/

    /*-------------------------------------DataSet为回调结果end-------------------------------------*/
}

코드의 주석은 비교적 명확합니다. 그 중이 클래스에는 오버로드 된 GetResultInfo () 메서드가 세 개 포함되어 있습니다. 첫 번째 클래스 는 두 개의 매개 변수 만 전달 합니다. 응답없이 서버에 요청 만 보내야하는 상황에 적합합니다. 사용 시나리오는 거의 없습니다.; 번째 매개 변수는 3 개의 매개 변수를 전달해야하며 세 번째 매개 변수는 OnMessageListener입니다. 즉, 맨 아래 계층에서 Json 데이터 분석이 수행되지 않고 반환 된 데이터는 비즈니스 계층으로 직접 호출됩니다. 모든 시나리오를 사용할 수 있지만 데이터 분석이 필요한 경우에는 그다지 편리하지 않습니다. 세 번째 매개 변수는 4 개의 매개 변수를 전달해야 하며 세 번째 매개 변수는 클래스 객체이며 네 번째 매개 변수는 OnDataTableListener로 Json 데이터를 반환하는 데 적합합니다. 이 클래스의 데이터를 직접 분석하고 비어 있지 않은 일반 List를 사용하여 비즈니스 계층을 다시 호출하기위한 DataTable 유형의.

완료해야 할 또 다른 GetResultInfo2 () 메서드는 일시적으로 주석이 추가되었습니다.이 메서드는 DataSet 유형의 Json 데이터에 대한 직접 분석을 완료하고 비즈니스 계층에 HashMap <String, ArrayList <T >> (여기서 키 is TableName, value for the parsed DataTable), 이것이 완료되지 않은 이유는 일시적으로 조정되지 않은 (또는 변수 매개 변수를 사용합니까?) List 유형을 표준화하기 위해 여러 개의 다른 제네릭이 필요하기 때문입니다. 좋은 해결책, 알려주세요 ...

DataTable 및 DataSet 분석은 이전 문서 : .net (C #) 서버를 사용한 Android 도킹 (1) : DataTable 및 DataSet 유형의 Json 데이터 구문 분석을 참조하십시오.

 

추천

출처blog.csdn.net/beita08/article/details/114685999