update ui
This commit is contained in:
parent
96b3b6a3f9
commit
2137f4b3f2
@ -51,7 +51,7 @@ MediaProjectionManager -> MediaProjection
|
|||||||
...
|
...
|
||||||
android:foregroundServiceType="mediaProjection"/>
|
android:foregroundServiceType="mediaProjection"/>
|
||||||
```
|
```
|
||||||
- API大于O(26)时需要startForegroundService,且需要正确设置通知栏,
|
- API大于O(26/Android8.0)时需要startForegroundService,且需要正确设置通知栏,
|
||||||
新特性中使用ForegroundService不会被系统杀掉
|
新特性中使用ForegroundService不会被系统杀掉
|
||||||
|
|
||||||
|
|
||||||
@ -247,6 +247,48 @@ Config::set_option("stop_service","")
|
|||||||
首次登录不显示id密码
|
首次登录不显示id密码
|
||||||
安卓前后分离的问题 通过IPC或者广播解耦
|
安卓前后分离的问题 通过IPC或者广播解耦
|
||||||
|
|
||||||
|
### 关于安卓的service和进程
|
||||||
|
实际测试 安卓7和安卓11表现不同 同一个apk下若有多个activity或service
|
||||||
|
安卓7 关闭activity后所有的服务都会强制关闭 可能是锤子手机特有
|
||||||
|
|
||||||
|
安卓8.1 和7类似 且安卓8.1和7录屏权限比较宽松 只需要获取一次 不需要每次播放都要获取录屏权限
|
||||||
|
|
||||||
|
*安卓7/8.1关闭activity 后就关闭service 可能是锤子OS的特质
|
||||||
|
|
||||||
|
理论上 非bind启动的service可以脱离activity运行 就像三星安卓11上测试的情况
|
||||||
|
|
||||||
|
安卓11 关闭activity后service可以单独运行 可能由于前台应用可以持续维持
|
||||||
|
再次进入程序新的activity会共用之前在内存中的so程序
|
||||||
|
|
||||||
|
安卓Service运行在主线程!才能实现脱离activity独立运行
|
||||||
|
|
||||||
|
>只有在内存过低且必须回收系统资源以供拥有用户焦点的 Activity 使用时,Android 系统才会停止服务。如果将服务绑定到拥有用户焦点的 Activity,则它其不太可能会终止;如果将服务声明为在前台运行,则其几乎永远不会终止。如果服务已启动并长时间运行,则系统逐渐降低其在后台任务列表中的位置,而服务被终止的概率也会大幅提升—如果服务是启动服务,则您必须将其设计为能够妥善处理系统执行的重启。如果系统终止服务,则其会在资源可用时立即重启服务,但这还取决于您从 onStartCommand() 返回的值。
|
||||||
|
|
||||||
|
>如服务文档中所述,您可以创建同时具有已启动和已绑定两种状态的服务。换言之,您可以通过调用 startService() 来启动服务,让服务无限期运行,您也可以通过调用 bindService() 让客户端绑定到该服务。
|
||||||
|
如果您确实允许服务同时具有已启动和已绑定状态,那么服务启动后,系统不会在所有客户端均与服务取消绑定后销毁服务,而必须由您通过调用 stopSelf() 或 stopService() 显式停止服务。
|
||||||
|
尽管您通常应实现 onBind() 或 onStartCommand(),但有时也需要同时实现这两种方法。例如,音乐播放器可能认为,让其服务无限期运行并同时提供绑定很有用处。如此一来,Activity 便可启动服务来播放音乐,并且即使用户离开应用,音乐播放也不会停止。然后,当用户返回应用时,Activity 便能绑定到服务,重新获得播放控制权。
|
||||||
|
|
||||||
|
onRebind()
|
||||||
|
|
||||||
|
[进程和应用生命周期](https://developer.android.com/guide/components/activities/process-lifecycle?hl=zh-cn)
|
||||||
|
|
||||||
|
[进程和线程概览](https://developer.android.com/guide/components/processes-and-threads?hl=zh-cn)
|
||||||
|
|
||||||
|
[绑定服务概览](https://developer.android.com/guide/components/bound-services?hl=zh-cn)
|
||||||
|
|
||||||
|
Service持久化与绑定具体操作 [已测试安卓7.1以上系统特性相同]
|
||||||
|
1.前台服务 service中调用startForeground,启用持久化Service需要保证至少一次通过startForegroundService/startService 启动了Service且在Service中主动startForeground,可以通过intent传参指定一次init操作,在init的过程中startForeground,最关键的操作就是startForeground
|
||||||
|
即 通过startService 调用过的Service并且Service中调用过startForeground的Service就是持久化的前台服务,服务不会被系统kill
|
||||||
|
2.通过使用bindService将Activity与Service绑定 使用unbindService在onDestroy中解绑,如果不解绑会造成对Service的引用泄漏引发错误。可以在Activity中的onCreate中进行绑定,也可以根据需求按需手动进行绑定,bindService startService的先后顺序无所谓
|
||||||
|
*只要注意至少一次对Service调用过startForegroundService/startService*
|
||||||
|
|
||||||
|
关于startForegroundService
|
||||||
|
https://developer.android.com/about/versions/oreo/background?hl=zh-cn#services
|
||||||
|
尽量使用startForegroundService传递start命令 注意首次使用的时候需要在5秒内在服务中主动调用startService
|
||||||
|
|
||||||
|
改成bindService逻辑
|
||||||
|
直接在activity onCreate时候进行绑定 onDestroy时解绑(注意判空),绑定的时候进行一些判断。如果已存在服务会话则恢复之前的情况。如果不存在不服务会话则等待需要的时候再启动前台服务
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
### 其他
|
### 其他
|
||||||
|
@ -219,10 +219,10 @@ toAndroidChannelInit() {
|
|||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case "try_start_without_auth":
|
case "try_start_without_auth":
|
||||||
{
|
{
|
||||||
var peerID = call.arguments["peerID"] as String;
|
// 可以不需要传递 通过FFI直接去获取 serverModel里面直接封装一个update通过FFI从rust端获取
|
||||||
var name = call.arguments["name"] as String;
|
ServerPage.serverModel.updateClientState();
|
||||||
ServerPage.serverModel.setPeer(false, name: name, id: peerID);
|
debugPrint("pre show loginAlert:${ServerPage.serverModel.isFileTransfer.toString()}");
|
||||||
showLoginReqAlert(nowCtx, peerID, name);
|
showLoginReqAlert(nowCtx, ServerPage.serverModel.peerID, ServerPage.serverModel.peerName);
|
||||||
debugPrint("from jvm:try_start_without_auth done");
|
debugPrint("from jvm:try_start_without_auth done");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:device_info/device_info.dart';
|
import 'package:device_info/device_info.dart';
|
||||||
|
import 'package:external_path/external_path.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
@ -518,11 +519,37 @@ class CursorModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ClientState {
|
||||||
|
bool isStart;
|
||||||
|
bool isFileTransfer;
|
||||||
|
String name;
|
||||||
|
String peerId;
|
||||||
|
|
||||||
|
ClientState({this.isStart, this.isFileTransfer, this.name, this.peerId});
|
||||||
|
|
||||||
|
ClientState.fromJson(Map<String, dynamic> json) {
|
||||||
|
isStart = json['is_start'];
|
||||||
|
isFileTransfer = json['is_file_transfer'];
|
||||||
|
name = json['name'];
|
||||||
|
peerId = json['peer_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['is_start'] = this.isStart;
|
||||||
|
data['is_file_transfer'] = this.isFileTransfer;
|
||||||
|
data['name'] = this.name;
|
||||||
|
data['peer_id'] = this.peerId;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ServerModel with ChangeNotifier {
|
class ServerModel with ChangeNotifier {
|
||||||
bool _mediaOk;
|
bool _mediaOk;
|
||||||
bool _inputOk;
|
bool _inputOk;
|
||||||
|
|
||||||
bool _peerEnabled;
|
bool _isStart;
|
||||||
|
bool _isFileTransfer;
|
||||||
String _peerName;
|
String _peerName;
|
||||||
String _peerID;
|
String _peerID;
|
||||||
|
|
||||||
@ -530,7 +557,9 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
bool get inputOk => _inputOk;
|
bool get inputOk => _inputOk;
|
||||||
|
|
||||||
bool get peerEnabled => _peerEnabled;
|
bool get isStart => _isStart;
|
||||||
|
|
||||||
|
bool get isFileTransfer => _isFileTransfer;
|
||||||
|
|
||||||
String get peerName => _peerName;
|
String get peerName => _peerName;
|
||||||
|
|
||||||
@ -539,7 +568,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
ServerModel() {
|
ServerModel() {
|
||||||
_mediaOk = false;
|
_mediaOk = false;
|
||||||
_inputOk = false;
|
_inputOk = false;
|
||||||
_peerEnabled = false;
|
_isStart = false;
|
||||||
_peerName = "";
|
_peerName = "";
|
||||||
_peerID = "";
|
_peerID = "";
|
||||||
}
|
}
|
||||||
@ -559,14 +588,28 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPeer(bool enabled, {String name = "", String id = ""}) {
|
setPeer(bool enabled, {String name = "", String id = ""}) {
|
||||||
_peerEnabled = enabled;
|
_isStart = enabled;
|
||||||
if (name != "") _peerName = name;
|
if (name != "") _peerName = name;
|
||||||
if (id != "") _peerID = id;
|
if (id != "") _peerID = id;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateClientState() {
|
||||||
|
var res = FFI.getByName("client_state");
|
||||||
|
debugPrint("getByName client_state string:$res");
|
||||||
|
try {
|
||||||
|
var clientState = ClientState.fromJson(jsonDecode(res));
|
||||||
|
_isStart = clientState.isStart;
|
||||||
|
_isFileTransfer = clientState.isFileTransfer;
|
||||||
|
_peerName = clientState.name;
|
||||||
|
_peerID = clientState.peerId;
|
||||||
|
debugPrint("updateClientState:${clientState.toJson()}");
|
||||||
|
} catch (e) {}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
clearPeer() {
|
clearPeer() {
|
||||||
_peerEnabled = false;
|
_isStart = false;
|
||||||
_peerName = "";
|
_peerName = "";
|
||||||
_peerID = "";
|
_peerID = "";
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -575,7 +618,8 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
class FFI {
|
class FFI {
|
||||||
static String id = "";
|
static String id = "";
|
||||||
static String _dir = '';
|
static String _dir = "";
|
||||||
|
static String _homeDir = "";
|
||||||
static F2 _getByName;
|
static F2 _getByName;
|
||||||
static F3 _setByName;
|
static F3 _setByName;
|
||||||
static F4 _freeRgba;
|
static F4 _freeRgba;
|
||||||
@ -742,6 +786,7 @@ class FFI {
|
|||||||
.lookupFunction<Void Function(Pointer<RgbaFrame>), F4>('free_rgba');
|
.lookupFunction<Void Function(Pointer<RgbaFrame>), F4>('free_rgba');
|
||||||
_getRgba = dylib.lookupFunction<F5, F5>('get_rgba');
|
_getRgba = dylib.lookupFunction<F5, F5>('get_rgba');
|
||||||
_dir = (await getApplicationDocumentsDirectory()).path;
|
_dir = (await getApplicationDocumentsDirectory()).path;
|
||||||
|
_homeDir = (await ExternalPath.getExternalStorageDirectories())[0];
|
||||||
String id = 'NA';
|
String id = 'NA';
|
||||||
String name = 'Flutter';
|
String name = 'Flutter';
|
||||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
@ -754,8 +799,10 @@ class FFI {
|
|||||||
name = iosInfo.utsname.machine;
|
name = iosInfo.utsname.machine;
|
||||||
id = iosInfo.identifierForVendor.hashCode.toString();
|
id = iosInfo.identifierForVendor.hashCode.toString();
|
||||||
}
|
}
|
||||||
|
debugPrint("info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir");
|
||||||
setByName('info1', id);
|
setByName('info1', id);
|
||||||
setByName('info2', name);
|
setByName('info2', name);
|
||||||
|
setByName('home_dir',_homeDir);
|
||||||
setByName('init', _dir);
|
setByName('init', _dir);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -11,7 +11,7 @@ class ServerPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// TODO: implement build
|
checkService();
|
||||||
return ChangeNotifierProvider.value(
|
return ChangeNotifierProvider.value(
|
||||||
value: serverModel,
|
value: serverModel,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
@ -55,13 +55,24 @@ class ServerPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkService() {
|
||||||
|
// 检测当前服务状态,若已存在服务则异步更新数据回来
|
||||||
|
toAndroidChannel.invokeMethod("check_service"); // jvm
|
||||||
|
ServerPage.serverModel.updateClientState();
|
||||||
|
// var state = FFI.getByName("client_state").split(":"); // rust
|
||||||
|
// var isStart = FFI.getByName("client_is_start") !="";// 使用JSON
|
||||||
|
// if(state.length == 2){
|
||||||
|
// ServerPage.serverModel.setPeer(isStart,name:state[0],id:state[1]);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
class ServerInfo extends StatefulWidget {
|
class ServerInfo extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_ServerInfoState createState() => _ServerInfoState();
|
_ServerInfoState createState() => _ServerInfoState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ServerInfoState extends State<ServerInfo> {
|
class _ServerInfoState extends State<ServerInfo> {
|
||||||
var _passwdShow = true;
|
var _passwdShow = false;
|
||||||
|
|
||||||
// TODO set ID / PASSWORD
|
// TODO set ID / PASSWORD
|
||||||
var _serverId = "";
|
var _serverId = "";
|
||||||
@ -96,7 +107,7 @@ class _ServerInfoState extends State<ServerInfo> {
|
|||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
obscureText: _passwdShow,
|
obscureText: !_passwdShow,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 25.0,
|
fontSize: 25.0,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@ -144,7 +155,7 @@ class _PermissionCheckerState extends State<PermissionChecker> {
|
|||||||
cardTitle("权限列表"),
|
cardTitle("权限列表"),
|
||||||
PermissionRow("媒体权限", serverModel.mediaOk, _toAndroidInitService),
|
PermissionRow("媒体权限", serverModel.mediaOk, _toAndroidInitService),
|
||||||
const Divider(height: 0),
|
const Divider(height: 0),
|
||||||
PermissionRow("输入权限", serverModel.inputOk, _toAndroidCheckInput),
|
PermissionRow("输入权限", serverModel.inputOk, _toAndroidInitInput),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
serverModel.mediaOk
|
serverModel.mediaOk
|
||||||
? ElevatedButton.icon(
|
? ElevatedButton.icon(
|
||||||
@ -172,7 +183,9 @@ void showLoginReqAlert(BuildContext context, String peerID, String name) {
|
|||||||
child: Text("接受"),
|
child: Text("接受"),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
FFI.setByName("login_res", "true");
|
FFI.setByName("login_res", "true");
|
||||||
|
if(!ServerPage.serverModel.isFileTransfer){
|
||||||
_toAndroidStartCapture();
|
_toAndroidStartCapture();
|
||||||
|
}
|
||||||
ServerPage.serverModel.setPeer(true);
|
ServerPage.serverModel.setPeer(true);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}),
|
}),
|
||||||
@ -224,7 +237,7 @@ class ConnectionManager extends StatelessWidget {
|
|||||||
final serverModel = Provider.of<ServerModel>(context);
|
final serverModel = Provider.of<ServerModel>(context);
|
||||||
var info =
|
var info =
|
||||||
"${serverModel.peerName != "" ? serverModel.peerName : "NA"}-${serverModel.peerID != "" ? serverModel.peerID : "NA"}";
|
"${serverModel.peerName != "" ? serverModel.peerName : "NA"}-${serverModel.peerID != "" ? serverModel.peerID : "NA"}";
|
||||||
return serverModel.peerEnabled
|
return serverModel.isStart
|
||||||
? myCard(Column(
|
? myCard(Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -239,7 +252,7 @@ class ConnectionManager extends StatelessWidget {
|
|||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
FFI.setByName("close_conn");
|
FFI.setByName("close_conn");
|
||||||
_toAndroidStopCapture();
|
// _toAndroidStopCapture();
|
||||||
serverModel.setPeer(false);
|
serverModel.setPeer(false);
|
||||||
},
|
},
|
||||||
label: Text("断开连接"))
|
label: Text("断开连接"))
|
||||||
@ -286,10 +299,10 @@ Future<Null> _toAndroidStartCapture() async {
|
|||||||
debugPrint("_toAndroidStartCapture:$res");
|
debugPrint("_toAndroidStartCapture:$res");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Null> _toAndroidStopCapture() async {
|
// Future<Null> _toAndroidStopCapture() async {
|
||||||
bool res = await toAndroidChannel.invokeMethod("stop_capture");
|
// bool res = await toAndroidChannel.invokeMethod("stop_capture");
|
||||||
debugPrint("_toAndroidStopCapture:$res");
|
// debugPrint("_toAndroidStopCapture:$res");
|
||||||
}
|
// }
|
||||||
|
|
||||||
Future<Null> _toAndroidStopService() async {
|
Future<Null> _toAndroidStopService() async {
|
||||||
FFI.setByName("stop_service");
|
FFI.setByName("stop_service");
|
||||||
@ -297,7 +310,7 @@ Future<Null> _toAndroidStopService() async {
|
|||||||
debugPrint("_toAndroidStopSer:$res");
|
debugPrint("_toAndroidStopSer:$res");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Null> _toAndroidCheckInput() async {
|
Future<Null> _toAndroidInitInput() async {
|
||||||
bool res = await toAndroidChannel.invokeMethod("check_input");
|
bool res = await toAndroidChannel.invokeMethod("init_input");
|
||||||
debugPrint("_toAndroidStopSer:$res");
|
debugPrint("_toAndroidInitInput:$res");
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
external_path:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: external_path
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -30,8 +30,9 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.3
|
cupertino_icons: ^1.0.3
|
||||||
ffi: ^1.1.2
|
ffi: ^1.1.2
|
||||||
path_provider: ^2.0.2
|
path_provider: ^2.0.2
|
||||||
|
external_path: ^1.0.1
|
||||||
provider: ^5.0.0
|
provider: ^5.0.0
|
||||||
flutter_easyloading:
|
flutter_easyloading: # not Null safety 2.2.0
|
||||||
git:
|
git:
|
||||||
url: git://github.com/open-trade/flutter_easyloading
|
url: git://github.com/open-trade/flutter_easyloading
|
||||||
#path: flutter_easyloading
|
#path: flutter_easyloading
|
||||||
|
Loading…
x
Reference in New Issue
Block a user