4

因此,我正在尝试编写一个非常简单的 Android 应用程序,该应用程序在按下按钮时从 URL 获取响应。kotlin Android 扩展已被宣传为 Java 中必需的样板的替代品,所以我尝试了自己的手。这是我到目前为止所尝试的:

package com.example.susemihl.myapplication

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.runBlocking
import java.net.URL

suspend fun fetch_url(url: String): String {
    return URL(url).readText()
}

fun fetch_async(url: String, view: TextView) = runBlocking {
    val result = async(CommonPool) { fetch_url(url) }
    view.setText(result.await())
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mainTextView.setText("Hello there.")
        mainButton.setOnClickListener {
            mainButton.setText("Check again.")
            fetch_async("https://random-app.appspot.com/", 
                        mainTextView)
        }

    }
}

这间歇性地工作,但现在完全坏了。按钮单击没有响应。打印调试显示线程被执行,但似乎挂在 readText() 调用上。有什么愚蠢的我在这里做错了吗?

4

3 回答 3

0

您必须切换到主线程才能从挂起功能更新 UI。我会在ViewModel中执行网络逻辑并将结果作为LiveData 公开给您的Activity

class MainViewModel : ViewModel() {
  val urlLiveData = MutableLiveData<String>()

  fun fetchUrl(url: String): String {
    viewModelScope.launch {
      // Dispatchers.IO (main-safety block)
      withContext(Dispatchers.IO) {
        fetchAsync(url)
      }
    }
  }

  private suspend fun fetchAsync(url: String) {
    urlLiveData.postValue(URL(url).readText())  
  }

}

class MainActivity : AppCompatActivity() {
  private val mainViewModel by viewModels<MainViewModel>()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    mainViewModel.urlLiveData.observe(
        viewLifecycleOwner,
        Observer { urlText ->
          mainTextView.setText(urlText)
        }
     )
   }

   mainViewModel.fetchUrl(""https://random-app.appspot.com/")
}
于 2020-03-22T20:22:40.893 回答
0

这是一个可与 kotlin 一起使用的异步示例,对我来说非常适合

val result = URL("<api call>").readText()

try {
        URL url = new URL("<api call>");
            urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestMethod("GET");
        urlConnection.connect();
        InputStream inputStream = urlConnection.getInputStream();
            StringBuffer buffer = new StringBuffer();
        if (inputStream == null) {
            // Nothing to do.
            return null;
        }
        reader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while ((line = reader.readLine()) != null) {
            buffer.append(line + "\n");
        }
        if (buffer.length() == 0) {
            return null;
        }
        result = buffer.toString();
    } catch (IOException e) {
        Log.e("Request", "Error ", e);
        return null;
    } finally{
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
        if (reader != null) {
            try {
                reader.close();
            } catch (final IOException e) {
                Log.e("Request", "Error closing stream", e);
            }
        }
    }

@Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    task = new AsyncTask<Void, Void, String>() {
        @Override protected String doInBackground(Void... params) {
            return requestFromServer("<api call>");
        }

        @Override protected void onPostExecute(String s) {
            if (!isFinishing() && !isCancelled()) {
                Log.d("Request", s);
                Toast.makeText(ExampleActivity.this, "Request performed", Toast.LENGTH_LONG).show();
            }
        }
    };
}

@Override protected void onDestroy() {
    super.onDestroy();

    if (task != null) {
        task.cancel(true);
        task = null;
    }
}

引用自——antonioleiva

于 2018-01-24T11:51:09.347 回答
0

我知道你的情况,是因为你在使用runBlocking,虽然await没有阻塞线程,但是它会挂起协程,并且由于当前协程还没有完成,runBlocking线程会被阻塞等待。

所以只是使用launc(UI)而不是runBlocking解决这个问题:

package com.example.susemihl.myapplication

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch
import java.net.URL

fun fetch_url(url: String): String {
    return URL(url).readText()
}

fun fetch_async(url: String, view: TextView) = launch(UI) {
    val result = async(CommonPool) { fetch_url(url) }
    view.text = result.await()
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mainTextView.text = "Hello there."
        mainButton.setOnClickListener {
            mainButton.text = "Check again."
            fetch_async("https://jacksgong.com", mainTextView)
        }

    }
}
于 2017-09-11T09:44:04.817 回答