我正在尝试执行一项相对简单的任务:我有一个片段可以启动服务以从远程服务器中提取一些JSON。然后,我想使用广播将JSON传递回原始调用片段,并将BroadcastReceiver定义为片段中的匿名类。
但是,每当我尝试这样做时,我都会收到以下错误:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.roundarch.codetest, PID: 21974
android.app.RemoteServiceException: can't deliver broadcast
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1631)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
码
public class Part3Fragment extends Fragment {
PostCodeAdapter postCodeAdapter;
ListView listView;
BroadcastReceiver receiver;
IntentFilter intentFilter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_part3, null);
initialiseReceiver();
View emptyView = (View) view.findViewById(R.id.empty_textview);
ListView listView = (ListView) view.findViewById(R.id.part3_listview);
listView.setEmptyView(emptyView);
// TODO - the listview will need to be provided with a source for data
// TODO - (optional) you can set up handling to list item selection if you wish
return view;
}
@Override
public void onResume() {
super.onResume();
initialiseReceiver();
getActivity().startService(new Intent(getActivity(), Part3Service.class));
}
public void initialiseReceiver(){
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getBundleExtra("bundle");
ArrayList<PostCodesResult.Result> postcodes = (ArrayList<PostCodesResult.Result>) bundle.getSerializable("postcodeArray");
updatePostcodes(postcodes);
}
};
intentFilter = new IntentFilter("JSON_RECEIVED");
getActivity().registerReceiver(receiver, intentFilter);
}
@Override
public void onPause() {
super.onPause();
}
private void updatePostcodes(ArrayList<PostCodesResult.Result> postcodes) {
if (postcodes.size() > 0){
postCodeAdapter = new PostCodeAdapter(getActivity(), R.layout.part3_listview_item, postcodes);
listView.setAdapter(postCodeAdapter);
postCodeAdapter.notifyDataSetChanged();
}
}
}
以下是服务的代码:
public class Part3Service extends Service {
private final String TAG = this.getClass().getSimpleName();
// TODO - we can use this as the broadcast intent to filter for in our Part3Fragment
public static final String ACTION_SERVICE_DATA_UPDATED = "com.roundarch.codetest.ACTION_SERVICE_DATA_UPDATED";
private List<Map<String, String>> data = null;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service has started");
new PostCodeRetriever().execute("http://gomashup.com/json.php?fds=geo/usa/zipcode/state/IL");
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void updateData() {
// TODO - start the update process for our data
}
private void broadcastDataUpdated(String jsonString) {
Intent intent = new Intent();
intent.setAction("JSON_RECEIVED");
ArrayList<PostCodesResult.Result> postcodes = new ArrayList<>();
if (jsonString != null) {
Gson gson = new Gson();
PostCodesResult result = gson.fromJson(jsonString, PostCodesResult.class);
postcodes.addAll(result.getResult());
}
Bundle bundle = new Bundle();
bundle.putSerializable("postcodeArray", postcodes);
intent.putExtra("bundle", bundle);
sendBroadcast(intent);
stopSelf();
}
class PostCodeRetriever extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
Uri uri = Uri.parse(params[0]);
String jsonString = "";
try {
URL postcodesUrl = new URL(uri.toString());
InputStream inputStream = null;
HttpURLConnection connection = (HttpURLConnection) postcodesUrl.openConnection();
connection.connect();
inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder stringBuilder = new StringBuilder();
String line = "";
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
stringBuilder.deleteCharAt(0);
stringBuilder.deleteCharAt(stringBuilder.length() -1);
jsonString = stringBuilder.toString();
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return jsonString;
}
@Override
protected void onPostExecute(String jsonString) {
broadcastDataUpdated(jsonString);
super.onPostExecute(jsonString);
}
}
}
我尝试了很多不同的事情:通过意图仅发送原始JSON字符串,将广播接收器定义为单独的类,将JSON转换为序列化的ArrayList并通过意图发送,最后尝试将它全部包含在一个包中并发送它。
奇怪的是,非常相似的代码似乎对我的同学很好 - 我不能看到他们的代码和我的代码之间可能导致问题的任何差异(他们也不能)。另外,如果我试图通过意图发送一个不同的字符串然后似乎工作正常 - 广播接收器选择它并且onReceive被调用它应该是。 &#34;无法提供广播&#34;尝试传递此特定数据集时似乎只出现错误,仅适用于我!
答案 0 :(得分:1)
我知道原来的问题是在不久前发布的,但我最近遇到了这个问题,我想发布我所做的修复它,以防它帮助其他人。
我能够通过进行以下2项更改来纠正问题。我无法明确确认问题的确切原因,因此我不确定是否有必要进行这两项更改。但是,考虑到应用正在做什么,这些是对旧代码的改进,所以我保留了它们。
意图动作常量的定义
该应用程序使用扩展IntentService的类来执行后台任务。该类定义了许多静态最终String值,这些值用作服务完成任务时广播的Intents的“action”值。
我注意到这些字符串是使用与操作含义匹配的值(例如“SAVE_COMMENT”)定义的,但它们不包含应用程序的包名称。
来自Android的Intents和IntentFilter文档...
如果您定义自己的操作,请务必将应用的包名称包含为前缀。
https://developer.android.com/guide/components/intents-filters.html
我更改了这些字符串的值以包含包名称。
我无法证明这一点,但我想知道其中一个动作名称是否与这些设备上另一个应用程序中定义的动作发生冲突。
切换到LocalBroadcastManager
应用程序在完成任务时使用Context :: sendBroadcast()来广播Intent。应用程序服务执行的所有逻辑仅适用于该应用程序,因此我将其切换为使用LocalBroadcastManager :: sendBroadcast()。这使广播保持在应用程序内,进一步防止了与另一个应用程序冲突的可能性。
答案 1 :(得分:0)
我也有这个问题。我发现我在意图大量的数据(这是一个大的字节数组...)广播。所以我通过减少字节数组的大小解决了它。看看吧!