【问题标题】:ContentProvider, Loader and ProgressBarContentProvider、Loader 和 ProgressBar
【发布时间】:2012-07-24 20:59:49
【问题描述】:

我有一个ContentProvider,它从 sqlite 数据库中获取数据并通过Loader 加载它。它由Service 更新,它运行AsyncTask 以从服务器下载数据并在ContentProvider 中更新它。我希望它很清楚,但要确定:

  • ListFragment 通过LoaderContentProvider 获取数据,

  • ContentProvider 更新为 Service

现在,当我第一次启动应用程序时我的本地 sqlite 数据库为空时,它显示它没有事件,即使它们当前正在通过 Service 下载。我宁愿此时显示ProgressBar(无限旋转的轮子,而不是条形图)。但是,如果我在数据库中没有结果时显示ProgressBar,即使在这种特定情况下从服务器获取数据后,当外部数据库中没有记录时它也会存在(在我的情况下它经常发生)。所以:

  • Service第一次下载数据时我 想显示一个 ProgressBar 直到ContentProvider 给出 非空结果 Service 完成了它的工作。

  • ContentProvider 什么也没返回时AND Service 完成 这是工作(并获取空结果)我希望应用程序显示 “未找到结果”。

我的问题可能是:如何通知ListFragment Service 仍在运行或它完成了 ts 工作。我的意思是-我不应该在Service 中存储对调用Fragment 的任何引用。这违背了ContentProviders 的想法,不是吗?那怎么办?

注意:我真的不知道哪个代码片段在这里会有所帮助,所以如果您觉得需要查看一些特定的片段,请在 cmets 中告诉我。谢谢!

【问题讨论】:

    标签: android android-contentprovider android-progressbar android-loader


    【解决方案1】:

    由于您对将实际进度发回 UI 不太感兴趣,因此实现此功能的最简单方法是使用一对自定义广播,也可能使用静态布尔值来显示运行状态。

    基本上,您的服务可以在开始下载和完成下载时通知您的应用程序中感兴趣的任何组件。因此您可以定义两个自定义操作字符串:

    public static final String ACTION_DOWNLOADSTART = "com.mypackage.intent.ACTION_DOWNLOADSTART";
    public static final String ACTION_DOWNLOADCOMPLETE = "com.mypackage.intent.ACTION_DOWNLOADCOMPLETE";
    

    然后让您的服务在代码中的适当位置广播它们:

    Intent start = new Intent(ACTION_DOWNLOADSTART);
    sendBroadcast(start);
    
    //...Service does its work
    
    Intent finish = new Intent(ACTION_DOWNLOADCOMPLETE);
    sendBroadcast(finish);
    

    您可以在应用程序中的任何位置使用BroadcastReceiver 注册这些回调并采取相应措施(即检查ContentProvider 的状态并在必要时显示/隐藏进度)。

    另一个常见的做法是,如果您希望能够检查 Service 是否在任何给定点运行,只需包含一个 private static boolean 成员,您可以在 Service 处于活动状态时切换该成员(可能介于onCreate()/onDestroy() 但可能在其他地方)和像 isRunning() 这样的访问器方法。然后,您的应用程序还可以通过调用该方法随时检查Service 是否正在运行。

    【讨论】:

    • 非常感谢你们,今天晚些时候我会研究你们的解决方案,但它们看起来都很有希望
    • 我还没写,但在我的情况下,你的第二个似乎不够用。但BrodcastReceiver 看起来正是我正在寻找的东西。非常感谢!
    • LocalBroadcastManager 更适合这项特殊工作:stackoverflow.com/questions/8802157/…。像魅力一样工作:)
    • 是的,这是一个很好的解决方案:)
    【解决方案2】:

    Fragment / ActivityService 之间有多种通信技术。

    其中一个正在使用 ResultReceiver 并将其发送到 IntentService in Intent extra。

    您创建自定义接收器ServiceResultReceiver 扩展ResultReceiver

    public class ServiceResultReceiver extends ResultReceiver {
    
        private Receiver mReceiver;
    
        public ServiceResultReceiver(Handler handler) {
            super(handler);
        }
    
        public void setReceiver(Receiver receiver) {
            mReceiver = receiver;
        }
    
        public interface Receiver {
            public void onReceiveResult(int resultCode, Bundle resultData);
        }
    
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            if (mReceiver != null) {
                mReceiver.onReceiveResult(resultCode, resultData);
            }
        }
    }
    

    让你的Fragment 实现ServiceResultReceiver.Receiver 接口。创建接收器 并将其初始化为您的Fragment。您比将接收器传递给服务和在服务中只需从意图中获取接收器并调用 receiver.send() 将任何内容发送回接收器。

    public class MyFragment extends ListFragment implements ServiceResultReceiver.Receiver {
        private ServiceResultReceiver mReceiver;
        ....
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            ...
            mReceiver = new ServiceResultReceiver(new Handler());
            mReceiver.setReceiver(this);
        }
    
        public void startMyService() {
            final Intent intent = new Intent(getActivity(), MyService.class);
            intent.putExtra("receiver", mReceiver);
            getActivity().startService(intent);
        }
    
        @Override
        public void onReceiveResult(int resultCode, Bundle resultData) {
            // service finished
        }
    }
    
    public class MyService extends IntentService {
        ...
        @Override
        protected void onHandleIntent(Intent intent) {
            // download data and update database
            ....
            final ResultReceiver receiver = intent.getParcelableExtra("receiver");
            if (receiver != null) {
                receiver.send(0, null);
            }
        }
    }
    

    【讨论】:

    • BroadcastReceiver 似乎是一个更简单的解决方案 + 我的 Service 不是 IntentService(或者这不是问题吗?),但非常感谢您的回答!
    • IntentService 是一个 Service 已经实现了工作线程,这种方法是在 google i/o 2010 应用程序中引入的。
    • 一个建议的调整是让服务持有对 ServiceResultReceiver 的弱引用。正如上面草拟的那样,Service 将持有对启动它的 Fragment 的引用,从而防止在其运行期间进行垃圾收集。虽然这可能是故意的,但我猜这样做只是为了示例的简单。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多