Android開發問題集錦(2023.09.20更新)

  1. 生成類庫的jar文件
通常情況下,我們需要把一個類庫打包成一個**Jar**文件,而不是**aar**文件,因此我們需要對app.gradle進行修改,如下所示:
task makeJar(type: Copy){
    delete 'build/libs/CrashCatcher.jar'
    from('build/intermediates/bundles/release/')
    into('build/libs')
    include('classes.jar')
    rename('classes.jar', 'CrashCatcher.jar')
}

makeJar.dependsOn(build)
配置好后,即可通過gradle生成我們想要的jar文件。
  1. 如何處理Linux不能啟用AVD的問題?

First, install some packages and libs:
$ sudo apt-get install lib64stdc++6:i386
$ sudo apt-get install mesa-utils

Second, tweak some links:
$ cd YOURPATH/Android/Sdk/tools/lib64
$ mv libstdc++/ libstdc++.bak
$ ln -s /usr/lib64/libstdc++.so.6 libstdc++
Third, relaunch your AVD device and test it.

3.遇到異常【A problem occurred starting process 'command 'E:\Tools\sdk\ndk-bundle\toolchains\mips64el-linux-android-4.9\prebuilt\windows-x86_64\bin\mips64el-linux-android-strip''】處理辦法 , windows下異常

處理辦法:修改項目根目錄的local.properties文件中ndk.dir的值,
如下,原值是:

ndk.dir=E\:\\Tools\\sdk\\ndk-bundle

修改為:

ndk.dir=E\:\\Tools\\sdk\\ndk-bundle.cmd

再次編輯即可正常,但是可能下次進入項目時還會報一樣的錯誤,同樣處理即可!

  1. 網絡安全配置, 有時候我們可能會網絡不可訪問的時候,可以進行一下配置

在Manifest.xml中的Application節點中配置network_security_config:

android:networkSecurityConfig="@xml/network_security_config"

配置文件(network_security_config.xml)內容如下:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
  1. Android 9.0的WebView打開URL頁面,報錯:ERR_CLEARTEXT_NOT_PERMITTED

需要在Manifest.xml中的Application節點中配置屬性:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
        android:usesCleartextTraffic="true"
        ...>
        ...
    </application>
</manifest>
  1. 32位SO庫與64位SO庫不匹配時,又不能找到64位SO庫,只能去掉64位SO庫的引用,以免運行時報錯,所以做一下處理
android {
      ........
       packagingOptions {
        exclude 'lib/arm64-v8a/*'
       }
        ........
}
  1. 處理Linux系統上出現的/dev/kvm權限問題

當我們想要運行模擬器的時候,出現以上問題,該怎么處理呢?首先我們先查看一下/dev/kvm所在權限組:

ls -l /dev/kvm

我們可能會得到類似于這樣的結果:
crw-rw---- 1 root kvm 10, 232 2月 15 09:22 /dev/kvm
就結果而看,我們的/dev/kvm屬于root用戶的kvm組

怎么把當前用戶加入kvm組呢?首先安裝一個軟件:

sudo apt install qemu-kvm

執行安裝完成后,我們添加用戶到組

sudo adduser UserName kvm

添加完成后即可,然后重啟電腦!

  1. AndroidStudio就開始打開項目了,這個過程會比較緩慢,有時,AS會出現如下信息:


    提示信息

1). 在/etc/sysctl.conf 文件末尾中添加如下代碼:

fs.inotify.max_user_watches = 524288

2). 然后在終端執行以下命令:

sudo sysctl -p --system

最后重啟AS

  1. 如何獲取手機的存儲設備目錄

Android N 提供了StorageManager類,并開放了StorageVolume類的部分方法,因此可以使用一下方法可以靠譜的獲得相關路徑和信息。

10.如何保存文件到手機并同步資源

看示例代碼:

 /**
     * 保存到本地
     * @param fileName 文件名
     */
    @SuppressLint("Recycle")
    private fun saveToLocal(fileName: String, bitmap: Bitmap): Boolean {
        try {
            var newFileName = fileName
            var isOriginalExists = true
            val cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null,
                    "${MediaStore.Images.Media.DISPLAY_NAME} like ?", arrayOf(fileName), null)
            if (cursor != null && cursor.count > 0) {
                if (cursor.moveToNext()) {
                    isOriginalExists = contentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            "${MediaStore.Images.Media._ID}=?",
                            arrayOf(cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID)).toString())) <= 0
                }
            }
            cursor?.close()
            if (isOriginalExists) newFileName = "${System.currentTimeMillis()}_$fileName"
            val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, ContentValues().apply {
                @Suppress("DEPRECATION")
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
                    put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/Pictures")
                else {
                    val dir = when (Environment.MEDIA_MOUNTED) {
                        Environment.getExternalStorageState() ->
                            Environment.getExternalStorageDirectory().absolutePath
                        else -> cacheDir.absolutePath
                    }
                    put(MediaStore.Images.Media.DATA,
                            File("${dir}/DCIM/Pictures", newFileName).absolutePath)
                }
                put(MediaStore.Images.Media.DISPLAY_NAME, newFileName)
                put(MediaStore.Images.Media.DESCRIPTION, newFileName)
                put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
            })
            if (uri != null) {
                val oos = contentResolver.openOutputStream(uri)
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, oos)
                oos?.flush()
                oos?.close()
                return true
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return false
    }

11.如何解決錯誤: android studio 3 字節的 UTF-8 序列的字節 3 無效。(3 ???? UTF-8 ???е???? 3 ??Ч??)

  1. 配置IDE file-coding 都為 UTF-8;
  2. 配置IDE vmOptions-Dfile.encoding=UTF-8;
  3. 配置 app.gradle
     compileOptions {
         encoding 'UTF-8' //設置為UTF-8
         .... //其他配置
     }
    

12.如何播放本地視頻,且不黑屏。 推薦使用TextureView+MediaPlayer+Surface


  private var mTexture: TextureView? = null

  private var mPlayer: MediaPlayer? = null

  private var mSurface: Surface? = null

  private var mVideoUri: Uri? = null

  private var mListener: OnmPlayerStateChangedListener? = null

  constructor(context: Context) : super(context)

  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
      context,
      attrs,
      defStyleAttr
  )

  constructor(
      context: Context,
      attrs: AttributeSet?,
      defStyleAttr: Int,
      defStyleRes: Int
  ) : super(context, attrs, defStyleAttr, defStyleRes)

  fun setOnPlayerStateChangedListener(listener: OnmPlayerStateChangedListener) {
      this.mListener = listener
  }

  var videoUri: Uri?
      get() = mVideoUri
      set(value) {
          mVideoUri = value
      }

  fun initializer(context: Context) {
      mTexture = TextureView(context).apply {
          layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
          surfaceTextureListener = this@CustomVideoView
      }
      addView(mTexture)
  }

  override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
      if (mVideoUri == null)
          throw Exception("請首先設置播放源")
      mSurface = Surface(surface)
      PlayerThread(mVideoUri!!).start()
      mListener?.onStarted()
  }

  override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {}

  override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
      onReleasemPlayer()
      mTexture = null
      mSurface = null
      return true
  }

  override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}

  fun onReleasemPlayer() {
      try {
          if (mPlayer != null) {
              if (mPlayer?.isPlaying == true)
                  mPlayer?.pause()
              mPlayer?.stop()
              mPlayer?.release()
          }
          mPlayer = null
      } catch (e: Exception) {
          Log.e("CustomVideoView", e.message)
      } finally {
          mListener?.onReleased()
      }
  }

  /**
   * 使視頻適配屏幕
   * @param textureWidth Texture寬度
   * @param textureHeight Texture高度
   */
  private fun stretchingVideoSize(textureWidth: Float, textureHeight: Float) {
      val matrix = Matrix()
      val videoWidth = mPlayer?.videoWidth ?: return
      val videoHeight = mPlayer?.videoHeight ?: return
      val scaleX = textureWidth / videoWidth
      val scaleY = textureHeight / videoHeight
      val originScaleX = videoWidth / textureWidth
      val originScaleY = videoHeight / textureHeight
      matrix.preScale(originScaleX, originScaleY)
      val maxScale = maxOf(scaleX, scaleY)
      matrix.preScale(maxScale, maxScale)
      mTexture?.setTransform(matrix)
      mTexture?.postInvalidate()
  }

  private inner class PlayerThread(private val uriSource: Uri) : Thread() {

      override fun run() {
          try {
              mPlayer = MediaPlayer().apply {
                  setDataSource(context, uriSource)
                  setSurface(mSurface)
                  setAudioAttributes(
                      AudioAttributes.Builder()
                          .setLegacyStreamType(AudioManager.STREAM_MUSIC)
                          .build()
                  )
                  setOnPreparedListener {
                      it?.start()
                      if (context is Activity) (context as Activity).runOnUiThread {
                          val textureWidth =
                              mTexture?.measuredWidth?.toFloat() ?: return@runOnUiThread
                          val textureHeight =
                              mTexture?.measuredHeight?.toFloat() ?: return@runOnUiThread
                          stretchingVideoSize(textureWidth, textureHeight)
                      }
                  }
                  setOnCompletionListener { mListener?.onCompleted() }
              }
              mPlayer?.prepare()
          } catch (e: Exception) {
              Log.e("CustomVideoView", "播放視頻失敗$e")
          }
      }
  }
  1. android界面置灰效果實現
        val paint = Paint()
        val cm = ColorMatrix()
        cm.setSaturation(0f)
        paint.colorFilter = ColorMatrixColorFilter(cm)
        window.decorView.setLayerType(View.LAYER_TYPE_HARDWARE, paint)
  1. WebView與Native交互,通過js方法獲取的Html內容攜帶unicode字段,如何處理?
Gson().fromJson("html-unicode", String::class.java) //這樣能去掉亂碼

如果是Flutter又該如何處理?可以考慮在js中把數據URIComponentEncode(), 取出來用時,使用Flutter的Uri.componentDecode().trim(start|end, "")去掉頭尾的雙引號。

Enjoy yourself !

如果能幫到您,請點贊,謝謝!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容