如何开发分离数据层和视图层的android应用程序

如何开发分离数据层和视图层的android应用程序,android,kotlin,Android,Kotlin,我是Android开发新手。在koltin中有一个android应用程序,其中我必须发出http post请求以获取数据列表作为响应。 我在活动课上做了如下的练习。 MainActivity.kt class MainActivity : AppCompatActivity(), { private fun getAppList() { var builder = AlertDialog.Builder(this@MainActivity) builde

我是Android开发新手。在koltin中有一个android应用程序,其中我必须发出http post请求以获取数据列表作为响应。 我在活动课上做了如下的练习。 MainActivity.kt

class MainActivity : AppCompatActivity(), {

    private fun getAppList() {
        var builder = AlertDialog.Builder(this@MainActivity)
        builder.setTitle("App Response")
        doAsync {
            sslCertficate.disableSSLCertificateChecking()
            var headers = HashMap<String, String>()

            headers["Content-type"] = "application/json; charset=UTF-8"
            val res = HTTPClient("https://sample-myapi-launcher.prod.com/list")
                    .setMethod("POST")
                    .setHeaders(headers)
                    .setBody(getRequestBody(userInfo.toString()))
                    .getResponse()
                    .response
            uiThread {
                builder.setMessage(res)
                var dialog: AlertDialog = builder.create()
                dialog.show()
            }

            Log.e("Response List", res)

        } 
    }

    private fun getRequestBody(userInfo: String): String {
   //code for geting request body

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_navigator)
        setSupportActionBar(toolbar)
      //calling api request method
        getAppList()

    }
}
class MainActivity:AppCompatActivity(){
私人娱乐getAppList(){
var builder=AlertDialog.builder(this@MainActivity)
builder.setTitle(“应用程序响应”)
doAsync{
sslcertificate.disableSSLCertificateChecking()
var headers=HashMap()
标题[“内容类型”]=“应用程序/json;字符集=UTF-8”
val res=HTTPClient(“https://sample-myapi-launcher.prod.com/list")
.setMethod(“POST”)
.setHeaders(标题)
.setBody(getRequestBody(userInfo.toString()))
.getResponse()
.答复
超线程{
builder.setMessage(res)
变量对话框:AlertDialog=builder.create()
dialog.show()
}
Log.e(“响应列表”,res)
} 
}
private fun getRequestBody(userInfo:String):String{
//获取请求主体的代码
}
重写创建时的乐趣(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity\u导航器)
设置支持操作栏(工具栏)
//调用api请求方法
getAppList()
}
}
我可以通过这个来实现我的结果,但我不想把所有的工作都放在活动线程中。有人能指导实现这一目标的正确方法吗? 或者帮我写一些文档。

这是Android。它能满足你的要求。这是Android Jetpack的一部分,它是一组Android库,可以帮助您以健壮、可测试和可维护的方式构建应用程序


这里还有一个园艺应用程序,演示了Android Jetpack的Android开发最佳实践。

要进行联网,我建议您使用改型2:

无论如何,要在另一个线程中执行网络操作,您需要从活动启动一个新的异步任务,并在其中执行网络操作。 在改造中,所有这些都要简单得多

(很抱歉,下面我没有Kotlin的例子!)

未经改装的Java示例: (这是我以前的项目,所以不太好^)

我还没有完成这门课*/ 类帮助程序(baseUrl:String){

private val TAG=this.javaClass.name
//超时
private val CONNECT\u TIMEOUT=“CONNECT\u TIMEOUT”
private val READ\u TIMEOUT=“READ\u TIMEOUT”
private val WRITE_TIMEOUT=“WRITE_TIMEOUT”
//标题名称
专用val BASE_REQ_HEADER_NAME=“BLEDABINDER”
private val REQ_HEADER_NAME=“$BASE_REQ_HEADER_NAME.Request”
private val REQ_HEADER_VERSION_NAME=“$BASE_REQ_HEADER_NAME.VersionName”
private val REQ_HEADER_VERSION_CODE=“$BASE_REQ_HEADER_NAME.VersionCode”
private val REQ_HEADER_DEVICE_IMEI=“$BASE_REQ_HEADER_NAME.DeviceIMEI”
private val REQ_HEADER_DEVICE_UNIQUE_ID=“$BASE_REQ_HEADER_NAME.DeviceUniqueID”
private val REQ_HEADER_DEVICE_MODEL=“$BASE_REQ_HEADER_NAME.DeviceModel”
private val REQ_HEADER_ANDROID_RELEASE=“$BASE_REQ_HEADER_NAME.AndroidRelease”
//标题值
专用val REQ_标题_VALUE=“emax”
//标签
private val LABEL_INIT=“INIT helper”
私有变量mBaseUrl:String
私人var mGson:Gson
私人var mRetrofit:改装
伴星{
@Volatile private var mInstance:RefughtHelper?=null
fun getInstance()=minInstance
fun initInstance(baseUrl:String):帮助程序=
MinInstance?:已同步(此){
MinInstance?:newInstance(baseUrl)。也是{MinInstance=it}
}
private-fun-newInstance(baseUrl:String)=帮助器(baseUrl)
}
初始化{
LogUtils.iLog(标记“START$LABEL\u INIT”)
val httpClient=OkHttpClient.Builder()
httpClient.addInterceptor(getInterceptor())
httpClient.addInterceptor(getLoggingInterceptor())
this.mBaseUrl=baseUrl
mGson=getGson()
mRetrofit=getReformation(httpClient.build())
LogUtils.iLog(标记“END$LABEL\u INIT”)
}
/*启动私有方法*/
私人娱乐改造(httpClient:OkHttpClient):改造{
return reformation.Builder()
.baseUrl(mBaseUrl)
.addConverterFactory(GsonConverterFactory.create(mGson))
.client(httpClient)
.build()
}
私人娱乐getGson():Gson{
返回GsonBuilder()
.setDateFormat(常数.DATETIME\u格式\u DB)
.registerTypeAdapter(Boolean::class.javaObjectType,BooleanDeserializer())
.create()
}
私人娱乐getLoggingInterceptor()=
HttpLoggingInterceptor{
getLoggingInterceptorLogger()
}.ALL{it.level=HttpLoggingInterceptor.level.BODY}
私人娱乐getLoggingInterceptorLogger()=
HttpLoggingInterceptor.Logger{
message->HyperLog.v(标记、消息)
}
private fun getInterceptor():拦截器=
拦截器{
buildInterceptorResponse(it)
}
private fun buildInterceptorResponse(链:Interceptor.chain):响应{
val builder:Request.builder=chain.Request().newBuilder().addHeader(请求标题名称、请求标题值)
SetRequestHeadServersOnName(生成器)
SetRequestHeadServersOnCode(生成器)
SetRequestHeaderDeviceMei(构建器)
setRequestHeaderDeviceUniqueID(生成器)
setRequestHeaderDeviceModel(生成器)
setRequestHeaderAndroidRelease(生成器)
/*这一部分允许您使用标签为“ReformationAPI”接口内的不同api调用设置自定义超时:(例如R
/* Really Simple Class I made to do networking operations (so use Retrofit or make a better one (: (I suggest you, again, to use Retrofit!) */
public class DBConnection {

    public String  performPostCall(String requestURL, HashMap<String, String> postDataParams )
    {

        URL url;
        String response = "";
        try {
            url = new URL(requestURL);

            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setReadTimeout(15000);
            conn.setConnectTimeout(15000);
            conn.setDoInput(true);
            conn.setDoOutput(true);

            conn.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
            conn.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());
            conn.connect();

            DataOutputStream dStream = new DataOutputStream(conn.getOutputStream());
            dStream.writeBytes(getPostDataString(postDataParams));
            dStream.flush();
            dStream.close();

            int responseCode = conn.getResponseCode();
            if (responseCode == HttpsURLConnection.HTTP_OK) {
                String line;
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                while ((line=br.readLine()) != null) {
                    response += line;
                }
            } else {
                response = "";
            }
            conn.disconnect();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return response;
    }

    private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException
    {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for(Map.Entry<String, String> entry : params.entrySet()){
            if (first) {
                first = false;
            } else {
                result.append("&");
            }

            result.append(entry.getKey());
            result.append("=");
            result.append(entry.getValue());
        }

        return result.toString();
    }

}

// AsyncTask to do Async Networking operations:

public class YourTask extends AsyncTask<String, Void, String> {

    private String yourData...;

    public YourTask(String token){
        // Set Your Data
    }
// "String..." is an array of arguments so you get the arguments usign: params[i]
    @Override
    protected String doInBackground(String... params)
    {
        DBConnection dbConn = new DBConnection();
        String stampAnswer;
        try{

            Integer param1 = Integer.parseInt(params[0]);
            Integer param2 = Integer.parseInt(params[1]);
            answer = dbConn.netwokirngOperation([..]);
        }catch(NumberFormatException nfe){
            nfe.getStackTrace();
            stampAnswer = "";
            Log.e("YourTask", " NumberFormatException:");
        }
        return answer;
    }

    protected void onPostExecute(String result) {
        Log.e("YourTask => ", " Result:" + result);
    }

}

// To call the task do in your activity and do async networking operation without wait for result (this mean you need to save the data inside Realm DB [REALM][2] or using SQLite DB and then get them when the networking operations ended (you can use an Observable and set it when the networking operation end, send a broadcast message and set a receiver in you activity, or any other method):

new YourTask(<put_here_your_asynctask_constructor_args>).execute(params);

// To call the task and do async networking operation but wait to get the result returned by the "doInBackground" method of the AsyncTask:

new YourTask(<put_here_your_asynctask_constructor_args>).execute(params).get();
/** in You Activity. Because in the Interface you use generic types (the 'T') you can specific the type of object returned by the interface inside the '<T>' if the interface WILL ALWAYS RETURN THE SAME OBJECT TYPE! 
If it WILL RETURN DIFFERENT OBJECT TYPES you MUST don't specific the type inside the '<T>', but you have to cast the return inside a switch statement to know which object is returned (to do that you can add a 'int requestCase' to the interface so you know which case returned!) **/

public class YourActivity extends AppCompatActivity 
    implements IYourCallback<YourObjectTypesReturned>{

     public interface IYourCallback<T>{
          onNetOperationSuccess(List<T> answer)
          onNetOperationError(Throwable t)
     }

     /** IMPLEMENTS HERE YOUR ACTIVITY BODY WITH THE INTERFACE METHODS ! **/

     // Then call your AsyncTask where you want and pass it your context which implements the interface ( because you are in activity your context with the interface is "this"!

     new YourTask(this).execute(params);

// Then inside your AsyncTask:

public class YourTask extends AsyncTask<String, Void, String> {

        private IYourCallback mCallback;

        public YourTask(Context context){
            try{
               mCallback = (IYourCallback) mCallback;
            } catch(ClassCastException e){
                  onException(e);  // Manage the exception and stop the operation
             }
        }

        /** THEN IMPLEMENT YOU IN BACKGROUND... AND WHEN THE NETWORKING OPERATION IS FINISHED USE THE CALLBACK TO RETURN BACK YOUR RESULT, SO THE METHOD IN YOUR ACTIVITY WILL GET TRIGGERED AND YOU CAN CONTINUE TO DO YOUR OPERATIONS! So do: **/

if(success)
   mCallback.onNetOperationSuccess(myListAnswer)
else
   mCallback.onNetOperationError(error) // Throwable or exception
/* This is a RetrofitHelper which init the Retrofit instance and where you should put your networking methods. Then to do a networking operation you have to get this instance using (RetrofitHelper.getInstance().yourNetworkingOperation(...) ).
Anyway here there isn't the asynchronous part, you can get it in the link of my other comment below! 
    private val TAG = this.javaClass.name
    // Timeouts
    private val CONNECT_TIMEOUT = "CONNECT_TIMEOUT"
    private val READ_TIMEOUT = "READ_TIMEOUT"
    private val WRITE_TIMEOUT = "WRITE_TIMEOUT"
    // Header Names
    private val BASE_REQ_HEADER_NAME = "BLEDataBinder"
    private val REQ_HEADER_NAME = "$BASE_REQ_HEADER_NAME.Request"
    private val REQ_HEADER_VERSION_NAME = "$BASE_REQ_HEADER_NAME.VersionName"
    private val REQ_HEADER_VERSION_CODE = "$BASE_REQ_HEADER_NAME.VersionCode"
    private val REQ_HEADER_DEVICE_IMEI = "$BASE_REQ_HEADER_NAME.DeviceIMEI"
    private val REQ_HEADER_DEVICE_UNIQUE_ID = "$BASE_REQ_HEADER_NAME.DeviceUniqueID"
    private val REQ_HEADER_DEVICE_MODEL = "$BASE_REQ_HEADER_NAME.DeviceModel"
    private val REQ_HEADER_ANDROID_RELEASE = "$BASE_REQ_HEADER_NAME.AndroidRelease"
    // Header Values
    private val REQ_HEADER_VALUE = "emax"
    // Labels
    private val LABEL_INIT = "Init RetrofitHelper"

    private var mBaseUrl: String
    private var mGson: Gson
    private var mRetrofit: Retrofit

    companion object {
        @Volatile private var mInstance: RetrofitHelper? = null

        fun getInstance() = mInstance

        fun initInstance(baseUrl: String): RetrofitHelper =
            mInstance ?: synchronized(this){
                mInstance ?: newInstance(baseUrl).also { mInstance = it }
            }

        private fun newInstance(baseUrl: String) = RetrofitHelper(baseUrl)
    }

    init {
        LogUtils.iLog(TAG, "START $LABEL_INIT")
        val httpClient = OkHttpClient.Builder()
        httpClient.addInterceptor( getInterceptor() )
        httpClient.addInterceptor( getLoggingInterceptor() )
        this.mBaseUrl = baseUrl
        mGson = getGson()
        mRetrofit = getRetrofit(httpClient.build())
        LogUtils.iLog(TAG, "END $LABEL_INIT")
    }

    /* START Private Methods */
    private fun getRetrofit(httpClient: OkHttpClient): Retrofit{
        return Retrofit.Builder()
            .baseUrl(mBaseUrl)
            .addConverterFactory(GsonConverterFactory.create(mGson))
            .client(httpClient)
            .build()
    }

    private fun getGson(): Gson{
        return GsonBuilder()
            .setDateFormat(Constants.DATETIME_FORMAT_DB)
            .registerTypeAdapter(Boolean::class.javaObjectType, BooleanDeserializer())
            .create()
    }

    private fun getLoggingInterceptor() =
        HttpLoggingInterceptor {
            getLoggingInterceptorLogger()
        }.also { it.level = HttpLoggingInterceptor.Level.BODY }

    private fun getLoggingInterceptorLogger() =
        HttpLoggingInterceptor.Logger {
                message -> HyperLog.v(TAG, message)
        }

    private fun getInterceptor(): Interceptor =
        Interceptor {
            buildInterceptorResponse(it)
        }

    private fun buildInterceptorResponse(chain: Interceptor.Chain): Response {
        val builder: Request.Builder = chain.request().newBuilder().addHeader(REQ_HEADER_NAME, REQ_HEADER_VALUE)
        setRequestHeaderVersionName(builder)
        setRequestHeaderVersionCode(builder)
        setRequestHeaderDeviceIMEI(builder)
        setRequestHeaderDeviceUniqueID(builder)
        setRequestHeaderDeviceModel(builder)
        setRequestHeaderAndroidRelease(builder)

        /* This part let you set custom timeout for different api call inside the "RetrofitAPI" interface using that labels: (example inside the RetrofitAPI interface)

public interface RetrofitAPI {

@Headers({RetrofitHelper.CONNECT_TIMEOUT + ":100000", RetrofitHelper.READ_TIMEOUT + ":100000"})
    @FormUrlEncoded
    @POST
    Call<JsonObject> doBaseJsonRequest(@Url String url, @Field("params") String params);

}
*/
        var connectTimeout = chain.connectTimeoutMillis()
        var readTimeout = chain.readTimeoutMillis()
        var writeTimeout = chain.writeTimeoutMillis()
        val request = chain.request()
        if(!TextUtils.isEmpty(request.header(CONNECT_TIMEOUT))){
            connectTimeout = request.header(CONNECT_TIMEOUT)!!.toInt()
        }
        if(!TextUtils.isEmpty(request.header(READ_TIMEOUT))){
            readTimeout = request.header(READ_TIMEOUT)!!.toInt()
        }
        if(!TextUtils.isEmpty(request.header(WRITE_TIMEOUT))){
            writeTimeout = request.header(WRITE_TIMEOUT)!!.toInt()
        }
        builder.removeHeader(CONNECT_TIMEOUT)
        builder.removeHeader(READ_TIMEOUT)
        builder.removeHeader(WRITE_TIMEOUT)
        return chain
            .withConnectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
            .withReadTimeout(readTimeout, TimeUnit.MILLISECONDS)
            .withWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS)
            .proceed(builder.build())
    }

    /*private fun setRequestHeaders(builder: Request.Builder): Request.Builder{
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionName)){
            builder.addHeader(REQ_HEADER_VERSION_NAME, AppEnvironment.getInstance()!!.mVersionName!!)
        }
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionCode.toString())){
            builder.addHeader(REQ_HEADER_VERSION_CODE, AppEnvironment.getInstance()!!.mVersionName!!)
        }
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceIMEI)){
            builder.addHeader(REQ_HEADER_DEVICE_IMEI, AppEnvironment.getInstance()!!.mVersionName!!)
        }
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceUniqueID)){
            builder.addHeader(REQ_HEADER_DEVICE_UNIQUE_ID, AppEnvironment.getInstance()!!.mVersionName!!)
        }
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceModel)){
            builder.addHeader(REQ_HEADER_DEVICE_MODEL, AppEnvironment.getInstance()!!.mVersionName!!)
        }
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mAndroidRelease)){
            builder.addHeader(REQ_HEADER_ANDROID_RELEASE, AppEnvironment.getInstance()!!.mVersionName!!)
        }
        return builder
    }*/

    private fun setRequestHeaderVersionName(builder: Request.Builder){
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionName)){
            builder.addHeader(REQ_HEADER_VERSION_NAME, AppEnvironment.getInstance()!!.mVersionName!!)
        }
    }

    private fun setRequestHeaderVersionCode(builder: Request.Builder){
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionCode.toString())){
            builder.addHeader(REQ_HEADER_VERSION_CODE, AppEnvironment.getInstance()!!.mVersionName!!)
        }
    }

    private fun setRequestHeaderDeviceIMEI(builder: Request.Builder){
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceIMEI)){
            builder.addHeader(REQ_HEADER_DEVICE_IMEI, AppEnvironment.getInstance()!!.mVersionName!!)
        }
    }

    private fun setRequestHeaderDeviceUniqueID(builder: Request.Builder){
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceUniqueID)){
            builder.addHeader(REQ_HEADER_DEVICE_UNIQUE_ID, AppEnvironment.getInstance()!!.mVersionName!!)
        }
    }

    private fun setRequestHeaderDeviceModel(builder: Request.Builder){
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceModel)){
            builder.addHeader(REQ_HEADER_DEVICE_MODEL, AppEnvironment.getInstance()!!.mVersionName!!)
        }
    }

    private fun setRequestHeaderAndroidRelease(builder: Request.Builder){
        if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mAndroidRelease)){
            builder.addHeader(REQ_HEADER_ANDROID_RELEASE, AppEnvironment.getInstance()!!.mVersionName!!)
        }
    }
    /* END Private Methods */

}