Android 小问题汇总(2)

<持续更新中>:更新目录

    1. 声网后台保活策略和IntentService使用回调监听,解决Android 临时后台任务问题
    1. 解决ViewPager + Fragment 数据刷新问题, 刷新无效, notifyDataSetChanged()无效

1.声网类似问题,就是直播中关于声网退到后台无法发出声音,类似声网保活问题。从而引发创建后台,startForegroundService的问题如下:

Android 8.0以上如果使用service就会报“ Context.startForegroundService() did not then call Service.startForeground()”这个错,是因为Google限制了在调用了startForegroundService方法以后,5s内一定要调用startForeground方法,但是这个其实很难杜绝,因为每台设备情况不一样,如果手机卡一点,很可能就卡过这5s了,所以我们只能尽量减少。

1.不要在fragment里启动service,建议在mainActivity里启动,并且要成对的start和stop,在onDestroy()里stop。

2.在onCreate和onStartCommand都startForeground(),并且id不要用0。

网上的几点意见都用了但是还是在华为手机,出现偶发崩溃,因为华为强制杀死后台的系统机制,让人还是挺头大的。给出我的方案,但是很多问题

1. 由于Serveice不稳定,而且开启后台杀死困难,所以选择用IntentService替代

/**
 * 后台服务,用于保持语音房,在后台服务不被回收
 */
class KeepAppLifeService : IntentService("KeepAppLifeService") {

    override fun onHandleIntent(intent: Intent?) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 注意notification也要适配Android 8 哦
            startForeground(1, getNotification())
        }
        // 静音
        FSAudioManager.getInstance()
            .sound(SPUtils.getInstance().getBoolean(SPUtils.SwitchRef.IS_OPEN_SOUND, false))

        //麦位静音处理
        val user = AudioRoomManager.getInstance().userStates
        user.entries.forEach {
            if (it.value.isShut) {
                FSAudioManager.getInstance().soundOne(it.key.toInt(), true)
            }
        }
    }

    private var notificationManager: NotificationManager? = null
    private val notificationId = "keep_app_live"
    private val notificationName = "keep_app_live_channel"

    override fun onCreate() {
        super.onCreate()
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
        //创建NotificationChannel
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                notificationId,
                notificationName,
                NotificationManager.IMPORTANCE_HIGH
            )
            //不震动
            channel.enableVibration(false)
            //静音
            channel.setSound(null, null)
            notificationManager?.createNotificationChannel(channel)
            startForeground(1, getNotification())
        }
    }

    /**
     * 获取通知(Android8.0后需要)
     */
    private fun getNotification(): Notification? {
        val builder: Notification.Builder = Notification.Builder(this)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle(getString(R.string.app_name))
            .setContentIntent(getIntent())
            .setContentText(getString(R.string.text_on_keep_live))
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder.setChannelId(notificationId)
        }
        return builder.build()
    }

    /**
     * 点击后,直接打开app(之前的页面),不跳转特定activity
     */
    @SuppressLint("UnspecifiedImmutableFlag")
    private fun getIntent(): PendingIntent? {
        val msgIntent =
            applicationContext.packageManager.getLaunchIntentForPackage(packageName) //获取启动Activity
        return PendingIntent.getActivity(
            applicationContext,
            1,
            msgIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )
    }
}

2. 启动和关闭时机

通常我们需要一个这样的Activity管理和Application回掉

/**
 * -----------
 * Activity生命周期监听管理
 * 在Application进行注册
 * 包括功能:Activity活动列表管理,退出App
 */
public class ActivityLifecycleCallback implements Application.ActivityLifecycleCallbacks

根据这个我们就可以做计数,判断在前台还是后台操作

/**
     * 根据getAppStatus,判断app状态
     */
    public void getAppStatus(Activity activity) {
        if (isForget == 0) {
            //App进入后台或者APP锁屏了
            //开启服务
            LogUtils.i("KeepAppLifeService:开启服务");
            //当前房间id
            long roomId = AudioRoomManager.getInstance().getRoomId();
            //如果当前在房间中 并且房间id != -1 开启服务
            if(roomId != -1 && AudioRoomManager.getInstance() != null && AudioRoomManager.getInstance().isInRoom()){
                if(App.getInstance() != null) {
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){
                        App.getInstance().startForegroundService(new Intent(activity, KeepAppLifeService.class));
                    }else{
                        App.getInstance().startService(new Intent(activity, KeepAppLifeService.class));
                    }
                }

            }
        } else {
            //App进入前台
            //结束服务
            //停止Service
            try {
                LogUtils.i("KeepAppLifeService:结束服务");
                Intent intentFour = new Intent(activity, KeepAppLifeService.class);
                activity.stopService(intentFour);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

2. 维护和重构老项目的时候,我们会遇到viewpager的一些bug或者业务上的bug无法刷新清除之前的缓存。结局如下

首先,你需要升级,viewPager2. 如果业务不允许。那么这么解决

重写两个方法:getItemId() getItemPosition()

实例

public class ViewPagerAdapter extends FragmentPagerAdapter {
    private final String TAG = ViewPagerAdapter.class.getSimpleName();

    private List mList;

    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    public void setData(List mList) {
        this.mList = mList;
        notifyDataSetChanged();
    }

    @Override
    public Fragment getItem(int i) {
        return mList == null ? null : mList.get(i);
    }


 
    @Override
    public int getCount() {
        return mList == null ? 0 : mList.size();
    }


 
    @Override
    public long getItemId(int position) {
        return mList.get(position).hashCode();
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        if (mList.contains(object)) {
            // 如果当前 item 未被 remove,则返回 item 的真实 position
            Logger.d(TAG, "包含");
            return mList.indexOf(object);
        } else {
            // 否则返回状态值 POSITION_NONE
            Logger.d(TAG, "不包含");
            return POSITION_NONE;
        }
    }
}

你可能感兴趣的:(Android 小问题汇总(2))