提交 fb96993f authored 作者: tomgui's avatar tomgui 提交者: kongdywang

fix memory leak & fix pip play error

上级 8aa805c1
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.tencent.vod.flutterr">
package="com.tencent.vod.flutter">
<!-- android:documentLaunchMode="intoExisting"-->
<!-- android:excludeFromRecents="true"-->
<application>
<activity
android:name="com.tencent.vod.flutter.ui.FlutterPipImplActivity"
android:theme="@style/Theme"
android:supportsPictureInPicture="true"
android:screenOrientation="portrait"
android:documentLaunchMode="intoExisting"
android:excludeFromRecents="true"
android:exported="true"
android:resizeableActivity="true"
......
......@@ -163,6 +163,7 @@ public class FTXDownloadManager implements MethodChannel.MethodCallHandler, ITXV
public void destroy() {
mMethodChannel.setMethodCallHandler(null);
mEventChannel.setStreamHandler(null);
TXVodDownloadManager.getInstance().setListener(null);
}
private Map<String,Object> buildMapFromDownloadMediaInfo(TXVodDownloadMediaInfo mediaInfo) {
......
......@@ -32,7 +32,7 @@ import com.tencent.vod.flutter.FTXEvent;
import com.tencent.vod.flutter.FTXPIPManager.PipParams;
import com.tencent.vod.flutter.model.PipResult;
import com.tencent.vod.flutter.model.VideoModel;
import com.tencent.vod.flutterr.R;
import com.tencent.vod.flutter.R;
import io.flutter.embedding.android.FlutterActivity;
public class FlutterPipImplActivity extends FlutterActivity implements Callback, ITXVodPlayListener,
......@@ -319,6 +319,9 @@ public class FlutterPipImplActivity extends FlutterActivity implements Callback,
@Override
protected void onStop() {
super.onStop();
unRegisterPipBroadcast();
mVodPlayer.stopPlay(true);
mLivePlayer.stopPlay(true);
mIsNeedToStop = true;
}
......@@ -331,9 +334,6 @@ public class FlutterPipImplActivity extends FlutterActivity implements Callback,
@Override
protected void onDestroy() {
super.onDestroy();
unRegisterPipBroadcast();
mVodPlayer.stopPlay(true);
mLivePlayer.stopPlay(true);
}
private void attachSurface(Surface surface) {
......
// Copyright (c) 2022 Tencent. All rights reserved.
package com.example.super_player_example;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugin.common.EventChannel;
/**
* 画中画模式activity父类,使用画中画模式,需要将自己项目中的activity修改为继承该类
*/
public class FTXFlutterPipActivity extends FlutterActivity {
private static final String PIP_CHANNEL_NAME = "cloud.tencent.com/playerPlugin/pipEvent";
private static final int EVENT_PIP_MODE_ALREADY_ENTER = 1;
private static final int EVENT_PIP_MODE_ALREADY_EXIT = 2;
private static final int EVENT_PIP_MODE_REQUEST_START = 3;
private static final int EVENT_PIP_MODE_UI_STATE_CHANGED = 4;
private EventChannel mPipEventChannel;
private EventChannel.EventSink mEventSink;
/**
* 这里使用needToExitPip作为标志位,在出现onPictureInPictureModeChanged回调画中画状态和isInPictureInPictureMode不一致的时候。
* 标志位true,然后在onConfigurationChanged监听到界面宽高发生变化的时候,进行画中画模式退出的事件通知。
* for MIUI 12.5.1
*/
private boolean needToExitPip = false;
private int configWidth = 0;
private int configHeight = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initPipEventChannel();
}
private void initPipEventChannel() {
if (null == mPipEventChannel && null != getFlutterEngine()) {
mPipEventChannel = new EventChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(),
PIP_CHANNEL_NAME);
mPipEventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
mEventSink = events;
}
@Override
public void onCancel(Object arguments) {
}
});
}
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
boolean isInPictureInPictureMode = isInPictureInPictureMode();
if (isInPictureInPictureMode) {
configWidth = newConfig.screenWidthDp;
configHeight = newConfig.screenHeightDp;
} else if (needToExitPip && configWidth != newConfig.screenWidthDp && configHeight != newConfig.screenHeightDp) {
if (null != mEventSink) {
mEventSink.success(getParams(EVENT_PIP_MODE_ALREADY_EXIT, null));
}
needToExitPip = false;
}
}
}
/**
* 为了兼容MIUI 12.5,PIP模式下,打开其他app然后上滑退出,再点击画中画窗口,onPictureInPictureModeChanged会异常回调关闭的情况
*
* @param ignore 校对画中画状态
*/
@Override
public void onPictureInPictureModeChanged(boolean ignore) {
boolean isInPictureInPictureMode = ignore;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
isInPictureInPictureMode = isInPictureInPictureMode();
}
if (isInPictureInPictureMode != ignore) {
needToExitPip = true;
} else {
if (null != mEventSink) {
if (isInPictureInPictureMode) {
mEventSink.success(getParams(EVENT_PIP_MODE_ALREADY_ENTER, null));
} else {
mEventSink.success(getParams(EVENT_PIP_MODE_ALREADY_EXIT, null));
}
}
}
super.onPictureInPictureModeChanged(isInPictureInPictureMode);
}
@Override
public void onPictureInPictureUiStateChanged(@NonNull PictureInPictureUiState pipState) {
super.onPictureInPictureUiStateChanged(pipState);
if (null != mEventSink) {
mEventSink.success(getParams(EVENT_PIP_MODE_UI_STATE_CHANGED, null));
}
}
/**
* enterPictureInPictureMode生效后的回调通知,only for android > 31
*/
@Override
public boolean onPictureInPictureRequested() {
return super.onPictureInPictureRequested();
}
@Override
public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
boolean enterResult = super.enterPictureInPictureMode(params);
if (enterResult && null != mEventSink) {
mEventSink.success(getParams(EVENT_PIP_MODE_REQUEST_START, null));
}
return enterResult;
}
private Map<String, Object> getParams(int event, Bundle bundle) {
Map<String, Object> param = new HashMap<>();
if (event != 0) {
param.put("event", event);
}
if (bundle != null && !bundle.isEmpty()) {
Set<String> keySet = bundle.keySet();
for (String key : keySet) {
Object val = bundle.get(key);
param.put(key, val);
}
}
return param;
}
}
......@@ -5,7 +5,6 @@ class ShortVideoPageWidget extends StatefulWidget {
String videoUrl;
String coverUrl;
int position;
late TXVodPlayerController _controller;
ShortVideoPageWidget(
{required this.position,
......@@ -30,6 +29,15 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
late StreamSubscription _streamSubscriptionApplicationPause;
StreamSubscription? _playEventSubscription;
TXVodPlayerController _controller;
_TXVodPlayerPageState() : _controller = TXVodPlayerController(){
_txPlayerVideo = new TXPlayerVideo(controller: _controller);
}
@override
void initState() {
super.initState();
......@@ -37,11 +45,9 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
}
_init() async {
widget._controller = new TXVodPlayerController();
_txPlayerVideo = new TXPlayerVideo(controller: widget._controller);
await widget._controller.initialize();
widget._controller.setConfig(FTXVodPlayConfig());
LogUtils.i(TAG, " [init] ${widget.position.toString()} ${this.hashCode.toString()} ${widget._controller.hashCode.toString()}");
await _controller?.initialize();
_controller.setConfig(FTXVodPlayConfig());
LogUtils.i(TAG, " [init] ${widget.position.toString()} ${this.hashCode.toString()} ${_controller.hashCode.toString()}");
_setPlayerListener();
_setEventBusListener();
if (widget.position == 0) {
......@@ -64,9 +70,10 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
_streamSubscriptionStopAndPlay.cancel();
_streamSubscriptionApplicationResume.cancel();
_streamSubscriptionApplicationPause.cancel();
_playEventSubscription?.cancel();
await _stop();
widget._controller.dispose();
LogUtils.i(TAG, " [dispose] ${widget.position.toString()} ${this.hashCode.toString()} ${widget._controller.hashCode.toString()}");
_controller.dispose();
LogUtils.i(TAG, " [dispose] ${widget.position.toString()} ${this.hashCode.toString()} ${_controller.hashCode.toString()}");
}
Widget _getTXVodPlayerMainPage() {
......@@ -95,15 +102,15 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
}
_onTapPageView() {
widget._controller.isPlaying().then((value) {
value == true ? _pause() :_resume();
_controller.isPlaying().then((value) {
value ? _pause() :_resume();
});
LogUtils.i(TAG, "tap ${_isVideoPlaying.toString()}");
}
Widget _getSeekBarView() {
return Positioned(
child: VideoSliderView(widget._controller, _progressSliderKey),
child: VideoSliderView(_controller, _progressSliderKey),
bottom: 20,
right: 0,
left: 0,
......@@ -137,24 +144,24 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
)));
}
_pause() {
_pause() async{
LogUtils.i(TAG, "[_pause]");
widget._controller.pause();
await _controller.pause();
setState(() {
_isVideoPlaying = false;
});
}
_resume() {
_resume() async{
LogUtils.i(TAG, "[_resume]");
widget._controller.resume();
await _controller.resume();
setState(() {
_isVideoPlaying = true;
});
}
_stopLastAndPlayCurrent(StopAndResumeEvent event) {
LogUtils.i(TAG, " [received at not current outside] ${widget.position.toString()} ${this.hashCode.toString()} ${widget.hashCode.toString()} ${widget._controller.hashCode.toString()}");
LogUtils.i(TAG, " [received at not current outside] ${widget.position.toString()} ${this.hashCode.toString()} ${widget.hashCode.toString()} ${_controller.hashCode.toString()}");
event.index != widget.position ? _stop() :_startPlay();
}
......@@ -163,7 +170,7 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
LogUtils.i(TAG, " [stop] ${widget.position.toString()} ${widget.hashCode.toString()}");
_isVideoPrepared = false;
_isVideoPlaying = true;
widget._controller.stop();
_controller.stop();
}
......@@ -173,8 +180,8 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
setState(() {
_isVideoPlaying = true;
});
await widget._controller.setLoop(true);
widget._controller.startVodPlay(widget.videoUrl);
await _controller.setLoop(true);
_controller.startVodPlay(widget.videoUrl);
}
_hideCover() {
......@@ -200,7 +207,7 @@ class _TXVodPlayerPageState extends State<ShortVideoPageWidget> {
}
_setPlayerListener() {
widget._controller.onPlayerEventBroadcast.listen((event) async {
_playEventSubscription = _controller.onPlayerEventBroadcast.listen((event) async {
if (event["event"] == TXVodPlayEvent.PLAY_EVT_PLAY_PROGRESS) {
if (!mounted) return;
double currentProgress = event["EVT_PLAY_PROGRESS"].toDouble();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论