如何让Flutter程序可以跨应用的更改其他应用的状态呢,先看原生如何实现,其次再用MethodChannel对接Flutter就行了,所以此篇更多的是安卓原生开发的知识,提到原生开发跨应用发送消息或者更改状态,有两个东西能够实现这样的需求,一个是ContentObserver,另一个就是Broadcast
ContentOberver
ContentObserver被称为安卓的内容观察者,目的是观察特定Uri引起的数据变化,例如通过一个key(String)来监听这个key对应值的变化,如果该值发生了变化,那么整个系统中所有对这个key做了监听的地方都能知道它被更改了,从而来根据这个key对应的新的值来刷新相关Widget的状态,这些值都被保存在了/data/system/users/0/下的settings_global.xml,settings_system.xml,settings_secure.xml,分别对应三种不同类型的key,
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吗?
以下的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了?
我也不想半路换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 {
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: [
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 {
List _list = [];
MethodChannel platform = MethodChannel("Nightmare");
@override
void initState() {
super.initState();
platform.setMethodCallHandler(platformCallHandler);
}
Future 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,
),
),
);
}
}