小菜以上一节 Android 原生集成 Flutter Module 为基础,针对不同的 Channel 进行学习尝试;且小菜通过 View / Fragment / Activity 三种原生加载方式进行测试;class _MyHomePageState extends State<MyHomePage> {
static const methodChannel = const MethodChannel('ace_demo_android_flutter');
String _result = '';
Future<Null> _getInvokeResult() async {
try {
_result = await methodChannel
.invokeMethod('ace_demo_user', {'name': '我不是老猪', 'gender': 1});
} on PlatformException catch (e) {
_result = "Failed: '${e.message}'.";
}
setState(() {});
}
void _incrementCounter() {
setState(() {
_getInvokeResult();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Text('${_result}', style: TextStyle(color: Colors.blueAccent, fontSize: 18.0))),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter, child: Icon(Icons.arrow_back)));
}
}
public class MyFlutterViewActivity extends FlutterFragmentActivity {
private static final String CHANNEL = "ace_demo_android_flutter";
private static final String TAG = "MyFlutterViewActivity";
private static final int REQUEST_CODE = 1000;
FlutterView flutterView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flutter);
DisplayMetrics outMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
int widthPixels = outMetrics.widthPixels;
int heightPixels = outMetrics.heightPixels;
flutterView = Flutter.createView(MyFlutterViewActivity.this, getLifecycle(), "/");
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(widthPixels, heightPixels);
addContentView(flutterView, layout);
new MethodChannel(flutterView, CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("ace_demo_user")) {
if (call.arguments != null) {
Log.e(TAG, "Flutter -> Android 回调内容:" + call.arguments.toString());
} else {
Log.e(TAG, "Flutter -> Android 回调参数为空!");
}
result.success("Android -> Flutter 接收回调后返回值:" + TAG);
Intent intent = new Intent();
intent.putExtra("data", call.arguments!=null?call.arguments.toString():"");
setResult(REQUEST_CODE, intent);
MyFlutterViewActivity.this.finish();
} else {
result.notImplemented();
}
}
});
}
}
public class MyFlutterFragment extends FlutterFragment {
private static final String CHANNEL = "ace_demo_android_flutter";
private static final String TAG = "MyFlutterFragment";
@SuppressWarnings("unchecked")
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
new MethodChannel((FlutterView) getView(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, final MethodChannel.Result result) {
if (call.method.equals("ace_demo_user")) {
if (call.arguments != null) {
Log.e(TAG, "Flutter -> Android 回调内容:" + call.arguments.toString());
} else {
Log.e(TAG, "Flutter -> Android 回调参数为空!");
}
result.success("Android -> Flutter 接收回调后返回值:" + TAG);
Toast.makeText(getActivity(), (call.arguments != null) ? "回调内容为:" + call.arguments.toString() : "回调参数为空!", Toast.LENGTH_SHORT).show();
} else {
result.notImplemented();
}
}
});
}
}
public class MyFlutterActivity extends FlutterActivity {
private static final String CHANNEL = "ace_demo_android_flutter";
private static final String TAG = "MyFlutterActivity";
private static final int REQUEST_CODE = 1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("ace_demo_user")) {
if (call.arguments != null) {
Log.e(TAG, "Flutter -> Android 回调内容:" + call.arguments.toString());
} else {
Log.e(TAG, "Flutter -> Android 回调参数为空!");
}
result.success("Android -> Flutter 接收回调后返回值:" + TAG);
Intent intent = new Intent();
intent.putExtra("data", call.arguments!=null?call.arguments.toString():"");
setResult(REQUEST_CODE, intent);
MyFlutterActivity.this.finish();
} else {
result.notImplemented();
}
}
});
}
}
我们分析 FlutterFragment 和 FlutterActivity 时会发现,依旧是一层层封装的 FlutterView;
小菜测试 onMethodCall 中若有与 Flutter 中传递的相同 method name 时可以尝试获取传递参数;若此时需要向 Flutter 返回传递参数可以通过 result.success() 方法进行数据传递,若无需传递则可不设置当前方法;
小菜理解,MethodChannel 主要是由 Flutter 主动向 Android 原生发起交互请求,小菜理解相对于于原生为被动式交互较多;
new EventChannel(flutterView, CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, final EventChannel.EventSink events) {
events.success("我来自 " + TAG +" !! 使用的是 EventChannel 方式");
}
@Override
public void onCancel(Object arguments) {
}
});
Flutter 端通过 receiveBroadcastStream 进行数据流监听;分析源码得知,其内部同样是通过 invokeMethod 方法进行发送;listen 方法中,onData 为必须参数用作收到 Android 端发送数据的回调;onError 为数据接收失败回调;onDone 为接收数据结束回调;
StreamSubscription<T> listen(void onData(T event),
{Function onError, void onDone(), bool cancelOnError});
class _MyHomePageState extends State<MyHomePage> {
static const eventChannel = const EventChannel('ace_demo_android_flutter');
String _result = '';
StreamSubscription _streamSubscription;
@override
void initState() {
super.initState();
_getEventResult();
}
@override
void dispose() {
super.dispose();
if (_streamSubscription != null) {
_streamSubscription.cancel();
}
}
_getEventResult() async {
try {
_streamSubscription =
eventChannel.receiveBroadcastStream().listen((data) {
setState(() {
_result = data;
});
});
} on PlatformException catch (e) {
setState(() {
_result = "event get data err: '${e.message}'.";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Text('${_result}',
style: TextStyle(color: Colors.blueAccent, fontSize: 18.0))),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter, child: Icon(Icons.arrow_back)));
}
}
// Flutter 端
static const basicChannel = BasicMessageChannel<String>('ace_demo_android_flutter', StringCodec());
@override
void initState() {
super.initState();
_getBasicResult();
}
_getBasicResult() async {
final String reply = await basicChannel.send('ace_demo_user');
setState(() {
_result = reply;
});
}
// Android 端
final BasicMessageChannel channel = new BasicMessageChannel<String> (flutterView, CHANNEL, StringCodec.INSTANCE);
channel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
@Override
public void onMessage(Object o, BasicMessageChannel.Reply reply) {
reply.reply("我来自 " + TAG +" !! 使用的是 BasicMessageChannel 方式");
}
});
public void send(T message) {
this.send(message, (BasicMessageChannel.Reply)null);
}
public void send(T message, BasicMessageChannel.Reply<T> callback) {
this.messenger.send(this.name, this.codec.encodeMessage(message), callback == null ? null : new BasicMessageChannel.IncomingReplyHandler(callback));
}
分析源码 send 有两个构造函数,有两个参数的构造方法用来接收 Flutter 回调的数据;
// Flutter 端
static const basicChannel = BasicMessageChannel<String>('ace_demo_android_flutter', StringCodec());
@override
void initState() {
super.initState();
_getBasicResult();
}
_getBasicResult() async {
final String reply = await
channel.setMessageHandler((String message) async {
print('Flutter Received: ${message}');
setState(() {
_result = message;
});
return "{'name': '我不是老猪', 'gender': 1}";
});
}
// Android 端
channel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
@Override
public void onMessage(Object o, BasicMessageChannel.Reply reply) {
reply.reply("我来自 " + TAG +" !! 使用的是 BasicMessageChannel 方式");
channel.send("ace_demo_user");
//channel.send("ace_demo_user", new BasicMessageChannel.Reply() {
// @Override
// public void reply(Object o) {
// Intent intent = new Intent();
// intent.putExtra("data", o!=null?o.toString():"");
// setResult(REQUEST_CODE, intent);
// MyFlutterViewActivity.this.finish();
// }
//});
}
});
小菜发现在 Application 中需要使用 FlutterApplication,FlutterApplication 的作用就是通过调用 FlutterMain 的 startInitialization 方法进行初始化;
import io.flutter.app.FlutterApplication;
public class MyApplication extends FlutterApplication {
}
来源:阿策小和尚 欢迎闲来吐槽~
这可能是个愚蠢的问题。但是,我是一个新手......你怎么能在交互式rubyshell中有多行代码?好像你只能有一条长线。按回车键运行代码。无论如何我可以在不运行代码的情况下跳到下一行吗?再次抱歉,如果这是一个愚蠢的问题。谢谢。 最佳答案 这是一个例子:2.1.2:053>a=1=>12.1.2:054>b=2=>22.1.2:055>a+b=>32.1.2:056>ifa>b#Thecode‘if..."startsthedefinitionoftheconditionalstatement.2.1.2:057?>puts"f
最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路
目录SpringBootStarter是什么?以前传统的做法使用SpringBootStarter之后starter的理念:starter的实现: 创建SpringBootStarter步骤在idea新建一个starter项目、直接执行下一步即可生成项目。 在xml中加入如下配置文件:创建proterties类来保存配置信息创建业务类:创建AutoConfiguration测试如下:SpringBootStarter是什么? SpringBootStarter是在SpringBoot组件中被提出来的一种概念、简化了很多烦琐的配置、通过引入各种SpringBootStarter包可以快速搭建出一
我最近正在进行Rails5升级,当我尝试启动Rails控制台时遇到了这个错误:/actionpack-5.0.0/lib/action_controller/test_case.rb:49:ininitialize':wrongnumberofarguments(0for2)(ArgumentError)当前bundleupdaterails已经完成了gem依赖项的解决,足以更新到5.0.0,rspec正在运行(尽管我正在修复很多中断)。我也可以运行railss没有错误。这里是代码中断行:https://github.com/rails/rails/blob/master/action
一、环境变量右键点击我的电脑-属性:然后找到环境变量 1.Android的SDK不在C盘的话需要额外配这个到用户环境变量:ANDROID_HOMED:\AndroidSDK2.然后在系统变量:Path中添加一条这样的值 D:\Flutter\flutter\bin 这个值写flutter包解压的实际地址即可 3.在系统变量中添加两个镜像变量: 变量名:FLUTTER_STORAGE_BASE_URL 变量值:https://storage.flutter-io.cn 变量名:PUB_HOSTED_URL 变量
据我了解,在Ruby和Perl之间没有“桥梁”可以让您直接从Ruby调用Perl函数。据我了解,要从Ruby调用Perl程序,只需将其放在反引号中(即result=`./helloWorld.pl`)。但是,这不允许与Perl程序交互(即您不能与提示交互或提供输入)。我的问题如下:有没有什么方法可以从Ruby向Perl程序提供输入(除了参数)?Ruby和Perl之间没有桥梁,我错了吗?在导航提示时与程序的标准输入交互似乎是错误的方式,我正在处理的程序设计良好,并且具有包含适当Perl函数的库。 最佳答案 有Inline::Ruby模
2022年伊始,默安科技联合数世咨询举办以“软件供应链安全的时与势”为主题的访谈活动,由数世咨询创始人李少鹏主持,邀请贝壳安全研发负责人李文鹏、北京邮电大学副教授张文博、默安科技副总裁沈锡镛三位行业大咖做客网安小酒馆,从产业、企业、学术的不同维度,共同探讨软件供应链安全建设的新思路,为业界呈现了一场开年网安盛宴。随着全球软件供应链安全事件频发,软件供应链安全逐渐成为业界关注焦点,也成为影响国家重要信息系统安全与关键信息基础设施安全的重要因素,以及网络安全保障体系和能力建设的重要环节。嘉宾们围绕软件供应链安全发展的主要驱动力、关基行业中的实施现状和落地难点、产学研成果转化、软件供应链安全的重要性
在我采用Ruby语言的过程中,我花了很多时间在IRb中。太棒了!但是,由于我不是很清楚它的功能,并且对Ruby仍然是个“笨蛋”,所以我想知道以下内容:如何在不重新启动IRb的情况下“刷新”session(或者这是不可能的)。如何配置IRb加载一堆源文件“hello.rb”和“hello_objects.rb”,即在启动时?我在这些方面投入了大量工作,如果知道加载这些类的速记,而无需再次为每个类手动键入“加载”,那就太好了。 最佳答案 我不确定是否可以“刷新”session。但是,您可以像这样加载您的类:irb-r'hello.rb'
我在/usr/local/lib中安装了一些本地库。我现在正在尝试安装一个需要这些的gem,以便正确构建,但是gem构建失败,因为它找不到图书馆。gem的extconf.rb文件试图确认它可以找到库have_library()但由于某种原因失败了。我尝试设置一堆环境变量,但似乎没有任何效果:irb(main):003:0>require'mkmf'=>trueirb(main):004:0>have_library('gecodesearch')checkingformain()in-lgecodesearch...no=>falseirb(main):005:0>ENV['LD_LI
Lisp是否适合Web编程/应用程序(交互式),就像ruby和php一样?需要考虑的事情是:易于使用可部署性难度(尤其是对于编程初学者而言)(编辑)在阅读PaulGraham'sessay之后,我特别提到了CommonLisp.将是我的第一门编程语言。在这方面。这样做合适吗?我听说Clojure的宏功能不如CommonLisp的强大,这就是我尝试学习Clojure的原因。它教授编程并且非常强大。 最佳答案 Lisp是一个语系,而不是单一的语言。为了稍微回答您的问题,是的,存在用于各种Lisp方言的Web框架,例如用于Common