ограниченный сервис, использующий Messenger для загрузки изображений

#android

#Android

Вопрос:

Я не могу загрузить изображение с помощью messenger в boundservice, хотя в манифесте Android служба отображается в отдельном процессе

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="edu.vuum.mocca"
    android:versionCode="1"
    android:versionName="1.0" >
<uses-permission android:name="android.permission.INTERNET"></uses-permission> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />
<supports-screens android:smallScreens="false" android:xlargeScreens="true" android:normalScreens="true" android:largeScreens="true"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" 
        android:uiOptions="splitActionBarWhenNarrow">
        <activity
            android:name="edu.vuum.mocca.DownloadActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       <service android:name="edu.vuum.mocca.DownloadService" android:process=":my_process"/>

    </application>

</manifest>
  

Обработчик Dowloadservice, вызывающий Dowloadmessageandresponse, выдает android.os.networkonmainthreadexception

Но обработчик служб выполняется в отдельном процессе, поэтому выполнение выше не должно выполняться?

Если я использую метод dowloadmessageandRespond в отдельном потоке, тогда программа работает нормально, что прокомментировано в методе dowloadmessageandRespond.

 package edu.vuum.mocca;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;



import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Base64;
import android.util.Log;

/**
 * @class DownloadService
 *
 * @brief DownloadService receives an Intent containing a URL (which
 *        is a type of URI) and a Messenger. It downloads the file at
 *        the URL, stores it on the file system, then returns the path
 *        name to the caller using the supplied Messenger.
 * 
 *        The DownloadService class implements the CommandProcessor
 *        pattern and The Messenger is used as part of the Active
 *        Object pattern.
 */
public class DownloadService extends Service 
{
    /**
     * Used for debugging.
     */
    private final String TAG = getClass().getName();
    private final static int LOG_OP = 1;
    /**
     * Looper associated with the HandlerThread.
     */
  //  private volatile Looper mServiceLooper;

    /**
     * Implementation a Messenger that encapsulates the RequestHandler
     * used to handle request Messages sent from the
     * UniqueIDGeneratorActivity.
     */
    private final static String MESSAGE_URL = "course.examples.Services.Logging.MESSAGE";
     final Messenger mMessengerImpl =
            new Messenger(new ServiceHandler());

    /**
     * Factory method to make the desired Intent.
     */
    public static Intent makeIntent(Context context

                                   ) {
        // Create the Intent that's associated to the DownloadService
        // class.
        Intent intent = new Intent(context,
                                   DownloadService.class);


        return intent;
    }

    /**
     * @class ServiceHandler
     *
     * @brief An inner class that inherits from Handler and uses its
     *        handleMessage() hook method to process Messages sent to
     *        it from onStartCommnand() that indicate which images to
     *        download.
     */
    private final class ServiceHandler extends Handler {


        /**
         * A factory method that creates a Message to return to the
         * DownloadActivity with the pathname of the downloaded image.
         */
        private Message makeReplyMessage(String pathname){
            Message message = Message.obtain();
            // Return the result to indicate whether the download
            // succeeded or failed.
            message.arg1 = pathname == null 
                ? Activity.RESULT_CANCELED 
                : Activity.RESULT_OK;

            Bundle bundle = new Bundle();

            // Pathname for the downloaded image.
            bundle.putString("PATHNAME", 
                             pathname);
            message.setData(bundle);
            return message;
        }

        /**
         * A factory method that creates a Message that contains
         * information on the image to download and how to stop the
         * Service.
         */
/*        private Message makeDownloadMessage(Intent intent,
                                            int startId){
            Message message = Message.obtain();
            // Include Intent amp; startId in Message to indicate which URI
            // to retrieve and which request is being stopped when
            // download completes.
            message.obj = intent;
            message.arg1 = startId;
            return message;
        }*/

        /**
         * Retrieves the designated image and reply to the
         * DownloadActivity via the Messenger .
         */
        private void downloadImageAndReply(Message message) {
            final Messenger messenger = message.replyTo;
            final String url=message.getData().getString(MESSAGE_URL);
             // Download the requested image.
            //final String pathname;
//          new Thread( new Runnable() {
//              public void run(){
                String pathname = downloadImage(DownloadService.this,url);
                Log.d(TAG, "pathname----- after downloadImage()" pathname);

                sendPath(messenger, pathname);
//              }
//          }).start();
        }

        /**
         * Send the pathname back to the DownloadActivity via the
         * messenger.
         */
        private void sendPath(Messenger messenger, 
                              String pathname) {
            // Call factory method to create Message.
            Message message = makeReplyMessage(pathname);

            try {
                if (messenger == null)
                    Log.d(TAG, "replyMessenger is null");
                else {
                    Log.d(TAG, "sending key"   pathname);
                // Send pathname to back to the DownloadActivity.
                    messenger.send(message);
                }
            } catch (RemoteException e) {
                Log.e(getClass().getName(),
                      "Exception while sending.",
                      e);
            }
        }

    /**
     * Create a file to store the result of a download.
     * 
     * @param context
     * @param url
     * @return
     * @throws IOException
     */
    private File getTemporaryFile(final Context context,
                                      final String url) throws IOException {
            return context.getFileStreamPath(Base64.encodeToString(url.getBytes(),
                                                                   Base64.NO_WRAP)
                                               System.currentTimeMillis());
    }

    /**
     * Copy the contents of an InputStream into an OutputStream.
     * 
     * @param in
     * @param out
     * @return
     * @throws IOException
     */
    private int copy(final InputStream in,
                         final OutputStream out) throws IOException {
            final int BUFFER_LENGTH = 1024;
            final byte[] buffer = new byte[BUFFER_LENGTH];
            int totalRead = 0;
            int read = 0;

            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
                totalRead  = read;          
            }

            return totalRead;
    }

    /**
     * Download the requested image and return the local file path.
     * 
     * @param context
     * @param url
     * @return
     */
    public String downloadImage(final Context context,
                                    final String url) {
            try {
                final File file = getTemporaryFile(context, url);
                Log.d(getClass().getName(), "    downloading to "   file);

                final InputStream in = (InputStream)
                    new URL(url).getContent();
                final OutputStream out =
                    new FileOutputStream(file);

                copy(in, out);
                in.close();
                out.close();
                return file.getAbsolutePath();
            } catch (Exception e) {
                Log.e(getClass().getName(),
                      "Exception while downloading. Returning null.");
                Log.e(getClass().getName(),
                      e.toString());
                e.printStackTrace();
                return null;
            }
    }

        /**
         * Hook method that retrieves an image from a remote server.
         */
        public void handleMessage( Message message) {
            // Download the designated image and reply to the
            // DownloadActivity via the Messenger sent with the
            // Intent.


            switch (message.what) {

            case LOG_OP:

                Log.i(TAG, message.getData().getString(MESSAGE_URL));

                            downloadImageAndReply(message);



                break;

            default:

                super.handleMessage(message);

            }



        }
    }

    /**
     * Hook method called when DownloadService is first launched by
     * the Android ActivityManager.
     */
 /*   public void onCreate() {
        super.onCreate();

        // Create and start a background HandlerThread since by
        // default a Service runs in the UI Thread, which we don't
        // want to block.
        HandlerThread thread =
            new HandlerThread("DownloadService");
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler.
        mServiceLooper = thread.getLooper();
        mServiceHandler =
            new ServiceHandler(mServiceLooper);
    }*/

    /**
     * Hook method called each time a Started Service is sent an
     * Intent via startService().
     */
/*    public int onStartCommand(Intent intent, 
                              int flags,
                              int startId) {
        // Create a Message that will be sent to ServiceHandler to
        // retrieve animagebased on the URI in the Intent.
        Message message =
            mServiceHandler.makeDownloadMessage(intent,
                                                startId);

        // Send the Message to ServiceHandler to retrieve an image
        // based on contents of the Intent.
        mServiceHandler.sendMessage(message);

        // Don't restart the DownloadService automatically if its
        // process is killed while it's running.
        return Service.START_NOT_STICKY;
    }*/



    @Override
    public IBinder onBind(Intent intent) {
        return mMessengerImpl.getBinder();
    }
}
  

downloadactivity метод downloadimage, вызываемый, когда пользователь нажимает кнопку Загрузить изображение, чтобы загрузить изображение с помощью службы загрузки

 package edu.vuum.mocca;

import java.lang.ref.WeakReference;



import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
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.view.Menu;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

/**
 * @class DownloadActivity
 * 
 * @brief A class that allows a user to download a bitmap image using
 *        a DownloadService.
 */
public class DownloadActivity extends Activity {
    /**
     * Used for debugging.
     */
    private final String TAG = getClass().getName();
    private final static int LOG_OP = 1;
    private final static String MESSAGE_URL = "course.examples.Services.Logging.MESSAGE";
    private boolean mIsBound;
    /**
     * User's selection of URL to download
     */
    private EditText mUrlEditText;

    /**
     * Image that's been downloaded
     */
    private ImageView mImageView;

    /**
     * Default URL.
     */
    private String mDefaultUrl = 
        "http://www.dre.vanderbilt.edu/~schmidt/ka.png";

    /**
     * Display progress of download
     */
    private ProgressDialog mProgressDialog;


    /**
     * Reference to the Messenger that's implemented in the
     * UniqueIDGeneratorService.
     */
    private Messenger mMessengerRef = null;

    /** 
     * This ServiceConnection is used to receive a Messenger proxy
     * after binding to the UniqueIDGeneratorService using bindService().
     */
    private ServiceConnection mConnection = new ServiceConnection() {
            /**
             * Called after the UniqueIDGeneratorService is connected to
             * convey the result returned from onBind().
             */
            public void onServiceConnected(ComponentName className,
                                           IBinder messenger) {
                //Log.d(TAG, "ComponentName:"   className);

                // Create a newq Messenger that encapsulates the
                // returned IBinder object and store it for later use
                // in mMessengerRef.
                mMessengerRef = new Messenger(messenger);
                mIsBound = true;
            }

            /**
             * Called if the Service crashes and is no longer
             * available.  The ServiceConnection will remain bound,
             * but the service will not respond to any requests.
             */
            public void onServiceDisconnected(ComponentName className) {
                mMessengerRef = null;


                mIsBound = false;
            }
    };


    /**
     * Method that initializes the Activity when it is first created.
     * 
     * @param savedInstanceState
     *            Activity's previously frozen state, if there was one.
     */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /**
         * Sets the content view specified in the main.xml file.
         */
        setContentView(R.layout.main);

        /**
         * Caches references to the EditText and ImageView objects in
         * data members to optimize subsequent access.
         */
        mUrlEditText = (EditText) findViewById(R.id.mUrlEditText);
        mImageView = (ImageView) findViewById(R.id.mImageView);
    }

    /**
     * Show a toast, notifying a user of an error when retrieving a
     * bitmap.
     */
    void showErrorToast(String errorString) {
        Toast.makeText(this,
                       errorString,
                       Toast.LENGTH_LONG).show();
    }

    /**
     * Display a downloaded bitmap image if it's non-null; otherwise,
     * it reports an error via a Toast.
     * 
     * @param image
     *            The bitmap image
     */
    void displayImage(Bitmap image)
    {   
        if (mImageView == null)
            showErrorToast("Problem with Application,"
                             " please contact the Developer.");
        else if (image != null)
            mImageView.setImageBitmap(image);
        else
            showErrorToast("image is corrupted,"
                             " please check the requested URL.");
    }

    /**
     * Called when a user clicks a button to reset an image to
     * default.
     * 
     * @param view
     *            The "Reset Image" button
     */
    public void resetImage(View view) {
        mImageView.setImageResource(R.drawable.default_image);
    }

    /**
     * Called when a user clicks the Download Image button to download
     * an image using the DownloadService
     * 
     * @param view
     *            The "Download Image" button
     */
    public void downloadImage(View view) {
        // Obtain the requested URL from the user input.
        if (mIsBound) {
        String url = getUrlString();

        Log.e(DownloadActivity.class.getSimpleName(),
              "Downloading "   url);

        hideKeyboard();

        // Inform the user that the download is starting.
        showDialog("downloading via startService()");
     // Create a request Message that indicates the Service should
        // send the reply back to ReplyHandler encapsulated by the
        // Messenger.
        Message msg = Message.obtain(null, LOG_OP);
        Bundle bundle = new Bundle();
        bundle.putString(MESSAGE_URL, url);
        msg.setData(bundle);

       // Message request = Message.obtain();
        msg.replyTo = new Messenger(new DownloadHandler(this));

        try {
            if (mMessengerRef != null) {
                Log.d(TAG, "sending message to service with url");
                // Send the request Message to the
                // UniqueIDGeneratorService.
                mMessengerRef.send(msg);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        // Create an Intent to download an image in the background via
        // a Service.  The downloaded image is later diplayed in the
        // UI Thread via the downloadHandler() method defined below.
     //  DownloadService.makeIntent(this, Uri.parse( getUrlString()));

        }else{
            Log.d(TAG, "service is not bounded restat again");
        }
    }
    /**
     * Hook method called by Android when this Activity becomes
     * visible.
     */
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart()");

        Log.d(TAG, "calling bindService()");
        if (mMessengerRef == null)
            // Bind to the UniqueIDGeneratorService associated with this
            // Intent.
            bindService(DownloadService.makeIntent(this),
                        this.mConnection,
                        Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onStop() {
         super.onStop();
         if (mIsBound)
            unbindService(mConnection);
    }

    /**
     * @class DownloadHandler
     *
     * @brief An inner class that inherits from Handler and uses its
     *        handleMessage() hook method to process Messages sent to
     *        it from the DownloadService.
     */
    private static class DownloadHandler extends Handler {
        /**
         * Allows Activity to be garbage collected properly.
         */
        private WeakReference<DownloadActivity> mActivity;

        /**
         * Class constructor constructs mActivity as weak reference
         * to the activity
         * 
         * @param activity
         *            The corresponding activity
         */
        public DownloadHandler(DownloadActivity activity) {
            mActivity = new WeakReference<DownloadActivity>(activity);
        }

        /**
        /**
         * This hook method is dispatched in response to receiving
         * the pathname back from the DownloadService.
         */
        public void handleMessage(Message msg) {
            DownloadActivity activity = mActivity.get();
            // Bail out of the DownloadActivity is gone.
            if (activity == null)
                return;

            // Extract the data from Message, which is in the form
            // of a Bundle that can be passed across processes.
            Bundle data = msg.getData();

            // Extract the pathname from the Bundle.
            String pathname = data.getString("PATHNAME");

            // See if things worked or not.
            if (msg.arg1 != RESULT_OK || pathname == null)
                activity.showDialog("failed download");

            // Stop displaying the progress dialog.
            activity.dismissDialog();

            // Display the image in the UI Thread.
            activity.displayImage(BitmapFactory.decodeFile(pathname));
        }
    };



    /**
     * Display the Dialog to the User.
     * 
     * @param message 
     *          The String to display what download method was used.
     */
    public void showDialog(String message) {
        mProgressDialog =
            ProgressDialog.show(this,
                                "Download",
                                message,
                                true);
    }

    /**
     * Dismiss the Dialog
     */
    public void dismissDialog() {
        if (mProgressDialog != null)
            mProgressDialog.dismiss();
    }

    /**
     * Hide the keyboard after a user has finished typing the url.
     */
    private void hideKeyboard() {
        InputMethodManager mgr =
            (InputMethodManager) getSystemService
            (Context.INPUT_METHOD_SERVICE);
        mgr.hideSoftInputFromWindow(mUrlEditText.getWindowToken(),
                                    0);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.download, menu);
        return true;
    }




    /**
     * Read the URL EditText and return the String it contains.
     * 
     * @return String value in mUrlEditText
     */
    String getUrlString() {
        String s = mUrlEditText.getText().toString();
        if (s.equals(""))
            s = mDefaultUrl;
        return s;
    }

}
  

Связанный сервис с Messenger, если он запущен в отдельном процессе, не нуждается в отдельном потоке для выполнения длительной задачи.

Мне нужно знать, почему этот сервис downloadimage не работает, хотя он запущен в отдельном процессе.

Спасибо за помощь.

Комментарии:

1. Не могли бы вы, пожалуйста, сузить вопрос до вашей проблемы, если это возможно, это кажется слишком длинным.

2. Я хотел бы использовать boundservice meseenger для длительного процесса загрузки изображения. Мой вопрос, почему мой сервис onhandlemessage не загружает изображение, хотя служба, запущенная в отдельном процессе, выдает ошибку android.os.networkonmainthreadexception

Ответ №1:

Поскольку есть NetworkOnMainThreadException решение, заключается в том, чтобы поместить сетевой код в AsyncTask или thread .

Комментарии:

1. что вы подразумеваете под сетевым кодом, пожалуйста, объясните. Или есть только возможность выполнять длительную задачу в качестве backgroundthread

2. Сетевой код — это весь код, который выполняется, когда ваше приложение подключается к серверу в Интернете или вашей локальной сети и использует потоки для отправки или получения данных.