Исправлено: Хотелось бы переместить класс LocalBinder (расширяет Binder реализует ILocalService) изнутри LocalService (расширяет Service) в LocalBinder.java - PullRequest
0 голосов
/ 22 июня 2011

My LocalService.java содержит класс LocalBinder, который определяет методы, объявленные в ILocalService.java , чьи определения загромождают мои LocalService.java ,Я хотел бы переместить его в свой собственный файл LocalBinder.java .Вот мой LocalBinder:

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();
    }
}

Я пробовал несколько раз и потерпел неудачу.Не лучше ли мне оставить LocalBinder в LocalService?

Ниже приведены все пять файлов исходного кода, помеченных комментариями заголовков.Пожалуйста, посмотрите на заголовок моего вопроса.Это объясняет, что я хочу сделать.Я хочу снять загромождение моего LocalService.java, удалив «public class LocalBinder» из LocalService.java и поместив его в свой собственный файл: LocalBinder.java.Любая помощь очень ценится.@Commonsware уже оказал значительную помощь, но мне немного сложно понять.

/**************************************************************************************************
 * 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);
}

1 Ответ

0 голосов
/ 23 июня 2011

Я пересмотрел свое решение для перемещения моего 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, похоже, не жалуется на утечки. Я знаю, это не значит, что их нет. Поэтому я надеюсь, что кто-то найдет это полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...