How to enable Flutter applications to change the state of other applications across applications? See how native implementations of Flutter are implemented, and then use MethodChannel to dock with Flutter. This article is more about Android native development. There are two things that meet this need: ContentObserver and Broadcast
ContentOberver
ContentObserver is called the ContentObserver of android. The purpose of ContentObserver is to observe changes in data caused by a particular Uri, such as listening for changes in the value of a key(String). If the value changes, all the places in the system that are listening for the key will know that it has changed. To refresh the state of the related Widget based on the new value corresponding to this key, These values are stored in /data/system/users/0/ settings_global.xml,settings_system. XML, and settings_secure.
Global: All applications can access and change
System: Only system-level applications can listen and make changes, either with su permission, or lower the compiled version of the app and import android.permission.WRITE_SETTINGS
Secure: The highest security level, used to save some security Settings for the entire Android system. Change the Settings in the same way as above, but requires android.permission.WRITE_SECURE_SETTINGS
So what do we need to implement content observer listening?
A ContentObserver and a Handler(which can be omitted), for example, we listen for “Nightmare_Test_Key”
Start by customizing a 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// Rewrite the ContentObserver onChange method
public void onChange(boolean z) {
// This method is triggered when the listening value changes and sends the message to a Handler
Message obtainMessage=mHandler.obainMessage();//
obtainMessage.obj=System.getString(mContext.getContentResolver(),"Nightmare_Test_Key"));
// Get the new value of Nightmare_Text_Key and send it to HandlermHandler.sedMessage(obtainMessage); }}Copy the code
And I’ll create a custom 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;// The value from ContentObserver
this.mTextView.setText(TextUtils.isEmpty(str) ? "No data obtained": str); }}Copy the code
The following code needs to be dropped from the Activty lifecycle. If this is implemented in the lifecycle of a View, change all this to this.getContext(), or get the Android Context by other means.
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); // The second argumentfalseIndicates exact matching, that is, the value matches the UriCopy the code
So we have a complete listener, we just need to call it in any App.
System.putString(this.getContentReslover(),"Nightmare_Test_Key"."I want to change the Text to this.");
Copy the code
As long as the App that registered the listener is still running, the contents of that TextView will be changed. #Broadcast As one of the four components of Android, Broadcast is also quite powerful. I won’t go into details. However, Android Broadcast can be used across all running Android apps, so how can we use Broadcast to update status across applications?
Customize a 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); }}Copy the code
We register here “test. Android. Intent. Action. The test” the custom radio, the radio registration method for dynamic registration, does not involve the use of the XML file
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);
Copy the code
The whole broadcast registration is complete. Next we send a broadcast. The following code can be executed in another App
Intent intent = new Intent();
intent.putExtra("Test_Key"."Messages from other applications");
intent.setAction("test.android.intent.action.TEST");
// Use bundle to pass parameters
sendBroadcast(intent);
Copy the code
What happened to Flutter?
The following Example does not need ContentObserver, use Broadcast and go to the code, hey hey hey 😜
Moving on to the code, let’s start with the Android part of the sender
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)
}
}
}
Copy the code
Why Kotlin again?
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("Add a button"),
onPressed: (){
_channel.invokeMethod("Button");
},
),
RaisedButton(
child: Text("Add a Card"),
onPressed: (){
_channel.invokeMethod("Card"); }, ), TextField( onSubmitted: (str){ _channel.invokeMethod(str); },)],),),); }}Copy the code
The Android part of the receiver
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)
}
}
Copy the code
The Dart part
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("Button"),),); 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, ), ), ); }}Copy the code