向Android发送数据

前面一篇已经介绍了Android主动向Flutter发送数据请求的流程,这一篇主页看看Flutter向Android发送数据的流程。有了前面的基础,反过来基本区别不大。

先看一下Flutter端的Demo, 创建了一个MethodChannel,然后调用了invokeMethod方法, 唯一的区别在于Flutter端的方法调用没有回调。但是有返回值,Android端MethodChannel 实际是包装了一下返回结果。

const platform = const MethodChannel('com.test.native/logger'); 
platform.invokeMethod("d", {"tag": "MyApp", "log": "build"});

类图

先看一下Dart中和消息相关的类图,整体结构和前面Java的差不多。 只是BinaryMessenger的Callback中缺少了一个Reply参数, 这是因为Dart中send是有返回值的 (应该得益于async/await机制)。DefaultBinaryMessenger扮演了Java中DartMessenger的角色。

继续阅读

Platform Channel Demo

Flutter是使用Dart语言开发,使用PUB管理package包,目前已经有很多可用的功能包,但是很多情况还是需要和Native代码进行交互,调用一些平台特偶的功能。Flutter提供了platform-specific API来和Native进行交互。

这张是官网上的图,描述了Flutter APP和Host平台之间是通过MethodChannel进行通信的。

继续阅读

一 树莓派监控推流

前面已经搭建好了流媒体服务器,并且利用OBS成功的进行了推流,VLC拉流成功看到了直播。但是对于树莓派来说没有OBS。所以只能使用其他的方式。

间接推流

第一篇文章中我们使用VLC成功的把树莓派监控的视频传送到指定端口,然后PC上也使用VLC进行播放。所以第一个想到的最简单的办法就是PC从树莓派获取视频,然后通过PC推送到服务器。

  1. 树莓派捕获视频
sudo raspivid -o - -t 0 -w 640 -h 360 -fps 25 | cvlc -vvv stream:///dev/stdin --sout '#standard{access=http,mux=ts,dst=:8090}' :demux=h264

这是第一片文章的方法,树莓派使用raspivid捕获视频,然后利用cvlc发送到指定的端口。

  1. PC端推流

    前面是通过VLC进行播放,我们知道VLC本身也可以推流,所以先尝试使用VLC推送RTMP到服务器。

继续阅读

一 网络直播

对于视频监控来说,以前主要是局域网监控,随着网络技术发展,网络摄像头出现,使得监控从局域网大量的扩展到了互联网上。买一个网络摄像头,进行简单的设置就可以随时随地使用手机来查看家里的情况。 这个和互联网直播其实也是一个道理,以前的直播主要是在PC端,而现在也是扩展到了手机端。所以监控和直播本质上是一样的,都是视频采集、推送到服务端、客户端从服务端获取视频播放的过程。所以在使用树莓派搭建监控的时候,正好可以学习了解一下直播相关的一些知识。

上图显示了一个直播平台大致的样子,主要有几个步骤:
1. 音视频采集、编码、封装
2. 视频流传送到服务器(推流)
3. 服务器对视频流进行相应的处理,比如转码、存储等,也可以进行一些高级的处理。
4. 服务器把视频流进行分片,然后发送到CDN
5. 客户端从CDN拉取视频流进行播放

继续阅读

一 摄像头

最近想监控一下家里的猫在我上班时候干什么,本来打算买个小米或360的监控摄像头,价格也不贵。 想到树莓派吃灰了一段时间了,就想利用树莓派来做监控。树莓派官方发布了2款CSI接口的摄像头,当然树莓派有USB接口也可以使用USB摄像头。

树莓派摄像头

以上是官方摄像头的2个版本对比。型号是OV5647和IMX219,参考: Camera Module

继续阅读

前面已经分析完了Flutter程序初始化的的过程,当FlutterView创建完成之后,Engine已经准备好了。这个时候FlutterView被设置到Activity上,并且增加了对应的LaunchView。然后偶FlutterActivityDelegate会开始执行Flutter程序。

 public void onCreate(Bundle savedInstanceState) {
    
        String[] args = getArgsFromIntent(activity.getIntent());
        FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);

        flutterView = viewFactory.createFlutterView(activity);
        if (flutterView == null) {
            FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
            flutterView = new FlutterView(activity, null, nativeView);
            flutterView.setLayoutParams(matchParent);
            activity.setContentView(flutterView);
            launchView = createLaunchView();
            if (launchView != null) {
                addLaunchView();
            }
        }

        if (loadIntent(activity.getIntent())) {
            return;
        }

        String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
        if (appBundlePath != null) {
            runBundle(appBundlePath);
        }
    }

前面我们已经分析过了runBundle方法在Java层的实现,最终调用了FlutterJNI的方法:

继续阅读

前面三篇文章我们通过Flutter Demo程序,从程序的初始化开始,到MainActivity的初始化(FlutterActivity),然后到FlutterView的初始化, 最后到FlutterView运行一个Flutter Bundle。整个过程中我们都是关注在Java层的代码。 但是通过FlutterJNI类我们知道,很多东西都是在C++层也就是Flutter Engine中实现的, 也就是Flutter.jar中的flutter.so文件。 所以这一篇从FlutterEngine的角度来看看初始化。

一 准备Flutter Engine源码

前面介绍Flutter.jar的时候有简单介绍一下下载Flutter Engine的源码。 源码地址:https://github.com/flutter/engine。建议使用git命令直接下载master分支

git clone https://github.com/flutter/engine.git

然后根据你Flutter SDK的版本来切换到对应的commit

➜  io git:(3757390fa) ✗ flutter --version
Flutter 1.2.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 8661d8aecd (7 weeks ago) • 2019-02-14 19:19:53 -0800
Engine • revision 3757390fa4
Tools • Dart 2.1.2 (build 2.1.2-dev.0.0 0a7dcf17eb)

切换到SDK Engine版本对应的源码,上面就是Flutter SDK 1.2.1对应的Engine的commit ID

flutter_engine git:(master) ✗ git checkout 3757390fa4
HEAD is now at 3757390fa Roll src/third_party/dart ecd7a88606..0a7dcf17eb (4 commits)

继续阅读

前面一篇介绍了承载Flutter的Activity的初始化过程,通过分析FlutterActivityDelegate我们知道了FlutterView是真正运行Flutter App的地方,所以这一篇文章主要来看一看FlutterView的功能。

 

 

一 FlutterView 类结构

 

先看一下FlutterView的UML图。

  • 继承SurfaceView,它的内部有两个线程即主线程和渲染线程,使用渲染线程中向屏幕上绘图可以避免主线程阻塞,从而提高了程序的反应速度。使用了双缓冲机制。
  • 实现BinaryMesaenger接口, 这个接口定义了Android代码和Flutter代码直接通信的方式,此接口还有2个内部接口。 FlutterView实现了这个接口,拥有了和flutter通信的的能力
  • 实现TextureRegistry接口,这个接口主要是创建一个SurfaceTexture对象,使用SurfaceTextureEntry包装起来,拥有唯一的ID。
  • 实现AccessibilityStateChangeListener接口,主要用来监听系统accessibility 状态变化的。

继续阅读

前面一篇介绍了Flutter Android App在进程启动时做的初始化,这一篇主要看一下Flutter的页面显示需要做的初始化。 在我们的Demo中只有一个MainActivity页面。

 

一 MainActivity

 

先看一下这个主页的声明

   <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- This keeps the window background of the activity showing
                 until Flutter renders its first frame. It can be removed if
                 there is no splash screen (such as the default splash screen
                 defined in @style/LaunchTheme). -->
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

没有什么特别,只是声明了一个meta-data,指定了显示一个splash画面,看一下这个页面的具体实现

public class MainActivity extends FlutterActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
  }
}

我们的主页继承与FlutterActivity,只在onCreate中调用了下面的方法

继续阅读