Flutter跨應用更改狀態在安卓端的實現

如何讓Flutter程序能夠跨應用的更改其餘應用的狀態呢,先看原生如何實現,其次再用MethodChannel對接Flutter就好了,因此此篇更多的是安卓原生開發的知識,提到原生開發跨應用發送消息或者更改狀態,有兩個東西可以實現這樣的需求,一個是ContentObserver,另外一個就是Broadcastjava

ContentOberver

ContentObserver被稱爲安卓的內容觀察者,目的是觀察特定Uri引發的數據變化,例如經過一個key(String)來監聽這個key對應值的變化,若是該值發生了變化,那麼整個系統中全部對這個key作了監聽的地方都能知道它被更改了,從而來根據這個key對應的新的值來刷新相關Widget的狀態,這些值都被保存在了/data/system/users/0/下的settings_global.xml,settings_system.xml,settings_secure.xml,分別對應三種不一樣類型的key,android

global:全部應用都可以訪問而且更改
system:只有系統級別的應用能進行監聽及更改,或者用su權限進行更改,亦或者下降app的編譯版本而後導入android.permission.WRITE_SETTINGS這個權限便可
secure:安全級別最高,用來保存整個安卓系統的一些安全設置,更改方式同上,不過須要android.permission.WRITE_SECURE_SETTINGS這個權限

因此咱們實現內容觀察者的監聽須要啥?

一個ContentObserver,一個Handler(可省略),好比咱們對"Nightmare_Test_Key"進行監聽

首先自定義一個ContentObserver

class MyObserver extends ContentObserver {
    final Handler mHandler;
    final Context mContext;
	public MyObserver(Context context,Handler handler) {
		super(handler);
        this.mHandler=handler;
        this.mContext=context;
	}
	@Override//重寫ContentObserver的onChange方法
	public void onChange(boolean z) {
		//此方法當監聽的值改變後會觸發,這裏將消息發送給一個Handler處理
        Message obtainMessage=mHandler.obainMessage();//
        obtainMessage.obj=System.getString(mContext.getContentResolver(),"Nightmare_Test_Key"));
        //拿到Nightmare_Text_Key的新值並將它發送給Handler處理
        mHandler.sedMessage(obtainMessage);
	}
}
複製代碼

再自定義一個Handler

class MyHandler extends Handler {
	final TextView mTextView;
	MyHandler(TextView view) {
		this.mTextView = view;
	}
	@Override
	public void handleMessage(Message message) {
		String str = (String) message.obj;//ContentObserver那邊傳過來的值
		this.mTextView.setText(TextUtils.isEmpty(str) ? "未獲取到數據" : str);
	}
}
複製代碼

貼上關鍵代碼,如下代碼須要放下Activty的生命週期中,若是是在一個View的生命週期中實現這樣的監聽,須要將全部的this更改爲this.getContext(),亦或者經過其餘的方式拿到安卓的Context(上下文)

TextView mTextView=new TextView(this);
Handler mHandler = new MyHandler(mTextView);
ContentObserver mContextObserser=new MyObserver(this,mHandler);
this.getContentResolver().registerContentObserver(System.getUriFor("Nightmare_Test_Key"),false,mContextObserser);
//第二個參數false表示精準匹配,即值匹配該Uri
複製代碼

這樣一個完整的監聽就寫好了,咱們只須要在任意App內調用(Activity內)

System.putString(this.getContentReslover(),"Nightmare_Test_Key","我想把Text的內容改爲這個");
複製代碼

只要註冊了監聽的那個App還在運行,其中的那個TextView的內容就會被更改 #Broadcast Broadcast做爲安卓四大組件之一,其做用也是至關的強大,具體就不詳細闡述,有點相似於EventBus,但安卓的Broadcast能夠貫穿安卓全部在運行的App,那麼咱們怎麼用Broadcast來實現跨應用更新狀態呢?安全

自定義一個Broadcast

class MyBroadcastReceiver extends BroadcastReceiver{
	final TextView mView;
	public MyBroadcastReceiver(TextView v){
		this.mView=v;
	}
    @Override
    public void onReceive(Context context, Intent intent) {
        String result = intent.getStringExtra("Test_Key") ;
        this.mView.setText(result);	
    }
}
複製代碼

咱們這裏註冊"test.android.intent.action.TEST"這個自定義廣播,整個廣播註冊 方式爲動態註冊,不涉及xml文件

TextView mTextView=new TextView(this);
BroadcastReceiver mBroadcastReceiver = new MyBroadcastReceiver(mTextView);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("test.android.intent.action.TEST");
this.registerReceiver(broadcastReceiver,intentFilter);
複製代碼

整個廣播註冊完成,接下來咱們來發送一個廣播,如下代碼可在另外的App中執行

Intent intent = new Intent();
intent.putExtra("Test_Key","來自其餘應用的消息");
intent.setAction("test.android.intent.action.TEST");
//使用bundle傳遞參數
sendBroadcast(intent);
複製代碼

不是說Flutter嗎? bash

如下的Example就不用ContentObserver了,使用Broadcast,直接上代碼,嘿嘿嘿😜

繼續上代碼,首先是發送端的安卓部分

class MainActivity: FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
    MethodChannel(flutterView, "Nightmare").setMethodCallHandler { call, _ ->
      val intent = Intent()
      intent.putExtra("Test_Key",call.method)
      intent.action = "test.android.intent.action.TEST"
      sendBroadcast(intent)
    }
  }
}

複製代碼

怎麼又是Kotlin了? app

我也不想半路換Kotlin,新建這個Example就是了,不過看起來比Java要簡潔多了 ####Dart部分

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  MethodChannel _channel=MethodChannel("Nightmare");
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text("添加一個按鈕"),
              onPressed: (){
                _channel.invokeMethod("Button");
              },
            ),
            RaisedButton(
              child: Text("添加一個Card"),
              onPressed: (){
                _channel.invokeMethod("Card");
              },
            ),
            TextField(
              onSubmitted: (str){
                _channel.invokeMethod(str);
              },
            )
          ],
        ),
      ),
    );
  }
}

複製代碼

接收端的安卓部分

class MainActivity: FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
    val methodChannel = MethodChannel(flutterView, "Nightmare")
    class MyBroadcastReceiver : BroadcastReceiver() {
      override fun onReceive(context: Context, intent: Intent) {
        val result = intent.getStringExtra("Test_Key")
        methodChannel.invokeMethod(result,"")
      }
    }
    val mBroadcastReceiver = MyBroadcastReceiver()
    val intentFilter = IntentFilter()
    intentFilter.addAction("test.android.intent.action.TEST")
    this.registerReceiver(mBroadcastReceiver, intentFilter)
  }
}
複製代碼

Dart部分

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Widget> _list = [];

  MethodChannel platform = MethodChannel("Nightmare");

  @override
  void initState() {
    super.initState();
    platform.setMethodCallHandler(platformCallHandler);
  }

  Future<dynamic> platformCallHandler(MethodCall call) async {
    print(call.method);
    switch (call.method) {
      case "Button":
        _list.add(
          RaisedButton(
            onPressed: () {},
            child: Text("按鈕"),
          ),
        );
        setState(() {});
        break;
      case "Card":
        _list.add(
            Card(
              child: Text("Card"),
            )
        );
        setState(() {});
        break;
      default:
        _list.add(
            Text(call.method)
        );
        setState(() {});
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          children: _list,
        ),
      ),
    );
  }
}
複製代碼

看一下預覽圖

其實就是用MethodChannel實現了Flutter到Android原生的雙向調用而已

看完估計會想,誰**會有這種需求啊?

我的項目實際需求

smali1.gif

上圖是Flutter App(MToolkit)控制原生App(SystemUI)的狀態,固然這個原生應用被我反編譯植入佈局,下篇可能會詳細說哦,纔開始寫帖子,哪兒有不對的地方但願你們多多指出哦😬
最後Mtoolkit,也是本人的Flutter與原生的混合項目
相關文章
相關標籤/搜索