Android 修订版:希望将类LocalBinder(扩展绑定器实现ILocalService)从LocalService(扩展服务)中移动到LocalBinder.java

Android 修订版:希望将类LocalBinder(扩展绑定器实现ILocalService)从LocalService(扩展服务)中移动到LocalBinder.java,android,api,service,Android,Api,Service,My LocalService.java包含一个类LocalBinder,该类定义在ILocalService.java中声明的方法,ILocalService.java的定义使My LocalService.java混乱不堪。我想将其移动到自己的文件LocalBinder.java中。这是我的LocalBinder: 我试了很多次,但都失败了。将LocalBinder留在LocalService是否更好 下面是我的所有五个源代码文件,它们都用标题注释标出。请看一下我问题的标题。它解释了我想做什

My LocalService.java包含一个类LocalBinder,该类定义在ILocalService.java中声明的方法,ILocalService.java的定义使My LocalService.java混乱不堪。我想将其移动到自己的文件LocalBinder.java中。这是我的LocalBinder:

我试了很多次,但都失败了。将LocalBinder留在LocalService是否更好

下面是我的所有五个源代码文件,它们都用标题注释标出。请看一下我问题的标题。它解释了我想做什么。我想通过从LocalService.java中删除公共类LocalBinder并将其放入自己的文件:LocalBinder.java来整理LocalService.java。非常感谢您的帮助@Commonware已经提供了重要的帮助,但对我来说,这有点难以理解

/**************************************************************************************************
 * Filename: Controller.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains the primary activity for this application
 **************************************************************************************************/
package com.marie.localservicesample;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

/*
 * Example of explicitly starting and stopping the local service.
 * This demonstrates the implementation of a service that runs in the same
 * process as the rest of the application, which is explicitly started and stopped
 * as desired.
 */
//public static class Controller extends Activity {
public class Controller extends Activity {

    // Message types sent from the BluetoothReadService Handler
    public static final int MESSAGE_SONG = 1;
    public static final int MESSAGE_NOTHING = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.local_service_controller);

        // Watch for button clicks.
        Button button = (Button)findViewById(R.id.start);
        button.setOnClickListener(mStartListener);
        button = (Button)findViewById(R.id.stop);
        button.setOnClickListener(mStopListener);
    }

    private OnClickListener mStartListener = new OnClickListener() {
        public void onClick(View v) {
            // Make sure the service is started.  It will continue running
            // until someone calls stopService().  The Intent we use to find
            // the service explicitly specifies our service component, because
            // we want it running in our own process and don't want other
            // applications to replace it.
            //startService(new Intent(Controller.this, LocalService.class));

            Intent startSvc = new Intent(Controller.this, LocalService.class);
            startSvc.putExtra(LocalService.EXTRA_MESSENGER, new Messenger(ctrlHandler));
            startSvc.putExtra(LocalService.EXTRA_SONG, 7);
            startService(startSvc);

            Intent binding = new Intent(Controller.this, Binding.class);
            startActivity(binding);
        }
    };

    private OnClickListener mStopListener = new OnClickListener() {
        public void onClick(View v) {
            // Cancel a previous call to startService().  Note that the
            // service will not actually stop at this point if there are
            // still bound clients.
            stopService(new Intent(Controller.this,
                    LocalService.class));
        }
    };

    /*
     * Here's a way to send a message to a handler - haven't tried it yet:
     * Give the new state to the Handler so the UI Activity can update
     * mHandler.obtainMessage(BlueSentry.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
     * But for now let's msg.what to our handler
     */
    private Handler ctrlHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
            case MESSAGE_SONG:
                String songObj = (String) msg.obj;
                int songArg1 = msg.arg1;
                Log.i("ctrlHandler", "MESSAGE_SONG: " + songObj + " " + songArg1);
                break;
            case MESSAGE_NOTHING:
                String nothingObj = (String) msg.obj;
                int nothingArg1 = msg.arg1;
                Log.i("ctrlHandler", "MESSAGE_NOTHING: " + nothingObj + " " + nothingArg1);
                break;
            default:
                break;
            }
        }
    };
}    

/**************************************************************************************************
 * Filename: LocalService.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains a local service
 **************************************************************************************************/

package com.marie.localservicesample;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class LocalService extends Service {
    private NotificationManager mNM;

    // Unique Identification Number for the Notification.
    // We use it on Notification start, and to cancel it.
    //private int NOTIFICATION = R.string.local_service_started;
    private int NOTIFICATION = R.string.local_service_started;

    private int statusCode = 99;
    private int emptyMsg = 549;

    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class LocalBinder extends Binder implements ILocalService {
        @Override
        public LocalService getService() {
            return LocalService.this;
        }

        @Override
        public int getStatusCode() {
            return statusCode;
        }

        @Override
        public void startRcvThread(Handler handler) {
            Thread thread = passHandlerToThread(handler);
            thread.start();
        }
    }

    public static final String EXTRA_MESSENGER = "com.marie.localservicesample.EXTRA_MESSENGER";
    private Messenger messenger;

    public static final String EXTRA_SONG = "com.marie.localservicesample.EXTRA_SONG";
    private int song;

    @Override
    public void onCreate() {
        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

        // Display a notification about us starting.  We put an icon in the status bar.
        showNotification();

        /* Move this stuff to afterStartCommand()
        Thread thr = new Thread(null, new ServiceWorker(), "LocalService");
        thr.start();  
        */
    }

    // Call this at the end of onStartCommand()
    public void afterStartCommand() {
        Thread thr = new Thread(null, new ServiceWorker(), "LocalService");
        thr.start();  
    }

    /*
     * see http://developer.android.com/reference/java/lang/Thread.html
     */
    class threadClass extends Thread {
        private Handler mHandler;
        private Message mMsg;
        // constructor
        public threadClass(Handler handler, Message msg) {
            // do something like save the Handler reference
            mHandler = handler;
            mMsg = msg;
        }
        @Override
        public void run() {
            // do some background processing, call the Handler?
            mHandler.sendMessage(mMsg);
        }
    }
    public Thread passHandlerToThread(Handler handler) {
        handler.sendEmptyMessage(emptyMsg);
        Message msg = Message.obtain();
        msg.what = emptyMsg;
        Thread thread = new threadClass(handler, msg);
        return thread;
    }

    /*
     * This is the ServiceWorker thread that passes messages to the handler defined in
     * the Controller activity.
     */
    class ServiceWorker implements Runnable
    {
        public void run() {
            // do background processing here... something simple

            while (messenger == null) {
                try {
                    throw new Exception();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            // send a message to the handler
            try {
                Message msg = Message.obtain();
                msg.what = Controller.MESSAGE_SONG;
                msg.obj = "Song";
                msg.arg1 = song;
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            } catch (NullPointerException e) {
                e.printStackTrace();
            }

            try {
                Message msg = Message.obtain();
                msg.what = Controller.MESSAGE_NOTHING;
                msg.obj = "Nothing";
                msg.arg1 = 0;
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            } catch (NullPointerException e) {
                e.printStackTrace();
            }

            // stop the service when done...
            // LocalService.this.stopSelf();
            // Or use the unbindBtn in the MainActivity class.
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("LocalService", "Received start id " + startId + ": " + intent);

        Bundle extras = intent.getExtras();

        messenger = (Messenger)extras.get(EXTRA_MESSENGER);
        try {
            song = (Integer) extras.get(EXTRA_SONG);
        } catch (NullPointerException e) {
            e.printStackTrace();
            song = 0;
        }

        afterStartCommand();

        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        // Cancel the persistent notification.
        mNM.cancel(NOTIFICATION);

        // Tell the user we stopped.
        Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
    }

    /**
     * Show a notification while this service is running.
     */
    private void showNotification() {
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.local_service_started);

        // Set the icon, scrolling text and timestamp
        Notification notification = new Notification(R.drawable.stat_sample, text,
                System.currentTimeMillis());

        // The PendingIntent to launch our activity if the user selects this notification
        //PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, LocalServiceActivities.Controller.class), 0);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0);

        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.local_service_label),
                text, contentIntent);

        // Send the notification.
        mNM.notify(NOTIFICATION, notification);
    }
}

/**************************************************************************************************
 * Filename: Binding.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains the binding for this application
 **************************************************************************************************/
package com.marie.localservicesample;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

/*
 * Example of binding and unbinding to the local service.
 * This demonstrates the implementation of a service which the client will
 * bind to, receiving an object through which it can communicate with the service.
 */
public class Binding extends Activity {
    private boolean mIsBound;

    private ILocalService mBoundService = null;
    private LocalService mBoundService1;
    private LocalService mBoundService2;

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  Because we have bound to a explicit
            // service that we know is running in our own process, we can
            // cast its IBinder to a concrete class and directly access it.
            mBoundService1 = ((LocalService.LocalBinder)service).getService();

            ILocalService localSvc = (ILocalService)service;
            mBoundService2 = localSvc.getService();
            if (mBoundService1 == mBoundService2) {
                Log.d("mBoundeService 1 and 2 ", "are equal");
                mBoundService = (ILocalService) service;
            }
            int statusCode = localSvc.getStatusCode();

            Log.d("Binding.java","called onServiceConnected. statusCode: " + statusCode);

            Toast.makeText(Binding.this, R.string.local_service_connected,
                    Toast.LENGTH_SHORT).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            // Because it is running in our same process, we should never
            // see this happen.
            mBoundService1 = null;
            mBoundService2 = null;

            Log.d("Binding", "called onServiceDisconnected");

            Toast.makeText(Binding.this, R.string.local_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };

    void doBindService() {
        // Establish a connection with the service.  We use an explicit
        // class name because we want a specific service implementation that
        // we know will be running in our own process (and thus won't be
        // supporting component replacement by other applications).
        bindService(new Intent(Binding.this, 
                LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    void doUnbindService() {
        if (mIsBound) {
            LocalService service = mBoundService.getService();
            if (service != null) Log.d("doUnbindService", "service: " + service);
            int statusCode = mBoundService.getStatusCode();
            if (statusCode != 0) Log.d("doUnbindService", "statusCode: " + statusCode);
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        doUnbindService();
    }

    private OnClickListener mBindListener = new OnClickListener() {
        public void onClick(View v) {
            doBindService();
            Intent rcvMsg = new Intent(Binding.this, RcvMessages.class);
            startActivity(rcvMsg);
        }
    };

    private OnClickListener mUnbindListener = new OnClickListener() {
        public void onClick(View v) {
            doUnbindService();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.local_service_binding);

        // Watch for button clicks.
        Button button = (Button)findViewById(R.id.bind);
        button.setOnClickListener(mBindListener);
        button = (Button)findViewById(R.id.unbind);
        button.setOnClickListener(mUnbindListener);
    }
}

/**************************************************************************************************
 * Filename: RcvMessages.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains stub code that displays a test message in an EditText.
 **************************************************************************************************/

package com.marie.localservicesample;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.text.InputType;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

/*
 * Class: RcvMessages
 * Purpose: RcvMessages is stub code that I want to extend in some way to receive messages from
 *     the background Service.
 */
public class RcvMessages extends Activity {
    //private LocalService mBoundService;
    private boolean mIsBound;
    private ILocalService mBoundService;

    //public static final String EXTRA_SONG = "com.marie.mainactivity.EXTRA_SONG";

    EditText myText;

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //mBoundService = ((LocalService.LocalBinder)service).getService();
            mBoundService = (ILocalService)service;

            ILocalService localSvc = (ILocalService)service;
            int statusCode = localSvc.getStatusCode();

            Log.d("RcvMessages.java","called onServiceConnected. statusCode: " + statusCode);

            //myText.setText("statusCode: " + statusCode);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }

    };

    void doBindService() {
        // Establish a connection with the service.  We use an explicit
        // class name because we want a specific service implementation that
        // we know will be running in our own process (and thus won't be
        // supporting component replacement by other applications).
        bindService(new Intent(RcvMessages.this, LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }

    void doUnbindService() {
        if (mIsBound) {
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        doUnbindService();
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.messages);

        myText = (EditText)findViewById(R.id.my_text);

        myText.setSingleLine();
        myText.setInputType(InputType.TYPE_NULL);

        // Display a simple test message for now.
        // myText.setText("RcvMessages here");

        Button button = (Button)findViewById(R.id.display);
        button.setOnClickListener(mDisplayListener);

        // I'm expecting this to call mConnection()'s onServiceConnected
        // bindService(new Intent(RcvMessages.this, LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
        // IBinder binder = mBoundService.onBind(new Intent(RcvMessages.this, LocalService.class));
        doBindService();
    }

    private OnClickListener mDisplayListener = new OnClickListener() {
        public void onClick(View v) {
            // Make sure the service is started.  It will continue running
            // until someone calls stopService().  The Intent we use to find
            // the service explicitly specifies our service component, because
            // we want it running in our own process and don't want other
            // applications to replace it.
            //startService(new Intent(Controller.this, LocalService.class));

            mBoundService.startRcvThread(rcvHandler);
        }
    };    

    /*
    @Override
    public void onStart(){
        super.onStart();
        Bundle extras = getIntent().getExtras();
        int song = (Integer) extras.get(EXTRA_SONG);
        //myText.setText("song " + song);

        // Start the background Service for sending canned messages to the handler as a test.
        Log.d("RcvMessages", "starting service from rcvmessages");
        Intent localSvc = new Intent(RcvMessages.this, LocalService.class);
        localSvc.putExtra(LocalService.EXTRA_MESSENGER, new Messenger(handler));
        localSvc.putExtra(EXTRA_SONG, song);
        startService(localSvc);
    }
    */

    /*
     * This is a handler to be passed to the local Service.
     */
    private Handler rcvHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            Log.i("rcvHandler", "rcvWhat: " + what);
            myText.setText("rcvWhat: " + what);
        }
    };

}

/**************************************************************************************************
 * Filename: ILocalService.java
 * Project name: Local Service Sample
 * Application name: Local Service
 * Description: This file contains an example interface for LocalService
 **************************************************************************************************/

package com.marie.localservicesample;

import android.os.Handler;

public interface ILocalService {

    public LocalService getService();

    public int getStatusCode();

    public void startRcvThread(Handler handler);
}

我修改了将LocalBinder移动到自己文件的解决方案,如下所示:

public class LocalBinder extends Binder implements ILocalService {
    @Override
    public int getStatusCode() {
        return LocalService.statusCode;
    }

    @Override
    public void startRcvThread(Handler handler) {
        Thread thread = passRcvHandlerToRcvThread(handler);
        thread.start();
    }

    public Thread passRcvHandlerToRcvThread(Handler handler) {
        Message msg = Message.obtain();
        msg.what = LocalService.emptyMsg;
        Thread thread = new RcvThread(handler, msg);
        return thread;
    }
}
正如您所看到的,我已经完全删除了getService方法,因为我不需要它。要使用上面LocalBinder中定义的方法,活动必须以类似以下方式定义onServiceConnected及其未实现的方法onServiceConnected和onServiceDisconnected:

private ILocalService mBoundService;
private boolean mIsBound;

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        // This is called when the connection with the service has been
        // established, giving us the service object we can use to
        // interact with the service.  Because we have bound to a explicit
        // service that we know is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        mBoundService = (ILocalService)service;

        int statusCode = mBoundService.getStatusCode();

        Log.d("Binding.java","called onServiceConnected. statusCode: " + statusCode);

        Toast.makeText(Binding.this, R.string.local_service_connected,
                Toast.LENGTH_SHORT).show();
    }

    public void onServiceDisconnected(ComponentName className) {
        // This is called when the connection with the service has been
        // unexpectedly disconnected -- that is, its process crashed.
        // Because it is running in our same process, we should never
        // see this happen.
        mBoundService = null;

        Log.d("Binding", "called onServiceDisconnected");

        Toast.makeText(Binding.this, R.string.local_service_disconnected,
                Toast.LENGTH_SHORT).show();
    }
};
使用上面声明和定义的mBoundService,我现在有了一个IBinder,可以用它调用上面LocalBinder中定义的方法

和以前一样,有一些事情需要注意,我不确定,但似乎有效:

在LocalBinder中,getStatusCode返回一个静态 LocalService.statusCode

在LocalBinder中,startRcvThread调用 pasRcvHandlerToRcvHandler哪个 需要一个静电传感器 LocalService.emptyMsg


我一直担心使用静力学,但logcat似乎没有抱怨任何泄漏。我知道这并不意味着没有。所以我希望有人会觉得这很有用。

失败是什么意思?您尝试了什么方法?@Egor,LocalBinder包含3个方法,每个方法都包含外部引用。问题。首先,导入LocalService不会修复错误LocalService的作用域中不能访问LocalService类型的封闭实例。我们把这些未解析的外部引用称为C。当我试图解析它们时,会弹出更多的提示。@Hussain谢谢。现在我的问题更加清晰易懂了!我想我知道如何用星星做粗体字,但是你能告诉我如何突出显示LocalBinder和LocalService吗。有了这样一个漂亮的问题,也许我会获得更多的选票:@commonware,你的答案非常有价值,即使我现在还不能完全理解它。我试着从每个文件的摘要开始。请你再贴一次样品的链接好吗?看一看。。这就是格式化的方式► 在段落之间放置返回► 对于换行符,在末尾添加2个空格► _斜体或**粗体**► 将代码缩进4个空格► backtick逃逸了`像u so `► 通过在行的开头放置>进行报价策略上,切勿通过其构造函数创建服务。只需完全删除getService—您的服务的API就在您的活页夹上。在组件之间传递处理程序和线程会导致内存泄漏和线程安全问题,因此我会在您的活动和服务之间提供一个更干净的API。这几乎意味着你的整个绑定方法是有缺陷的,需要从头重写。从战略上讲,我建议你完全避免绑定模式。在经历了6-12个月的Android开发经验之后,再来看看吧。绑定很少需要,而且常常很危险。坚持命令模式,避免可变的静态数据成员,这样您就可以确信您的服务和它的客户端之间会有一个清晰的分离。@commonware ok delete getService。我该怎么办?我不明白你的活页夹上有你的服务API,但那只是我。你能解释一下吗。是的,稍后再讨论这个问题是个好主意,但我想把它放在一个适当的地方——如果可能的话:你能给我指一些关于命令模式的阅读材料吗?我会怎么做在Binder子类上实现服务的公共API。你能给我指一些关于命令模式的阅读材料吗服务的命令模式是通过startService发送命令,通过onStartCommand或OnHandleContent进行处理。后者是您将在IntentService上使用的。根据服务和命令的性质,客户机最终将通过stopService停止服务,或者服务将通过stopSelf停止自身(例如,非IntentService)。
private ILocalService mBoundService;
private boolean mIsBound;

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        // This is called when the connection with the service has been
        // established, giving us the service object we can use to
        // interact with the service.  Because we have bound to a explicit
        // service that we know is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        mBoundService = (ILocalService)service;

        int statusCode = mBoundService.getStatusCode();

        Log.d("Binding.java","called onServiceConnected. statusCode: " + statusCode);

        Toast.makeText(Binding.this, R.string.local_service_connected,
                Toast.LENGTH_SHORT).show();
    }

    public void onServiceDisconnected(ComponentName className) {
        // This is called when the connection with the service has been
        // unexpectedly disconnected -- that is, its process crashed.
        // Because it is running in our same process, we should never
        // see this happen.
        mBoundService = null;

        Log.d("Binding", "called onServiceDisconnected");

        Toast.makeText(Binding.this, R.string.local_service_disconnected,
                Toast.LENGTH_SHORT).show();
    }
};