记录一次WeakReference 的错误用法

记录一次WeakReference 的错误用法背景开发一款简单的闪光灯应用。可以通过某个键去控制闪光灯开关(公司是搞Android 设备的,系统定制,因此有权限监听键值)在闪灯灯页面有个图标用来控制闪光灯开关闪光灯应用只有一个界面,界面中会显示当

背景

开发一款简单的闪光灯应用。

  1. 可以通过某个键去控制闪光灯开关(公司是搞Android 设备的,系统定制,因此有权限监听键值)
  2. 在闪灯灯页面有个图标用来控制闪光灯开关

闪光灯应用只有一个界面,界面中会显示当前是开或者关。

image.png

image.png 但是我发现有时候界面中闪光灯状态始终是灰色。

代码

写了一个用于控制闪光灯开启的单例类,FlashLightUtil.

package com.seuic.flashlight.util;

import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.util.Log;

import com.seuic.flashlight.MyApplication;

import java.lang.ref.WeakReference;


public class FlashLightUtil {
    private volatile static FlashLightUtil instance;
    private boolean isFlashLightOn = false;

    private Context mContext;
    private WeakReference<FlashLightStatusListener> flashLightStatusListener = null;

    private FlashLightUtil(Context context) {
        mContext = context;
    }

    public static FlashLightUtil getInstance() {
        if (instance == null) {
            synchronized (FlashLightUtil.class) {
                if (null == instance) {
                    instance = new FlashLightUtil(MyApplication.mApplication);
                }
            }
        }
        return instance;
    }

    private static final String TAG = "FlashLightUtil";

    public boolean isFlashLightOn() {
        return isFlashLightOn;
    }

    public synchronized void toggleFlashLight() {
        isFlashLightOn = !isFlashLightOn;
        changeFlashLight(isFlashLightOn);
    }

    public void changeFlashLight(boolean open) {
        try {
            //获取CameraManager
            CameraManager mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
            //获取当前手机所有摄像头设备ID
            String[] ids = mCameraManager.getCameraIdList();
            for (String id : ids) {
                CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
                //查询该摄像头组件是否包含闪光灯
                Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
                Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
                if (flashAvailable != null && flashAvailable
                        && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
                    notifyChange(open);
                    //打开或关闭手电筒
                    mCameraManager.setTorchMode(id, open);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean supportFlashLight() {
        boolean isSupport = false;
        try {
            //获取CameraManager
            CameraManager mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
            //获取当前手机所有摄像头设备ID
            String[] ids = mCameraManager.getCameraIdList();
            for (String id : ids) {
                CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
                //查询该摄像头组件是否包含闪光灯
                Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
                Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
                if (flashAvailable != null && flashAvailable
                        && lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
                    //打开或关闭手电筒
                    isSupport = true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isSupport;
    }

    public void setFlashLightStatusListener(FlashLightStatusListener flashLightStatusListener) {
        this.flashLightStatusListener = new WeakReference<>(flashLightStatusListener);
    }

    private void notifyChange(boolean open) {
        if (this.flashLightStatusListener != null) {
            FlashLightStatusListener f = flashLightStatusListener.get();
            f.onStatusChange(open);
        }
    }

    public interface FlashLightStatusListener {
        void onStatusChange(boolean isOpen);
    }
}

然后一个MainActivity.kt代码如下:

package com.seuic.flashlight

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.ImageButton
import com.seuic.flashlight.service.PptService
import com.seuic.flashlight.util.FlashLightUtil

class MainActivity : AppCompatActivity() {
    companion object{
        private const val TAG = "MainActivity"
    }
    private val flashLightUtil by lazy { FlashLightUtil.getInstance() }
    private val ivControl by lazy { findViewById<ImageButton>(R.id.iv_control) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ivControl.setOnClickListener {
            flashLightUtil.toggleFlashLight()
        }
        flashLightUtil.setFlashLightStatusListener { isOpen ->
            if (isOpen) {
                ivControl.setBackgroundResource(R.drawable.ic_flash_light_open)
            } else {
                ivControl.setBackgroundResource(R.drawable.ic_flash_light_colse)
            }
        }
        startService(Intent(this.application, KeyService::class.java))
    }

    override fun onResume() {
        super.onResume()
        if (flashLightUtil.isFlashLightOn) {
            ivControl.setBackgroundResource(R.drawable.ic_flash_light_open)
        } else {
            ivControl.setBackgroundResource(R.drawable.ic_flash_light_colse)
        }
    }
}

另外还有一个监听自定义键值的服务(因为是系统应用,不需要使用前台服务保活)

class KeyService : Service(){
    companion object {
        private const val TAG = "PptService"
        private const val KEY_MONITORING = "252"
    }

    private val flashLightUtil by lazy { FlashLightUtil.getInstance() }
    private val mScanKeyService by lazy { ScanKeyService.getInstance() }
    private val mCallback: IKeyEventCallback = object : IKeyEventCallback.Stub() {
        @Throws(RemoteException::class)
        override fun onKeyDown(keyCode: Int) {
            flashLightUtil.toggleFlashLight()
        }

        @Throws(RemoteException::class)
        override fun onKeyUp(keyCode: Int) = Unit
    }

    override fun onCreate() {
        super.onCreate()
        Log.i(TAG, "onCreate: ")
        mScanKeyService.registerCallback(
            mCallback,
            KEY_MONITORING
        )
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.i(TAG, "onStartCommand: ")

        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i(TAG, "onDestroy: ")
        mScanKeyService.unregisterCallback(mCallback)
    }
}

我发现代码始终没有走到 ivControl.setBackgroundResource 这个地方。

原因

代码没走到这个地方的原因是因为setFlashLightStatusListener 传入的对象是没有强引用的,因此flashLightStatusListener.get();获取的是一个null对象。一开始之所以使用WeakReference来保存flashLightStatusListener对象是防止强引用了MainActivity的对象,影响MainActivity的回收(MainActivity退出后仍然有一个服务活在后台,持有FlashLightUtil的对象),但是实际上我传入的是一个局部对象而不是MainActivity的属性,此局部的FlashLightStatusListener对象由于没有强引用,因此就被回收了。

今天的文章记录一次WeakReference 的错误用法分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16904.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注