如何讓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<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,
),
),
);
}
}
複製程式碼