OkHttp4 写法kotlin版本：import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.asRequestBody //String转RequestBody String、ByteArray、ByteString都可以用toRequestBody() val stringBody ="body参数".toRequestBody("application/json;charset=utf-8".toMediaType()) val request:Request=Request .Builder() .post(stringBody) .build() //File转RequestBody val file=File("") val fileBody=file.asRequestBody("text/x-markdown; charset=utf-8".toMediaType()) val request = MultipartBody.Builder() .addFormDataPart("file", file.name,fileBody) .build()java版本:import okhttp3.MediaType.Companion.*; import okhttp3.RequestBody.Companion.*; //String转RequestBody String、ByteArray、ByteString都可以用toRequestBody() MediaType mediaType=MediaType.Companion.parse("application/json;charset=utf-8"); RequestBody stringBody=RequestBody.Companion.create("body参数",mediaType); Request request=new Request .Builder() .post(stringBody) .build(); //File转RequestBody MediaType mediaType=MediaType.Companion.parse("text/x-markdown; charset=utf-8"); File file=new File(""); RequestBody fileBody=RequestBody.Companion.create(file,mediaType); Request request=new MultipartBody.Builder() .addFormDataPart("file", file.getName(),fileBody) .build();4.0之前写法:val request:Request=Request .Builder() .post(RequestBody.create(MediaType.parse("application/json;charset=utf-8"),"body参数")) .build()编译器提示如下：又是一个小技巧
Android Kotlin 文件读写操作扩展函数，Raw资源读取，Assets资源读取
作者：snow_lyGirl链接：https://blog.csdn.net/qq_31028313/article/details/125299445来源：CSDN小记一下1、保存内容到文件及文件读取/** * 保存信息到File * @param fileName 文件名 * @param content 保存内容 * @param mode 模式 */ fun Context.saveIntoFile( fileName: String, content: String, mode: Int = Context.MODE_PRIVATE ): Boolean { return try { openFileOutput(fileName, mode).use { it.write(content.toByteArray()) true } } catch (e: Exception) { false } } /** * 读取文件 * @param fileName 文件名 */ fun Context.readFromFile(fileName: String): String { val sb = StringBuilder() openFileInput(fileName).use { val temp = ByteArray(1024) var len = 0 while (true) { len = it.read(temp) if (len <= 0) break sb.append(String(temp, 0, len)) } } return sb.toString() }在Activity中使用//保存 saveIntoFile(FILE_NAME, content) //读取 val content = readFromFile(FILE_NAME)2、保存内容到SD卡中的文件及SD卡文件读取/** * 写文件到SD卡 * eg:/storage/emulated/0/Android/data/com.guc.androiddemo/files/Documents/fileName * @param fileName 文件名 * @param content 内容 * @param type 文件类型 * {@link android.os.Environment#DIRECTORY_MUSIC}, * {@link android.os.Environment#DIRECTORY_PODCASTS}, * {@link android.os.Environment#DIRECTORY_RINGTONES}, * {@link android.os.Environment#DIRECTORY_ALARMS}, * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, * {@link android.os.Environment#DIRECTORY_PICTURES}, or * {@link android.os.Environment#DIRECTORY_MOVIES}. */ fun Context.saveFileToSD( fileName: String, content: String, type: String = Environment.DIRECTORY_DOCUMENTS ) { if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { val fn = getExternalFilesDir(type)?.canonicalPath + "/" + fileName FileOutputStream(fn).use { it.write(content.toByteArray()) } } else { throw Exception("没有存储卡") } } /** * 读取SD中文件 * eg:/storage/emulated/0/Android/data/com.guc.androiddemo/files/Documents/fileName * @param fileName 文件名 * @param type 文件类型-Documents */ fun Context.readFromSD( fileName: String, type: String = Environment.DIRECTORY_DOCUMENTS ): String { return if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { val sb = StringBuilder() val fn = getExternalFilesDir(type)?.canonicalPath + "/" + fileName FileInputStream(fn).use { val temp = ByteArray(1024) var len = 0 while (true) { len = it.read(temp) if (len <= 0) break sb.append(String(temp, 0, len)) } } sb.toString() } else { throw Exception("没有存储卡") } }Activity中直接使用//保存 saveFileToSD(FILE_NAME, content) //读取 val content = readFromSD(FILE_NAME)3、读取Raw文件/** * 读取Raw文件中的内容 * @param rawId Raw文件ID:R.id.info */ fun Context.readRaw(rawId: Int): String { return BufferedReader(InputStreamReader(resources.openRawResource(rawId))).use { val sb = StringBuilder() it.forEachLine { s -> sb.append(s) } sb.toString() } }4、读取Assets文件/** * 读取Raw文件中的内容 * @param rawId Raw文件ID:R.id.info */ fun Context.readRaw(rawId: Int): String { return BufferedReader(InputStreamReader(resources.openRawResource(rawId))).use { val sb = StringBuilder() it.forEachLine { s -> sb.append(s) } sb.toString() } }
Android Result API详解旧版本startActivityForResult使用方式（传统方式）在两个Activity之间交换数据class FirstActivity : AppCompatActivity() { //传统方式 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val firstButton = findViewById (R.id.button2) firstButton.setOnClickListener { val intent = Intent(this, SecondActivity::class.java) startActivityForResult(intent, 1) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when(resultCode){ 1 -> { if (resultCode == RESULT_OK){ val data = data?.getStringExtra("data") } } } } }调用statActivityForResult()方法去向SecondActivity请求数据，然后在onActivityResult()方法去解析SecondActivity返回的结果。另一个SecondActivityclass SecondActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val secondButton = findViewById (R.id.button2) secondButton.setOnClickListener { val intent = Intent() intent.putExtra("data","data from SecondActivity") setResult(RESULT_OK,intent) finish() } } }使用Activity Result API来实现同样的功能class FirstActivity : AppCompatActivity() { //传统方式 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val firstButton = findViewById (R.id.button2) firstButton.setOnClickListener { //传统方式 val intent = Intent(this, SecondActivity::class.java) requestDataLauncher.launch(intent) } } //使用 Activity Result API来实现同样的功能 private val requestDataLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if(result.resultCode == RESULT_OK){ val data = result.data?.getStringExtra("data") } } }首先移除来对onActivityResult()方法的重写，通过调用registerForActivityResult()方法注册一个对Activity结果的监听。registerForActivityResult()：接收两个参数，第一个参数是一种Contract类型；第二个是一个Lambda表达式，当有结果返回时会回调到这里，然后我们在这里获取并处理数据。registerForActivityResult()方法的返回值是一个ActivityResultLauncher对象，这个对象当中有一个launch()方法可以用于去启用Intent。这样我们就不需要再调用startActivityForResult()方法了，而是直接调用launch()方法，并把Intent传入即可。请求运行时权限class FirstActivity : AppCompatActivity() { private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted -> if (granted) { // User allow the permission. } else { // User deny the permission. } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_first) val firstButton = findViewById (R.id.first_button) firstButton.setOnClickListener { requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) } } }请求运行时的权限，需要使用RequestPermission这种Contract。指定了不同的Contract类型，Lambda表达式的参数也会发生变化，现在Lambda表达式会传入一个布尔型的参数，用于告诉我们用户是否允许了我们的请求的权限。在launch()方法的参数也发生了变化，需要传入要请求的权限名即可。内置ContractStartActivityForResult() StartIntentSenderForResult() RequestMultiplePermissions() RequestPermission() TakePicturePreview() TakePicture() TakeVideo() PickContact() GetContent() GetMultipleContents() OpenDocument() OpenMultipleDocuments() OpenDocumentTree() CreateDocument(）自定义Contract除了以上内置Contract之外，还可以自定义自己的Conteact类型。首先继承ActivityResultContract,它的内部定义了两个抽象方法，如下所示：public abstract class ActivityResultContract<I, O> { public abstract @NonNull Intent createIntent(@NonNull Context context, I input); public abstract O parseResult(int resultCode, @Nullable Intent intent); ... }也就是说，任何一个继承自ActivityResultContract的Contract，都需要重写createIntent()和parseResult()这两个方法。而这两个方法的作用也非常明显。createIntent()就是用于创建一个Intent，后续会使用这个Intent来发起动作，比如启动另外一个Activity去获取数据，或者打开相机去拍照等等。而parseResult()则是用于解析响应的结果，并把解析出来的结果作为输出参数返回到Lambda表达式当中。每一个内置的Contract都是使用的这种规则来封装的自己的逻辑。那么我们要自定义一个什么样的Contract来进行演示呢？我想了一下，刚才在编写两个Activity之间交换数据的时候，我们需要显示地启动SecondActivity，并手动将SecondActivity返回的数据从Intent中解析出来，这就稍微有些麻烦。而借助自定义Contract就可以对此处进行优化。新建一个叫做GetDataFromSecondActivity的Contract，代码如下所示：class GetDataFromSecondActivity : ActivityResultContract<Void, String?>() { override fun createIntent(context: Context, input: Void?): Intent { return Intent(context, SecondActivity::class.java) } override fun parseResult(resultCode: Int, intent: Intent?): String? { if (resultCode == Activity.RESULT_OK) { if (intent != null) { return intent.getStringExtra("data") } } return null } }我们通过泛型指定，这个Contract的输入参数是Void，输出参数是一个字符串。然后在createIntent()方法中，我们手动创建了一个Intent，并将它的用途设置为打开SecondActivity。最后在parseResult()方法中，我们对SecondActivity返回的结果进行解析，并将解析出来的字符串作为输出参数返回。这样一个自定义的Contract就完成了，而我们使用这个Contract再去实现最开始的在两个Activity之间交换数据的功能，就会变得更加简单：class FirstActivity : AppCompatActivity() { private val getDataLauncher = registerForActivityResult(GetDataFromSecondActivity()) { data -> // Handle data from SecondActivity } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_first) val firstButton = findViewById (R.id.first_button) firstButton.setOnClickListener { getDataLauncher.launch(null) } } }可以看到，借助GetDataFromSecondActivity这个Contract，我们不需要再显式地声明去启动SecondActivity，launch()方法直接传入null即可。另外，我们也不需要再去手动解析SecondActivity返回的数据，lambda表达式上的参数就是解析出来的结果了。小提示：在多任务的场景中，我们调用startActivityForResult()方式时，除了传入Intent之外，还需要传入一个requestCode，用于进行区分。例如：class FirstActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_first) val firstButton = findViewById (R.id.first_button) val secondButton = findViewById (R.id.second_button) firstButton.setOnClickListener { val intent = Intent(Intent.ACTION_VIEW) startActivityForResult(intent, 1) } secondButton.setOnClickListener { val intent = Intent(Intent.ACTION_DIAL) startActivityForResult(intent, 2) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { 1 -> { // Handle result for ACTION_VIEW } 2 -> { // Handle result for ACTION_DIAL } } } }这里我们分别在两处调用了startActivityForResult()方法，它们各自用于处理不同的任务，因此需要给它们设置不同的requestCode。在onActivityResult()方法当中，我们为了区分这个结果是来自之前的哪个任务的，所以要在这里再对requestCode进行判断。这是以前使用startActivityForResult()方法时的传统写法。而Activity Result API是没有地方让你传入requestCode的。我在刚接触Activity Result API的时候受思维惯性的影响被这个问题困扰了一下，没有地方传入requestCode该怎么办呢？后来思维转过来弯之后发现，原来Activity Result API根本就不需要requestCode这种东西，我们可以使用如下写法来实现和刚才完全一样的功能：class FirstActivity : AppCompatActivity() { private val actionViewLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> // Handle result for ACTION_VIEW } private val actionDialLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> // Handle result for ACTION_DIAL } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_first) val firstButton = findViewById (R.id.first_button) val secondButton = findViewById (R.id.second_button) firstButton.setOnClickListener { val intent = Intent(Intent.ACTION_VIEW) actionViewLauncher.launch(intent) } secondButton.setOnClickListener { val intent = Intent(Intent.ACTION_DIAL) actionDialLauncher.launch(intent) } } }由此也可以看出，Activity Result API的设计更加合理，不需要借助requestCode这种魔术数字也能对多个任务进行区分。一切都更加简单和清晰。