https://facebook.github.io/react-native/docs/headless-js-android.html
当app在 后台运行 时,我们可以使用RN服务来同时地刷新数据、推送或者播放音乐。
JS API
在JS中注册一个后台任务(不能操作UI,一般是网络请求、定时器等)
AppRegistry.registerHeadlessTask('SomeTaskName', () => require('SomeTaskName')); // SomeTaskName.js: module.exports = async (taskData) => { // do stuff };
Java API
定义一个服务类,继承HeadlessJsTaskService
public class MyTaskService extends HeadlessJsTaskService { @Override protected @Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) { Bundle extras = intent.getExtras(); if (extras != null) { return new HeadlessJsTaskConfig( "SomeTaskName", Arguments.fromBundle(extras), 5000, // timeout for the task false // optional: defines whether or not the task is allowed in foreground. Default is false ); } return null; } }
在清单文件中声明这个服务
<service android:name="com.example.MyTaskService" />
在Java中开启这个服务
Intent service = new Intent(getApplicationContext(), MyTaskService.class); Bundle bundle = new Bundle(); bundle.putString("foo", "bar"); service.putExtras(bundle); getApplicationContext().startService(service);
警告
- 默认情况下,当app前台运行时,开启这个服务会导致app崩溃。这是为了防止app在展示界面的同时运行大量的后台工作,可以通过第四个参数来改变这种行为(默认为false,不允许任务在前台运行)
- 如果从广播接收者中开启服务,必须保证在onReceive结束前调用HeadlessJsTaskService.aquireWakeLockNow()
例子
这是一个可以响应网络连接变化的例子。首先在清单文件中配置广播接收者
<receiver android:name=".NetworkChangeReceiver" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
广播接收者在onReceive中处理广播,可以在这里检查app是否运行在前台:
public class NetworkChangeReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, final Intent intent) { /** This part will be called everytime network connection is changed e.g. Connected -> Not Connected **/ if (!isAppOnForeground((context))) { /** We will start our service and send extra info about network connections **/ boolean hasInternet = isNetworkAvailable(context); Intent serviceIntent = new Intent(context, MyTaskService.class); serviceIntent.putExtra("hasInternet", hasInternet); context.startService(serviceIntent); HeadlessJsTaskService.acquireWakeLockNow(context); } } private boolean isAppOnForeground(Context context) { /** We need to check if app is in foreground otherwise the app will crash. http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not **/ ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses(); if (appProcesses == null) { return false; } final String packageName = context.getPackageName(); for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) { return true; } } return false; } public static boolean isNetworkAvailable(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); return (netInfo != null && netInfo.isConnected()); } }
实践
测试发现必须要多加一个权限:
<uses-permission android:name="android.permission.WAKE_LOCK"/>
对于以下代码:
module.exports = async (taskData) => { await getData() await getData() await getData() await getData() await getData() await getData() }; function getData(){ return new Promise((resolve)=>{ setTimeout(()=>{ console.log("get data from server") resolve(1) },5000) }); }
当设置允许运行在前台时(allowedInForeground=true),前台或者后台开启服务都没问题,只是只要app进入后台,以上的代码就会被暂停,恢复前后后继续执行。
当不允许前台运行时,前台开启服务则闪退,后台开启不会。后台开启后,仅仅执行了一个promise,就暂停了,恢复前台后剩余的代码继续执行,再次进入后台,则代码暂停,恢复前后剩余代码继续执行。
以上运行的环境是夜神模拟器,还没到真机上测试。