【问题标题】:Singleton Pattern "Access to Constructor not Allowed"单例模式“不允许访问构造函数”
【发布时间】:2014-11-30 07:11:54
【问题描述】:

我对 Java 比较陌生,并且是自学成才的,所以我希望会有一些我缺少的基本理解。

背景:我正在使用我想在服务中使用的蓝牙,以便我可以将接收到的信息广播到多个活动。这适用于我的 MainActivity,但不适用于其他任何人。每当从设备中为任何其他 Activity 读取蓝牙特征时,它都无法读取并使程序崩溃。我发现该特征在广播之前是非空的,但无论何时收到它都是空的。我相信这是由于错误地创建了两个服务实例并从一个从未处理过的实例中读取。因此,为了消除这种可能性,我想将我的服务创建为单例。 (我想保持问题的主题,所以不会问,但建议是赞赏。)

所以,我需要一个服务 (BluetoothService) 实例,我用它来运行和处理一些蓝牙操作。我遇到了单例设计模式,这似乎是我所追求的。我按照网上各种来源描述的方式实现了它:

    private static BluetoothService serviceInstance;
    private BluetoothService() {} // Error occurs here

    public static BluetoothService getSharedBluetoothService() {
        if(serviceInstance == null)
            serviceInstance = new BluetoothService();
        return serviceInstance;
    }

当 Activity 尝试绑定到服务时,我在 Activity 中定义了绑定器类:

public class BtBinder extends Binder {
    BluetoothService getService() {
        return getSharedBluetoothService(); // returns reference to current service
        //return BluetoothService.this; // Line used prior to introducing a Singleton pattern.
    }
}

如果我正确理解错误,则私有构造函数会导致我的 MainActivity 出错 - 它无法实例化服务,因为它的构造函数是私有的。如果构造函数公开,则 MainActivity 无法绑定到服务。 onBind() 永远不会在其他地方导致空指针异常。

我哪里错了?据我所知,我遵循了正确的程序。我不知道该转向哪里。

编辑: 活页夹:

@Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "Currently binding");
    BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
    mBluetoothAdapter = manager.getAdapter();   
    return myBinder; // When bound, return the whole MyLocalBinder Binder.
}

主活动:

public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
    // ...
    BluetoothService BtService;
    boolean isBound = false;

    BluetoothBroadcast btSetupReceiver = null;
    BluetoothBroadcast resultsReceiver = null;
    boolean receiversAreRegistered = false;

        private static final String TAG = "BluetoothGattActivity";
        private static final String DEVICE_NAME = "Suspensionometer 3333";

        private BluetoothAdapter mBluetoothAdapter;
        private SparseArray<BluetoothDevice> mDevices;

        private BluetoothGatt mConnectedGatt;

        private ProgressDialog mProgress;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // Bind the service
            Intent intent = new Intent(this, BluetoothService.class);
            Log.d(TAG, "Attempting to bind to service.");
            bindService(intent, btServiceConnection, Context.BIND_AUTO_CREATE);

            // Set up BroadcastReceiver
            btSetupReceiver = new BluetoothBroadcast(){

            @Override
            public void onReceive(Context context, Intent intent) 
            {               
                // ...
        };

        resultsReceiver = new BluetoothBroadcast() {
            @Override
            public void onReceive(Context context, Intent intent)
            {
                // ...
        };
    }

【问题讨论】:

  • 你能显示Binder类的代码吗?
  • 1.您可以发布 MainActivity 代码以便我们查看错误发生的位置吗? 2.虽然我知道的不够多,但我认为你不应该让一个Android服务成为单例。在您深入研究之前,我会进行更多研究...
  • 我已经添加了 Binder 和 MainActivity。如果它不是你所追求的,请告诉我。除了 hatcyl 所说的之外,我确信可能还有其他方法可以做到这一点,并且单例是不必要的。然而,这个想法当时似乎很简单,可以实施,我想我被冲昏了头脑......

标签: java android service constructor singleton


【解决方案1】:

您不能将 Service 构造函数设为私有。 Android 需要能够创建许多组件来响应设备上发生的事件。例如,Android 可以创建 Service 类的实例来响应意图。

您可以做的是设置一个单独的单例对象来完成您的工作,并在您的服务中获取该对象。但是,请注意服务的生命周期。如果您的服务是响应意图或 startService 调用而启动的,那么该服务运行的时间取决于 onStartCommand 的返回值(START_STICKY 等)。该文档提供了一个很好的概述: http://developer.android.com/reference/android/app/Service.html

编辑:

听起来您主要只需要一个公共位置来存储服务的数据,然后当其中一个活动绑定到服务并请求它时,Android 创建的任何服务实例都可以获取该数据。看起来您正在使用文档中的“LocalService”模式,这听起来是正确的做法。

我不知道您的蓝牙数据有多复杂,但在 Android 中存储数据的最常用方法是 SharedPreferences(用于更简单的键/值数据)和数据库。如果您认为可以摆脱 SharedPreferences,那么您可以使用 JSON 将相当多的复杂性存储为长字符串。

如果你的数据太复杂,或者更多是基于表的,那么 Android 自带 SQLiteDatabase 和相关类,还有一个 ContentProvider 类,它提供对数据库的 URI 类型访问。

我的方向正确吗?我现在就停下来,但是如果您需要更多解释,或者我是否走错了路,请告诉我。

【讨论】:

  • 感谢您的回复。这似乎很明智。它将解释为什么在线示例使用私有 - 因为它们不是指单例服务。只是为了检查我是否正确理解了这一点(因为我仍在思考面向对象的代码)你建议我创建一个类来充当单例,然后使用它在服务类和活动之间进行通信?
  • 抱歉耽搁了,已经睡着了/正在查看 SharedPreferences 和 JSON。 LocalService 是我正在做的事情,我有非常简单的数据,SharedPreferences 可能对这个项目有用。为了更好的背景,你所说的和我想做的很接近。该服务用于存储数据以及设置和运行蓝牙操作。服务在事件触发时将信息发送到处理数据并更新其 UI 显示的活动。无论如何,我想如果我不使用单例,它会回来确保我获得对服务的正确引用。
  • 也许我应该将上述“单身”问题表述为更开放的讨论,我认为这是我最需要的。我知道这听起来很傻,但是您对如何获取在另一个活动中实例化的服务引用有什么建议,然后我可以绑定到它吗?我尝试将服务实例公开,但没有成功。
  • 我真的认为您需要摆脱将 Service 视为单例对象的想法——这不是 Android 设计的工作方式。将您的服务视为一段代码,它知道如何收集蓝牙数据,将其存储在 SharedPrefs 中,并发送您的活动可以接收的广播,以便他们可以获取数据。但是您不应该在服务中保持非静态状态(仅在 SharedPrefs 中),因此活动绑定到哪个 Service 实例并不重要。
  • 好的,谢谢。你给了我一些想法,希望在重新设计我的代码结构之后,它应该结合在一起。我将此标记为已接受的答案,因为我认为它是正确的,并且在技术上回答了“为什么构造函数不起作用?”的问题。虽然这个问题在技术上还没有解决。感谢您抽出宝贵时间帮助我度过难关!
【解决方案2】:

将有问题的私有更改为受保护的,它应该可以解决问题。

在这个很棒的博客上阅读单身人士:http://www.javaworld.com/article/2073352/core-java/simply-singleton.html

【讨论】:

  • 不幸的是,运气不好。不过,我目前正在阅读该链接,以确保我对单身人士的了解与所说的相符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-12
  • 2019-02-21
  • 2013-11-15
相关资源
最近更新 更多