Flutter 混合開發(fā) 之 Android studio 加載Flutter module 以及通信Channel

在Android原生項目中添加flutter

官方文檔
1.創(chuàng)建flutter module,有兩種方法:
用Android studio創(chuàng)建flutter module;
用命令創(chuàng)建flutter module

cd some/path/(原生項目的根目錄)
 flutter create -t module --org com.example my_flutter

2.設(shè)置 settings.gradle 添加下列代碼

setBinding(new Binding([gradle: this]))                                 
evaluate(new File(                                                     
  settingsDir.parentFile,                                              
  'my_flutter/.android/include_flutter.groovy'   // 必須是flutter module的真實路徑
)) 
  1. 設(shè)置app build.gradle
dependencies {
  implementation project(':flutter')
}
  1. 注意事項

 //Java 版本號
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
minSdkVersion 16(最低)
  1. Android 代碼加載 flutter 界面
    Flutter提供兩種引入方法:一個是View,會默認生成一個View,一個是FlutterFragment方式。
package com.example.administrator.shadowapplication.flutter

import android.os.Bundle
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.EventChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment

/**
 * native項目加載flutter頁面
   將flutter頁面構(gòu)建成View,通過addView來顯示flutter頁面
   將flutter頁面構(gòu)建成Fragment,通過對fragment的操作來顯示flutter頁面
 */
class FlutterActivity : FragmentActivity() {
    companion object {
        private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"
        private var flutterFragment: FlutterFragment? = null
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //方法1
        /* setContentView(R.layout.activity_flutter)
         val fragmentManager: FragmentManager = supportFragmentManager
         flutterFragment = fragmentManager.findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?
         if (flutterFragment == null) {
             val newFlutterFragment = Flutter.createFragment("這里是flutter頁面")
             flutterFragment = newFlutterFragment
             fragmentManager.beginTransaction()
                     .add(R.id.flutterContent, newFlutterFragment, TAG_FLUTTER_FRAGMENT)
                     .commit()
         }*/

        //方法2
        val flutterView = Flutter.createView(this, lifecycle, "route1")
        val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
        layoutParams.topMargin = 20
        addContentView(flutterView, layoutParams)
        

    }
}
  1. flutter 代碼
import 'dart:async';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp(
      initParams: window.defaultRouteName,
    ));

class MyApp extends StatelessWidget {
  final String initParams;

  MyApp({Key key, @required this.initParams}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter 混合開發(fā)',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: _getHomePage(),
    );
  }

  Widget _getHomePage() {
    switch (initParams) {
      case "route1":
        return MyHomePage(
          title: 'flutter 混合開發(fā)',
          initParams: "hello shadow!!",
        );
        break;
    }
  }
}

class MyHomePage extends StatefulWidget {
  final String initParams;

  MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState(initParams);
}

class _MyHomePageState extends State<MyHomePage> {
  final String initParams;
  int _counter = 0;
  _MyHomePageState(this.initParams);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'initParams:$initParams',
              style: TextStyle(color: Colors.red),
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
    );
  }
}

flutter 和Android 平臺通信

官方文檔

平臺通道簡介

所謂“平臺特定”或“特定平臺”,平臺指的就是指Flutter運?的平臺,如Android或IOS,可以認為就是應(yīng)?的原?部分。 所以,平臺通道正是Flutter和原?之間通信的橋梁,它也是Flutter插件的底層基礎(chǔ)設(shè)施。
Flutter使?了?個靈活的系統(tǒng),允許您調(diào)?特定平臺的API,?論在Android上的Java或Kotlin代碼中,還是iOS上的 ObjectiveC或Swift代碼中均可?。

Flutter與原?之間的通信依賴靈活的消息傳遞?式:

應(yīng)?的Flutter部分通過平臺通道(platform channel)將消息發(fā)送到其應(yīng)?程序的所在的宿主(iOS或Android)應(yīng) ?(原?應(yīng)?)。 宿主監(jiān)聽平臺通道,并接收該消息。然后它會調(diào)?該平臺的API,并將響應(yīng)發(fā)送回客戶端,即應(yīng)?程序的Flutter部 分。
平臺通道
使?平臺通道在Flutter(client)和原?(host)之間傳遞消息


1975877-6079b324ae9d7bf7.png

圖中可以看到,Flutter 是 Client 端,Native 是 Host,Client 和 host 通信是通過 PlatformChannel,Client 通過 PlatformChannel 向 Host 發(fā)送消息,Host 監(jiān)聽 PlatformChannel 并接收消息,然后將響應(yīng)結(jié)果發(fā)送給 Client。
消息和響應(yīng)以異步方式傳遞,以確保 UI 不阻塞。另外,PlatformChannel 是雙工的,這意味著 Flutter 和 Native 可以交替做 Client 和 Host。
Flutter調(diào)用原生主要使用插件方式,通過MethodChannel的方式。
Flutter 定義了三種不同類型的 PlatformChannel,它們分別是:

  1. MethodChannel:用于傳遞方法調(diào)用,是比較常用的 PlatformChannel。
  2. EventChannel: 用于傳遞事件。
  3. BasicMessageChannel:用于傳遞數(shù)據(jù)。

BinaryMessenger
BinaryMessenger 是 PlatformChannel 與 Flutter 端的通信的工具,其通信使用的消息格式為二進制格式數(shù)據(jù),BinaryMessenger 在 Android 中是一個接口,它的實現(xiàn)類為 FlutterNativeView。

Codec
Codec 是消息編解碼器,主要用于將二進制格式的數(shù)據(jù)轉(zhuǎn)化為 Handler 能夠識別的數(shù)據(jù),F(xiàn)lutter 定義了兩種 Codec:MessageCodec 和 MethodCodec。MessageCodec 用于二進制格式數(shù)據(jù)與基礎(chǔ)數(shù)據(jù)之間的編解碼,BasicMessageChannel 所使用的編解碼器是 MessageCodec。MethodChannel 和 EventChannel 所使用的編解碼均為 MethodCodec。

Handler
Flutter 定義了三種類型的 Handler,它們與 PlatformChannel 類型一一對應(yīng),分別是 MessageHandler、MethodHandler、StreamHandler。
在使用 PlatformChannel 時,會為它注冊一個 Handler,PlatformChannel 會將該二進制數(shù)據(jù)通過 Codec 解碼為轉(zhuǎn)化為 Handler 能夠識別的數(shù)據(jù),并交給 Handler 處理。
當(dāng) Handler 處理完消息之后,會通過回調(diào)函數(shù)返回 result,將 result 通過編解碼器編碼為二進制格式數(shù)據(jù),通過 BinaryMessenger 發(fā)送回 Flutter 端。

MethodChannel 可以實現(xiàn) Flutter 調(diào)用 Android,也可以實現(xiàn) Android 調(diào)用 Flutter,這里分別來進行舉例。

MethodChannel 實現(xiàn)通信

Android端 代碼 實現(xiàn)

package com.example.administrator.shadowapplication.flutter

import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.Bundle
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment

/**
 * native項目加載flutter頁面
將flutter頁面構(gòu)建成View,通過addView來顯示flutter頁面
將flutter頁面構(gòu)建成Fragment,通過對fragment的操作來顯示flutter頁面
 */
class FlutterActivity : FragmentActivity() {
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val flutterView = Flutter.createView(this, lifecycle, "route1")
        val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
        layoutParams.topMargin = 20
        addContentView(flutterView, layoutParams)

        //生成plateForm,注冊 handle
       MethodChannelPlugin(flutterView)

    }

    /**
     * 獲取當(dāng)前面 電量
     */
     fun getBatteryLevel(): Int {
        val batteryLevel: Int
        batteryLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
            batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
        } else {
            val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
            intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
        }
        return batteryLevel
    }
}

//======================== 封裝了 MethodChannelPlugin====
package com.example.administrator.shadowapplication.flutter.channel

import android.app.Activity
import com.example.administrator.shadowapplication.flutter.FlutterActivity
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterView

/**
 * @author 付影影
 * @desc 用于數(shù)據(jù)流 event streams的通信,持續(xù)通信,通常用于native向dart的通信
 * 如手機電量變化,網(wǎng)絡(luò)連接變化,陀螺儀,傳感器等
 * @date 2020/1/9
 */
class MethodChannelPlugin constructor(flutterView: FlutterView) : MethodChannel.MethodCallHandler {

    var activity: Activity = flutterView.context as Activity
    private val CHANNEL_NAME = "MethodChannelPlugin"

    init {
        MethodChannel(flutterView, CHANNEL_NAME).setMethodCallHandler(this)
    }


    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "getBatteryLevel" -> {
                val batteryLevel = (activity as FlutterActivity).getBatteryLevel()
                if (batteryLevel != -1) {
                    result.success(batteryLevel)
                } else {
                    result.error("UNAVAILABLE", "Battery    level   not available.", null)
                }
            }
            else -> {
                result.notImplemented()
            }
        }
    }
}

Dart 代碼 實現(xiàn)

import 'dart:async';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp(
      initParams: window.defaultRouteName,
    ));

class MyApp extends StatelessWidget {
  final String initParams;

  MyApp({Key key, @required this.initParams}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter 混合開發(fā)',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: _getHomePage(),
    );
  }

  ///根據(jù) native 傳遞的參數(shù),選擇要展示的界面
  Widget _getHomePage() {
    switch (initParams) {
      case "route1":
        return MyHomePage(
          title: 'flutter 混合開發(fā)',
          initParams: "hello shadow!!",
        );
        break;
    }
  }
}

class MyHomePage extends StatefulWidget {
  final String initParams;

  MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState(initParams);
}

class _MyHomePageState extends State<MyHomePage> {
  final String initParams;

  _MyHomePageState(this.initParams);

  ///調(diào)?通道上的?法,指定通過字符串標識符調(diào)??法    getBatteryLevel 。
  ///該調(diào)?可能失敗(平臺不?持平臺 API,例如在模擬器中運?時),所以我們將invokeMethod調(diào)?包裝在try-catch語句中。
  ///我們使?返回的結(jié)果,在    setState    中來更新?戶界?狀態(tài)  batteryLevel
  String _batteryLevel = "unknow battery level";
  MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
///獲取 當(dāng)前 電量
  Future<Null> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await _methodChannel.invokeMethod("getBatteryLevel");
      batteryLevel = 'Battery   level   at  $result %   .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed    to  get battery level:  '${e.message}'.";
    }
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              onPressed: () {
                _getBatteryLevel();
              },
              child: Text(
                'FlutterActivity 傳遞數(shù)據(jù):$initParams',
                style: TextStyle(color: Colors.red),
              ),
            ),
            Text(
              '$_batteryLevel',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
    );
  }
}

實現(xiàn) 結(jié)果

微信圖片_20200110105330.png

MethodChannel 實現(xiàn) 在Android 中調(diào)用 flutter 方法

有的時候 Flutter 調(diào)用 Android 后,Android 還會將結(jié)果返回給 Flutter,雖然有時可以用 result 來實現(xiàn),但 Android 端的處理可能是異步的,result 對象也不能長期的持有,這時就需要 Android 來調(diào)用 Flutter。
因為頁面 UI 是 Flutter 端繪制的,我們很難在頁面中控制 Android 端,要實現(xiàn) Android 調(diào)用 Flutter,可以利用 Android 的 Activty 的生命周期,如果將應(yīng)用切到后臺再切回前臺,這樣 Activty 的 onResume 方法就會被調(diào)用,我們在 onResume 方法中實現(xiàn)調(diào)用 Flutter 的功能就可以了。

Android 端代碼實現(xiàn)

package com.example.administrator.shadowapplication.flutter

import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterView
import java.util.*


/**
 * native項目加載flutter頁面
將flutter頁面構(gòu)建成View,通過addView來顯示flutter頁面
將flutter頁面構(gòu)建成Fragment,通過對fragment的操作來顯示flutter頁面
 */
 private lateinit var flutterView: FlutterView;
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
  
        flutterView = Flutter.createView(this, lifecycle, "route1")
        val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
        layoutParams.topMargin = 20
        addContentView(flutterView, layoutParams)
    }

    override fun onResume() {
        super.onResume()
        val map = HashMap<String, String>()
        map["content"] = "Hello 付小影子,我是Android"
        val methodChannel = MethodChannel(flutterView, "MethodChannelPlugin")
        methodChannel.invokeMethod("showText", map, object : MethodChannel.Result {
            //2
            override fun success(o: Any?) {
                Log.d("hh", o as String?)
            }

            override fun error(errorCode: String, errorMsg: String?, errorDetail: Any?) {
                Log.d("hh", "errorCode:" + errorCode + " errorMsg:" + errorMsg + " errorDetail:" + errorDetail as String?)
            }

            override fun notImplemented() {
                Log.d("hh", "notImplemented")
            }
        })
    }
}

Dart 端 代碼實現(xiàn)

class _MyHomePageState extends State<MyHomePage> {
  final String initParams;

  _MyHomePageState(this.initParams);
  //通信方式2
  MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
  String textContent = " Flutter 初始數(shù)據(jù)";

  @override
  void initState() {
    super.initState();
    _methodChannel.setMethodCallHandler((MethodCall call) async {
      switch (call.method) {
        case "showText":
          String content = await call.arguments["content"];
          if (content != null && content.isNotEmpty) {
            setState(() {
              textContent = content;
            });
            return "success";
          } else {
            throw PlatformException(
                code: "error", message: "失敗", details: "content is null");
          }
          break;
        default:
          throw MissingPluginException();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
              child: Text(
                'FlutterActivity 傳遞數(shù)據(jù):$initParams',
                style: TextStyle(color: Colors.red),
              ),
            ),
            Text(
              '$_batteryLevel',
              style: Theme.of(context).textTheme.display1,
            ),

            Text(" Android端數(shù)據(jù) -- $textContent")

          ],
        ),
      ),
    );
  }
}

BasicMessageChannel 實現(xiàn) 通信

Android 代碼實現(xiàn)

package com.example.administrator.shadowapplication.flutter.channel

import android.app.Activity
import com.example.administrator.shadowapplication.crash_log.ToastUtil
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.StringCodec
import io.flutter.view.FlutterView

/**
 * @author 付影影
 * @desc BasicMessageChannel (主要是傳遞字符串和一些半結(jié)構(gòu)體的數(shù)據(jù))
 * BasicMessageChannel(@NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec<T> codec)
 * @date 2020/1/9
 */
class BasicMessageChannelPlugin constructor(flutterView: FlutterView) : BasicMessageChannel.MessageHandler<String> {
    lateinit var activity: Activity;
    lateinit var messageChannel: BasicMessageChannel<String>;
    val CHANNEL_NAME = "BasicMessageChannelPlugin"

    /**
     * BasicMessageChannel的初始化,入?yún)⒂腥齻€,
     * 第一個是BinaryMessage,
     * 第二個參數(shù)是定義的channel name,
     * 第三個參數(shù)是MessageCodec解碼器
     * 官方提供了四種消息編碼器,最終自動轉(zhuǎn)為ByteBuffer
     */
    init {
        activity = flutterView.context as Activity
        messageChannel = BasicMessageChannel<String>(flutterView, CHANNEL_NAME, StringCodec.INSTANCE);
        //設(shè)置消息處理器,處理來自dart的消息
        messageChannel.setMessageHandler(this)
    }


    /**
     * 第二種 使用:dart 主動發(fā)起,Android端為接收方(收到dart的消息,并且發(fā)送回復(fù))
     */
    override fun onMessage(message: String?, reply: BasicMessageChannel.Reply<String>) {
        reply.reply("BasicMessageChannel收到:$message") //可以通過reply進行回復(fù)
        ToastUtil.showMsg(message) //接收到的消息
    }

    /**
     * 第一種 使用:發(fā)送消息到Dart ,并且 接收到 dart的回復(fù)
     */
    fun send(message: String, callBack: BasicMessageChannel.Reply<String>) {
        messageChannel.send(message, callBack)
    }
}

Dart 端代碼實現(xiàn)

class BasicMessageChannelPage extends StatefulWidget {
  @override
  _BasicMessageChannelPageState createState() =>
      _BasicMessageChannelPageState();
}

class _BasicMessageChannelPageState extends State<BasicMessageChannelPage> {

  String _receiveData ="flutter 默認數(shù)據(jù)";
  var _messageChannel = BasicMessageChannel<String>("BasicMessageChannelPlugin", StringCodec());

  //發(fā)送消息 到 Android,并且收到Android的回復(fù)
  Future<String> sendMessage() async {
    String reply = await _messageChannel.send("hello shadow,我是 dart");
    print(reply);
    return reply;
  }

  //從Android 端接收消息,并且發(fā)送回復(fù)
  void receiveMessage() {
    _messageChannel.setMessageHandler((message) async {
      print("接收 Android 端發(fā)送的消息,$message");
      return "哈哈哈哈,這是我給你的回復(fù)";
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("EventChanne 接收的Android數(shù)據(jù):$_receiveData"),
    );
  }
}

EventChannel 實現(xiàn)通信

Android 代碼實現(xiàn)

package com.example.administrator.shadowapplication.flutter.channel

import io.flutter.plugin.common.EventChannel
import io.flutter.view.FlutterView

/**
 * @author 付影影
 * @desc 用于數(shù)據(jù)流 event streams的通信,持續(xù)通信,通常用于native向dart的通信
 * 如手機電量變化,網(wǎng)絡(luò)連接變化,陀螺儀,傳感器等
 * @date 2020/1/9
 */
class EventChannelPlugin constructor(flutterView: FlutterView) : EventChannel.StreamHandler {
    private val CHANNEL_NAME = "EventChannelPlugin"

    init {
        EventChannel(flutterView, CHANNEL_NAME).setStreamHandler(this)
    }

    /**
     * onCancel代表對面不再接收,這里我們應(yīng)該做一些clean up的事情。
     * onListen則代表通道已經(jīng)建好,Native可以發(fā)送數(shù)據(jù)了。
     * 注意onListen里帶的EventSink這個參數(shù),后續(xù)Native發(fā)送數(shù)據(jù)都是經(jīng)過EventSink的
     */
    var events: EventChannel.EventSink? = null
    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
        this.events = events
    }

    override fun onCancel(arguments: Any?) {
        events = null
    }

    /**
     * 給 dart端發(fā)送消息
     */
    fun send(params: Any) {
        if (events != null) {
            events!!.success(params)
        }
    }
}

Dart 代碼實現(xiàn)

class EventChannelPage extends StatefulWidget {
  @override
  _EventChannelPageState createState() => _EventChannelPageState();
}

class _EventChannelPageState extends State<EventChannelPage> {
  String _receiveData = "Flutter 默認數(shù)據(jù)";
  var CHANNEL_NAME = "EventChannelPlugin";

  var __eventChannel = EventChannel("CHANNEL_NAME");

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    __eventChannel.receiveBroadcastStream().listen(eventListener);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("EventChanne 接收的Android數(shù)據(jù):$_receiveData"),
    );
  }

  void eventListener(event) {
    print("收到的數(shù)據(jù):$event");
    setState(() {
      _receiveData = event;
    });
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容