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